Skip to content

Commit b1c6707

Browse files
jensjohaCommit Queue
authored andcommitted
[analysis server] Make LSP change request processing faster
When processing a change request we have to construct the new content for the changed file from the "diff" we receive. Before this CL we calcualted a `LineInfo` to normally only use 1 entry in the list, then calculated the new String, then calculated the new String once more. This CL only gets the offset of the line we actually need (in what I assume to be the normal case of only needing 1 line) and only calculates the new string once for a reduction in processing time of about 50%. For small files it probably wont matter much, but for bigger files it will likely be noticeable. To get the actual data in the benchmark this CL introduces `asJson` in the timing pages, and picks out of the performance data the "real number" for the change request. Turns out the overall number can be very wrong because there's an async gap (where other stuff can be happening) between the calculation being done and the timing being stopped. Data: Original -> Not doing the line starts: Difference at 95.0% confidence -284.7 +/- 35.8987 -33.0624% +/- 4.16893% (Student's t, pooled s = 38.2065) Not doing the line starts -> also not doing the newContent calculation twice: Difference at 95.0% confidence -207.2 +/- 31.3105 -35.9473% +/- 5.43207% (Student's t, pooled s = 33.3233) In total, i.e. from original -> this CL: Difference at 95.0% confidence -491.9 +/- 34.8949 -57.1246% +/- 4.05237% (Student's t, pooled s = 37.1383) This is with a size of 16,000 in the benchmark, but the processing should be linear so the ~57% decrease should be valid overall - although naturally in raw numbers it's not going to cut of ~half a second per 25 change requests in smaller files. Raw data: ``` Original: {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":830,"size 16000: peak virtual memory size (kb)":2473160,"size 16000: total program size (virtual) (kb)":2309328,"size 16000: peak resident set size (\"high water mark\") (kb)":540228,"size 16000: size of memory portions (rss) (kb)":373328} {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":938,"size 16000: peak virtual memory size (kb)":2365488,"size 16000: total program size (virtual) (kb)":2306336,"size 16000: peak resident set size (\"high water mark\") (kb)":509212,"size 16000: size of memory portions (rss) (kb)":438796} {"size 16000: Initial analysis (ms)":7,"size 16000: Processing time for 25 change requests (ms)":900,"size 16000: peak virtual memory size (kb)":2354420,"size 16000: total program size (virtual) (kb)":2300740,"size 16000: peak resident set size (\"high water mark\") (kb)":496768,"size 16000: size of memory portions (rss) (kb)":443892} {"size 16000: Initial analysis (ms)":9,"size 16000: Processing time for 25 change requests (ms)":803,"size 16000: peak virtual memory size (kb)":2296448,"size 16000: total program size (virtual) (kb)":2223740,"size 16000: peak resident set size (\"high water mark\") (kb)":503040,"size 16000: size of memory portions (rss) (kb)":431608} {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":821,"size 16000: peak virtual memory size (kb)":2315888,"size 16000: total program size (virtual) (kb)":2158996,"size 16000: peak resident set size (\"high water mark\") (kb)":520212,"size 16000: size of memory portions (rss) (kb)":360704} {"size 16000: Initial analysis (ms)":15,"size 16000: Processing time for 25 change requests (ms)":835,"size 16000: peak virtual memory size (kb)":2323752,"size 16000: total program size (virtual) (kb)":2215748,"size 16000: peak resident set size (\"high water mark\") (kb)":534380,"size 16000: size of memory portions (rss) (kb)":416532} {"size 16000: Initial analysis (ms)":8,"size 16000: Processing time for 25 change requests (ms)":848,"size 16000: peak virtual memory size (kb)":2349108,"size 16000: total program size (virtual) (kb)":2219696,"size 16000: peak resident set size (\"high water mark\") (kb)":469712,"size 16000: size of memory portions (rss) (kb)":361812} {"size 16000: Initial analysis (ms)":7,"size 16000: Processing time for 25 change requests (ms)":870,"size 16000: peak virtual memory size (kb)":2288416,"size 16000: total program size (virtual) (kb)":2204484,"size 16000: peak resident set size (\"high water mark\") (kb)":486716,"size 16000: size of memory portions (rss) (kb)":396940} {"size 16000: Initial analysis (ms)":9,"size 16000: Processing time for 25 change requests (ms)":895,"size 16000: peak virtual memory size (kb)":2280008,"size 16000: total program size (virtual) (kb)":2188336,"size 16000: peak resident set size (\"high water mark\") (kb)":476664,"size 16000: size of memory portions (rss) (kb)":398232} {"size 16000: Initial analysis (ms)":18,"size 16000: Processing time for 25 change requests (ms)":871,"size 16000: peak virtual memory size (kb)":2281128,"size 16000: total program size (virtual) (kb)":2216500,"size 16000: peak resident set size (\"high water mark\") (kb)":477080,"size 16000: size of memory portions (rss) (kb)":426856} No line starts: {"size 16000: Initial analysis (ms)":8,"size 16000: Processing time for 25 change requests (ms)":633,"size 16000: peak virtual memory size (kb)":2279560,"size 16000: total program size (virtual) (kb)":2169640,"size 16000: peak resident set size (\"high water mark\") (kb)":467740,"size 16000: size of memory portions (rss) (kb)":365508} {"size 16000: Initial analysis (ms)":9,"size 16000: Processing time for 25 change requests (ms)":578,"size 16000: peak virtual memory size (kb)":2481552,"size 16000: total program size (virtual) (kb)":2307804,"size 16000: peak resident set size (\"high water mark\") (kb)":547824,"size 16000: size of memory portions (rss) (kb)":370972} {"size 16000: Initial analysis (ms)":6,"size 16000: Processing time for 25 change requests (ms)":568,"size 16000: peak virtual memory size (kb)":2412752,"size 16000: total program size (virtual) (kb)":2301564,"size 16000: peak resident set size (\"high water mark\") (kb)":546168,"size 16000: size of memory portions (rss) (kb)":447020} {"size 16000: Initial analysis (ms)":5,"size 16000: Processing time for 25 change requests (ms)":530,"size 16000: peak virtual memory size (kb)":2426432,"size 16000: total program size (virtual) (kb)":2364240,"size 16000: peak resident set size (\"high water mark\") (kb)":565100,"size 16000: size of memory portions (rss) (kb)":503364} {"size 16000: Initial analysis (ms)":12,"size 16000: Processing time for 25 change requests (ms)":620,"size 16000: peak virtual memory size (kb)":2276668,"size 16000: total program size (virtual) (kb)":2226296,"size 16000: peak resident set size (\"high water mark\") (kb)":477488,"size 16000: size of memory portions (rss) (kb)":434240} {"size 16000: Initial analysis (ms)":12,"size 16000: Processing time for 25 change requests (ms)":534,"size 16000: peak virtual memory size (kb)":2391200,"size 16000: total program size (virtual) (kb)":2349392,"size 16000: peak resident set size (\"high water mark\") (kb)":531700,"size 16000: size of memory portions (rss) (kb)":490400} {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":588,"size 16000: peak virtual memory size (kb)":2377112,"size 16000: total program size (virtual) (kb)":2278476,"size 16000: peak resident set size (\"high water mark\") (kb)":518740,"size 16000: size of memory portions (rss) (kb)":412952} {"size 16000: Initial analysis (ms)":10,"size 16000: Processing time for 25 change requests (ms)":589,"size 16000: peak virtual memory size (kb)":2373204,"size 16000: total program size (virtual) (kb)":2258000,"size 16000: peak resident set size (\"high water mark\") (kb)":516188,"size 16000: size of memory portions (rss) (kb)":390972} {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":583,"size 16000: peak virtual memory size (kb)":2349184,"size 16000: total program size (virtual) (kb)":2206488,"size 16000: peak resident set size (\"high water mark\") (kb)":555888,"size 16000: size of memory portions (rss) (kb)":404356} {"size 16000: Initial analysis (ms)":9,"size 16000: Processing time for 25 change requests (ms)":541,"size 16000: peak virtual memory size (kb)":2335140,"size 16000: total program size (virtual) (kb)":2227132,"size 16000: peak resident set size (\"high water mark\") (kb)":540204,"size 16000: size of memory portions (rss) (kb)":433256} Additionally don't do it twice: {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":379,"size 16000: peak virtual memory size (kb)":2274520,"size 16000: total program size (virtual) (kb)":2197944,"size 16000: peak resident set size (\"high water mark\") (kb)":470816,"size 16000: size of memory portions (rss) (kb)":393124} {"size 16000: Initial analysis (ms)":8,"size 16000: Processing time for 25 change requests (ms)":431,"size 16000: peak virtual memory size (kb)":2353820,"size 16000: total program size (virtual) (kb)":2287616,"size 16000: peak resident set size (\"high water mark\") (kb)":494148,"size 16000: size of memory portions (rss) (kb)":428836} {"size 16000: Initial analysis (ms)":15,"size 16000: Processing time for 25 change requests (ms)":334,"size 16000: peak virtual memory size (kb)":2210904,"size 16000: total program size (virtual) (kb)":2164796,"size 16000: peak resident set size (\"high water mark\") (kb)":472180,"size 16000: size of memory portions (rss) (kb)":437368} {"size 16000: Initial analysis (ms)":16,"size 16000: Processing time for 25 change requests (ms)":364,"size 16000: peak virtual memory size (kb)":2284260,"size 16000: total program size (virtual) (kb)":2239292,"size 16000: peak resident set size (\"high water mark\") (kb)":482420,"size 16000: size of memory portions (rss) (kb)":437232} {"size 16000: Initial analysis (ms)":8,"size 16000: Processing time for 25 change requests (ms)":397,"size 16000: peak virtual memory size (kb)":2148164,"size 16000: total program size (virtual) (kb)":2118072,"size 16000: peak resident set size (\"high water mark\") (kb)":479676,"size 16000: size of memory portions (rss) (kb)":446752} {"size 16000: Initial analysis (ms)":5,"size 16000: Processing time for 25 change requests (ms)":327,"size 16000: peak virtual memory size (kb)":2292012,"size 16000: total program size (virtual) (kb)":2269972,"size 16000: peak resident set size (\"high water mark\") (kb)":487932,"size 16000: size of memory portions (rss) (kb)":476524} {"size 16000: Initial analysis (ms)":8,"size 16000: Processing time for 25 change requests (ms)":339,"size 16000: peak virtual memory size (kb)":2348844,"size 16000: total program size (virtual) (kb)":2286660,"size 16000: peak resident set size (\"high water mark\") (kb)":484112,"size 16000: size of memory portions (rss) (kb)":432036} {"size 16000: Initial analysis (ms)":16,"size 16000: Processing time for 25 change requests (ms)":393,"size 16000: peak virtual memory size (kb)":2277080,"size 16000: total program size (virtual) (kb)":2248636,"size 16000: peak resident set size (\"high water mark\") (kb)":472864,"size 16000: size of memory portions (rss) (kb)":440092} {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":365,"size 16000: peak virtual memory size (kb)":2397828,"size 16000: total program size (virtual) (kb)":2201648,"size 16000: peak resident set size (\"high water mark\") (kb)":530176,"size 16000: size of memory portions (rss) (kb)":334264} {"size 16000: Initial analysis (ms)":11,"size 16000: Processing time for 25 change requests (ms)":363,"size 16000: peak virtual memory size (kb)":2286312,"size 16000: total program size (virtual) (kb)":2253760,"size 16000: peak resident set size (\"high water mark\") (kb)":480004,"size 16000: size of memory portions (rss) (kb)":464720} ``` Change-Id: If122d4667da677fc07cab83ae97135d3a7ac99d9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/457722 Reviewed-by: Brian Wilkerson <[email protected]> Reviewed-by: Johnni Winther <[email protected]> Commit-Queue: Jens Johansen <[email protected]>
1 parent 8b3e11a commit b1c6707

File tree

13 files changed

+393
-47
lines changed

13 files changed

+393
-47
lines changed

pkg/analysis_server/lib/src/lsp/handlers/handler_text_document_changes.dart

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ import 'package:analysis_server/src/lsp/handlers/handlers.dart';
1111
import 'package:analysis_server/src/lsp/registration/feature_registration.dart';
1212
import 'package:analysis_server/src/lsp/source_edits.dart';
1313

14+
const String textDocumentChangeHandlerPerformanceCaption =
15+
'change file request processing';
16+
1417
typedef StaticOptions = Either2<TextDocumentSyncKind, TextDocumentSyncOptions>;
1518

1619
class TextDocumentChangeHandler
@@ -29,15 +32,20 @@ class TextDocumentChangeHandler
2932
MessageInfo message,
3033
CancellationToken token,
3134
) {
32-
var doc = params.textDocument;
33-
// Editors should never try to change our macro files, but just in case
34-
// we get these requests, ignore them.
35-
if (!isEditableDocument(doc.uri)) {
36-
return success(null);
37-
}
38-
39-
var path = pathOfDoc(doc);
40-
return path.mapResultSync((path) => _changeFile(path, params));
35+
return message.performance.run(
36+
textDocumentChangeHandlerPerformanceCaption,
37+
(_) {
38+
var doc = params.textDocument;
39+
// Editors should never try to change our macro files, but just in case
40+
// we get these requests, ignore them.
41+
if (!isEditableDocument(doc.uri)) {
42+
return success(null);
43+
}
44+
45+
var path = pathOfDoc(doc);
46+
return path.mapResultSync((path) => _changeFile(path, params));
47+
},
48+
);
4149
}
4250

4351
ErrorOr<void> _changeFile(String path, DidChangeTextDocumentParams params) {

pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -660,28 +660,25 @@ class LspAnalysisServer extends AnalysisServer {
660660
}
661661

662662
/// Updates an overlay on [path] by applying the [edits] to the current
663-
/// overlay.
664-
///
665-
/// If the result of applying the edits is already known, [newContent] can be
666-
/// set to avoid doing that calculation twice.
663+
/// overlay with the result [newContent].
667664
void onOverlayUpdated(
668665
String path,
669666
List<plugin.SourceEdit> edits, {
670-
String? newContent,
667+
required String newContent,
671668
}) {
672669
assert(resourceProvider.hasOverlay(path));
673-
if (newContent == null) {
674-
var oldContent = resourceProvider.getFile(path).readAsStringSync();
675-
newContent = plugin.applySequenceOfEdits(oldContent, edits);
676-
}
677670

678671
resourceProvider.setOverlay(
679672
path,
680673
content: newContent,
681674
modificationStamp: overlayModificationStamp++,
682675
);
683676

684-
_afterOverlayChanged(path, plugin.ChangeContentOverlay(edits));
677+
_afterOverlayChanged(
678+
path,
679+
plugin.ChangeContentOverlay(edits),
680+
precomputedNewContentForChange: newContent,
681+
);
685682
}
686683

687684
void publishClosingLabels(String path, List<ClosingLabel> labels) {
@@ -1034,11 +1031,19 @@ class LspAnalysisServer extends AnalysisServer {
10341031
await _refreshAnalysisRoots();
10351032
}
10361033

1037-
void _afterOverlayChanged(String path, plugin.HasToJson changeForPlugins) {
1034+
void _afterOverlayChanged(
1035+
String path,
1036+
plugin.HasToJson changeForPlugins, {
1037+
String? precomputedNewContentForChange,
1038+
}) {
10381039
for (var driver in driverMap.values) {
10391040
driver.changeFile(path);
10401041
}
1041-
_notifyPluginsOverlayChanged(path, changeForPlugins);
1042+
_notifyPluginsOverlayChanged(
1043+
path,
1044+
changeForPlugins,
1045+
precomputedNewContentForChange: precomputedNewContentForChange,
1046+
);
10421047

10431048
notifyDeclarationsTracker(path);
10441049
notifyFlutterWidgetDescriptions(path);
@@ -1112,10 +1117,12 @@ class LspAnalysisServer extends AnalysisServer {
11121117

11131118
void _notifyPluginsOverlayChanged(
11141119
String path,
1115-
plugin.HasToJson changeForPlugins,
1116-
) {
1120+
plugin.HasToJson changeForPlugins, {
1121+
String? precomputedNewContentForChange,
1122+
}) {
11171123
pluginManager.setAnalysisUpdateContentParams(
11181124
plugin.AnalysisUpdateContentParams({path: changeForPlugins}),
1125+
precomputedNewContentForChange: precomputedNewContentForChange,
11191126
);
11201127
}
11211128

pkg/analysis_server/lib/src/lsp/mapping.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,26 @@ ErrorOr<int> toOffset(
14731473
);
14741474
}
14751475

1476+
ErrorOr<int> toOffsetFromOffsetLineAndColumn(
1477+
int? offsetOfLine,
1478+
int column,
1479+
int originalLine, {
1480+
bool failureIsCritical = false,
1481+
}) {
1482+
if (offsetOfLine == null) {
1483+
return ErrorOr<int>.error(
1484+
lsp.ResponseError(
1485+
code: failureIsCritical
1486+
? lsp.ServerErrorCodes.ClientServerInconsistentState
1487+
: lsp.ServerErrorCodes.InvalidFileLineCol,
1488+
message: 'Invalid line number',
1489+
data: originalLine.toString(),
1490+
),
1491+
);
1492+
}
1493+
return ErrorOr<int>.success(offsetOfLine + column);
1494+
}
1495+
14761496
lsp.Outline toOutline(server.LineInfo lineInfo, server.Outline outline) {
14771497
var children = outline.children;
14781498
return lsp.Outline(

pkg/analysis_server/lib/src/lsp/source_edits.dart

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,40 @@ applyAndConvertEditsToServer(
4545
// TextDocumentContentChangeEvent1
4646
// {range, text}
4747
(change) {
48-
var lines = LineInfo.fromContent(newContent);
49-
var offsetStart = toOffset(
50-
lines,
51-
change.range.start,
52-
failureIsCritical: failureIsCritical,
53-
);
54-
var offsetEnd = toOffset(
55-
lines,
56-
change.range.end,
57-
failureIsCritical: failureIsCritical,
58-
);
48+
ErrorOr<int> offsetStart;
49+
ErrorOr<int> offsetEnd;
50+
51+
var start = change.range.start;
52+
var end = change.range.end;
53+
if (start.line == end.line) {
54+
// We only need a single line, so we skip the creation of `LineInfo`
55+
// which is faster.
56+
var offsetOfLine = LineInfo.getOffsetForLine(start.line, newContent);
57+
offsetStart = toOffsetFromOffsetLineAndColumn(
58+
offsetOfLine,
59+
start.character,
60+
start.line,
61+
failureIsCritical: failureIsCritical,
62+
);
63+
offsetEnd = toOffsetFromOffsetLineAndColumn(
64+
offsetOfLine,
65+
end.character,
66+
end.line,
67+
failureIsCritical: failureIsCritical,
68+
);
69+
} else {
70+
var lines = LineInfo.fromContent(newContent);
71+
offsetStart = toOffset(
72+
lines,
73+
start,
74+
failureIsCritical: failureIsCritical,
75+
);
76+
offsetEnd = toOffset(
77+
lines,
78+
end,
79+
failureIsCritical: failureIsCritical,
80+
);
81+
}
5982
if (offsetStart.isError) {
6083
return failure(offsetStart);
6184
}

pkg/analysis_server/lib/src/plugin/plugin_manager.dart

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -432,7 +432,10 @@ class PluginManager {
432432
/// content overlays to those specified by the [params]. As a side-effect,
433433
/// update the overlay state so that it can be sent to any newly started
434434
/// plugins.
435-
void setAnalysisUpdateContentParams(AnalysisUpdateContentParams params) {
435+
void setAnalysisUpdateContentParams(
436+
AnalysisUpdateContentParams params, {
437+
String? precomputedNewContentForChange,
438+
}) {
436439
for (var plugin in _pluginMap.values) {
437440
plugin.sendRequest(params);
438441
}
@@ -444,12 +447,18 @@ class PluginManager {
444447
} else if (overlay is AddContentOverlay) {
445448
_overlayState[file] = overlay;
446449
} else if (overlay is ChangeContentOverlay) {
447-
var previousOverlay = _overlayState[file]!;
448-
var newContent = SourceEdit.applySequence(
449-
previousOverlay.content,
450-
overlay.edits,
451-
);
452-
_overlayState[file] = AddContentOverlay(newContent);
450+
if (precomputedNewContentForChange != null) {
451+
_overlayState[file] = AddContentOverlay(
452+
precomputedNewContentForChange,
453+
);
454+
} else {
455+
var previousOverlay = _overlayState[file]!;
456+
var newContent = SourceEdit.applySequence(
457+
previousOverlay.content,
458+
overlay.edits,
459+
);
460+
_overlayState[file] = AddContentOverlay(newContent);
461+
}
453462
} else {
454463
throw ArgumentError('Invalid class of overlay: ${overlay.runtimeType}');
455464
}

pkg/analysis_server/lib/src/status/pages/timing_page.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io';
68

79
import 'package:analysis_server/src/status/diagnostics.dart';
810
import 'package:analysis_server/src/status/pages.dart';
@@ -13,6 +15,14 @@ class TimingPage extends DiagnosticPageWithNav with PerformanceChartMixin {
1315
TimingPage(DiagnosticsSite site)
1416
: super(site, 'timing', 'Timing', description: 'Timing statistics.');
1517

18+
@override
19+
ContentType contentType(Map<String, String> params) {
20+
if (params['asJson'] != null) {
21+
return ContentType.json;
22+
}
23+
return super.contentType(params);
24+
}
25+
1626
@override
1727
Future<void> generateContent(Map<String, String> params) async {
1828
var kind = params['kind'];
@@ -40,6 +50,26 @@ class TimingPage extends DiagnosticPageWithNav with PerformanceChartMixin {
4050
}
4151
}
4252

53+
@override
54+
Future<void> generatePage(Map<String, String> params) async {
55+
if (params['asJson'] != null) {
56+
var data = [];
57+
// No added header etc.
58+
for (var item in server.recentPerformance.requests.items.toList()) {
59+
data.add({
60+
'id': item.id,
61+
'operation': item.operation,
62+
'elapsedMs': item.performance.elapsed.inMilliseconds,
63+
'latency': item.requestLatency,
64+
'performance': item.performance.toJson(),
65+
});
66+
}
67+
buf.write(json.encode(data));
68+
return;
69+
}
70+
return await super.generatePage(params);
71+
}
72+
4373
void _emitTable(List<RequestPerformance> items) {
4474
buf.writeln('<table>');
4575
buf.writeln('<tr><th>Time</th><th>Request</th></tr>');

pkg/analysis_server/lib/src/utilities/mocks.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,8 +298,9 @@ class TestPluginManager implements PluginManager {
298298

299299
@override
300300
void setAnalysisUpdateContentParams(
301-
plugin.AnalysisUpdateContentParams params,
302-
) {
301+
plugin.AnalysisUpdateContentParams params, {
302+
String? precomputedNewContentForChange,
303+
}) {
303304
analysisUpdateContentParams = params;
304305
}
305306
}

pkg/analysis_server/tool/benchmark_tools/lsp_messages.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ class LspMessages {
9494
};
9595
}
9696

97+
static Map<String, dynamic> diagnosticServer(int id) {
98+
return {
99+
'jsonrpc': '2.0',
100+
'id': id,
101+
'method': 'dart/diagnosticServer',
102+
'clientRequestTime': DateTime.now().millisecondsSinceEpoch,
103+
};
104+
}
105+
97106
static Map<String, dynamic> didChange(
98107
Uri uri, {
99108
required int version,

0 commit comments

Comments
 (0)