Skip to content

Commit 85d8939

Browse files
committed
Format: support syntax of padded string
Ref: #1237
1 parent 093940e commit 85d8939

File tree

4 files changed

+150
-13
lines changed

4 files changed

+150
-13
lines changed

CMakeLists.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,9 +1342,17 @@ if (BUILD_TESTS)
13421342
PRIVATE libfastfetch
13431343
)
13441344

1345+
add_executable(fastfetch-test-format
1346+
tests/format.c
1347+
)
1348+
target_link_libraries(fastfetch-test-format
1349+
PRIVATE libfastfetch
1350+
)
1351+
13451352
enable_testing()
13461353
add_test(NAME test-strbuf COMMAND fastfetch-test-strbuf)
13471354
add_test(NAME test-list COMMAND fastfetch-test-list)
1355+
add_test(NAME test-format COMMAND fastfetch-test-format)
13481356
endif()
13491357

13501358
##################

src/common/format.c

Lines changed: 58 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -265,18 +265,34 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
265265
continue;
266266
}
267267

268-
int32_t truncLength = INT32_MAX;
269-
char* pColon = memchr(placeholderValue.chars, ':', placeholderValue.length);
270-
if (pColon != NULL)
268+
int32_t truncLength = 0;
269+
char align = '\0';
270+
char* pSep = memchr(placeholderValue.chars, ':', placeholderValue.length);
271+
if (pSep)
272+
align = ':';
273+
else
274+
{
275+
pSep = memchr(placeholderValue.chars, '<', placeholderValue.length);
276+
if (pSep)
277+
align = '<';
278+
else
279+
{
280+
pSep = memchr(placeholderValue.chars, '>', placeholderValue.length);
281+
if (pSep)
282+
align = '>';
283+
}
284+
}
285+
286+
if (pSep)
271287
{
272288
char* pEnd = NULL;
273-
truncLength = (int32_t) strtol(pColon + 1, &pEnd, 10);
289+
truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
274290
if (*pEnd != '\0')
275291
{
276292
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
277293
continue;
278294
}
279-
*pColon = '\0';
295+
*pSep = '\0';
280296
}
281297

282298
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
@@ -287,7 +303,7 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
287303

288304
if (index > numArgs)
289305
{
290-
if (pColon) *pColon = ':';
306+
if (pSep) *pSep = align;
291307
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
292308
continue;
293309
}
@@ -299,14 +315,43 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
299315
truncLength = -truncLength;
300316
}
301317

302-
uint32_t oldLength = buffer->length;
303-
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
304-
if (buffer->length - oldLength > (uint32_t) truncLength)
318+
if (!align)
319+
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
320+
else
305321
{
306-
ffStrbufSubstrBefore(buffer, oldLength + (uint32_t) truncLength);
307-
ffStrbufTrimRightSpace(buffer);
308-
if (ellipsis)
309-
ffStrbufAppendS(buffer, "…");
322+
ffStrbufClear(&placeholderValue);
323+
ffFormatAppendFormatArg(&placeholderValue, &arguments[index - 1]);
324+
if (placeholderValue.length == (uint32_t) truncLength)
325+
ffStrbufAppend(buffer, &placeholderValue);
326+
else if (placeholderValue.length > (uint32_t) truncLength)
327+
{
328+
if (align == ':')
329+
{
330+
ffStrbufSubstrBefore(&placeholderValue, (uint32_t) truncLength);
331+
ffStrbufTrimRightSpace(&placeholderValue);
332+
}
333+
else
334+
ffStrbufSubstrBefore(&placeholderValue, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
335+
ffStrbufAppend(buffer, &placeholderValue);
336+
337+
if (ellipsis)
338+
ffStrbufAppendS(buffer, "…");
339+
}
340+
else if (align == ':')
341+
ffStrbufAppend(buffer, &placeholderValue);
342+
else
343+
{
344+
if (align == '<')
345+
{
346+
ffStrbufAppend(buffer, &placeholderValue);
347+
ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' ');
348+
}
349+
else
350+
{
351+
ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' ');
352+
ffStrbufAppend(buffer, &placeholderValue);
353+
}
354+
}
310355
}
311356
}
312357

src/data/help_format.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ For example: "--title-format '{user-name:5}'" will truncate user name into 5-len
1313
If '<trunc-length>' is negative, an ellipsis … will be appended at the end when the original string is truncated.
1414
Note: The string length is counted in raw bytes. Multi-byte unicode characters and ANSI escape codes are not taken into account.
1515

