|
| 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 '../legacy_messages.dart'; |
| 7 | +import 'utils.dart'; |
| 8 | + |
| 9 | +/// At least until "https://github.com/flutter/flutter-intellij/issues/7980" is |
| 10 | +/// fixed, when a user in IntelliJ in a Flutter project opens a file it's added |
| 11 | +/// to a list and never gets off that list. Whenever a file is added to the list |
| 12 | +/// (i.e. every time the user opens a file it hasn't opened before) a |
| 13 | +/// "flutter.setSubscriptions" request is sent, with every file on the list. |
| 14 | +/// At the same time a "edit.getAssists" request for the newly opened file is |
| 15 | +/// issued (as well as a few others). |
| 16 | +/// While processing the "edit.getAssists" request it (currently) has to wait |
| 17 | +/// for the processing of all the requested files from the |
| 18 | +/// "flutter.setSubscriptions" request, and any subsequent request is not |
| 19 | +/// handled before after the "edit.getAssists" is done processing. |
| 20 | +/// Said anther way: No user requests are handled before all the files have |
| 21 | +/// been processed. |
| 22 | +/// While the list will start out empty, opening many files in a session is a |
| 23 | +/// natural way of working so it will over type become many files and this |
| 24 | +/// benchmark is thus not completely unrealistic. |
| 25 | +Future<void> main(List<String> args) async { |
| 26 | + await runHelper( |
| 27 | + args, |
| 28 | + LegacyManyFilesInFlutterSetSubscriptionsBenchmark.new, |
| 29 | + runAsLsp: false, |
| 30 | + ); |
| 31 | +} |
| 32 | + |
| 33 | +class LegacyManyFilesInFlutterSetSubscriptionsBenchmark |
| 34 | + extends DartLanguageServerBenchmark { |
| 35 | + @override |
| 36 | + final Uri rootUri; |
| 37 | + @override |
| 38 | + final Uri cacheFolder; |
| 39 | + |
| 40 | + final RunDetails runDetails; |
| 41 | + |
| 42 | + LegacyManyFilesInFlutterSetSubscriptionsBenchmark( |
| 43 | + super.args, |
| 44 | + this.rootUri, |
| 45 | + this.cacheFolder, |
| 46 | + this.runDetails, |
| 47 | + ) : super(useLspProtocol: false); |
| 48 | + |
| 49 | + @override |
| 50 | + LaunchFrom get launchFrom => LaunchFrom.Dart; |
| 51 | + |
| 52 | + @override |
| 53 | + Future<void> afterInitialization() async { |
| 54 | + // The main file is an open tab. |
| 55 | + await send( |
| 56 | + LegacyMessages.setPriorityFiles(largestIdSeen + 1, [ |
| 57 | + runDetails.mainFile.uri, |
| 58 | + ]), |
| 59 | + ); |
| 60 | + |
| 61 | + // Does this change anything? |
| 62 | + await Future.delayed(const Duration(milliseconds: 100)); |
| 63 | + |
| 64 | + // The user starts typing - types 'ge' in the empty line in main... |
| 65 | + await send( |
| 66 | + LegacyMessages.updateContent( |
| 67 | + largestIdSeen + 1, |
| 68 | + runDetails.mainFile.uri, |
| 69 | + runDetails.mainFileTypingContent, |
| 70 | + ), |
| 71 | + ); |
| 72 | + |
| 73 | + // ...and ask for completion. |
| 74 | + var firstCompletionStopwatch = Stopwatch()..start(); |
| 75 | + var completion1Result = |
| 76 | + await (await send( |
| 77 | + LegacyMessages.getSuggestions2( |
| 78 | + largestIdSeen + 1, |
| 79 | + runDetails.mainFile.uri, |
| 80 | + runDetails.typingAtOffset, |
| 81 | + ), |
| 82 | + ))!.completer.future; |
| 83 | + firstCompletionStopwatch.stop(); |
| 84 | + List<dynamic> completion1Items = |
| 85 | + completion1Result['result']['suggestions'] as List; |
| 86 | + durationInfo.add( |
| 87 | + DurationInfo( |
| 88 | + 'Completion without opening files', |
| 89 | + firstCompletionStopwatch.elapsed, |
| 90 | + ), |
| 91 | + ); |
| 92 | + if (verbosity >= 0) { |
| 93 | + print( |
| 94 | + 'Got ${completion1Items.length} completion items ' |
| 95 | + 'in ${firstCompletionStopwatch.elapsed}', |
| 96 | + ); |
| 97 | + } |
| 98 | + |
| 99 | + // The user wants to check something. Pretending we've already opened all |
| 100 | + // (other) files previously, we're now opening the last one. |
| 101 | + await send( |
| 102 | + LegacyMessages.getFlutterSetSubscriptions( |
| 103 | + largestIdSeen + 1, |
| 104 | + runDetails.orderedFileCopies.map((copy) => copy.uri).toList(), |
| 105 | + ), |
| 106 | + ); |
| 107 | + |
| 108 | + // So now we have both the main file and the last file open in tabs. |
| 109 | + await send( |
| 110 | + LegacyMessages.setPriorityFiles(largestIdSeen + 1, [ |
| 111 | + runDetails.mainFile.uri, |
| 112 | + runDetails.orderedFileCopies.last.uri, |
| 113 | + ]), |
| 114 | + ); |
| 115 | + |
| 116 | + // The IDE sends a "edit.getAssists" request for the newly opened file at |
| 117 | + // offset 0 length 0. |
| 118 | + var getAssistsStopwatch = Stopwatch()..start(); |
| 119 | + var getAssistsFuture = (await send( |
| 120 | + LegacyMessages.getAssists( |
| 121 | + largestIdSeen + 1, |
| 122 | + runDetails.orderedFileCopies.last.uri, |
| 123 | + 0, |
| 124 | + ), |
| 125 | + ))!.completer.future.then((_) => getAssistsStopwatch.stop()); |
| 126 | + |
| 127 | + // The user tabs back into the main file and ask for completion again. |
| 128 | + Future<Map<String, dynamic>> completionFuture = |
| 129 | + (await send( |
| 130 | + LegacyMessages.getSuggestions2( |
| 131 | + largestIdSeen + 1, |
| 132 | + runDetails.mainFile.uri, |
| 133 | + runDetails.typingAtOffset, |
| 134 | + ), |
| 135 | + ))!.completer.future; |
| 136 | + |
| 137 | + Stopwatch stopwatch = Stopwatch()..start(); |
| 138 | + var completionResponse = await completionFuture; |
| 139 | + List<dynamic> completionItems = |
| 140 | + completionResponse['result']['suggestions'] as List; |
| 141 | + var completionAfterChange = stopwatch.elapsed; |
| 142 | + durationInfo.add( |
| 143 | + DurationInfo('Completion after open of new file', completionAfterChange), |
| 144 | + ); |
| 145 | + if (verbosity >= 0) { |
| 146 | + print( |
| 147 | + 'Got ${completionItems.length} completion items ' |
| 148 | + 'in $completionAfterChange', |
| 149 | + ); |
| 150 | + } |
| 151 | + |
| 152 | + // This should be complete already. |
| 153 | + await getAssistsFuture; |
| 154 | + |
| 155 | + durationInfo.add( |
| 156 | + DurationInfo('getAssists call', getAssistsStopwatch.elapsed), |
| 157 | + ); |
| 158 | + } |
| 159 | +} |
0 commit comments