diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart index b28472cc00c..699159caebf 100644 --- a/pkg/analysis_server/lib/src/analysis_server.dart +++ b/pkg/analysis_server/lib/src/analysis_server.dart @@ -29,9 +29,7 @@ import 'package:analysis_server/src/protocol_server.dart' as server; import 'package:analysis_server/src/server/crash_reporting_attachments.dart'; import 'package:analysis_server/src/server/diagnostic_server.dart'; import 'package:analysis_server/src/server/message_scheduler.dart'; -import 'package:analysis_server/src/server/performance.dart'; import 'package:analysis_server/src/services/completion/completion_performance.dart'; -import 'package:analysis_server/src/services/correction/assist_performance.dart'; import 'package:analysis_server/src/services/correction/fix_performance.dart'; import 'package:analysis_server/src/services/correction/namespace.dart'; import 'package:analysis_server/src/services/dart_tooling_daemon/dtd_services.dart'; @@ -50,6 +48,8 @@ import 'package:analysis_server/src/utilities/process.dart'; import 'package:analysis_server/src/utilities/request_statistics.dart'; import 'package:analysis_server/src/utilities/tee_string_sink.dart'; import 'package:analysis_server/src/utilities/timing_byte_store.dart'; +import 'package:analysis_server_plugin/src/correction/assist_performance.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/analysis/session.dart'; import 'package:analyzer/dart/ast/ast.dart'; diff --git a/pkg/analysis_server/lib/src/cider/assists.dart b/pkg/analysis_server/lib/src/cider/assists.dart index fccffcd8ee1..b6c48f784bf 100644 --- a/pkg/analysis_server/lib/src/cider/assists.dart +++ b/pkg/analysis_server/lib/src/cider/assists.dart @@ -2,9 +2,9 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/plugin/edit/assist/assist_core.dart'; import 'package:analysis_server/src/services/correction/assist.dart'; -import 'package:analysis_server/src/services/correction/assist_internal.dart'; +import 'package:analysis_server_plugin/src/correction/assist_core.dart'; +import 'package:analysis_server_plugin/src/correction/assist_processor.dart'; import 'package:analysis_server_plugin/src/correction/dart_change_workspace.dart'; import 'package:analyzer/dart/analysis/session.dart'; import 'package:analyzer/instrumentation/service.dart'; diff --git a/pkg/analysis_server/lib/src/handler/legacy/edit_get_assists.dart b/pkg/analysis_server/lib/src/handler/legacy/edit_get_assists.dart index b3ef6362bd4..c9e523a2780 100644 --- a/pkg/analysis_server/lib/src/handler/legacy/edit_get_assists.dart +++ b/pkg/analysis_server/lib/src/handler/legacy/edit_get_assists.dart @@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:analysis_server/plugin/edit/assist/assist_core.dart'; import 'package:analysis_server/protocol/protocol.dart'; import 'package:analysis_server/protocol/protocol_generated.dart'; import 'package:analysis_server/src/handler/legacy/legacy_handler.dart'; @@ -12,8 +11,9 @@ import 'package:analysis_server/src/legacy_analysis_server.dart'; import 'package:analysis_server/src/plugin/result_converter.dart'; import 'package:analysis_server/src/request_handler_mixin.dart'; import 'package:analysis_server/src/services/correction/assist.dart'; -import 'package:analysis_server/src/services/correction/assist_internal.dart'; -import 'package:analysis_server/src/services/correction/assist_performance.dart'; +import 'package:analysis_server_plugin/src/correction/assist_core.dart'; +import 'package:analysis_server_plugin/src/correction/assist_performance.dart'; +import 'package:analysis_server_plugin/src/correction/assist_processor.dart'; import 'package:analysis_server_plugin/src/correction/dart_change_workspace.dart'; import 'package:analyzer/dart/analysis/session.dart'; import 'package:analyzer/src/exception/exception.dart'; diff --git a/pkg/analysis_server/lib/src/legacy_analysis_server.dart b/pkg/analysis_server/lib/src/legacy_analysis_server.dart index 3ecb9b0d75a..0717609ecb6 100644 --- a/pkg/analysis_server/lib/src/legacy_analysis_server.dart +++ b/pkg/analysis_server/lib/src/legacy_analysis_server.dart @@ -91,7 +91,6 @@ import 'package:analysis_server/src/server/diagnostic_server.dart'; import 'package:analysis_server/src/server/error_notifier.dart'; import 'package:analysis_server/src/server/features.dart'; import 'package:analysis_server/src/server/message_scheduler.dart'; -import 'package:analysis_server/src/server/performance.dart'; import 'package:analysis_server/src/server/sdk_configuration.dart'; import 'package:analysis_server/src/services/completion/completion_state.dart'; import 'package:analysis_server/src/services/execution/execution_context.dart'; @@ -99,6 +98,7 @@ import 'package:analysis_server/src/services/flutter/widget_descriptions.dart'; import 'package:analysis_server/src/services/refactoring/legacy/refactoring_manager.dart'; import 'package:analysis_server/src/utilities/extensions/resource_provider.dart'; import 'package:analysis_server/src/utilities/process.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/analysis/session.dart'; import 'package:analyzer/dart/ast/ast.dart'; diff --git a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart index aa52be72c72..9301a3ed2b7 100644 --- a/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart +++ b/pkg/analysis_server/lib/src/lsp/handlers/code_actions/dart.dart @@ -5,20 +5,20 @@ import 'dart:async'; import 'package:analysis_server/lsp_protocol/protocol.dart'; -import 'package:analysis_server/plugin/edit/assist/assist_core.dart'; import 'package:analysis_server/src/lsp/constants.dart'; import 'package:analysis_server/src/lsp/handlers/code_actions/abstract_code_actions_producer.dart'; import 'package:analysis_server/src/lsp/mapping.dart'; import 'package:analysis_server/src/protocol_server.dart' hide AnalysisOptions, Position; import 'package:analysis_server/src/services/correction/assist.dart'; -import 'package:analysis_server/src/services/correction/assist_internal.dart'; -import 'package:analysis_server/src/services/correction/assist_performance.dart'; import 'package:analysis_server/src/services/correction/fix_performance.dart'; import 'package:analysis_server/src/services/refactoring/framework/refactoring_context.dart'; import 'package:analysis_server/src/services/refactoring/framework/refactoring_processor.dart'; import 'package:analysis_server/src/services/refactoring/legacy/refactoring.dart'; import 'package:analysis_server_plugin/edit/fix/dart_fix_context.dart'; +import 'package:analysis_server_plugin/src/correction/assist_core.dart'; +import 'package:analysis_server_plugin/src/correction/assist_performance.dart'; +import 'package:analysis_server_plugin/src/correction/assist_processor.dart'; import 'package:analysis_server_plugin/src/correction/dart_change_workspace.dart'; import 'package:analysis_server_plugin/src/correction/fix_processor.dart'; import 'package:analyzer/dart/analysis/results.dart'; diff --git a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart index 90c660baf45..94e1951209b 100644 --- a/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart +++ b/pkg/analysis_server/lib/src/lsp/lsp_analysis_server.dart @@ -28,8 +28,8 @@ import 'package:analysis_server/src/server/detachable_filesystem_manager.dart'; import 'package:analysis_server/src/server/diagnostic_server.dart'; import 'package:analysis_server/src/server/error_notifier.dart'; import 'package:analysis_server/src/server/message_scheduler.dart'; -import 'package:analysis_server/src/server/performance.dart'; import 'package:analysis_server/src/utilities/process.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/dart/analysis/session.dart'; import 'package:analyzer/error/error.dart'; @@ -1205,7 +1205,12 @@ class LspAnalysisServer extends AnalysisServer { // TODO(dantup): Consider supporting per-workspace config by // calling workspace/configuration whenever workspace folders change // and caching the config for each one. - : _workspaceFolders.map((root) => resourceProvider.pathContext.join(root, excludePath)), + : _workspaceFolders.map( + (root) => resourceProvider.pathContext.join( + root, + excludePath, + ), + ), ) .map(pathContext.normalize) .toSet(); diff --git a/pkg/analysis_server/lib/src/server/performance.dart b/pkg/analysis_server/lib/src/server/performance.dart deleted file mode 100644 index 031902b3e85..00000000000 --- a/pkg/analysis_server/lib/src/server/performance.dart +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file -// for details. All rights reserved. Use of this source code is governed by a -// BSD-style license that can be found in the LICENSE file. - -import 'package:analysis_server/src/utilities/strings.dart'; -import 'package:analysis_server_plugin/src/correction/performance.dart'; -import 'package:analyzer/src/util/performance/operation_performance.dart'; - -abstract class ProducerRequestPerformance extends RequestPerformance { - final String path; - - final String snippet; - final List producerTimings; - ProducerRequestPerformance({ - required String operation, - required this.path, - required super.performance, - super.requestLatency, - super.startTime, - required String content, - required int offset, - required this.producerTimings, - }) : snippet = addCaretAtOffset(content, offset), - super(operation: 'GetAssists'); - - int get elapsedInMilliseconds => performance.elapsed.inMilliseconds; -} - -class RequestPerformance { - static var _nextId = 1; - final int id; - final OperationPerformance performance; - final int? requestLatency; - final String operation; - final DateTime? startTime; - - RequestPerformance({ - required this.operation, - required this.performance, - this.requestLatency, - this.startTime, - }) : id = _nextId++; -} diff --git a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart index 7ff44d31500..7f82261c699 100644 --- a/pkg/analysis_server/lib/src/services/completion/completion_performance.dart +++ b/pkg/analysis_server/lib/src/services/completion/completion_performance.dart @@ -2,8 +2,8 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/src/server/performance.dart'; -import 'package:analysis_server/src/utilities/strings.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; +import 'package:analysis_server_plugin/src/utilities/string_extensions.dart'; /// Overall performance of a code completion operation. class CompletionPerformance extends RequestPerformance { @@ -18,7 +18,7 @@ class CompletionPerformance extends RequestPerformance { super.requestLatency, required String content, required int offset, - }) : snippet = addCaretAtOffset(content, offset), + }) : snippet = content.withCaretAt(offset), super(operation: 'Completion'); String get computedSuggestionCountStr { diff --git a/pkg/analysis_server/lib/src/services/correction/assist.dart b/pkg/analysis_server/lib/src/services/correction/assist.dart index b5bdcdcb877..23697dff897 100644 --- a/pkg/analysis_server/lib/src/services/correction/assist.dart +++ b/pkg/analysis_server/lib/src/services/correction/assist.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/plugin/edit/assist/assist_dart.dart'; +import 'package:analysis_server_plugin/src/correction/assist_dart.dart'; import 'package:analysis_server_plugin/src/correction/change_workspace.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/instrumentation/service.dart'; diff --git a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart index 1531fe99965..ac92be4cf40 100644 --- a/pkg/analysis_server/lib/src/services/correction/assist_internal.dart +++ b/pkg/analysis_server/lib/src/services/correction/assist_internal.dart @@ -2,10 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/plugin/edit/assist/assist_core.dart'; -import 'package:analysis_server/plugin/edit/assist/assist_dart.dart'; -import 'package:analysis_server/src/services/correction/assist_generators.dart'; -import 'package:analysis_server/src/services/correction/assist_performance.dart'; import 'package:analysis_server/src/services/correction/dart/add_diagnostic_property_reference.dart'; import 'package:analysis_server/src/services/correction/dart/add_digit_separators.dart'; import 'package:analysis_server/src/services/correction/dart/add_return_type.dart'; @@ -76,15 +72,8 @@ import 'package:analysis_server/src/services/correction/dart/split_and_condition import 'package:analysis_server/src/services/correction/dart/split_variable_declaration.dart'; import 'package:analysis_server/src/services/correction/dart/surround_with.dart'; import 'package:analysis_server/src/services/correction/dart/use_curly_braces.dart'; -import 'package:analysis_server_plugin/edit/dart/correction_producer.dart'; +import 'package:analysis_server_plugin/src/correction/assist_generators.dart'; import 'package:analysis_server_plugin/src/correction/fix_generators.dart'; -import 'package:analyzer/error/error.dart'; -import 'package:analyzer/src/dart/ast/utilities.dart'; -import 'package:analyzer/src/generated/java_core.dart'; -import 'package:analyzer_plugin/utilities/assist/assist.dart' - hide AssistContributor; -import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; -import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart'; /// The set of built-in generators used to produce assists. const Set _builtInGenerators = { @@ -178,131 +167,3 @@ void registerBuiltInAssistGenerators() { registeredAssistGenerators.registerMultiGenerator, ); } - -/// The computer for Dart assists. -class AssistProcessor { - final AssistPerformance? _performance; - final DartAssistContext _assistContext; - final Stopwatch _timer = Stopwatch(); - - final List _assists = []; - - AssistProcessor(this._assistContext, {AssistPerformance? performance}) - : _performance = performance; - - Future> compute() async { - _timer.start(); - await _addFromProducers(); - _timer.stop(); - _performance?.computeTime = _timer.elapsed; - return _assists; - } - - void _addAssistFromBuilder( - ChangeBuilder builder, - AssistKind kind, { - List? args, - }) { - var change = builder.sourceChange; - if (change.edits.isEmpty) { - return; - } - change.id = kind.id; - change.message = formatList(kind.message, args); - _assists.add(Assist(kind, change)); - } - - Future _addFromProducers() async { - var context = CorrectionProducerContext.createResolved( - libraryResult: _assistContext.libraryResult, - unitResult: _assistContext.unitResult, - selectionOffset: _assistContext.selectionOffset, - selectionLength: _assistContext.selectionLength, - ); - - Future compute(CorrectionProducer producer) async { - var builder = ChangeBuilder( - workspace: _assistContext.workspace, - eol: producer.eol, - ); - try { - if (_performance != null) { - var startTime = _timer.elapsedMilliseconds; - await producer.compute(builder); - _performance.producerTimings.add(( - className: producer.runtimeType.toString(), - elapsedTime: _timer.elapsedMilliseconds - startTime, - )); - } else { - await producer.compute(builder); - } - - var assistKind = producer.assistKind; - if (assistKind != null) { - _addAssistFromBuilder( - builder, - assistKind, - args: producer.assistArguments, - ); - } - } on ConflictingEditException catch (exception, stackTrace) { - // Handle the exception by (a) not adding an assist based on the - // producer and (b) logging the exception. - _assistContext.instrumentationService.logException( - exception, - stackTrace, - ); - } - } - - for (var generator in registeredAssistGenerators.producerGenerators) { - if (!_generatorAppliesToAnyLintRule( - generator, - registeredAssistGenerators.lintRuleMap[generator] ?? {}, - )) { - var producer = generator(context: context); - await compute(producer); - } - } - for (var multiGenerator - in registeredAssistGenerators.multiProducerGenerators) { - var multiProducer = multiGenerator(context: context); - for (var producer in await multiProducer.producers) { - await compute(producer); - } - } - } - - /// Returns whether [generator] applies to any enabled lint rule, among - /// [errorCodes]. - bool _generatorAppliesToAnyLintRule( - ProducerGenerator generator, - Set errorCodes, - ) { - if (errorCodes.isEmpty) { - return false; - } - - var selectionEnd = - _assistContext.selectionOffset + _assistContext.selectionLength; - var locator = NodeLocator(_assistContext.selectionOffset, selectionEnd); - var node = locator.searchWithin(_assistContext.unitResult.unit); - if (node == null) { - return false; - } - - var fileOffset = node.offset; - for (var error in _assistContext.unitResult.errors) { - var errorSource = error.source; - if (_assistContext.unitResult.path == errorSource.fullName) { - if (fileOffset >= error.offset && - fileOffset <= error.offset + error.length) { - if (errorCodes.contains(error.errorCode)) { - return true; - } - } - } - } - return false; - } -} diff --git a/pkg/analysis_server/lib/src/services/correction/fix_performance.dart b/pkg/analysis_server/lib/src/services/correction/fix_performance.dart index f9e7897237a..c532e71ba82 100644 --- a/pkg/analysis_server/lib/src/services/correction/fix_performance.dart +++ b/pkg/analysis_server/lib/src/services/correction/fix_performance.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/src/server/performance.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; /// Overall performance of a request for quick fixes operation. class GetFixesPerformance extends ProducerRequestPerformance { diff --git a/pkg/analysis_server/lib/src/services/dart_tooling_daemon/dtd_services.dart b/pkg/analysis_server/lib/src/services/dart_tooling_daemon/dtd_services.dart index 4899305ae1f..2de1610d354 100644 --- a/pkg/analysis_server/lib/src/services/dart_tooling_daemon/dtd_services.dart +++ b/pkg/analysis_server/lib/src/services/dart_tooling_daemon/dtd_services.dart @@ -12,7 +12,7 @@ import 'package:analysis_server/src/lsp/error_or.dart'; import 'package:analysis_server/src/lsp/handlers/handler_states.dart'; import 'package:analysis_server/src/lsp/handlers/handlers.dart'; import 'package:analysis_server/src/server/message_scheduler.dart'; -import 'package:analysis_server/src/server/performance.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; import 'package:analyzer/src/util/performance/operation_performance.dart'; import 'package:dtd/dtd.dart'; import 'package:json_rpc_2/json_rpc_2.dart'; diff --git a/pkg/analysis_server/lib/src/status/diagnostics.dart b/pkg/analysis_server/lib/src/status/diagnostics.dart index 50bcaceff19..9eaee538b70 100644 --- a/pkg/analysis_server/lib/src/status/diagnostics.dart +++ b/pkg/analysis_server/lib/src/status/diagnostics.dart @@ -16,9 +16,7 @@ import 'package:analysis_server/src/lsp/lsp_analysis_server.dart' show LspAnalysisServer; import 'package:analysis_server/src/plugin/plugin_manager.dart'; import 'package:analysis_server/src/server/http_server.dart'; -import 'package:analysis_server/src/server/performance.dart'; import 'package:analysis_server/src/services/completion/completion_performance.dart'; -import 'package:analysis_server/src/services/correction/assist_performance.dart'; import 'package:analysis_server/src/services/correction/fix_performance.dart'; import 'package:analysis_server/src/socket_server.dart'; import 'package:analysis_server/src/status/ast_writer.dart'; @@ -26,6 +24,8 @@ import 'package:analysis_server/src/status/element_writer.dart'; import 'package:analysis_server/src/status/pages.dart'; import 'package:analysis_server/src/utilities/profiling.dart'; import 'package:analysis_server/src/utilities/stream_string_stink.dart'; +import 'package:analysis_server_plugin/src/correction/assist_performance.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; import 'package:analyzer/dart/analysis/context_root.dart'; import 'package:analyzer/dart/analysis/results.dart'; import 'package:analyzer/src/context/source.dart'; diff --git a/pkg/analysis_server/lib/src/status/pages.dart b/pkg/analysis_server/lib/src/status/pages.dart index 7e661c17cf8..0045724efef 100644 --- a/pkg/analysis_server/lib/src/status/pages.dart +++ b/pkg/analysis_server/lib/src/status/pages.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'package:analysis_server/src/server/performance.dart'; +import 'package:analysis_server_plugin/src/correction/performance.dart'; import 'package:collection/collection.dart'; String escape(String? text) => text == null ? '' : htmlEscape.convert(text); diff --git a/pkg/analysis_server/lib/src/utilities/strings.dart b/pkg/analysis_server/lib/src/utilities/strings.dart index c05b5d9f638..41b186a2ac2 100644 --- a/pkg/analysis_server/lib/src/utilities/strings.dart +++ b/pkg/analysis_server/lib/src/utilities/strings.dart @@ -12,35 +12,6 @@ const int CHAR_DOLLAR = 0x24; /// "_" const int CHAR_UNDERSCORE = 0x5F; -/// Build a string from [contents] that includes a caret (`^`) at the -/// given [offset]. -/// -/// This string is useful for displaying to users in a diagnostic context. -String addCaretAtOffset(String contents, int offset) { - if (offset < 0 || contents.length < offset) { - return '???'; - } - var start = offset; - while (start > 0) { - var ch = contents[start - 1]; - if (ch == '\r' || ch == '\n') { - break; - } - --start; - } - var end = offset; - while (end < contents.length) { - var ch = contents[end]; - if (ch == '\r' || ch == '\n') { - break; - } - ++end; - } - var prefix = contents.substring(start, offset); - var suffix = contents.substring(offset, end); - return '$prefix^$suffix'; -} - String? capitalize(String? str) { if (str == null || str.isEmpty) { return str; diff --git a/pkg/analysis_server/test/src/cider/assists_test.dart b/pkg/analysis_server/test/src/cider/assists_test.dart index a535d45e296..7fe13f8df2a 100644 --- a/pkg/analysis_server/test/src/cider/assists_test.dart +++ b/pkg/analysis_server/test/src/cider/assists_test.dart @@ -2,10 +2,10 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/plugin/edit/assist/assist_core.dart'; import 'package:analysis_server/src/cider/assists.dart'; import 'package:analysis_server/src/services/correction/assist.dart'; import 'package:analysis_server/src/services/correction/assist_internal.dart'; +import 'package:analysis_server_plugin/src/correction/assist_core.dart'; import 'package:analyzer/source/line_info.dart'; import 'package:analyzer_plugin/protocol/protocol_common.dart' show SourceEdit; import 'package:analyzer_plugin/utilities/assist/assist.dart'; diff --git a/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart b/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart index 3103a53fe4b..45d978eca27 100644 --- a/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart +++ b/pkg/analysis_server/test/src/services/correction/assist/assist_processor.dart @@ -2,9 +2,9 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/plugin/edit/assist/assist_core.dart'; import 'package:analysis_server/src/services/correction/assist.dart'; -import 'package:analysis_server/src/services/correction/assist_internal.dart'; +import 'package:analysis_server_plugin/src/correction/assist_core.dart'; +import 'package:analysis_server_plugin/src/correction/assist_processor.dart'; import 'package:analysis_server_plugin/src/correction/change_workspace.dart'; import 'package:analysis_server_plugin/src/correction/dart_change_workspace.dart'; import 'package:analyzer/src/test_utilities/platform.dart'; diff --git a/pkg/analysis_server/lib/plugin/edit/assist/assist_core.dart b/pkg/analysis_server_plugin/lib/src/correction/assist_core.dart similarity index 72% rename from pkg/analysis_server/lib/plugin/edit/assist/assist_core.dart rename to pkg/analysis_server_plugin/lib/src/correction/assist_core.dart index 5606b508cda..9a3fe3641fe 100644 --- a/pkg/analysis_server/lib/plugin/edit/assist/assist_core.dart +++ b/pkg/analysis_server_plugin/lib/src/correction/assist_core.dart @@ -7,16 +7,14 @@ import 'package:analyzer_plugin/protocol/protocol_common.dart' import 'package:analyzer_plugin/utilities/assist/assist.dart'; /// A description of a single proposed assist. -/// -/// Clients may not extend, implement or mix-in this class. -class Assist { +final class Assist { /// A description of the assist being proposed. final AssistKind kind; /// The change to be made in order to apply the assist. final SourceChange change; - /// Initialize a newly created assist to have the given [kind] and [change]. + /// Initializes a newly created assist to have the given [kind] and [change]. Assist(this.kind, this.change); @override @@ -24,10 +22,7 @@ class Assist { return 'Assist(kind=$kind, change=$change)'; } - /// A function that can be used to sort assists by their relevance. - /// - /// The most relevant assists will be sorted before assists with a lower - /// relevance. Assists with the same relevance are sorted alphabetically. + /// Compares two assists by their relevance, then their message. static int compareAssists(Assist a, Assist b) { if (a.kind.priority != b.kind.priority) { // A higher priority indicates a higher relevance diff --git a/pkg/analysis_server/lib/plugin/edit/assist/assist_dart.dart b/pkg/analysis_server_plugin/lib/src/correction/assist_dart.dart similarity index 73% rename from pkg/analysis_server/lib/plugin/edit/assist/assist_dart.dart rename to pkg/analysis_server_plugin/lib/src/correction/assist_dart.dart index 591ec9bc479..5fa231fe58f 100644 --- a/pkg/analysis_server/lib/plugin/edit/assist/assist_dart.dart +++ b/pkg/analysis_server_plugin/lib/src/correction/assist_dart.dart @@ -10,22 +10,22 @@ import 'package:analyzer/instrumentation/service.dart'; /// /// Clients may not extend, implement or mix-in this class. abstract class DartAssistContext { - /// Return the instrumentation service used to report errors that prevent a - /// fix from being composed. + /// The instrumentation service used to report errors that prevent a fix from + /// being composed. InstrumentationService get instrumentationService; - /// The resolved library result in which assist operates. + /// The resolved library result in which an assist operates. ResolvedLibraryResult get libraryResult; /// The length of the selection. int get selectionLength; - /// The start of the selection. + /// The starting offset of the selection. int get selectionOffset; - /// The unit result in which assist operates. + /// The unit result in which an assist operates. ResolvedUnitResult get unitResult; - /// The workspace in which the fix contributor operates. + /// The workspace in which an assist operates. ChangeWorkspace get workspace; } diff --git a/pkg/analysis_server/lib/src/services/correction/assist_generators.dart b/pkg/analysis_server_plugin/lib/src/correction/assist_generators.dart similarity index 95% rename from pkg/analysis_server/lib/src/services/correction/assist_generators.dart rename to pkg/analysis_server_plugin/lib/src/correction/assist_generators.dart index 4dfa00e2646..c1927b9c329 100644 --- a/pkg/analysis_server/lib/src/services/correction/assist_generators.dart +++ b/pkg/analysis_server_plugin/lib/src/correction/assist_generators.dart @@ -23,8 +23,7 @@ class _RegisteredAssistGenerators { /// A mapping from registered _assist_ producer generators to the [LintCode]s /// for which they may also act as a _fix_ producer generator. - Map> get lintRuleMap => - _lintRuleMap ??= { + Map> get lintRuleMap => _lintRuleMap ??= { for (var generator in producerGenerators) generator: { for (var MapEntry(key: lintName, value: generators) diff --git a/pkg/analysis_server/lib/src/services/correction/assist_performance.dart b/pkg/analysis_server_plugin/lib/src/correction/assist_performance.dart similarity index 92% rename from pkg/analysis_server/lib/src/services/correction/assist_performance.dart rename to pkg/analysis_server_plugin/lib/src/correction/assist_performance.dart index dd29bbb7bf9..0c3ea5da453 100644 --- a/pkg/analysis_server/lib/src/services/correction/assist_performance.dart +++ b/pkg/analysis_server_plugin/lib/src/correction/assist_performance.dart @@ -2,7 +2,6 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import 'package:analysis_server/src/server/performance.dart'; import 'package:analysis_server_plugin/src/correction/performance.dart'; /// A callback for recording assist request timings. diff --git a/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart b/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart new file mode 100644 index 00000000000..9a9ccfec15f --- /dev/null +++ b/pkg/analysis_server_plugin/lib/src/correction/assist_processor.dart @@ -0,0 +1,131 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:analysis_server_plugin/edit/dart/correction_producer.dart'; +import 'package:analysis_server_plugin/src/correction/assist_core.dart'; +import 'package:analysis_server_plugin/src/correction/assist_dart.dart'; +import 'package:analysis_server_plugin/src/correction/assist_generators.dart'; +import 'package:analysis_server_plugin/src/correction/assist_performance.dart'; +import 'package:analysis_server_plugin/src/correction/fix_generators.dart'; +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/src/dart/ast/utilities.dart'; +import 'package:analyzer/src/generated/java_core.dart'; +import 'package:analyzer_plugin/utilities/change_builder/change_builder_core.dart'; +import 'package:analyzer_plugin/utilities/change_builder/conflicting_edit_exception.dart'; + +/// The computer for Dart assists. +class AssistProcessor { + final AssistPerformance? _performance; + final DartAssistContext _assistContext; + final Stopwatch _timer = Stopwatch(); + + final List _assists = []; + + AssistProcessor(this._assistContext, {AssistPerformance? performance}) + : _performance = performance; + + Future> compute() async { + _timer.start(); + await _addFromProducers(); + _timer.stop(); + _performance?.computeTime = _timer.elapsed; + return _assists; + } + + Future _addFromProducer(CorrectionProducer producer) async { + var builder = ChangeBuilder( + workspace: _assistContext.workspace, + eol: producer.eol, + ); + try { + if (_performance != null) { + var startTime = _timer.elapsedMilliseconds; + await producer.compute(builder); + _performance.producerTimings.add(( + className: producer.runtimeType.toString(), + elapsedTime: _timer.elapsedMilliseconds - startTime, + )); + } else { + await producer.compute(builder); + } + + var assistKind = producer.assistKind; + if (assistKind != null) { + var change = builder.sourceChange; + if (change.edits.isEmpty) { + return; + } + change.id = assistKind.id; + change.message = formatList( + assistKind.message, + producer.assistArguments, + ); + _assists.add(Assist(assistKind, change)); + } + } on ConflictingEditException catch (exception, stackTrace) { + // Handle the exception by (a) not adding an assist based on the + // producer and (b) logging the exception. + _assistContext.instrumentationService.logException(exception, stackTrace); + } + } + + Future _addFromProducers() async { + var context = CorrectionProducerContext.createResolved( + libraryResult: _assistContext.libraryResult, + unitResult: _assistContext.unitResult, + selectionOffset: _assistContext.selectionOffset, + selectionLength: _assistContext.selectionLength, + ); + + for (var generator in registeredAssistGenerators.producerGenerators) { + if (!_generatorAppliesToAnyLintRule( + generator, + registeredAssistGenerators.lintRuleMap[generator] ?? {}, + )) { + var producer = generator(context: context); + await _addFromProducer(producer); + } + } + for (var multiGenerator + in registeredAssistGenerators.multiProducerGenerators) { + var multiProducer = multiGenerator(context: context); + for (var producer in await multiProducer.producers) { + await _addFromProducer(producer); + } + } + } + + /// Returns whether [generator] applies to any enabled lint rule, among + /// [errorCodes]. + bool _generatorAppliesToAnyLintRule( + ProducerGenerator generator, + Set errorCodes, + ) { + if (errorCodes.isEmpty) { + return false; + } + + var selectionEnd = + _assistContext.selectionOffset + _assistContext.selectionLength; + var locator = NodeLocator(_assistContext.selectionOffset, selectionEnd); + var node = locator.searchWithin(_assistContext.unitResult.unit); + if (node == null) { + return false; + } + + var fileOffset = node.offset; + for (var error in _assistContext.unitResult.errors) { + var errorSource = error.source; + if (_assistContext.unitResult.path == errorSource.fullName) { + if (fileOffset >= error.offset && + fileOffset <= error.offset + error.length) { + if (errorCodes.contains(error.errorCode)) { + return true; + } + } + } + } + return false; + } +} diff --git a/pkg/analysis_server_plugin/lib/src/correction/performance.dart b/pkg/analysis_server_plugin/lib/src/correction/performance.dart index 8f4234c738b..e7f85fe1575 100644 --- a/pkg/analysis_server_plugin/lib/src/correction/performance.dart +++ b/pkg/analysis_server_plugin/lib/src/correction/performance.dart @@ -2,6 +2,9 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:analysis_server_plugin/src/utilities/string_extensions.dart'; +import 'package:analyzer/src/util/performance/operation_performance.dart'; + /// Timing information for a (correction) producer's call to `compute()`. typedef ProducerTiming = ({ /// The producer class name. @@ -10,3 +13,42 @@ typedef ProducerTiming = ({ /// The time elapsed during `compute()`. int elapsedTime, }); + +abstract class ProducerRequestPerformance extends RequestPerformance { + final String path; + + final String snippet; + final List producerTimings; + + ProducerRequestPerformance({ + // TODO(srawlins): This should probably be used in the super call? + // ignore: avoid_unused_constructor_parameters + required String operation, + required this.path, + required super.performance, + super.requestLatency, + super.startTime, + required String content, + required int offset, + required this.producerTimings, + }) : snippet = content.withCaretAt(offset), + super(operation: 'GetAssists'); + + int get elapsedInMilliseconds => performance.elapsed.inMilliseconds; +} + +class RequestPerformance { + static var _nextId = 1; + final int id; + final OperationPerformance performance; + final int? requestLatency; + final String operation; + final DateTime? startTime; + + RequestPerformance({ + required this.operation, + required this.performance, + this.requestLatency, + this.startTime, + }) : id = _nextId++; +} diff --git a/pkg/analysis_server_plugin/lib/src/utilities/string_extensions.dart b/pkg/analysis_server_plugin/lib/src/utilities/string_extensions.dart new file mode 100644 index 00000000000..45a31cc2b0a --- /dev/null +++ b/pkg/analysis_server_plugin/lib/src/utilities/string_extensions.dart @@ -0,0 +1,34 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +extension StringExtension on String { + /// Builds a string from `this` that includes a caret (`^`) at the given + /// [offset]. + /// + /// This string is useful for displaying to users in a diagnostic context. + String withCaretAt(int offset) { + if (offset < 0 || length < offset) { + return '???'; + } + var start = offset; + while (start > 0) { + var ch = this[start - 1]; + if (ch == '\r' || ch == '\n') { + break; + } + --start; + } + var end = offset; + while (end < length) { + var ch = this[end]; + if (ch == '\r' || ch == '\n') { + break; + } + ++end; + } + var prefix = substring(start, offset); + var suffix = substring(offset, end); + return '$prefix^$suffix'; + } +}