Skip to content

Commit dc541ac

Browse files
committed
Formatting: support syntax of sub string
Fix fastfetch-cli#1256; Ref fastfetch-cli#1255
1 parent 68ab41f commit dc541ac

File tree

3 files changed

+120
-50
lines changed

3 files changed

+120
-50
lines changed

src/common/format.c

Lines changed: 78 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -280,34 +280,18 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
280280
continue;
281281
}
282282

283-
int32_t truncLength = 0;
284-
char align = '\0';
285-
char* pSep = memchr(placeholderValue.chars, ':', placeholderValue.length);
286-
if (pSep)
287-
align = ':';
288-
else
283+
char* pSep = placeholderValue.chars;
284+
char cSep = '\0';
285+
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '~')
286+
++pSep;
287+
if (*pSep)
289288
{
290-
pSep = memchr(placeholderValue.chars, '<', placeholderValue.length);
291-
if (pSep)
292-
align = '<';
293-
else
294-
{
295-
pSep = memchr(placeholderValue.chars, '>', placeholderValue.length);
296-
if (pSep)
297-
align = '>';
298-
}
289+
cSep = *pSep;
290+
*pSep = '\0';
299291
}
300-
301-
if (pSep)
292+
else
302293
{
303-
char* pEnd = NULL;
304-
truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
305-
if (*pEnd != '\0')
306-
{
307-
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
308-
continue;
309-
}
310-
*pSep = '\0';
294+
pSep = NULL;
311295
}
312296

313297
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
@@ -318,53 +302,97 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
318302

319303
if (index > numArgs)
320304
{
321-
if (pSep) *pSep = align;
305+
if (pSep) *pSep = cSep;
322306
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
323307
continue;
324308
}
325309

326-
bool ellipsis = false;
327-
if (truncLength < 0)
310+
if (!cSep)
311+
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
312+
else if (cSep == '~')
328313
{
329-
ellipsis = true;
330-
truncLength = -truncLength;
331-
}
314+
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
315+
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
332316

333-
if (!align)
334-
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
317+
char* pEnd = NULL;
318+
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
319+
if (start < 0)
320+
start = (int32_t) tempString.length + start;
321+
if (start >= 0 && (uint32_t) start < tempString.length)
322+
{
323+
if (*pEnd == '\0')
324+
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
325+
else if (*pEnd == ',')
326+
{
327+
int32_t end = (int32_t) strtol(pEnd + 1, &pEnd, 10);
328+
if (!*pEnd)
329+
{
330+
if (end < 0)
331+
end = (int32_t) tempString.length + end;
332+
if ((uint32_t) end > tempString.length)
333+
end = (int32_t) tempString.length;
334+
if (end > start)
335+
ffStrbufAppendNS(buffer, (uint32_t) (end - start), &tempString.chars[start]);
336+
}
337+
}
338+
}
339+
340+
if (*pEnd)
341+
{
342+
*pSep = cSep;
343+
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
344+
continue;
345+
}
346+
}
335347
else
336348
{
337-
ffStrbufClear(&placeholderValue);
338-
ffFormatAppendFormatArg(&placeholderValue, &arguments[index - 1]);
339-
if (placeholderValue.length == (uint32_t) truncLength)
340-
ffStrbufAppend(buffer, &placeholderValue);
341-
else if (placeholderValue.length > (uint32_t) truncLength)
349+
char* pEnd = NULL;
350+
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
351+
if (*pEnd != '\0')
352+
{
353+
*pSep = cSep;
354+
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
355+
continue;
356+
}
357+
358+
bool ellipsis = false;
359+
if (truncLength < 0)
360+
{
361+
ellipsis = true;
362+
truncLength = -truncLength;
363+
}
364+
365+
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
366+
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
367+
if (tempString.length == (uint32_t) truncLength)
368+
ffStrbufAppend(buffer, &tempString);
369+
else if (tempString.length > (uint32_t) truncLength)
342370
{
343-
if (align == ':')
371+
if (cSep == ':')
344372
{
345-
ffStrbufSubstrBefore(&placeholderValue, (uint32_t) truncLength);
346-
ffStrbufTrimRightSpace(&placeholderValue);
373+
ffStrbufSubstrBefore(&tempString, (uint32_t) truncLength);
374+
ffStrbufTrimRightSpace(&tempString);
347375
}
348376
else
349-
ffStrbufSubstrBefore(&placeholderValue, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
350-
ffStrbufAppend(buffer, &placeholderValue);
377+
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
378+
ffStrbufAppend(buffer, &tempString);
351379

352380
if (ellipsis)
353381
ffStrbufAppendS(buffer, "…");
354382
}
355-
else if (align == ':')
356-
ffStrbufAppend(buffer, &placeholderValue);
383+
else if (cSep == ':')
384+
ffStrbufAppend(buffer, &tempString);
357385
else
358386
{
359-
if (align == '<')
387+
if (cSep == '<')
360388
{
361-
ffStrbufAppend(buffer, &placeholderValue);
362-
ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' ');
389+
ffStrbufAppend(buffer, &tempString);
390+
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
363391
}
364392
else
365393
{
366-
ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' ');
367-
ffStrbufAppend(buffer, &placeholderValue);
394+
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
395+
ffStrbufAppend(buffer, &tempString);
368396
}
369397
}
370398
}

