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

feat: add |||- chomped text block syntax #1175

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
9 changes: 8 additions & 1 deletion core/formatter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,11 @@ class Unparser {
o << encode_utf8(ast->value);
o << "'";
} else if (ast->tokenKind == LiteralString::BLOCK) {
o << "|||\n";
o << "|||";
if (ast->value.back() != U'\n') {
o << "-";
}
o << "\n";
if (ast->value.c_str()[0] != U'\n')
o << ast->blockIndent;
for (const char32_t *cp = ast->value.c_str(); *cp != U'\0'; ++cp) {
Expand All @@ -513,6 +517,9 @@ class Unparser {
o << ast->blockIndent;
}
}
if (ast->value.back() != U'\n') {
o << "\n";
}
o << ast->blockTermIndent << "|||";
} else if (ast->tokenKind == LiteralString::VERBATIM_DOUBLE) {
o << "@\"";
Expand Down
11 changes: 11 additions & 0 deletions core/lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,13 @@ Tokens jsonnet_lex(const std::string &filename, const char *input)
// Text block
if (*c == '|' && *(c + 1) == '|' && *(c + 2) == '|') {
c += 3; // Skip the "|||".

bool chomp_trailing_nl = false;
if (*c == '-') {
chomp_trailing_nl = true;
c++;
}

while (is_horz_ws(*c)) ++c; // Chomp whitespace at end of line.
if (*c != '\n') {
auto msg = "text block syntax requires new line after |||.";
Expand Down Expand Up @@ -762,6 +769,10 @@ Tokens jsonnet_lex(const std::string &filename, const char *input)
c += 3; // Leave after the last |
data = block.str();
kind = Token::STRING_BLOCK;
if (chomp_trailing_nl) {
assert(data.back() == '\n');
data.pop_back();
}
break; // Out of the while loop.
}
}
Expand Down
2 changes: 1 addition & 1 deletion doc/js/codemirror-mode-jsonnet.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
}

// Enter text block.
if (stream.match(/\|\|\|/)) {
if (stream.match(/\|\|\|-?/)) {
state.textBlock = true;
state.textBlockIndent = null;
return "string";
Expand Down
22 changes: 13 additions & 9 deletions doc/ref/spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,19 @@ <h2 id="lexing">Lexing</h2>
subsequent non-quoted <code>'</code>
</li>
<li>
Text block, beginning with <code>|||</code>, followed by optional whitespace and a
new-line. The next non-empty line must be prefixed with some non-zero length
whitespace <i>W</i>. The block ends at the first subsequent line that is non-empty
and does not begin with <i>W</i>, and it is an error if this line does not contain
some optional whitespace followed by <code>|||</code>. The content of the string is
the concatenation of all the lines between the two <code>|||</code>, which either
begin with <i>W</i> (in which case that prefix is stripped) or they are empty lines
(in which case they remain as empty lines). The line ending style in the file is
preserved in the string. This form cannot be used in <code>import</code> statements.
Text block, beginning with <code>|||</code> followed by an optional
<code>-</code>, then optional whitespace and a new-line. The next non-empty
line must be prefixed with some non-zero length whitespace <i>W</i>. The
block ends at the first subsequent line that is non-empty and does not begin
with <i>W</i>, and it is an error if this line does not contain some
optional whitespace followed by <code>|||</code>. The content of the string
is the concatenation of all the lines between the two <code>|||</code>,
which either begin with <i>W</i> (in which case that prefix is stripped) or
they are empty lines (in which case they remain as empty lines). The line
ending style in the file is preserved in the string. If the beginning
<code>|||</code> was followed by <code>-</code> then the final new-line is
stripped from the resulting string. This form cannot be used in
<code>import</code> statements.
</li>
</ul>
<p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
OPTABLE: {
prefix: {
'||': [0,0,TEXCLASS.BIN,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: ||
'|||': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: |||
'|||': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: |||
'|||-': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: |||-
},
postfix: {
'!!': [1,0,TEXCLASS.BIN], // multiple character operator: !!
Expand All @@ -36,7 +37,8 @@
'..': [0,0,TEXCLASS.BIN], // multiple character operator: ..
'...': MO.ORD, // multiple character operator: ...
'||': [0,0,TEXCLASS.BIN,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: ||
'|||': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: |||
'|||': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: |||
'|||-': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: |||-
},
infix: {
'!=': MO.BIN4, // multiple character operator: !=
Expand All @@ -55,7 +57,8 @@
'>=': MO.BIN5, // multiple character operator: >=
'@': MO.ORD11, // commercial at
'||': [2,2,TEXCLASS.BIN,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: ||
'|||': [2,2,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: |||
'|||': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}], // multiple character operator: |||
'|||-': [0,0,TEXCLASS.ORD,{fence: true, stretchy: true, symmetric: true}] // multiple character operator: |||-
}
}
});
Expand Down
27 changes: 27 additions & 0 deletions test_suite/text_block.jsonnet
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,33 @@ local bash_mixed = |||
std.assertEqual(bash_golden, bash_mixed) &&


// Chomp trailing newline
local str1 = |||-
foo bar baz
|||;

std.assertEqual(str1, "foo bar baz") &&


// Chomp just one trailing newline
local str1 = |||-
foo bar baz

|||;

std.assertEqual(str1, "foo bar baz\n") &&


// Concatenate chomped blocks
local str1 = |||-
foo bar baz
||| + " " + |||-
biz buzz
|||;

std.assertEqual(str1, "foo bar baz biz buzz") &&


// More indent
local str1 = |||
text
Expand Down
26 changes: 26 additions & 0 deletions test_suite/text_block.jsonnet.fmt.golden
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,32 @@ local bash_mixed = |||
std.assertEqual(bash_golden, bash_mixed) &&


// Chomp trailing newline
local str1 = |||-
foo bar baz
|||;

std.assertEqual(str1, 'foo bar baz') &&


// Chomp just one trailing newline
local str1 = |||
foo bar baz
|||;

std.assertEqual(str1, 'foo bar baz\n') &&


// Concatenate chomped blocks
local str1 = |||-
foo bar baz
||| + ' ' + |||-
biz buzz
|||;

std.assertEqual(str1, 'foo bar baz biz buzz') &&


// More indent
local str1 = |||
text
Expand Down