Skip to content

Commit 1967fb2

Browse files
jensjohaCommit Queue
authored andcommitted
[analysis_server] Add benchmark for missing end brace in string interpolation
I found that in VSCode, if you have a pre-existing print, say ``` print("hello world"); ``` if you then want to add a string interpolation right at the start of the string, VSCode doesn't automatically insert the end brace and that currently recoveres badly: ``` print("${whateverhello world"); ``` This adds a benchmark: ``` $ out/ReleaseX64/dart-sdk/bin/dart pkg/analysis_server/tool/benchmark_tools/big_chain_benchmark/lsp_typing_temporarily_missing_end_brace_in_string_interpolation.dart --types=ImportCycleExportChain --sizes=1024 [...] ==================== size 1024 / CodeType.ImportCycleExportChain: Initial analysis: 16.173549 Completion after change: 7.824299 Fully done after change: 24.515470 peak virtual memory size: 3941 MB total program size (virtual): 3941 MB peak resident set size ("high water mark"): 2125 MB size of memory portions (rss): 2125 MB ==================== ================================== size 1024 / CodeType.ImportCycleExportChain: Initial analysis: 16.173549 Completion after change: 7.824299 Fully done after change: 24.515470 peak virtual memory size: 3941 MB total program size (virtual): 3941 MB peak resident set size ("high water mark"): 2125 MB size of memory portions (rss): 2125 MB ================================== ``` Change-Id: I6596252d07a21a487337c39de0cca83e8ec87322 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/453340 Commit-Queue: Jens Johansen <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 661d4a6 commit 1967fb2

File tree

2 files changed

+118
-0
lines changed

2 files changed

+118
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import '../language_server_benchmark.dart';
6+
import '../lsp_messages.dart';
7+
import '../run_utils.dart';
8+
import 'benchmark_utils.dart';
9+
10+
/// In VSCode, if you have a line, say
11+
/// `print("hello world");` and start adding a string interpolation with the
12+
/// cursor right at the first quote an ending brace isn't inserted
13+
/// automatically, so we'll end up - until we add the end brace ourselves - with
14+
/// something like
15+
/// `print("${whatnothello world");`
16+
///
17+
/// This recoveres badly in the scanner/parser and changes the outline if
18+
/// parsing here.
19+
///
20+
/// This benchmark tests this by typing
21+
/// `print("${type.runtimeTyhello world");`, then requesting
22+
/// completion to (hopefully) get `runtimeType`, measuring how long it takes
23+
/// before we get an answer to the completion request and before it's done
24+
/// analyzing.
25+
Future<void> main(List<String> args) async {
26+
await runHelper(
27+
args,
28+
LSPTypingTemporaryMissingEndBraceInterpolationBenchmark.new,
29+
copyData,
30+
extraIterations: getExtraIterations,
31+
runAsLsp: true,
32+
);
33+
}
34+
35+
class LSPTypingTemporaryMissingEndBraceInterpolationBenchmark
36+
extends DartLanguageServerBenchmark {
37+
@override
38+
final Uri rootUri;
39+
@override
40+
final Uri cacheFolder;
41+
42+
final RunDetails runDetails;
43+
44+
LSPTypingTemporaryMissingEndBraceInterpolationBenchmark(
45+
super.args,
46+
this.rootUri,
47+
this.cacheFolder,
48+
this.runDetails,
49+
) : super(useLspProtocol: true);
50+
51+
@override
52+
LaunchFrom get launchFrom => LaunchFrom.Dart;
53+
54+
@override
55+
Future<void> afterInitialization() async {
56+
await send(
57+
LspMessages.open(
58+
runDetails.orderedFileCopies.last.uri,
59+
1,
60+
runDetails.orderedFileCopies.last.content,
61+
),
62+
);
63+
64+
var lastFileLines = runDetails.orderedFileCopies.last.content.split('\n');
65+
var foundLine = lastFileLines.indexWhere(
66+
(line) => line.contains('void appendBeginGroup(TokenType type) {'),
67+
);
68+
69+
// Delaying a bit here seems to make it more consistant..?
70+
await Future.delayed(const Duration(milliseconds: 200));
71+
72+
// Change last file: Add a print with a missing end brace on the string
73+
// interpolation.
74+
await send(
75+
LspMessages.didChange(
76+
runDetails.orderedFileCopies.last.uri,
77+
version: 2,
78+
insertAtLine: foundLine + 1,
79+
insert: r' print("${type.runtimeTyhello world");\n',
80+
),
81+
);
82+
83+
Future<Map<String, dynamic>> completionFuture = (await send(
84+
LspMessages.completion(
85+
runDetails.orderedFileCopies.last.uri,
86+
largestIdSeen + 1,
87+
line: foundLine + 1,
88+
character: 25 /* inside the 'runtimeTy' somewhere */,
89+
),
90+
))!.completer.future;
91+
92+
var stopwatch = Stopwatch()..start();
93+
var completionResponse = await completionFuture;
94+
List<dynamic> completionItems =
95+
completionResponse['result']['items'] as List;
96+
var completionAfterChange = stopwatch.elapsed;
97+
durationInfo.add(
98+
DurationInfo('Completion after change', completionAfterChange),
99+
);
100+
if (verbosity >= 0) {
101+
print(
102+
'Got ${completionItems.length} completion items '
103+
'in $completionAfterChange',
104+
);
105+
}
106+
if (latestIsAnalyzing) {
107+
await waitWhileAnalyzing();
108+
}
109+
stopwatch.stop();
110+
var doneAfterChange = stopwatch.elapsed;
111+
durationInfo.add(DurationInfo('Fully done after change', doneAfterChange));
112+
if (verbosity >= 0) {
113+
print('Fully done after $doneAfterChange');
114+
}
115+
}
116+
}

pkg/analysis_server/tool/benchmark_tools/language_server_benchmark.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ abstract class DartLanguageServerBenchmark {
2424
int? _headerContentLength;
2525
bool _printedVmServiceStuff = false;
2626
final String executableToUse;
27+
bool latestIsAnalyzing = false;
2728

2829
/// There's something weird about getting (several) id 3's that wasn't
2930
/// requested...
@@ -405,6 +406,7 @@ abstract class DartLanguageServerBenchmark {
405406
? params['isAnalyzing']
406407
: params['analysis']?['isAnalyzing'];
407408
if (isAnalyzing is bool) {
409+
latestIsAnalyzing = isAnalyzing;
408410
_analyzingCompleter.complete(isAnalyzing);
409411
_analyzingCompleter = Completer<bool>();
410412
if (verbosity > 0) {

0 commit comments

Comments
 (0)