diff --git a/core/formatter.cpp b/core/formatter.cpp index cad3369c1..ac6d4b1e3 100644 --- a/core/formatter.cpp +++ b/core/formatter.cpp @@ -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) { @@ -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 << "@\""; diff --git a/core/lexer.cpp b/core/lexer.cpp index 31599f4d7..10319875c 100644 --- a/core/lexer.cpp +++ b/core/lexer.cpp @@ -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 |||."; @@ -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. } } diff --git a/doc/js/codemirror-mode-jsonnet.js b/doc/js/codemirror-mode-jsonnet.js index b0b826bbe..ec8649746 100644 --- a/doc/js/codemirror-mode-jsonnet.js +++ b/doc/js/codemirror-mode-jsonnet.js @@ -176,7 +176,7 @@ } // Enter text block. - if (stream.match(/\|\|\|/)) { + if (stream.match(/\|\|\|-?/)) { state.textBlock = true; state.textBlockIndent = null; return "string"; diff --git a/doc/ref/spec.html b/doc/ref/spec.html index 9cfd6c01b..75c933e62 100644 --- a/doc/ref/spec.html +++ b/doc/ref/spec.html @@ -168,15 +168,19 @@
'
|||
, followed by optional whitespace and a
- new-line. The next non-empty line must be prefixed with some non-zero length
- whitespace W. The block ends at the first subsequent line that is non-empty
- and does not begin with W, and it is an error if this line does not contain
- some optional whitespace followed by |||
. The content of the string is
- the concatenation of all the lines between the two |||
, which either
- begin with W (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 import
statements.
+ Text block, beginning with |||
followed by an optional
+ -
, then optional whitespace and a new-line. The next non-empty
+ line must be prefixed with some non-zero length whitespace W. The
+ block ends at the first subsequent line that is non-empty and does not begin
+ with W, and it is an error if this line does not contain some
+ optional whitespace followed by |||
. The content of the string
+ is the concatenation of all the lines between the two |||
,
+ which either begin with W (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
+ |||
was followed by -
then the final new-line is
+ stripped from the resulting string. This form cannot be used in
+ import
statements.
diff --git a/doc/third_party/MathJax-2.7.2/unpacked/jax/element/mml/optable/BasicLatin.js b/doc/third_party/MathJax-2.7.2/unpacked/jax/element/mml/optable/BasicLatin.js index eaf18b5cc..7c9171bb6 100644 --- a/doc/third_party/MathJax-2.7.2/unpacked/jax/element/mml/optable/BasicLatin.js +++ b/doc/third_party/MathJax-2.7.2/unpacked/jax/element/mml/optable/BasicLatin.js @@ -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: !! @@ -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: != @@ -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: |||- } } }); diff --git a/test_suite/text_block.jsonnet b/test_suite/text_block.jsonnet index b57eb4971..db958f848 100644 --- a/test_suite/text_block.jsonnet +++ b/test_suite/text_block.jsonnet @@ -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 diff --git a/test_suite/text_block.jsonnet.fmt.golden b/test_suite/text_block.jsonnet.fmt.golden index 7c7ae6e25..ac9d41334 100644 --- a/test_suite/text_block.jsonnet.fmt.golden +++ b/test_suite/text_block.jsonnet.fmt.golden @@ -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