| // Copyright (c) 2019, 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 'dart:async'; |
| |
| import 'package:analysis_server/lsp_protocol/protocol.dart'; |
| import 'package:analysis_server/src/lsp/handlers/commands/abstract_refactor.dart'; |
| import 'package:analysis_server/src/lsp/handlers/handlers.dart'; |
| import 'package:analysis_server/src/lsp/mapping.dart'; |
| import 'package:analysis_server/src/lsp/progress.dart'; |
| import 'package:analysis_server/src/protocol_server.dart'; |
| |
| class PerformRefactorCommandHandler extends AbstractRefactorCommandHandler { |
| PerformRefactorCommandHandler(super.server); |
| |
| @override |
| String get commandName => 'Perform Refactor'; |
| |
| @override |
| FutureOr<ErrorOr<void>> execute( |
| String path, |
| String kind, |
| int offset, |
| int length, |
| Map<String, Object?>? options, |
| CancellationToken cancellationToken, |
| ProgressReporter reporter, |
| int? docVersion, |
| ) async { |
| final result = await requireResolvedUnit(path); |
| return result.mapResult((result) async { |
| final refactoring = await getRefactoring( |
| RefactoringKind(kind), result, offset, length, options); |
| return refactoring.mapResult((refactoring) async { |
| // If the token we were given is not cancellable, replace it with one that |
| // is for the rest of this request, as a future refactor may need to cancel |
| // this request. |
| // The original token should be kept and also checked for cancellation. |
| final cancelableToken = cancellationToken is CancelableToken |
| ? cancellationToken |
| : CancelableToken(); |
| manager.begin(cancelableToken); |
| |
| try { |
| reporter.begin('Refactoring…'); |
| final status = await refactoring.checkAllConditions(); |
| |
| if (status.hasError) { |
| // Show the error to the user but don't fail the request, as the |
| // LSP Client may show a failed request in a way that looks like a |
| // server error. |
| server.showErrorMessageToUser(status.message!); |
| return success(null); |
| } |
| |
| if (cancellationToken.isCancellationRequested || |
| cancelableToken.isCancellationRequested) { |
| return error(ErrorCodes.RequestCancelled, 'Request was cancelled'); |
| } |
| |
| final change = await refactoring.createChange(); |
| |
| if (cancellationToken.isCancellationRequested || |
| cancelableToken.isCancellationRequested) { |
| return error(ErrorCodes.RequestCancelled, 'Request was cancelled'); |
| } |
| |
| if (change.edits.isEmpty) { |
| return success(null); |
| } |
| |
| // If the file changed while we were validating and preparing the change, |
| // we should fail to avoid sending bad edits. |
| if (fileHasBeenModified(path, docVersion)) { |
| return fileModifiedError; |
| } |
| |
| final edit = createWorkspaceEdit(server, change); |
| return await sendWorkspaceEditToClient(edit); |
| } finally { |
| manager.end(cancelableToken); |
| reporter.end(); |
| } |
| }); |
| }); |
| } |
| } |