16+
In 2.23.0 or newer, `<` or `>` can be specified instead of `:` to set a left or right padding.
17+
For example: "--title-format '{user-name<20}'" will generate `<user-name> `;
18+
"--title-format '{user-name>20}'" will generate ` <user-name>`
19+
1620
If the value index is missing, meaning the placeholder is "{}", an internal counter sets the value index.
1721
This means that the format string "Values: {1} ({2})" is equivalent to "Values: {} ({})".
1822
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: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#include "common/format.h"
2+
#include "util/textModifier.h"
3+
#include "fastfetch.h"
4+
5+
static void verify(const char* format, const char* arg, const char* expected, int lineNo)
6+
{
7+
FF_STRBUF_AUTO_DESTROY result = ffStrbufCreate();
8+
FF_STRBUF_AUTO_DESTROY formatter = ffStrbufCreateStatic(format);
9+
const FFformatarg arguments[] = {
10+
{ .type = FF_FORMAT_ARG_TYPE_STRING, arg }
11+
};
12+
ffParseFormatString(&result, &formatter, 1, arguments);
13+
if (!ffStrbufEqualS(&result, expected))
14+
{
15+
fprintf(stderr, FASTFETCH_TEXT_MODIFIER_ERROR "[%d] %s: expected \"%s\", got \"%s\"\n" FASTFETCH_TEXT_MODIFIER_RESET, lineNo, format, expected, result.chars);
16+
exit(1);
17+
}
18+
}
19+
20+
#define VERIFY(format, argument, expected) verify((format), (argument), (expected), __LINE__)
21+
22+
int main(void)
23+
{
24+
instance.config.display.pipe = true;
25+
26+
{
27+
VERIFY("output({})", "12345 67890", "output(12345 67890)");
28+
VERIFY("output({1})", "12345 67890", "output(12345 67890)");
29+
VERIFY("output({})", "", "output()");
30+
VERIFY("output({1})", "", "output()");
31+
}
32+
33+
{
34+
VERIFY("output({1:20})", "12345 67890", "output(12345 67890)");
35+
VERIFY("output({1:11})", "12345 67890", "output(12345 67890)");
36+
VERIFY("output({1:-11})", "12345 67890", "output(12345 67890)");
37+
VERIFY("output({1:6})", "12345 67890", "output(12345)");
38+
VERIFY("output({:6})", "12345 67890", "output(12345)");
39+
VERIFY("output({:-6})", "12345 67890", "output(12345…)");
40+
VERIFY("output({:0})", "12345 67890", "output()");
41+
VERIFY("output({:})", "12345 67890", "output()");
42+
}
43+
44+
{
45+
VERIFY("output({1<20})", "12345 67890", "output(12345 67890 )");
46+
VERIFY("output({1<-20})", "12345 67890", "output(12345 67890 )");
47+
VERIFY("output({1<11})", "12345 67890", "output(12345 67890)");
48+
VERIFY("output({1<-11})", "12345 67890", "output(12345 67890)");
49+
VERIFY("output({1<6})", "12345 67890", "output(12345 )");
50+
VERIFY("output({<6})", "12345 67890", "output(12345 )");
51+
VERIFY("output({<-6})", "12345 67890", "output(12345…)");
52+
VERIFY("output({<0})", "12345 67890", "output()");
53+
VERIFY("output({<})", "12345 67890", "output()");
54+
}
55+
56+
{
57+
VERIFY("output({1>20})", "12345 67890", "output( 12345 67890)");
58+
VERIFY("output({1>-20})", "12345 67890", "output( 12345 67890)");
59+
VERIFY("output({1>11})", "12345 67890", "output(12345 67890)");
60+
VERIFY("output({1>-11})", "12345 67890", "output(12345 67890)");
61+
VERIFY("output({1>6})", "12345 67890", "output(12345 )");
62+
VERIFY("output({>6})", "12345 67890", "output(12345 )");
63+
VERIFY("output({>-6})", "12345 67890", "output(12345…)");
64+
VERIFY("output({>0})", "12345 67890", "output()");
65+
VERIFY("output({>})", "12345 67890", "output()");
66+
}
67+
68+
{
69+
VERIFY("output({1n>20})", "12345 67890", "output({1n>20})");
70+
VERIFY("output({120})", "12345 67890", "output({120})");
71+
VERIFY("output({1:11})", "", "output()");
72+
}
73+
74+
{
75+
VERIFY("output({1:20}{1<20}{1>20})", "12345 67890", "output(12345 6789012345 67890 12345 67890)");
76+
}
77+
78+
//Success
79+
puts("\033[32mAll tests passed!" FASTFETCH_TEXT_MODIFIER_RESET);
80+
}

0 commit comments

Comments
 (0)