Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit f5b451f

Browse files
author
Dart CI
committed
Version 2.12.0-175.0.dev
Merge commit '4245ca05a9d1c4851b7accc080624b48f5e9fc4f' into 'dev'
2 parents 7d35f9b + 4245ca0 commit f5b451f

File tree

8 files changed

+965
-765
lines changed

8 files changed

+965
-765
lines changed

DEPS

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ vars = {
149149
"source_span_rev": "49ff31eabebed0da0ae6634124f8ba5c6fbf57f1",
150150
"sse_tag": "9a486d058a17e880afa9cc1c3c0dd8bad726bcbc",
151151
"stack_trace_tag": "6788afc61875079b71b3d1c3e65aeaa6a25cbc2f",
152-
"stagehand_tag": "v3.3.11",
152+
"stagehand_rev": "e64ac90cac508981011299c4ceb819149e71f1bd",
153153
"stream_channel_tag": "d7251e61253ec389ee6e045ee1042311bced8f1d",
154154
"string_scanner_rev": "1b63e6e5db5933d7be0a45da6e1129fe00262734",
155155
"sync_http_rev": "a85d7ec764ea485cbbc49f3f3e7f1b43f87a1c74",
@@ -421,7 +421,7 @@ deps = {
421421
Var("dart_root") + "/third_party/pkg/stack_trace":
422422
Var("dart_git") + "stack_trace.git" + "@" + Var("stack_trace_tag"),
423423
Var("dart_root") + "/third_party/pkg/stagehand":
424-
Var("dart_git") + "stagehand.git" + "@" + Var("stagehand_tag"),
424+
Var("dart_git") + "stagehand.git" + "@" + Var("stagehand_rev"),
425425
Var("dart_root") + "/third_party/pkg/stream_channel":
426426
Var("dart_git") + "stream_channel.git" +
427427
"@" + Var("stream_channel_tag"),

pkg/analysis_server/lib/src/lsp/semantic_tokens/encoder.dart

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,39 @@ class SemanticTokenEncoder {
9595
return SemanticTokens(data: encodedTokens);
9696
}
9797

98+
/// Sorted for highlight regions that ensures tokens are sorted in offset order
99+
/// then longest first, then by priority, and finally by name. This ensures
100+
/// the order is always stable.
101+
int _regionOffsetLengthPrioritySorter(
102+
HighlightRegion r1, HighlightRegion r2) {
103+
const priorities = {
104+
// Ensure boolean comes above keyword.
105+
HighlightRegionType.LITERAL_BOOLEAN: 1,
106+
};
107+
108+
// First sort by offset.
109+
if (r1.offset != r2.offset) {
110+
return r1.offset.compareTo(r2.offset);
111+
}
112+
113+
// Then length (so longest are first).
114+
if (r1.length != r2.length) {
115+
return -r1.length.compareTo(r2.length);
116+
}
117+
118+
// Next sort by priority (if different).
119+
final priority1 = priorities[r1.type] ?? 0;
120+
final priority2 = priorities[r2.type] ?? 0;
121+
if (priority1 != priority2) {
122+
return priority1.compareTo(priority2);
123+
}
124+
125+
// If the tokens had the same offset and length, sort by name. This
126+
// is completely arbitrary but it's only important that it is consistent
127+
// between regions and the sort is stable.
128+
return r1.type.name.compareTo(r2.type.name);
129+
}
130+
98131
/// Splits multiline regions into multiple regions for clients that do not support
99132
/// multiline tokens.
100133
Iterable<HighlightRegion> _splitMultilineRegions(
@@ -143,12 +176,10 @@ class SemanticTokenEncoder {
143176
return;
144177
}
145178

146-
// When tokens have the same offset, the longest should come first so
147-
// the nested token "overwrites" it during the flattening.
179+
// Sort tokens so by offset, shortest length, priority then name to ensure
180+
// tne sort is always stable.
148181
final sortedRegions = regions.toList()
149-
..sort((r1, r2) => r1.offset != r2.offset
150-
? r1.offset.compareTo(r2.offset)
151-
: -r1.length.compareTo(r2.length));
182+
..sort(_regionOffsetLengthPrioritySorter);
152183

153184
final firstRegion = sortedRegions.first;
154185
final stack = ListQueue<HighlightRegion>()..add(firstRegion);
@@ -194,7 +225,8 @@ class SemanticTokenInfo {
194225
SemanticTokenInfo(
195226
this.line, this.column, this.length, this.type, this.modifiers);
196227

197-
static int offsetSort(t1, t2) => t1.line == t2.line
198-
? t1.column.compareTo(t2.column)
199-
: t1.line.compareTo(t2.line);
228+
static int offsetSort(SemanticTokenInfo t1, SemanticTokenInfo t2) =>
229+
t1.line == t2.line
230+
? t1.column.compareTo(t2.column)
231+
: t1.line.compareTo(t2.line);
200232
}

pkg/analysis_server/test/lsp/semantic_tokens_test.dart

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,61 @@ class SemanticTokensTest extends AbstractLspAnalysisServerTest {
429429
expect(decoded, equals(expected));
430430
}
431431

432+
Future<void> test_manyBools_bug() async {
433+
// Similar to test_manyImports_sortBug, this code triggered inconsistent tokens
434+
// for "false" because tokens were sorted incorrectly (because both boolean and
435+
// keyword had the same offset and length, which is all that were sorted by).
436+
final content = '''
437+
class MyTestClass {
438+
/// test
439+
/// test
440+
bool test1 = false;
441+
442+
/// test
443+
/// test
444+
bool test2 = false;
445+
446+
/// test
447+
/// test
448+
bool test3 = false;
449+
450+
/// test
451+
/// test
452+
bool test4 = false;
453+
454+
/// test
455+
/// test
456+
bool test5 = false;
457+
458+
/// test
459+
/// test
460+
bool test6 = false;
461+
}
462+
''';
463+
464+
final expected = [
465+
_Token('class', SemanticTokenTypes.keyword),
466+
_Token('MyTestClass', SemanticTokenTypes.class_),
467+
for (var i = 1; i <= 6; i++) ...[
468+
_Token('/// test', SemanticTokenTypes.comment,
469+
[SemanticTokenModifiers.documentation]),
470+
_Token('/// test', SemanticTokenTypes.comment,
471+
[SemanticTokenModifiers.documentation]),
472+
_Token('bool', SemanticTokenTypes.class_),
473+
_Token('test$i', SemanticTokenTypes.variable,
474+
[SemanticTokenModifiers.declaration]),
475+
_Token('false', CustomSemanticTokenTypes.boolean),
476+
],
477+
];
478+
479+
await initialize();
480+
await openFile(mainFileUri, withoutMarkers(content));
481+
482+
final tokens = await getSemanticTokens(mainFileUri);
483+
final decoded = decodeSemanticTokens(content, tokens);
484+
expect(decoded, equals(expected));
485+
}
486+
432487
Future<void> test_manyImports_sortBug() async {
433488
// This test is for a bug where some "import" tokens would not be highlighted
434489
// correctly. Imports are made up of a DIRECTIVE token that spans a

pkg/compiler/lib/src/common/codegen.dart

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -489,8 +489,13 @@ class CodegenResult {
489489
final Iterable<ModularName> modularNames;
490490
final Iterable<ModularExpression> modularExpressions;
491491

492-
CodegenResult(
493-
this.code, this.impact, this.modularNames, this.modularExpressions);
492+
CodegenResult(this.code, this.impact, List<ModularName> modularNames,
493+
List<ModularExpression> modularExpressions)
494+
: this.modularNames =
495+
modularNames.isEmpty ? const [] : List.unmodifiable(modularNames),
496+
this.modularExpressions = modularExpressions.isEmpty
497+
? const []
498+
: List.unmodifiable(modularExpressions);
494499

495500
/// Reads a [CodegenResult] object from [source].
496501
///
@@ -505,7 +510,7 @@ class CodegenResult {
505510
js.Fun code = source.readJsNodeOrNull();
506511
CodegenImpact impact = CodegenImpact.readFromDataSource(source);
507512
source.end(tag);
508-
return new CodegenResult(code, impact, modularNames, modularExpressions);
513+
return CodegenResult(code, impact, modularNames, modularExpressions);
509514
}
510515

511516
/// Writes the [CodegenResult] object to [sink].

0 commit comments

Comments
 (0)