Skip to content

Commit 0de0d0e

Browse files
FMorschelCommit Queue
authored and
Commit Queue
committed
[DAS] Fixes escape characters coloring in interpolation strings
Fixes: #60692 Change-Id: If9f310405b424ffb1f15d1bda1db51f59d64e34b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/428020 Reviewed-by: Samuel Rawlins <[email protected]> Auto-Submit: Felipe Morschel <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Brian Wilkerson <[email protected]>
1 parent d67a7d8 commit 0de0d0e

File tree

2 files changed

+148
-21
lines changed

2 files changed

+148
-21
lines changed

pkg/analysis_server/lib/src/computer/computer_highlights.dart

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,6 +1346,17 @@ class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {
13461346
@override
13471347
void visitInterpolationString(InterpolationString node) {
13481348
computer._addRegion_node(node, HighlightRegionType.LITERAL_STRING);
1349+
if (computer._computeSemanticTokens) {
1350+
var string = node.contents.lexeme;
1351+
var parent = node.parent as StringInterpolation;
1352+
_addRegions_stringEscapes(
1353+
string,
1354+
parent.quote,
1355+
node.offset,
1356+
0,
1357+
string.length,
1358+
);
1359+
}
13491360
super.visitInterpolationString(node);
13501361
}
13511362

@@ -1663,7 +1674,17 @@ class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {
16631674
void visitSimpleStringLiteral(SimpleStringLiteral node) {
16641675
computer._addRegion_node(node, HighlightRegionType.LITERAL_STRING);
16651676
if (computer._computeSemanticTokens) {
1666-
_addRegions_stringEscapes(node);
1677+
var string = node.literal.lexeme;
1678+
var quote = analyzeQuote(string);
1679+
var startIndex = firstQuoteLength(string, quote);
1680+
var endIndex = string.length - lastQuoteLength(quote);
1681+
_addRegions_stringEscapes(
1682+
string,
1683+
quote,
1684+
node.offset,
1685+
startIndex,
1686+
endIndex,
1687+
);
16671688
}
16681689
super.visitSimpleStringLiteral(node);
16691690
}
@@ -1929,47 +1950,48 @@ class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {
19291950
}
19301951
}
19311952

1932-
void _addRegions_stringEscapes(SimpleStringLiteral node) {
1933-
var string = node.literal.lexeme;
1934-
var quote = analyzeQuote(string);
1935-
var startIndex = firstQuoteLength(string, quote);
1936-
var endIndex = string.length - lastQuoteLength(quote);
1953+
void _addRegions_stringEscapes(
1954+
String string,
1955+
Quote quote,
1956+
int nodeOffset,
1957+
int startIndex,
1958+
int endIndex,
1959+
) {
19371960
switch (quote) {
1938-
case Quote.Single:
1939-
case Quote.Double:
1940-
case Quote.MultiLineSingle:
1941-
case Quote.MultiLineDouble:
1961+
case Quote.RawSingle ||
1962+
Quote.RawDouble ||
1963+
Quote.RawMultiLineSingle ||
1964+
Quote.RawMultiLineDouble:
1965+
// Raw strings don't have escape characters.
1966+
break;
1967+
case Quote.Single ||
1968+
Quote.Double ||
1969+
Quote.MultiLineSingle ||
1970+
Quote.MultiLineDouble:
19421971
_findEscapes(
1943-
node,
1972+
string,
19441973
startIndex: startIndex,
19451974
endIndex: endIndex,
19461975
listener: (offset, end) {
19471976
var length = end - offset;
19481977
computer._addRegion(
1949-
node.offset + offset,
1978+
nodeOffset + offset,
19501979
length,
19511980
HighlightRegionType.VALID_STRING_ESCAPE,
19521981
);
19531982
},
19541983
);
1955-
case Quote.RawSingle:
1956-
case Quote.RawDouble:
1957-
case Quote.RawMultiLineSingle:
1958-
case Quote.RawMultiLineDouble:
1959-
// Raw strings don't have escape characters.
1960-
break;
19611984
}
19621985
}
19631986

19641987
/// Finds escaped regions within a string between [startIndex] and [endIndex],
19651988
/// calling [listener] for each found region.
19661989
void _findEscapes(
1967-
SimpleStringLiteral node, {
1990+
String string, {
19681991
required int startIndex,
19691992
required int endIndex,
19701993
required void Function(int offset, int end) listener,
19711994
}) {
1972-
var string = node.literal.lexeme;
19731995
var codeUnits = string.codeUnits;
19741996
var length = string.length;
19751997

@@ -2029,3 +2051,16 @@ class _DartUnitHighlightsComputerVisitor extends RecursiveAstVisitor<void> {
20292051
}
20302052
}
20312053
}
2054+
2055+
extension on StringInterpolation {
2056+
Quote get quote => switch ((isRaw, isMultiline, isSingleQuoted)) {
2057+
(true, true, true) => Quote.RawMultiLineSingle,
2058+
(true, true, false) => Quote.RawMultiLineDouble,
2059+
(true, false, true) => Quote.RawSingle,
2060+
(true, false, false) => Quote.RawDouble,
2061+
(false, true, true) => Quote.MultiLineSingle,
2062+
(false, true, false) => Quote.MultiLineDouble,
2063+
(false, false, true) => Quote.Single,
2064+
(false, false, false) => Quote.Double,
2065+
};
2066+
}

pkg/analysis_server/test/lsp/semantic_tokens_test.dart

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2073,9 +2073,10 @@ multi
20732073
// The 9's in these strings are not part of the escapes (they make the
20742074
// strings too long).
20752075
var content = r'''
2076-
const string1 = 'it\'s escaped\\\n';
2076+
const string1 = 'it\'s escaped\\\n\$';
20772077
const string2 = 'hex \x12\x1299';
20782078
const string3 = 'unicode \u1234\u123499\u{123456}\u{12345699}';
2079+
const string4 = "\"";
20792080
''';
20802081

20812082
var expected = [
@@ -2094,6 +2095,9 @@ const string3 = 'unicode \u1234\u123499\u{123456}\u{12345699}';
20942095
_Token(r'\n', SemanticTokenTypes.string, [
20952096
CustomSemanticTokenModifiers.escape,
20962097
]),
2098+
_Token(r'\$', SemanticTokenTypes.string, [
2099+
CustomSemanticTokenModifiers.escape,
2100+
]),
20972101
_Token(r"'", SemanticTokenTypes.string),
20982102
_Token('const', SemanticTokenTypes.keyword),
20992103
_Token('string2', SemanticTokenTypes.variable, [
@@ -2126,6 +2130,94 @@ const string3 = 'unicode \u1234\u123499\u{123456}\u{12345699}';
21262130
]),
21272131
// The 99 makes this invalid so i's not an escape
21282132
_Token(r"\u{12345699}'", SemanticTokenTypes.string),
2133+
_Token('const', SemanticTokenTypes.keyword),
2134+
_Token('string4', SemanticTokenTypes.variable, [
2135+
SemanticTokenModifiers.declaration,
2136+
]),
2137+
_Token('"', SemanticTokenTypes.string),
2138+
_Token(r'\"', SemanticTokenTypes.string, [
2139+
CustomSemanticTokenModifiers.escape,
2140+
]),
2141+
_Token('"', SemanticTokenTypes.string),
2142+
];
2143+
2144+
await _initializeAndVerifyTokens(content, expected);
2145+
}
2146+
2147+
Future<void> test_strings_escape_interpolation1() async {
2148+
var content = r'''
2149+
const value = 1;
2150+
const string1 = 'it\'s $value escaped\\\n';
2151+
''';
2152+
2153+
var expected = [
2154+
_Token('const', SemanticTokenTypes.keyword),
2155+
_Token('value', SemanticTokenTypes.variable, [
2156+
SemanticTokenModifiers.declaration,
2157+
]),
2158+
_Token('1', SemanticTokenTypes.number),
2159+
_Token('const', SemanticTokenTypes.keyword),
2160+
_Token('string1', SemanticTokenTypes.variable, [
2161+
SemanticTokenModifiers.declaration,
2162+
]),
2163+
_Token("'it", SemanticTokenTypes.string),
2164+
_Token(r"\'", SemanticTokenTypes.string, [
2165+
CustomSemanticTokenModifiers.escape,
2166+
]),
2167+
_Token('s ', SemanticTokenTypes.string),
2168+
_Token(r'$', CustomSemanticTokenTypes.source, [
2169+
CustomSemanticTokenModifiers.interpolation,
2170+
]),
2171+
_Token('value', SemanticTokenTypes.property),
2172+
_Token(' escaped', SemanticTokenTypes.string),
2173+
_Token(r'\\', SemanticTokenTypes.string, [
2174+
CustomSemanticTokenModifiers.escape,
2175+
]),
2176+
_Token(r'\n', SemanticTokenTypes.string, [
2177+
CustomSemanticTokenModifiers.escape,
2178+
]),
2179+
_Token(r"'", SemanticTokenTypes.string),
2180+
];
2181+
2182+
await _initializeAndVerifyTokens(content, expected);
2183+
}
2184+
2185+
Future<void> test_strings_escape_interpolation2() async {
2186+
var content = r'''
2187+
const value = 1;
2188+
const string1 = 'it\'s ${value} escaped\\\n';
2189+
''';
2190+
2191+
var expected = [
2192+
_Token('const', SemanticTokenTypes.keyword),
2193+
_Token('value', SemanticTokenTypes.variable, [
2194+
SemanticTokenModifiers.declaration,
2195+
]),
2196+
_Token('1', SemanticTokenTypes.number),
2197+
_Token('const', SemanticTokenTypes.keyword),
2198+
_Token('string1', SemanticTokenTypes.variable, [
2199+
SemanticTokenModifiers.declaration,
2200+
]),
2201+
_Token("'it", SemanticTokenTypes.string),
2202+
_Token(r"\'", SemanticTokenTypes.string, [
2203+
CustomSemanticTokenModifiers.escape,
2204+
]),
2205+
_Token('s ', SemanticTokenTypes.string),
2206+
_Token(r'${', CustomSemanticTokenTypes.source, [
2207+
CustomSemanticTokenModifiers.interpolation,
2208+
]),
2209+
_Token('value', SemanticTokenTypes.property),
2210+
_Token('}', CustomSemanticTokenTypes.source, [
2211+
CustomSemanticTokenModifiers.interpolation,
2212+
]),
2213+
_Token(' escaped', SemanticTokenTypes.string),
2214+
_Token(r'\\', SemanticTokenTypes.string, [
2215+
CustomSemanticTokenModifiers.escape,
2216+
]),
2217+
_Token(r'\n', SemanticTokenTypes.string, [
2218+
CustomSemanticTokenModifiers.escape,
2219+
]),
2220+
_Token(r"'", SemanticTokenTypes.string),
21292221
];
21302222

21312223
await _initializeAndVerifyTokens(content, expected);

0 commit comments

Comments
 (0)