Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow tab usage in indented strings #2911

Draft
wants to merge 3 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion doc/manual/expressions/language-values.xml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ configureFlags = "
This kind of string literal intelligently strips indentation from
the start of each line. To be precise, it strips from each line a
number of spaces equal to the minimal indentation of the string as
a whole (disregarding the indentation of empty lines). For
a whole (disregarding the indentation of empty lines). Tabs can
also be used, so long as spaces and tabs are not mixed. For
instance, the first and second line are indented two space, while
the third line is indented four spaces. Thus, two spaces are
stripped from each line, so the resulting string is
Expand Down
9 changes: 6 additions & 3 deletions src/libexpr/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
bool atStartOfLine = true; /* = seen only whitespace in the current line */
size_t minIndent = 1000000;
size_t curIndent = 0;
char indChar = 0;
for (auto & i : es) {
ExprIndStr * e = dynamic_cast<ExprIndStr *>(i);
if (!e) {
Expand All @@ -168,7 +169,9 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
}
for (size_t j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (e->s[j] == ' ')
if (indChar == 0 && (e->s[j] == ' ' || e->s[j] == '\t'))
indChar = e->s[j];
if (e->s[j] == indChar)
curIndent++;
else if (e->s[j] == '\n') {
/* Empty line, doesn't influence minimum
Expand Down Expand Up @@ -202,7 +205,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
string s2;
for (size_t j = 0; j < e->s.size(); ++j) {
if (atStartOfLine) {
if (e->s[j] == ' ') {
if (e->s[j] == indChar) {
if (curDropped++ >= minIndent)
s2 += e->s[j];
}
Expand All @@ -224,7 +227,7 @@ static Expr * stripIndentation(const Pos & pos, SymbolTable & symbols, vector<Ex
spaces. */
if (n == 1) {
string::size_type p = s2.find_last_of('\n');
if (p != string::npos && s2.find_first_not_of(' ', p + 1) == string::npos)
if (p != string::npos && s2.find_first_not_of(" \t", p + 1) == string::npos)
s2 = string(s2, 0, p + 1);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/lang/eval-okay-ind-string.exp
Original file line number Diff line number Diff line change
@@ -1 +1 @@
"This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n Tabs are not interpreted as whitespace (since we can't guess\n what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\ncut -d $'\\t' -f 1\nending dollar $$\n"
"This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n Tabs are only interpreted as whitespace when they are\n the only indentation used (since we can't guess\n what tab settings are intended), so don't mix them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\ncut -d $'\\t' -f 1\nending dollar $$\n\tThis is a test of tabs-only indentation.\n Two tabs should be stripped from each line.\n This line contains mixed tabs and spaces.\n This line contains two tabs and then a space.\n This is another test of tabs-only indentation.\n\t This line begins with a tab, so nothing should be stripped.\n"
19 changes: 16 additions & 3 deletions tests/lang/eval-okay-ind-string.nix
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ let
'';

s6 = ''
Tabs are not interpreted as whitespace (since we can't guess
what tab settings are intended), so don't use them.
Tabs are only interpreted as whitespace when they are
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whitespace when should be whitespace when. No need for two spaces.

the only indentation used (since we can't guess
what tab settings are intended), so don't mix them.
This line starts with a space and a tab, so only one
space will be stripped from each line.
'';
Expand Down Expand Up @@ -125,4 +126,16 @@ let
# Accept dollars at end of strings
s17 = ''ending dollar $'' + ''$'' + "\n";

in s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17
s18 = ''
This is a test of tabs-only indentation.
Two tabs should be stripped from each line.
This line contains mixed tabs and spaces.
This line contains two tabs and then a space.
'';

s19 = ''
This is another test of tabs-only indentation.
This line begins with a tab, so nothing should be stripped.
'';

in s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17 + s18 + s19