src/data/help_format.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ In 2.23.0 or newer, `<` or `>` can be specified instead of `:` to set a left or
1717
For example: "--title-format '{user-name<20}'" will generate `<user-name> `;
1818
"--title-format '{user-name>20}'" will generate ` <user-name>`
1919

20+
In 2.24.0 or newer, `{~startIndex,endIndex}` can be specified to slice a string. Negative index counts back from the end of the string.
21+
If an index is omitted, 0 is used. For example, both `{~,0}` `{~0,}` and `{~,}` are same as `{~0,0}` and will always generate a empty string.
22+
If `,endIndex` is omitted or greater than the length of the string, the length of string is used.
23+
2024
If the value index is missing, meaning the placeholder is "{}", an internal counter sets the value index.
2125
This means that the format string "Values: {1} ({2})" is equivalent to "Values: {} ({})".
2226
Note that this counter only counts empty placeholders, so the format string "{2} {} {}" will contain the second value, then the first, and then the second again.

tests/format.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,48 @@ int main(void)
6969
VERIFY("output({1n>20})", "12345 67890", "output({1n>20})");
7070
VERIFY("output({120})", "12345 67890", "output({120})");
7171
VERIFY("output({1:11})", "", "output()");
72+
VERIFY("output({2:2})", "", "output({2:2})");
73+
}
74+
75+
{
76+
VERIFY("output({1~0})", "12345 67890", "output(12345 67890)");
77+
VERIFY("output({1~1})", "12345 67890", "output(2345 67890)");
78+
VERIFY("output({1~10})", "12345 67890", "output(0)");
79+
VERIFY("output({1~20})", "12345 67890", "output()");
80+
VERIFY("output({1~-5})", "12345 67890", "output(67890)");
81+
VERIFY("output({1~-50})", "12345 67890", "output()");
82+
VERIFY("output({1~0,1})", "12345 67890", "output(1)");
83+
VERIFY("output({1~0,6})", "12345 67890", "output(12345 )");
84+
VERIFY("output({1~0,10})", "12345 67890", "output(12345 6789)");
85+
VERIFY("output({1~5,10})", "12345 67890", "output( 6789)");
86+
VERIFY("output({1~5,100})", "12345 67890", "output( 67890)");
87+
VERIFY("output({1~10,10})", "12345 67890", "output()");
88+
VERIFY("output({1~10,5})", "12345 67890", "output()");
89+
VERIFY("output({1~3,-3})", "12345 67890", "output(45 67)");
90+
VERIFY("output({1~-5,-3})", "12345 67890", "output(67)");
91+
VERIFY("output({1~-0,10})", "12345 67890", "output(12345 6789)");
92+
VERIFY("output({1~-3,-5})", "12345 67890", "output()");
93+
VERIFY("output({1~})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
94+
VERIFY("output({1~-0})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
95+
VERIFY("output({1~,-1})", "12345 67890", "output(12345 6789)"); // Same as {1~0,-1}
96+
VERIFY("output({1~,})", "12345 67890", "output()"); // Same as {1~0,0}
97+
}
98+
99+
{
100+
VERIFY("output({1n~0})", "12345 67890", "output({1n~0})");
101+
VERIFY("output({1~<0})", "12345 67890", "output({1~<0})");
102+
VERIFY("output({1~-})", "12345 67890", "output({1~-})");
103+
VERIFY("output({1~-,1})", "12345 67890", "output({1~-,1})");
104+
VERIFY("output({1~0,,1})", "12345 67890", "output({1~0,,1})");
105+
VERIFY("output({1~0,1,})", "12345 67890", "output({1~0,1,})");
106+
VERIFY("output({1~,0,1})", "12345 67890", "output({1~,0,1})");
107+
VERIFY("output({2,0})", "12345 67890", "output({2,0})");
72108
}
73109

74110
{
75111
VERIFY("output({1:20}{1<20}{1>20})", "12345 67890", "output(12345 6789012345 67890 12345 67890)");
112+
VERIFY("output({?1}OK{?}{/1}NOT OK{/})", "12345 67890", "output(OK)");
113+
VERIFY("output({?1}OK{?}{/1}NOT OK{/})", "", "output(NOT OK)");
76114
}
77115

78116
//Success

0 commit comments

Comments
 (0)