From f81fad758bfd1d7ae41e976f3c49eb95c643bca9 Mon Sep 17 00:00:00 2001 From: deathaxe Date: Tue, 24 Sep 2024 19:33:43 +0200 Subject: [PATCH] [D] Add support for interpolated strings Resolves #3988 --- D/D.sublime-syntax | 105 +++++++++++++++++++++++++++++++++++----- D/tests/syntax_test_d.d | 75 ++++++++++++++++++++++++++++ 2 files changed, 168 insertions(+), 12 deletions(-) diff --git a/D/D.sublime-syntax b/D/D.sublime-syntax index 3fb62719f5..286037d032 100644 --- a/D/D.sublime-syntax +++ b/D/D.sublime-syntax @@ -43,7 +43,7 @@ variables: hex_exponent: '(?:[pP]{{exponent}})' character_lookahead: (?=') - string_lookahead: '(?=`|[rxq]?"|q{)' + string_lookahead: '(?=i?`|[irxq]?"|i?q{)' definitely_value_lookahead: '(?=!|~|\+|\-|\*|&|\bcast\b|\bdelete\b|\bnew\b|\bimport\b|\bis\b|\b__traits\b|\bfunction\b|\bdelegate\b|[0-9]|\[|\(|{{string_lookahead}}|\b({{language_constant}})\b|\b({{language_variable}})\b)' definitely_declaration_lookahead: '(?=(?:{{name}}\.)*{{name}}(\s+|\s*\*+\s*|\**\[.*\]\s*){{name}}|\b({{type_qualifier}})\b)' @@ -364,10 +364,22 @@ contexts: 1: punctuation.definition.string.end.d 2: storage.type.string.d pop: true - - match: '{{escape_sequence}}' - scope: constant.character.escape.d - - match: \\. - scope: invalid.illegal.unknown-escape.d + - include: string-escapes + # Interpolated regular string + - match: (i)(") + captures: + 1: storage.modifier.string.d + 2: punctuation.definition.string.begin.d + set: + - meta_include_prototype: false + - meta_scope: meta.string.d string.quoted.double.d + - match: '(")({{string_postfix}})' + captures: + 1: punctuation.definition.string.end.d + 2: storage.type.string.d + pop: true + - include: string-interpolations + - include: string-escapes # Wysiwyg string - match: (r)(") captures: @@ -392,6 +404,20 @@ contexts: 1: punctuation.definition.string.end.d 2: storage.type.string.d pop: true + # Interpolated alternate Wysiwyg string + - match: (i)(`) + captures: + 1: storage.modifier.string.d + 2: punctuation.definition.string.begin.d + set: + - meta_include_prototype: false + - meta_scope: meta.string.d string.quoted.double.raw.backtick.d + - match: '(`)({{string_postfix}})' + captures: + 1: punctuation.definition.string.end.d + 2: storage.type.string.d + pop: true + - include: string-interpolations # Deprecated Hex string - match: (x)(") captures: @@ -502,9 +528,71 @@ contexts: scope: string.unquoted.embedded.d punctuation.definition.string.end.d pop: true - include: tokens-in + # Interpolated token string + - match: '(iq)({)' + captures: + 1: storage.modifier.string.d + 2: punctuation.definition.string.begin.d + scope: string.unquoted.embedded.d + set: + - meta_scope: meta.string.d + - match: '}' + scope: string.unquoted.embedded.d punctuation.definition.string.end.d + pop: true + - include: tokens-interpolated-in + + string-escapes: + - match: '{{escape_sequence}}' + scope: constant.character.escape.d + - match: \\. + scope: invalid.illegal.unknown-escape.d + + string-interpolations: + - match: \\\$ + scope: constant.character.escape.d + - match: \$\( + scope: punctuation.section.interpolation.begin.d + push: string-interpolation-in + + string-interpolation-in: + - clear_scopes: 1 + - meta_include_prototype: false + - meta_scope: meta.interpolation.d + - match: \) + scope: punctuation.section.interpolation.end.d + pop: true + - match: (?=\S) + push: first-value # Purely a set of un-verified tokens + tokens-interpolated-in: + - match: \{ + scope: punctuation.section.braces.begin.d + push: + - match: \} + scope: punctuation.section.braces.end.d + pop: true + - include: tokens-interpolated-in + - match: \$\( + scope: punctuation.section.interpolation.begin.d + push: + # don't clear non-existing `string` scope + - meta_include_prototype: false + - meta_scope: meta.interpolation.d + - include: string-interpolation-in + - include: tokens-in-common + tokens-in: + - match: \{ + scope: punctuation.section.braces.begin.d + push: + - match: \} + scope: punctuation.section.braces.end.d + pop: true + - include: tokens-in + - include: tokens-in-common + + tokens-in-common: - match: '\b({{keyword}})\b' scope: keyword.d - match: '\b({{basic_type}})\b' @@ -533,13 +621,6 @@ contexts: scope: punctuation.section.brackets.begin.d - match: '\]' scope: punctuation.section.brackets.end.d - - match: '\{' - scope: punctuation.section.braces.begin.d - push: - - match: '\}' - scope: punctuation.section.braces.end.d - pop: true - - include: tokens-in - include: not-whitespace-illegal attribute-specifier-in: diff --git a/D/tests/syntax_test_d.d b/D/tests/syntax_test_d.d index ba6167a1df..e059939669 100644 --- a/D/tests/syntax_test_d.d +++ b/D/tests/syntax_test_d.d @@ -54,11 +54,38 @@ auto wysiwyg = r"f// \n\"; // ^ punctuation.definition.string.begin.d // ^^ - constant.character.escape.d // ^ punctuation.definition.string.end.d + auto wysiwygAlt = `f//\n\`; // ^^^^^^^^ meta.string.d string.quoted.double.raw.backtick.d // ^ punctuation.definition.string.begin.d // ^^ - constant.character.escape.d // ^ punctuation.definition.string.end.d + +auto wysiwygInter = i`string $(this.foo)\r\nescaped: \$(bar) func: $(this.baz())\r\n`; +// ^ storage.modifier.string.d +// ^^^^^^^^ meta.string.d string.quoted.double.raw.backtick.d +// ^^^^^^^^^^^ meta.string.d meta.interpolation.d +// ^^ punctuation.section.interpolation.begin.d +// ^^^^ variable.language.d +// ^ punctuation.accessor.dot.d +// ^^^ variable.other.d +// ^ punctuation.section.interpolation.end.d +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.d string.quoted.double.raw.backtick.d +// ^^^^ - constant.character +// ^^ constant.character.escape.d +// ^^^^^^^^^^^^^ meta.string.d meta.interpolation.d +// ^^ punctuation.section.interpolation.begin.d +// ^^^^ variable.language.d +// ^ punctuation.accessor.dot.d +// ^^^ variable.function.d +// ^ punctuation.section.parens.begin.d +// ^ punctuation.section.parens.end.d +// ^ punctuation.section.interpolation.end.d +// ^^^^^ meta.string.d string.quoted.double.raw.backtick.d +// ^^^^ - constant.character +// ^ punctuation.definition.string.end.d +// ^ punctuation.terminator.d - meta.string + auto doubleQuoted = "c://\'\"\?\\\0\a\b\f\n\r\t\v\x0B\2\12\762\u0feb\Uabcdef98\""; // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.d string.quoted.double.d // ^ punctuation.definition.string.begin.d @@ -76,6 +103,32 @@ auto invalidEscape4 = "\u12398"; // ^^^^^^^^^ meta.string.d string.quoted.double.d // ^^^^^^ constant.character.escape.d // ^ - constant.character.escape.d + +auto interpolated = i"string $(this.foo)\r\nescaped: \$(bar) func: $(this.baz())\r\n"; +// ^ storage.modifier.string.d +// ^^^^^^^^ meta.string.d string.quoted.double.d +// ^^^^^^^^^^^ meta.string.d meta.interpolation.d +// ^^ punctuation.section.interpolation.begin.d +// ^^^^ variable.language.d +// ^ punctuation.accessor.dot.d +// ^^^ variable.other.d +// ^ punctuation.section.interpolation.end.d +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.d string.quoted.double.d +// ^^^^ constant.character.escape.d +// ^^ constant.character.escape.d +// ^^^^^^^^^^^^^ meta.string.d meta.interpolation.d +// ^^ punctuation.section.interpolation.begin.d +// ^^^^ variable.language.d +// ^ punctuation.accessor.dot.d +// ^^^ variable.function.d +// ^ punctuation.section.parens.begin.d +// ^ punctuation.section.parens.end.d +// ^ punctuation.section.interpolation.end.d +// ^^^^^ meta.string.d string.quoted.double.d +// ^^^^ constant.character.escape.d +// ^ punctuation.definition.string.end.d +// ^ punctuation.terminator.d - meta.string + auto hexString = x"00 ba // ^^^^^^^^ meta.string.d string.quoted.double.raw.d // ^ storage.modifier.string.d @@ -200,6 +253,28 @@ auto tokenString = q{ if { () /*}*/ else /*{*/ } }; // ^ string.unquoted.embedded.d punctuation.definition.string.end.d // ^ punctuation.terminator.d +auto interpolTokenStr = iq{ if $(this.var) { me = $(this.bar) } } +// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ meta.string.d +// ^^ storage.modifier.string.d +// ^ punctuation.definition.string.begin.d +// ^^ keyword.d +// ^^^^^^^^^^^ meta.interpolation.d +// ^^ punctuation.section.interpolation.begin.d +// ^^^^ variable.language.d +// ^ punctuation.accessor.dot.d +// ^^^ variable.other.d +// ^ punctuation.section.interpolation.end.d +// ^ punctuation.section.braces.begin.d +// ^ keyword.operator.assignment.d +// ^^^^^^^^^^^ meta.interpolation.d +// ^^ punctuation.section.interpolation.begin.d +// ^^^^ variable.language.d +// ^ punctuation.accessor.dot.d +// ^^^ variable.other.d +// ^ punctuation.section.interpolation.end.d +// ^ punctuation.section.braces.end.d +// ^ punctuation.definition.string.end.d + auto c = 'a'; // ^^^ meta.string.d string.quoted.single.d c = 'Ó';