Formatting: support syntax of sub string

Fix #1256; Ref #1255
This commit is contained in:
李通洲 2024-09-11 11:07:00 +08:00
parent 68ab41f748
commit dc541ac781
No known key found for this signature in database
GPG Key ID: 269AD4F5325A22A3
3 changed files with 122 additions and 52 deletions

View File

@ -280,34 +280,18 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
continue;
}
int32_t truncLength = 0;
char align = '\0';
char* pSep = memchr(placeholderValue.chars, ':', placeholderValue.length);
if (pSep)
align = ':';
char* pSep = placeholderValue.chars;
char cSep = '\0';
while (*pSep && *pSep != ':' && *pSep != '<' && *pSep != '>' && *pSep != '~')
++pSep;
if (*pSep)
{
cSep = *pSep;
*pSep = '\0';
}
else
{
pSep = memchr(placeholderValue.chars, '<', placeholderValue.length);
if (pSep)
align = '<';
else
{
pSep = memchr(placeholderValue.chars, '>', placeholderValue.length);
if (pSep)
align = '>';
}
}
if (pSep)
{
char* pEnd = NULL;
truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
if (*pEnd != '\0')
{
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
continue;
}
*pSep = '\0';
pSep = NULL;
}
uint32_t index = getArgumentIndex(placeholderValue.chars, numArgs, arguments);
@ -318,53 +302,97 @@ void ffParseFormatString(FFstrbuf* buffer, const FFstrbuf* formatstr, uint32_t n
if (index > numArgs)
{
if (pSep) *pSep = align;
if (pSep) *pSep = cSep;
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
continue;
}
bool ellipsis = false;
if (truncLength < 0)
{
ellipsis = true;
truncLength = -truncLength;
}
if (!align)
if (!cSep)
ffFormatAppendFormatArg(buffer, &arguments[index - 1]);
else if (cSep == '~')
{
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
char* pEnd = NULL;
int32_t start = (int32_t) strtol(pSep + 1, &pEnd, 10);
if (start < 0)
start = (int32_t) tempString.length + start;
if (start >= 0 && (uint32_t) start < tempString.length)
{
if (*pEnd == '\0')
ffStrbufAppendNS(buffer, tempString.length - (uint32_t) start, &tempString.chars[start]);
else if (*pEnd == ',')
{
int32_t end = (int32_t) strtol(pEnd + 1, &pEnd, 10);
if (!*pEnd)
{
if (end < 0)
end = (int32_t) tempString.length + end;
if ((uint32_t) end > tempString.length)
end = (int32_t) tempString.length;
if (end > start)
ffStrbufAppendNS(buffer, (uint32_t) (end - start), &tempString.chars[start]);
}
}
}
if (*pEnd)
{
*pSep = cSep;
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
continue;
}
}
else
{
ffStrbufClear(&placeholderValue);
ffFormatAppendFormatArg(&placeholderValue, &arguments[index - 1]);
if (placeholderValue.length == (uint32_t) truncLength)
ffStrbufAppend(buffer, &placeholderValue);
else if (placeholderValue.length > (uint32_t) truncLength)
char* pEnd = NULL;
int32_t truncLength = (int32_t) strtol(pSep + 1, &pEnd, 10);
if (*pEnd != '\0')
{
if (align == ':')
*pSep = cSep;
appendInvalidPlaceholder(buffer, "{", &placeholderValue, i, formatstr->length);
continue;
}
bool ellipsis = false;
if (truncLength < 0)
{
ellipsis = true;
truncLength = -truncLength;
}
FF_STRBUF_AUTO_DESTROY tempString = ffStrbufCreate();
ffFormatAppendFormatArg(&tempString, &arguments[index - 1]);
if (tempString.length == (uint32_t) truncLength)
ffStrbufAppend(buffer, &tempString);
else if (tempString.length > (uint32_t) truncLength)
{
if (cSep == ':')
{
ffStrbufSubstrBefore(&placeholderValue, (uint32_t) truncLength);
ffStrbufTrimRightSpace(&placeholderValue);
ffStrbufSubstrBefore(&tempString, (uint32_t) truncLength);
ffStrbufTrimRightSpace(&tempString);
}
else
ffStrbufSubstrBefore(&placeholderValue, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
ffStrbufAppend(buffer, &placeholderValue);
ffStrbufSubstrBefore(&tempString, (uint32_t) (!ellipsis? truncLength : truncLength - 1));
ffStrbufAppend(buffer, &tempString);
if (ellipsis)
ffStrbufAppendS(buffer, "");
}
else if (align == ':')
ffStrbufAppend(buffer, &placeholderValue);
else if (cSep == ':')
ffStrbufAppend(buffer, &tempString);
else
{
if (align == '<')
if (cSep == '<')
{
ffStrbufAppend(buffer, &placeholderValue);
ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' ');
ffStrbufAppend(buffer, &tempString);
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
}
else
{
ffStrbufAppendNC(buffer, (uint32_t) truncLength - placeholderValue.length, ' ');
ffStrbufAppend(buffer, &placeholderValue);
ffStrbufAppendNC(buffer, (uint32_t) truncLength - tempString.length, ' ');
ffStrbufAppend(buffer, &tempString);
}
}
}

View File

@ -17,6 +17,10 @@ In 2.23.0 or newer, `<` or `>` can be specified instead of `:` to set a left or
For example: "--title-format '{user-name<20}'" will generate `<user-name> `;
"--title-format '{user-name>20}'" will generate ` <user-name>`
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.
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.
If `,endIndex` is omitted or greater than the length of the string, the length of string is used.
If the value index is missing, meaning the placeholder is "{}", an internal counter sets the value index.
This means that the format string "Values: {1} ({2})" is equivalent to "Values: {} ({})".
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.

View File

@ -69,10 +69,48 @@ int main(void)
VERIFY("output({1n>20})", "12345 67890", "output({1n>20})");
VERIFY("output({120})", "12345 67890", "output({120})");
VERIFY("output({1:11})", "", "output()");
VERIFY("output({2:2})", "", "output({2:2})");
}
{
VERIFY("output({1~0})", "12345 67890", "output(12345 67890)");
VERIFY("output({1~1})", "12345 67890", "output(2345 67890)");
VERIFY("output({1~10})", "12345 67890", "output(0)");
VERIFY("output({1~20})", "12345 67890", "output()");
VERIFY("output({1~-5})", "12345 67890", "output(67890)");
VERIFY("output({1~-50})", "12345 67890", "output()");
VERIFY("output({1~0,1})", "12345 67890", "output(1)");
VERIFY("output({1~0,6})", "12345 67890", "output(12345 )");
VERIFY("output({1~0,10})", "12345 67890", "output(12345 6789)");
VERIFY("output({1~5,10})", "12345 67890", "output( 6789)");
VERIFY("output({1~5,100})", "12345 67890", "output( 67890)");
VERIFY("output({1~10,10})", "12345 67890", "output()");
VERIFY("output({1~10,5})", "12345 67890", "output()");
VERIFY("output({1~3,-3})", "12345 67890", "output(45 67)");
VERIFY("output({1~-5,-3})", "12345 67890", "output(67)");
VERIFY("output({1~-0,10})", "12345 67890", "output(12345 6789)");
VERIFY("output({1~-3,-5})", "12345 67890", "output()");
VERIFY("output({1~})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
VERIFY("output({1~-0})", "12345 67890", "output(12345 67890)"); // Same as {1~0}
VERIFY("output({1~,-1})", "12345 67890", "output(12345 6789)"); // Same as {1~0,-1}
VERIFY("output({1~,})", "12345 67890", "output()"); // Same as {1~0,0}
}
{
VERIFY("output({1n~0})", "12345 67890", "output({1n~0})");
VERIFY("output({1~<0})", "12345 67890", "output({1~<0})");
VERIFY("output({1~-})", "12345 67890", "output({1~-})");
VERIFY("output({1~-,1})", "12345 67890", "output({1~-,1})");
VERIFY("output({1~0,,1})", "12345 67890", "output({1~0,,1})");
VERIFY("output({1~0,1,})", "12345 67890", "output({1~0,1,})");
VERIFY("output({1~,0,1})", "12345 67890", "output({1~,0,1})");
VERIFY("output({2,0})", "12345 67890", "output({2,0})");
}
{
VERIFY("output({1:20}{1<20}{1>20})", "12345 67890", "output(12345 6789012345 67890 12345 67890)");
VERIFY("output({?1}OK{?}{/1}NOT OK{/})", "12345 67890", "output(OK)");
VERIFY("output({?1}OK{?}{/1}NOT OK{/})", "", "output(NOT OK)");
}
//Success