diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c20068..0d1e364 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,10 +54,10 @@ * Around `=` for props (`x = 1`) * The BOM is now only allowed as the first character in a document. It was previously treated as generic whitespace. -* Multi-line strings are now automatically dedented, according to the - least-indented line in the body. Multiline strings and raw strings now must - have a newline immediately following their opening `"`, and a final newline - preceding the closing `"`. +* Multi-line strings are now automatically dedented, according to the common + whitespace matching the whitespace prefix of the closing line. Multiline + strings and raw strings now must have a newline immediately following their + opening `"`, and a final newline plus whitespace preceding the closing `"`. * SMALL EQUALS SIGN (`U+FE66`), FULLWIDTH EQUALS SIGN (`U+FF1D`), and HEAVY EQUALS SIGN (`U+1F7F0`) are now treated the same as `=` and can be used for properties (e.g. `お名前=☜(゚ヮ゚☜)`). They are also no longer valid in bare diff --git a/SPEC.md b/SPEC.md index dc1321c..dacb03d 100644 --- a/SPEC.md +++ b/SPEC.md @@ -436,30 +436,31 @@ The string contains the literal characters `hello\n\r\asd"#world` ### Multi-line Strings -When a Quoted or Raw String spans multiple lines with literal, non-escaped Newlines, -it follows a special multi-line syntax -that automatically "dedents" the string, -allowing its value to be indented to a visually matching level if desired. +When a Quoted or Raw String spans multiple lines with literal, non-escaped +Newlines, it follows a special multi-line syntax that automatically "dedents" +the string, allowing its value to be indented to a visually matching level if +desired. -A Multi-line string _MUST_ start with a [Newline](#newline) -immediately following its opening `"`. -Its final line, preceding the closing `"`, -_MUST_ contain only whitespace. -All in-between lines that contain non-whitespace characters -_MUST_ start with the exact same whitespace as the final line -(precisely matching codepoints, not merely counting characters). +A Multi-line string _MUST_ start with a [Newline](#newline) immediately +following its opening `"`. Its final line _MUST_ contain only whitespace, +followed by a single closing `"`. All in-between lines that contain +non-whitespace characters _MUST_ start with the exact same whitespace as the +final line (precisely matching codepoints, not merely counting characters). -The value of the Multi-line String omits the first and last Newline, -the Whitespace of the last line, -the matching Whitespace prefix on all intermediate lines, -and all Whitespace on intermediate Whitespace-only lines. -The first and last Newline can be the same character -(that is, empty multi-line strings are legal). +The value of the Multi-line String omits the first and last Newline, the +Whitespace of the last line, and the matching Whitespace prefix on all +intermediate lines. The first and last Newline can be the same character (that +is, empty multi-line strings are legal). Strings with literal Newlines that do not immediately start with a Newline and -whose final `"` is not preceeded by optional whitespace and a Newline are illegal. +whose final `"` is not preceeded by optional whitespace and a Newline are +illegal. -In other words, the final line specifies the whitespace prefix that will be removed from all other lines. +In other words, the final line specifies the whitespace prefix that will be +removed from all other lines. + +It is a syntax error for any body lines of the multi-line string to not match +the whitespace prefix of the last line with the final quote. #### Example @@ -526,6 +527,28 @@ A second indented paragraph. Equivalent to `"Indented a bit.\n\nA second indented paragraph."` +----------- + +The following yield syntax errors: + +```kdl +multi-line " + closing quote with non-whitespace prefix" +``` + +```kdl +multi-line "stuff + " +``` + +```kdl +// Every line must share the exact same prefix as the closing line. +multi-line "[\n] +[tab]a[\n] +[space][space]b[\n] +[space][tab][\n] +[tab]" +``` ### Number diff --git a/tests/test_cases/expected_kdl/multiline_raw_string_indented.kdl b/tests/test_cases/expected_kdl/multiline_raw_string_indented.kdl new file mode 100644 index 0000000..e7638d8 --- /dev/null +++ b/tests/test_cases/expected_kdl/multiline_raw_string_indented.kdl @@ -0,0 +1,2 @@ +node " hey\n everyone\n how goes?" + diff --git a/tests/test_cases/expected_kdl/multiline_string_indented.kdl b/tests/test_cases/expected_kdl/multiline_string_indented.kdl new file mode 100644 index 0000000..e7638d8 --- /dev/null +++ b/tests/test_cases/expected_kdl/multiline_string_indented.kdl @@ -0,0 +1,2 @@ +node " hey\n everyone\n how goes?" + diff --git a/tests/test_cases/input/multiline_raw_string_indented.kdl b/tests/test_cases/input/multiline_raw_string_indented.kdl new file mode 100644 index 0000000..67ef76d --- /dev/null +++ b/tests/test_cases/input/multiline_raw_string_indented.kdl @@ -0,0 +1,5 @@ +node #" + hey + everyone + how goes? + "# diff --git a/tests/test_cases/input/multiline_raw_string_non_matching_prefix_character_error.kdl b/tests/test_cases/input/multiline_raw_string_non_matching_prefix_character_error.kdl new file mode 100644 index 0000000..c5650e9 --- /dev/null +++ b/tests/test_cases/input/multiline_raw_string_non_matching_prefix_character_error.kdl @@ -0,0 +1,5 @@ +node #" + hey + everyone + how goes? + "# diff --git a/tests/test_cases/input/multiline_raw_string_non_matching_prefix_count_error.kdl b/tests/test_cases/input/multiline_raw_string_non_matching_prefix_count_error.kdl new file mode 100644 index 0000000..c0f4f56 --- /dev/null +++ b/tests/test_cases/input/multiline_raw_string_non_matching_prefix_count_error.kdl @@ -0,0 +1,5 @@ +node #" + hey + everyone + how goes? + "# diff --git a/tests/test_cases/input/multiline_string_indented.kdl b/tests/test_cases/input/multiline_string_indented.kdl new file mode 100644 index 0000000..ce9ca16 --- /dev/null +++ b/tests/test_cases/input/multiline_string_indented.kdl @@ -0,0 +1,5 @@ +node " + hey + everyone + how goes? + " diff --git a/tests/test_cases/input/multiline_string_non_matching_prefix_character_error.kdl b/tests/test_cases/input/multiline_string_non_matching_prefix_character_error.kdl new file mode 100644 index 0000000..1c2ca85 --- /dev/null +++ b/tests/test_cases/input/multiline_string_non_matching_prefix_character_error.kdl @@ -0,0 +1,5 @@ +node " + hey + everyone + how goes? + " diff --git a/tests/test_cases/input/multiline_string_non_matching_prefix_count_error.kdl b/tests/test_cases/input/multiline_string_non_matching_prefix_count_error.kdl new file mode 100644 index 0000000..86a2867 --- /dev/null +++ b/tests/test_cases/input/multiline_string_non_matching_prefix_count_error.kdl @@ -0,0 +1,5 @@ +node " + hey + everyone + how goes? + "