| // Copyright (c) 2014, 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/plugin/edit/assist/assist_core.dart'; |
| import 'package:analysis_server/plugin/edit/fix/fix_core.dart'; |
| import 'package:analysis_server/protocol/protocol_constants.dart'; |
| import 'package:analysis_server/src/analysis_server.dart'; |
| import 'package:analysis_server/src/collections.dart'; |
| import 'package:analysis_server/src/computer/import_elements_computer.dart'; |
| import 'package:analysis_server/src/domain_abstract.dart'; |
| import 'package:analysis_server/src/edit/edit_dartfix.dart' show EditDartFix; |
| import 'package:analysis_server/src/edit/fix/dartfix_info.dart' show allFixes; |
| import 'package:analysis_server/src/plugin/plugin_manager.dart'; |
| import 'package:analysis_server/src/plugin/result_converter.dart'; |
| import 'package:analysis_server/src/protocol_server.dart' |
| hide AnalysisError, Element; |
| import 'package:analysis_server/src/services/completion/postfix/postfix_completion.dart'; |
| import 'package:analysis_server/src/services/completion/statement/statement_completion.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/bulk_fix_processor.dart'; |
| import 'package:analysis_server/src/services/correction/change_workspace.dart'; |
| import 'package:analysis_server/src/services/correction/fix.dart'; |
| import 'package:analysis_server/src/services/correction/fix/analysis_options/fix_generator.dart'; |
| import 'package:analysis_server/src/services/correction/fix/dart/top_level_declarations.dart'; |
| import 'package:analysis_server/src/services/correction/fix/manifest/fix_generator.dart'; |
| import 'package:analysis_server/src/services/correction/fix/pubspec/fix_generator.dart'; |
| import 'package:analysis_server/src/services/correction/fix_internal.dart'; |
| import 'package:analysis_server/src/services/correction/organize_imports.dart'; |
| import 'package:analysis_server/src/services/correction/sort_members.dart'; |
| import 'package:analysis_server/src/services/correction/status.dart'; |
| import 'package:analysis_server/src/services/refactoring/refactoring.dart'; |
| import 'package:analysis_server/src/services/search/search_engine.dart'; |
| import 'package:analyzer/dart/analysis/results.dart'; |
| import 'package:analyzer/dart/analysis/session.dart'; |
| import 'package:analyzer/dart/element/element.dart'; |
| import 'package:analyzer/error/error.dart' as engine; |
| import 'package:analyzer/exception/exception.dart'; |
| import 'package:analyzer/file_system/file_system.dart'; |
| import 'package:analyzer/src/analysis_options/analysis_options_provider.dart'; |
| import 'package:analyzer/src/dart/analysis/analysis_context_collection.dart'; |
| import 'package:analyzer/src/dart/analysis/results.dart' as engine; |
| import 'package:analyzer/src/dart/ast/utilities.dart'; |
| import 'package:analyzer/src/dart/scanner/scanner.dart' as engine; |
| import 'package:analyzer/src/exception/exception.dart'; |
| import 'package:analyzer/src/generated/parser.dart' as engine; |
| import 'package:analyzer/src/generated/source.dart'; |
| import 'package:analyzer/src/manifest/manifest_validator.dart'; |
| import 'package:analyzer/src/manifest/manifest_values.dart'; |
| import 'package:analyzer/src/pubspec/pubspec_validator.dart'; |
| import 'package:analyzer/src/task/options.dart'; |
| import 'package:analyzer/src/util/file_paths.dart' as file_paths; |
| import 'package:analyzer_plugin/protocol/protocol.dart' as plugin; |
| import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin; |
| import 'package:dart_style/dart_style.dart'; |
| import 'package:html/parser.dart'; |
| import 'package:yaml/yaml.dart'; |
| |
| int test_resetCount = 0; |
| |
| bool test_simulateRefactoringException_change = false; |
| bool test_simulateRefactoringException_final = false; |
| bool test_simulateRefactoringException_init = false; |
| |
| bool test_simulateRefactoringReset_afterCreateChange = false; |
| bool test_simulateRefactoringReset_afterFinalConditions = false; |
| bool test_simulateRefactoringReset_afterInitialConditions = false; |
| |
| /// Instances of the class [EditDomainHandler] implement a [RequestHandler] |
| /// that handles requests in the edit domain. |
| class EditDomainHandler extends AbstractRequestHandler { |
| /// The workspace for rename refactorings. |
| RefactoringWorkspace? refactoringWorkspace; |
| |
| /// The object used to manage uncompleted refactorings. |
| _RefactoringManager? refactoringManager; |
| |
| /// Initialize a newly created handler to handle requests for the given |
| /// [server]. |
| EditDomainHandler(AnalysisServer server) : super(server) { |
| refactoringWorkspace = |
| RefactoringWorkspace(server.driverMap.values, server.searchEngine); |
| _newRefactoringManager(); |
| } |
| |
| Future bulkFixes(Request request) async { |
| // |
| // Compute bulk fixes |
| // |
| try { |
| var params = EditBulkFixesParams.fromRequest(request); |
| for (var file in params.included) { |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| } |
| |
| var workspace = DartChangeWorkspace(server.currentSessions); |
| var processor = BulkFixProcessor(server.instrumentationService, workspace, |
| useConfigFiles: params.inTestMode ?? false); |
| |
| var collection = AnalysisContextCollectionImpl( |
| includedPaths: params.included, |
| resourceProvider: server.resourceProvider, |
| sdkPath: server.sdkPath, |
| ); |
| var changeBuilder = await processor.fixErrors(collection.contexts); |
| |
| var response = EditBulkFixesResult( |
| changeBuilder.sourceChange.edits, processor.fixDetails) |
| .toResponse(request.id); |
| server.sendResponse(response); |
| } catch (exception, stackTrace) { |
| server.sendServerErrorNotification('Exception while getting bulk fixes', |
| CaughtException(exception, stackTrace), stackTrace); |
| } |
| } |
| |
| Future dartfix(Request request) async { |
| // TODO(danrubel): Add support for dartfix plugins |
| |
| // |
| // Compute fixes |
| // |
| try { |
| var dartFix = EditDartFix(server, request); |
| var response = await dartFix.compute(); |
| |
| server.sendResponse(response); |
| } catch (exception, stackTrace) { |
| server.sendServerErrorNotification('Exception while running dartfix', |
| CaughtException(exception, stackTrace), stackTrace); |
| } |
| } |
| |
| Response format(Request request) { |
| server.options.analytics?.sendEvent('edit', 'format'); |
| |
| var params = EditFormatParams.fromRequest(request); |
| var file = params.file; |
| |
| String unformattedCode; |
| try { |
| var resource = server.resourceProvider.getFile(file); |
| unformattedCode = resource.readAsStringSync(); |
| } catch (e) { |
| return Response.formatInvalidFile(request); |
| } |
| |
| int? start = params.selectionOffset; |
| int? length = params.selectionLength; |
| |
| // No need to preserve 0,0 selection |
| if (start == 0 && length == 0) { |
| start = null; |
| length = null; |
| } |
| |
| var code = SourceCode(unformattedCode, |
| uri: null, |
| isCompilationUnit: true, |
| selectionStart: start, |
| selectionLength: length); |
| var formatter = DartFormatter(pageWidth: params.lineLength); |
| SourceCode formattedResult; |
| try { |
| formattedResult = formatter.formatSource(code); |
| } on FormatterException { |
| return Response.formatWithErrors(request); |
| } |
| var formattedSource = formattedResult.text; |
| |
| var edits = <SourceEdit>[]; |
| |
| if (formattedSource != unformattedCode) { |
| //TODO: replace full replacements with smaller, more targeted edits |
| var edit = SourceEdit(0, unformattedCode.length, formattedSource); |
| edits.add(edit); |
| } |
| |
| var newStart = formattedResult.selectionStart; |
| var newLength = formattedResult.selectionLength; |
| |
| // Sending null start/length values would violate protocol, so convert back |
| // to 0. |
| newStart ??= 0; |
| newLength ??= 0; |
| |
| return EditFormatResult(edits, newStart, newLength).toResponse(request.id); |
| } |
| |
| Future getAssists(Request request) async { |
| var params = EditGetAssistsParams.fromRequest(request); |
| var file = params.file; |
| var offset = params.offset; |
| var length = params.length; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| // |
| // Allow plugins to start computing assists. |
| // |
| Map<PluginInfo, Future<plugin.Response>> pluginFutures; |
| var requestParams = plugin.EditGetAssistsParams(file, offset, length); |
| var driver = server.getAnalysisDriver(file); |
| if (driver == null) { |
| pluginFutures = <PluginInfo, Future<plugin.Response>>{}; |
| } else { |
| pluginFutures = server.pluginManager.broadcastRequest( |
| requestParams, |
| contextRoot: driver.analysisContext!.contextRoot, |
| ); |
| } |
| |
| // |
| // Compute fixes associated with server-generated errors. |
| // |
| var changes = await _computeServerAssists(request, file, offset, length); |
| |
| // |
| // Add the fixes produced by plugins to the server-generated fixes. |
| // |
| var responses = |
| await waitForResponses(pluginFutures, requestParameters: requestParams); |
| server.requestStatistics?.addItemTimeNow(request, 'pluginResponses'); |
| var converter = ResultConverter(); |
| var pluginChanges = <plugin.PrioritizedSourceChange>[]; |
| for (var response in responses) { |
| var result = plugin.EditGetAssistsResult.fromResponse(response); |
| pluginChanges.addAll(result.assists); |
| } |
| pluginChanges |
| .sort((first, second) => first.priority.compareTo(second.priority)); |
| changes.addAll(pluginChanges.map(converter.convertPrioritizedSourceChange)); |
| |
| // |
| // Send the response. |
| // |
| server.sendResponse(EditGetAssistsResult(changes).toResponse(request.id)); |
| } |
| |
| Response getDartfixInfo(Request request) => |
| EditGetDartfixInfoResult(allFixes.map((i) => i.asDartFix()).toList()) |
| .toResponse(request.id); |
| |
| Future<void> getFixes(Request request) async { |
| var params = EditGetFixesParams.fromRequest(request); |
| var file = params.file; |
| var offset = params.offset; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| if (!server.isAnalyzed(file)) { |
| server.sendResponse(Response.getFixesInvalidFile(request)); |
| return; |
| } |
| |
| // |
| // Allow plugins to start computing fixes. |
| // |
| Map<PluginInfo, Future<plugin.Response>> pluginFutures; |
| var requestParams = plugin.EditGetFixesParams(file, offset); |
| var driver = server.getAnalysisDriver(file); |
| if (driver == null) { |
| pluginFutures = <PluginInfo, Future<plugin.Response>>{}; |
| } else { |
| pluginFutures = server.pluginManager.broadcastRequest( |
| requestParams, |
| contextRoot: driver.analysisContext!.contextRoot, |
| ); |
| } |
| // |
| // Compute fixes associated with server-generated errors. |
| // |
| List<AnalysisErrorFixes>? errorFixesList; |
| while (errorFixesList == null) { |
| try { |
| errorFixesList = await _computeServerErrorFixes(request, file, offset); |
| } on InconsistentAnalysisException { |
| // Loop around to try again to compute the fixes. |
| } |
| } |
| // |
| // Add the fixes produced by plugins to the server-generated fixes. |
| // |
| var responses = |
| await waitForResponses(pluginFutures, requestParameters: requestParams); |
| server.requestStatistics?.addItemTimeNow(request, 'pluginResponses'); |
| var converter = ResultConverter(); |
| for (var response in responses) { |
| var result = plugin.EditGetFixesResult.fromResponse(response); |
| errorFixesList |
| .addAll(result.fixes.map(converter.convertAnalysisErrorFixes)); |
| } |
| // |
| // Send the response. |
| // |
| server.sendResponse( |
| EditGetFixesResult(errorFixesList).toResponse(request.id)); |
| } |
| |
| Future getPostfixCompletion(Request request) async { |
| server.options.analytics?.sendEvent('edit', 'getPostfixCompletion'); |
| |
| var params = EditGetPostfixCompletionParams.fromRequest(request); |
| var file = params.file; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| SourceChange? change; |
| |
| var result = await server.getResolvedUnit(file); |
| if (result != null) { |
| var context = PostfixCompletionContext( |
| result, |
| params.offset, |
| params.key, |
| ); |
| var processor = PostfixCompletionProcessor(context); |
| var completion = await processor.compute(); |
| change = completion.change; |
| } |
| change ??= SourceChange('', edits: []); |
| |
| var response = |
| EditGetPostfixCompletionResult(change).toResponse(request.id); |
| server.sendResponse(response); |
| } |
| |
| Future getStatementCompletion(Request request) async { |
| var params = EditGetStatementCompletionParams.fromRequest(request); |
| var file = params.file; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| SourceChange? change; |
| |
| var result = await server.getResolvedUnit(file); |
| if (result != null) { |
| var context = StatementCompletionContext(result, params.offset); |
| var processor = StatementCompletionProcessor(context); |
| var completion = await processor.compute(); |
| change = completion.change; |
| } |
| change ??= SourceChange('', edits: []); |
| |
| var response = |
| EditGetStatementCompletionResult(change, false).toResponse(request.id); |
| server.sendResponse(response); |
| } |
| |
| @override |
| Response? handleRequest(Request request) { |
| try { |
| var requestName = request.method; |
| if (requestName == EDIT_REQUEST_FORMAT) { |
| return format(request); |
| } else if (requestName == EDIT_REQUEST_GET_ASSISTS) { |
| getAssists(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS) { |
| return _getAvailableRefactorings(request); |
| } else if (requestName == EDIT_REQUEST_BULK_FIXES) { |
| bulkFixes(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_GET_DARTFIX_INFO) { |
| return getDartfixInfo(request); |
| } else if (requestName == EDIT_REQUEST_GET_FIXES) { |
| getFixes(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_DARTFIX) { |
| dartfix(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_GET_REFACTORING) { |
| return _getRefactoring(request); |
| } else if (requestName == EDIT_REQUEST_IMPORT_ELEMENTS) { |
| importElements(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_ORGANIZE_DIRECTIVES) { |
| organizeDirectives(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_SORT_MEMBERS) { |
| sortMembers(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_GET_STATEMENT_COMPLETION) { |
| getStatementCompletion(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_IS_POSTFIX_COMPLETION_APPLICABLE) { |
| isPostfixCompletionApplicable(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == EDIT_REQUEST_GET_POSTFIX_COMPLETION) { |
| getPostfixCompletion(request); |
| return Response.DELAYED_RESPONSE; |
| } else if (requestName == |
| EDIT_REQUEST_LIST_POSTFIX_COMPLETION_TEMPLATES) { |
| return listPostfixCompletionTemplates(request); |
| } |
| } on RequestFailure catch (exception) { |
| return exception.response; |
| } |
| return null; |
| } |
| |
| /// Implement the `edit.importElements` request. |
| Future<void> importElements(Request request) async { |
| var params = EditImportElementsParams.fromRequest(request); |
| var file = params.file; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| // |
| // Prepare the resolved unit. |
| // |
| var result = await server.getResolvedUnit(file); |
| if (result == null) { |
| server.sendResponse(Response.importElementsInvalidFile(request)); |
| return; |
| } |
| var libraryUnit = result.libraryElement.definingCompilationUnit; |
| if (libraryUnit != result.unit!.declaredElement) { |
| // The file in the request is a part of a library. We need to pass the |
| // defining compilation unit to the computer, not the part. |
| result = await server.getResolvedUnit(libraryUnit.source.fullName); |
| if (result == null) { |
| server.sendResponse(Response.importElementsInvalidFile(request)); |
| return; |
| } |
| } |
| // |
| // Compute the edits required to import the required elements. |
| // |
| var computer = ImportElementsComputer(server.resourceProvider, result); |
| var change = await computer.createEdits(params.elements); |
| var edits = change.edits; |
| var edit = edits.isEmpty ? null : edits[0]; |
| // |
| // Send the response. |
| // |
| server.sendResponse( |
| EditImportElementsResult(edit: edit).toResponse(request.id)); |
| } |
| |
| Future isPostfixCompletionApplicable(Request request) async { |
| var params = EditGetPostfixCompletionParams.fromRequest(request); |
| var file = params.file; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| var value = false; |
| |
| var result = await server.getResolvedUnit(file); |
| if (result != null) { |
| var context = PostfixCompletionContext( |
| result, |
| params.offset, |
| params.key, |
| ); |
| var processor = PostfixCompletionProcessor(context); |
| value = await processor.isApplicable(); |
| } |
| |
| var response = |
| EditIsPostfixCompletionApplicableResult(value).toResponse(request.id); |
| server.sendResponse(response); |
| } |
| |
| Response listPostfixCompletionTemplates(Request request) { |
| var templates = DartPostfixCompletion.ALL_TEMPLATES |
| .map((PostfixCompletionKind kind) => |
| PostfixTemplateDescriptor(kind.name, kind.key, kind.example)) |
| .toList(); |
| |
| return EditListPostfixCompletionTemplatesResult(templates) |
| .toResponse(request.id); |
| } |
| |
| Future<void> organizeDirectives(Request request) async { |
| server.options.analytics?.sendEvent('edit', 'organizeDirectives'); |
| |
| var params = EditOrganizeDirectivesParams.fromRequest(request); |
| var file = params.file; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| var pathContext = server.resourceProvider.pathContext; |
| if (!file_paths.isDart(pathContext, file)) { |
| server.sendResponse(Response.fileNotAnalyzed(request, file)); |
| return; |
| } |
| |
| // Prepare the file information. |
| var result = await server.getResolvedUnit(file); |
| if (result == null) { |
| server.sendResponse(Response.fileNotAnalyzed(request, file)); |
| return; |
| } |
| var fileStamp = -1; |
| var code = result.content!; |
| var unit = result.unit!; |
| var errors = result.errors; |
| // check if there are scan/parse errors in the file |
| var numScanParseErrors = _getNumberOfScanParseErrors(errors); |
| if (numScanParseErrors != 0) { |
| server.sendResponse(Response.organizeDirectivesError( |
| request, 'File has $numScanParseErrors scan/parse errors.')); |
| return; |
| } |
| // do organize |
| var sorter = ImportOrganizer(code, unit, errors); |
| var edits = sorter.organize(); |
| var fileEdit = SourceFileEdit(file, fileStamp, edits: edits); |
| server.sendResponse( |
| EditOrganizeDirectivesResult(fileEdit).toResponse(request.id)); |
| } |
| |
| Future<void> sortMembers(Request request) async { |
| var params = EditSortMembersParams.fromRequest(request); |
| var file = params.file; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| var pathContext = server.resourceProvider.pathContext; |
| if (!file_paths.isDart(pathContext, file)) { |
| server.sendResponse(Response.sortMembersInvalidFile(request)); |
| return; |
| } |
| |
| // Prepare the file information. |
| var result = server.getParsedUnit(file); |
| if (result == null) { |
| server.sendResponse(Response.fileNotAnalyzed(request, file)); |
| return; |
| } |
| |
| var fileStamp = -1; |
| var code = result.content; |
| var unit = result.unit; |
| var errors = result.errors; |
| // Check if there are scan/parse errors in the file. |
| var numScanParseErrors = _getNumberOfScanParseErrors(errors); |
| if (numScanParseErrors != 0) { |
| server.sendResponse( |
| Response.sortMembersParseErrors(request, numScanParseErrors)); |
| return; |
| } |
| // Do sort. |
| var sorter = MemberSorter(code, unit); |
| var edits = sorter.sort(); |
| var fileEdit = SourceFileEdit(file, fileStamp, edits: edits); |
| server.sendResponse(EditSortMembersResult(fileEdit).toResponse(request.id)); |
| } |
| |
| /// Compute and return the fixes associated with server-generated errors in |
| /// analysis options files. |
| Future<List<AnalysisErrorFixes>> _computeAnalysisOptionsFixes( |
| String file, int offset) async { |
| var errorFixesList = <AnalysisErrorFixes>[]; |
| var optionsFile = server.resourceProvider.getFile(file); |
| var content = _safelyRead(optionsFile); |
| if (content == null) { |
| return errorFixesList; |
| } |
| var driver = server.getAnalysisDriver(file); |
| if (driver == null) { |
| return errorFixesList; |
| } |
| var session = driver.currentSession; |
| var sourceFactory = driver.sourceFactory; |
| var errors = analyzeAnalysisOptions( |
| optionsFile.createSource(), |
| content, |
| sourceFactory, |
| driver.currentSession.analysisContext.contextRoot.root.path, |
| ); |
| var options = _getOptions(sourceFactory, content); |
| if (options == null) { |
| return errorFixesList; |
| } |
| for (var error in errors) { |
| var generator = AnalysisOptionsFixGenerator(error, content, options); |
| var fixes = await generator.computeFixes(); |
| if (fixes.isNotEmpty) { |
| fixes.sort(Fix.SORT_BY_RELEVANCE); |
| var lineInfo = LineInfo.fromContent(content); |
| ResolvedUnitResult result = engine.ResolvedUnitResultImpl(session, file, |
| Uri.file(file), true, content, lineInfo, false, null, errors); |
| var serverError = newAnalysisError_fromEngine(result, error); |
| var errorFixes = AnalysisErrorFixes(serverError); |
| errorFixesList.add(errorFixes); |
| fixes.forEach((fix) { |
| errorFixes.fixes.add(fix.change); |
| }); |
| } |
| } |
| return errorFixesList; |
| } |
| |
| /// Compute and return the fixes associated with server-generated errors in |
| /// Dart files. |
| Future<List<AnalysisErrorFixes>> _computeDartFixes( |
| Request request, String file, int offset) async { |
| var errorFixesList = <AnalysisErrorFixes>[]; |
| var result = await server.getResolvedUnit(file); |
| server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit'); |
| if (result != null) { |
| var lineInfo = result.lineInfo; |
| var requestLine = lineInfo.getLocation(offset).lineNumber; |
| for (var error in result.errors) { |
| var errorLine = lineInfo.getLocation(error.offset).lineNumber; |
| if (errorLine == requestLine) { |
| var workspace = DartChangeWorkspace(server.currentSessions); |
| var context = DartFixContextImpl( |
| server.instrumentationService, workspace, result, error, (name) { |
| var tracker = server.declarationsTracker; |
| if (tracker == null) { |
| return const []; |
| } |
| var provider = TopLevelDeclarationsProvider(tracker); |
| return provider.get( |
| result.session.analysisContext, |
| result.path!, |
| name, |
| ); |
| }, extensionCache: server.getExtensionCacheFor(result)); |
| |
| List<Fix> fixes; |
| try { |
| fixes = await DartFixContributor().computeFixes(context); |
| } on InconsistentAnalysisException { |
| fixes = []; |
| } catch (exception, stackTrace) { |
| var parametersFile = ''' |
| offset: $offset |
| error: $error |
| error.errorCode: ${error.errorCode} |
| '''; |
| throw CaughtExceptionWithFiles(exception, stackTrace, { |
| file: result.content!, |
| 'parameters': parametersFile, |
| }); |
| } |
| |
| if (fixes.isNotEmpty) { |
| fixes.sort(Fix.SORT_BY_RELEVANCE); |
| var serverError = newAnalysisError_fromEngine(result, error); |
| var errorFixes = AnalysisErrorFixes(serverError); |
| errorFixesList.add(errorFixes); |
| fixes.forEach((fix) { |
| errorFixes.fixes.add(fix.change); |
| }); |
| } |
| } |
| } |
| } |
| server.requestStatistics?.addItemTimeNow(request, 'computedFixes'); |
| return errorFixesList; |
| } |
| |
| /// Compute and return the fixes associated with server-generated errors in |
| /// Android manifest files. |
| Future<List<AnalysisErrorFixes>> _computeManifestFixes( |
| String file, int offset) async { |
| var errorFixesList = <AnalysisErrorFixes>[]; |
| var manifestFile = server.resourceProvider.getFile(file); |
| var content = _safelyRead(manifestFile); |
| if (content == null) { |
| return errorFixesList; |
| } |
| var document = |
| parseFragment(content, container: MANIFEST_TAG, generateSpans: true); |
| var validator = ManifestValidator(manifestFile.createSource()); |
| var driver = server.getAnalysisDriver(file); |
| if (driver == null) { |
| return errorFixesList; |
| } |
| var session = driver.currentSession; |
| var errors = validator.validate(content, true); |
| for (var error in errors) { |
| var generator = ManifestFixGenerator(error, content, document); |
| var fixes = await generator.computeFixes(); |
| if (fixes.isNotEmpty) { |
| fixes.sort(Fix.SORT_BY_RELEVANCE); |
| var lineInfo = LineInfo.fromContent(content); |
| ResolvedUnitResult result = engine.ResolvedUnitResultImpl(session, file, |
| Uri.file(file), true, content, lineInfo, false, null, errors); |
| var serverError = newAnalysisError_fromEngine(result, error); |
| var errorFixes = AnalysisErrorFixes(serverError); |
| errorFixesList.add(errorFixes); |
| fixes.forEach((fix) { |
| errorFixes.fixes.add(fix.change); |
| }); |
| } |
| } |
| return errorFixesList; |
| } |
| |
| /// Compute and return the fixes associated with server-generated errors in |
| /// pubspec.yaml files. |
| Future<List<AnalysisErrorFixes>> _computePubspecFixes( |
| String file, int offset) async { |
| var errorFixesList = <AnalysisErrorFixes>[]; |
| var pubspecFile = server.resourceProvider.getFile(file); |
| var content = _safelyRead(pubspecFile); |
| if (content == null) { |
| return errorFixesList; |
| } |
| var driver = server.getAnalysisDriver(file); |
| if (driver == null) { |
| return errorFixesList; |
| } |
| var sourceFactory = driver.sourceFactory; |
| var pubspec = _getOptions(sourceFactory, content); |
| if (pubspec == null) { |
| return errorFixesList; |
| } |
| var validator = |
| PubspecValidator(server.resourceProvider, pubspecFile.createSource()); |
| var session = driver.currentSession; |
| var errors = validator.validate(pubspec.nodes); |
| for (var error in errors) { |
| var generator = PubspecFixGenerator(error, content, pubspec); |
| var fixes = await generator.computeFixes(); |
| if (fixes.isNotEmpty) { |
| fixes.sort(Fix.SORT_BY_RELEVANCE); |
| var lineInfo = LineInfo.fromContent(content); |
| ResolvedUnitResult result = engine.ResolvedUnitResultImpl(session, file, |
| Uri.file(file), true, content, lineInfo, false, null, errors); |
| var serverError = newAnalysisError_fromEngine(result, error); |
| var errorFixes = AnalysisErrorFixes(serverError); |
| errorFixesList.add(errorFixes); |
| fixes.forEach((fix) { |
| errorFixes.fixes.add(fix.change); |
| }); |
| } |
| } |
| return errorFixesList; |
| } |
| |
| Future<List<SourceChange>> _computeServerAssists( |
| Request request, String file, int offset, int length) async { |
| var changes = <SourceChange>[]; |
| |
| var result = await server.getResolvedUnit(file); |
| server.requestStatistics?.addItemTimeNow(request, 'resolvedUnit'); |
| |
| if (result != null) { |
| var context = DartAssistContextImpl( |
| server.instrumentationService, |
| DartChangeWorkspace(server.currentSessions), |
| result, |
| offset, |
| length, |
| ); |
| |
| try { |
| var processor = AssistProcessor(context); |
| var assists = await processor.compute(); |
| assists.sort(Assist.SORT_BY_RELEVANCE); |
| for (var assist in assists) { |
| changes.add(assist.change); |
| } |
| } on InconsistentAnalysisException { |
| // ignore |
| } catch (exception, stackTrace) { |
| var parametersFile = ''' |
| offset: $offset |
| length: $length |
| '''; |
| throw CaughtExceptionWithFiles(exception, stackTrace, { |
| file: result.content!, |
| 'parameters': parametersFile, |
| }); |
| } |
| |
| server.requestStatistics?.addItemTimeNow(request, 'computedAssists'); |
| } |
| |
| return changes; |
| } |
| |
| /// Compute and return the fixes associated with server-generated errors. |
| Future<List<AnalysisErrorFixes>> _computeServerErrorFixes( |
| Request request, String file, int offset) async { |
| var pathContext = server.resourceProvider.pathContext; |
| if (file_paths.isDart(pathContext, file)) { |
| return _computeDartFixes(request, file, offset); |
| } else if (file_paths.isAnalysisOptionsYaml(pathContext, file)) { |
| return _computeAnalysisOptionsFixes(file, offset); |
| } else if (file_paths.isPubspecYaml(pathContext, file)) { |
| return _computePubspecFixes(file, offset); |
| } else if (file_paths.isAndroidManifestXml(pathContext, file)) { |
| // TODO(brianwilkerson) Do we need to check more than the file name? |
| return _computeManifestFixes(file, offset); |
| } |
| return <AnalysisErrorFixes>[]; |
| } |
| |
| Response _getAvailableRefactorings(Request request) { |
| _getAvailableRefactoringsImpl(request); |
| return Response.DELAYED_RESPONSE; |
| } |
| |
| Future _getAvailableRefactoringsImpl(Request request) async { |
| var params = EditGetAvailableRefactoringsParams.fromRequest(request); |
| var file = params.file; |
| var offset = params.offset; |
| var length = params.length; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(request, file)) { |
| return; |
| } |
| |
| // add refactoring kinds |
| var kinds = <RefactoringKind>[]; |
| // Check nodes. |
| final searchEngine = server.searchEngine; |
| { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| // Try EXTRACT_LOCAL_VARIABLE. |
| if (ExtractLocalRefactoring(resolvedUnit, offset, length) |
| .isAvailable()) { |
| kinds.add(RefactoringKind.EXTRACT_LOCAL_VARIABLE); |
| } |
| // Try EXTRACT_METHOD. |
| if (ExtractMethodRefactoring(searchEngine, resolvedUnit, offset, length) |
| .isAvailable()) { |
| kinds.add(RefactoringKind.EXTRACT_METHOD); |
| } |
| // Try EXTRACT_WIDGETS. |
| if (ExtractWidgetRefactoring(searchEngine, resolvedUnit, offset, length) |
| .isAvailable()) { |
| kinds.add(RefactoringKind.EXTRACT_WIDGET); |
| } |
| } |
| } |
| // check elements |
| { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| var node = NodeLocator(offset).searchWithin(resolvedUnit.unit); |
| var element = server.getElementOfNode(node); |
| if (element != null) { |
| // try CONVERT_METHOD_TO_GETTER |
| if (element is ExecutableElement) { |
| Refactoring refactoring = ConvertMethodToGetterRefactoring( |
| searchEngine, resolvedUnit.session, element); |
| var status = await refactoring.checkInitialConditions(); |
| if (!status.hasFatalError) { |
| kinds.add(RefactoringKind.CONVERT_METHOD_TO_GETTER); |
| } |
| } |
| // try RENAME |
| final refactoringWorkspace = this.refactoringWorkspace; |
| if (refactoringWorkspace != null) { |
| var renameRefactoring = RenameRefactoring.create( |
| refactoringWorkspace, resolvedUnit, element); |
| if (renameRefactoring != null) { |
| kinds.add(RefactoringKind.RENAME); |
| } |
| } |
| } |
| } |
| } |
| // respond |
| var result = EditGetAvailableRefactoringsResult(kinds); |
| server.sendResponse(result.toResponse(request.id)); |
| } |
| |
| YamlMap? _getOptions(SourceFactory sourceFactory, String content) { |
| var optionsProvider = AnalysisOptionsProvider(sourceFactory); |
| try { |
| return optionsProvider.getOptionsFromString(content); |
| } on OptionsFormatException { |
| return null; |
| } |
| } |
| |
| Response _getRefactoring(Request request) { |
| final refactoringManager = this.refactoringManager; |
| if (refactoringManager == null) { |
| return Response.unsupportedFeature(request.id, 'Search is not enabled.'); |
| } |
| if (refactoringManager.hasPendingRequest) { |
| refactoringManager.cancel(); |
| _newRefactoringManager(); |
| } |
| refactoringManager.getRefactoring(request); |
| return Response.DELAYED_RESPONSE; |
| } |
| |
| /// Initializes [refactoringManager] with a new instance. |
| void _newRefactoringManager() { |
| final refactoringWorkspace = this.refactoringWorkspace; |
| if (refactoringWorkspace != null) { |
| refactoringManager = _RefactoringManager(server, refactoringWorkspace); |
| } |
| } |
| |
| /// Return the contents of the [file], or `null` if the file does not exist or |
| /// cannot be read. |
| String? _safelyRead(File file) { |
| try { |
| return file.readAsStringSync(); |
| } on FileSystemException { |
| return null; |
| } |
| } |
| |
| static int _getNumberOfScanParseErrors(List<engine.AnalysisError> errors) { |
| var numScanParseErrors = 0; |
| for (var error in errors) { |
| if (error.errorCode is engine.ScannerErrorCode || |
| error.errorCode is engine.ParserErrorCode) { |
| numScanParseErrors++; |
| } |
| } |
| return numScanParseErrors; |
| } |
| } |
| |
| /// An object managing a single [Refactoring] instance. |
| /// |
| /// The instance is identified by its kind, file, offset and length. |
| /// It is initialized when the a set of parameters is given for the first time. |
| /// All subsequent requests are performed on this [Refactoring] instance. |
| /// |
| /// Once new set of parameters is received, the previous [Refactoring] instance |
| /// is invalidated and a new one is created and initialized. |
| class _RefactoringManager { |
| static const List<RefactoringProblem> EMPTY_PROBLEM_LIST = |
| <RefactoringProblem>[]; |
| |
| final AnalysisServer server; |
| final RefactoringWorkspace refactoringWorkspace; |
| final SearchEngine searchEngine; |
| StreamSubscription? subscriptionToReset; |
| |
| RefactoringKind? kind; |
| String? file; |
| int? offset; |
| int? length; |
| Refactoring? refactoring; |
| RefactoringFeedback? feedback; |
| late RefactoringStatus initStatus; |
| late RefactoringStatus optionsStatus; |
| late RefactoringStatus finalStatus; |
| |
| Request? request; |
| EditGetRefactoringResult? result; |
| |
| _RefactoringManager(this.server, this.refactoringWorkspace) |
| : searchEngine = refactoringWorkspace.searchEngine { |
| _reset(); |
| } |
| |
| /// Returns `true` if a response for the current request has not yet been |
| /// sent. |
| bool get hasPendingRequest => request != null; |
| |
| bool get _hasFatalError { |
| return initStatus.hasFatalError || |
| optionsStatus.hasFatalError || |
| finalStatus.hasFatalError; |
| } |
| |
| /// Checks if [refactoring] requires options. |
| bool get _requiresOptions { |
| return refactoring is ExtractLocalRefactoring || |
| refactoring is ExtractMethodRefactoring || |
| refactoring is ExtractWidgetRefactoring || |
| refactoring is InlineMethodRefactoring || |
| refactoring is MoveFileRefactoring || |
| refactoring is RenameRefactoring; |
| } |
| |
| /// Cancels processing of the current request and cleans up. |
| void cancel() { |
| var currentRequest = request; |
| if (currentRequest != null) { |
| server.sendResponse(Response.refactoringRequestCancelled(currentRequest)); |
| request = null; |
| } |
| _reset(); |
| } |
| |
| void getRefactoring(Request _request) { |
| // prepare for processing the request |
| request = _request; |
| final result = this.result = EditGetRefactoringResult( |
| EMPTY_PROBLEM_LIST, EMPTY_PROBLEM_LIST, EMPTY_PROBLEM_LIST); |
| // process the request |
| var params = EditGetRefactoringParams.fromRequest(_request); |
| var file = params.file; |
| |
| if (server.sendResponseErrorIfInvalidFilePath(_request, file)) { |
| return; |
| } |
| |
| server.options.analytics |
| ?.sendEvent('refactor', params.kind.name.toLowerCase()); |
| |
| runZonedGuarded(() async { |
| await _init(params.kind, file, params.offset, params.length); |
| if (initStatus.hasFatalError) { |
| feedback = null; |
| _sendResultResponse(); |
| return; |
| } |
| // set options |
| if (_requiresOptions) { |
| if (params.options == null) { |
| optionsStatus = RefactoringStatus(); |
| _sendResultResponse(); |
| return; |
| } |
| optionsStatus = _setOptions(params); |
| if (_hasFatalError) { |
| _sendResultResponse(); |
| return; |
| } |
| } |
| // done if just validation |
| if (params.validateOnly) { |
| finalStatus = RefactoringStatus(); |
| _sendResultResponse(); |
| return; |
| } |
| // simulate an exception |
| if (test_simulateRefactoringException_final) { |
| throw 'A simulated refactoring exception - final.'; |
| } |
| // validation and create change |
| final refactoring = this.refactoring!; |
| finalStatus = await refactoring.checkFinalConditions(); |
| _checkForReset_afterFinalConditions(); |
| if (_hasFatalError) { |
| _sendResultResponse(); |
| return; |
| } |
| // simulate an exception |
| if (test_simulateRefactoringException_change) { |
| throw 'A simulated refactoring exception - change.'; |
| } |
| // create change |
| result.change = await refactoring.createChange(); |
| _checkForReset_afterCreateChange(); |
| result.potentialEdits = nullIfEmpty(refactoring.potentialEditIds); |
| _sendResultResponse(); |
| }, (exception, stackTrace) { |
| if (exception is _ResetError || |
| exception is InconsistentAnalysisException) { |
| cancel(); |
| } else { |
| server.instrumentationService.logException(exception, stackTrace); |
| server.sendResponse( |
| Response.serverError(_request, exception, stackTrace)); |
| } |
| _reset(); |
| }); |
| } |
| |
| void _checkForReset_afterCreateChange() { |
| if (test_simulateRefactoringReset_afterCreateChange) { |
| _reset(); |
| } |
| if (refactoring == null) { |
| throw _ResetError(); |
| } |
| } |
| |
| void _checkForReset_afterFinalConditions() { |
| if (test_simulateRefactoringReset_afterFinalConditions) { |
| _reset(); |
| } |
| if (refactoring == null) { |
| throw _ResetError(); |
| } |
| } |
| |
| void _checkForReset_afterInitialConditions() { |
| if (test_simulateRefactoringReset_afterInitialConditions) { |
| _reset(); |
| } |
| if (refactoring == null) { |
| throw _ResetError(); |
| } |
| } |
| |
| Future<void> _createRefactoringFromKind( |
| String file, int offset, int length) async { |
| if (kind == RefactoringKind.CONVERT_GETTER_TO_METHOD) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| var node = NodeLocator(offset).searchWithin(resolvedUnit.unit); |
| var element = server.getElementOfNode(node); |
| if (element != null) { |
| if (element is PropertyAccessorElement) { |
| refactoring = ConvertGetterToMethodRefactoring( |
| searchEngine, resolvedUnit.session, element); |
| } |
| } |
| } |
| } else if (kind == RefactoringKind.CONVERT_METHOD_TO_GETTER) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| var node = NodeLocator(offset).searchWithin(resolvedUnit.unit); |
| var element = server.getElementOfNode(node); |
| if (element != null) { |
| if (element is ExecutableElement) { |
| refactoring = ConvertMethodToGetterRefactoring( |
| searchEngine, resolvedUnit.session, element); |
| } |
| } |
| } |
| } else if (kind == RefactoringKind.EXTRACT_LOCAL_VARIABLE) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| refactoring = ExtractLocalRefactoring(resolvedUnit, offset, length); |
| feedback = ExtractLocalVariableFeedback(<String>[], <int>[], <int>[], |
| coveringExpressionOffsets: <int>[], |
| coveringExpressionLengths: <int>[]); |
| } |
| } else if (kind == RefactoringKind.EXTRACT_METHOD) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| refactoring = ExtractMethodRefactoring( |
| searchEngine, resolvedUnit, offset, length); |
| feedback = ExtractMethodFeedback(offset, length, '', <String>[], false, |
| <RefactoringMethodParameter>[], <int>[], <int>[]); |
| } |
| } else if (kind == RefactoringKind.EXTRACT_WIDGET) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| refactoring = ExtractWidgetRefactoring( |
| searchEngine, resolvedUnit, offset, length); |
| feedback = ExtractWidgetFeedback(); |
| } |
| } else if (kind == RefactoringKind.INLINE_LOCAL_VARIABLE) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| refactoring = InlineLocalRefactoring( |
| searchEngine, |
| resolvedUnit, |
| offset, |
| ); |
| } |
| } else if (kind == RefactoringKind.INLINE_METHOD) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| refactoring = InlineMethodRefactoring( |
| searchEngine, |
| resolvedUnit, |
| offset, |
| ); |
| } |
| } else if (kind == RefactoringKind.MOVE_FILE) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| refactoring = MoveFileRefactoring( |
| server.resourceProvider, refactoringWorkspace, resolvedUnit, file); |
| } |
| } else if (kind == RefactoringKind.RENAME) { |
| var resolvedUnit = await server.getResolvedUnit(file); |
| if (resolvedUnit != null) { |
| var node = NodeLocator(offset).searchWithin(resolvedUnit.unit); |
| var element = server.getElementOfNode(node); |
| if (node != null && element != null) { |
| final renameElement = |
| RenameRefactoring.getElementToRename(node, element); |
| if (renameElement != null) { |
| // do create the refactoring |
| refactoring = RenameRefactoring.create( |
| refactoringWorkspace, resolvedUnit, renameElement.element); |
| feedback = RenameFeedback( |
| renameElement.offset, renameElement.length, 'kind', 'oldName'); |
| } |
| } |
| } |
| } |
| } |
| |
| /// Initializes this context to perform a refactoring with the specified |
| /// parameters. The existing [Refactoring] is reused or created as needed. |
| Future _init( |
| RefactoringKind kind, String file, int offset, int length) async { |
| // check if we can continue with the existing Refactoring instance |
| if (this.kind == kind && |
| this.file == file && |
| this.offset == offset && |
| this.length == length) { |
| return; |
| } |
| _reset(); |
| _resetOnAnalysisSetChanged(); |
| this.kind = kind; |
| this.file = file; |
| this.offset = offset; |
| this.length = length; |
| // simulate an exception |
| if (test_simulateRefactoringException_init) { |
| throw 'A simulated refactoring exception - init.'; |
| } |
| // create a new Refactoring instance |
| await _createRefactoringFromKind(file, offset, length); |
| final refactoring = this.refactoring; |
| if (refactoring == null) { |
| initStatus = RefactoringStatus.fatal('Unable to create a refactoring'); |
| return; |
| } |
| // check initial conditions |
| initStatus = await refactoring.checkInitialConditions(); |
| _checkForReset_afterInitialConditions(); |
| if (refactoring is ExtractLocalRefactoring) { |
| final feedback = this.feedback as ExtractLocalVariableFeedback; |
| feedback.names = refactoring.names; |
| feedback.offsets = refactoring.offsets; |
| feedback.lengths = refactoring.lengths; |
| feedback.coveringExpressionOffsets = |
| refactoring.coveringExpressionOffsets; |
| feedback.coveringExpressionLengths = |
| refactoring.coveringExpressionLengths; |
| } else if (refactoring is ExtractMethodRefactoring) { |
| final feedback = this.feedback as ExtractMethodFeedback; |
| feedback.canCreateGetter = refactoring.canCreateGetter; |
| feedback.returnType = refactoring.returnType; |
| feedback.names = refactoring.names; |
| feedback.parameters = refactoring.parameters; |
| feedback.offsets = refactoring.offsets; |
| feedback.lengths = refactoring.lengths; |
| } else if (refactoring is InlineLocalRefactoring) { |
| if (!initStatus.hasFatalError) { |
| feedback = InlineLocalVariableFeedback( |
| refactoring.variableName ?? '', refactoring.referenceCount); |
| } |
| } else if (refactoring is InlineMethodRefactoring) { |
| if (!initStatus.hasFatalError) { |
| feedback = InlineMethodFeedback( |
| refactoring.methodName ?? '', refactoring.isDeclaration, |
| className: refactoring.className); |
| } |
| } else if (refactoring is RenameRefactoring) { |
| final feedback = this.feedback as RenameFeedback; |
| feedback.elementKindName = refactoring.elementKindName; |
| feedback.oldName = refactoring.oldName; |
| } |
| } |
| |
| void _reset() { |
| test_resetCount++; |
| kind = null; |
| offset = null; |
| length = null; |
| refactoring = null; |
| feedback = null; |
| initStatus = RefactoringStatus(); |
| optionsStatus = RefactoringStatus(); |
| finalStatus = RefactoringStatus(); |
| subscriptionToReset?.cancel(); |
| subscriptionToReset = null; |
| } |
| |
| void _resetOnAnalysisSetChanged() { |
| subscriptionToReset?.cancel(); |
| subscriptionToReset = server.onAnalysisSetChanged.listen((_) { |
| _reset(); |
| }); |
| } |
| |
| void _sendResultResponse() { |
| // ignore if was cancelled |
| final request = this.request; |
| if (request == null) { |
| return; |
| } |
| // set feedback |
| final result = this.result; |
| if (result == null) { |
| return; |
| } |
| result.feedback = feedback; |
| // set problems |
| result.initialProblems = initStatus.problems; |
| result.optionsProblems = optionsStatus.problems; |
| result.finalProblems = finalStatus.problems; |
| // send the response |
| server.sendResponse(result.toResponse(request.id)); |
| // done with this request |
| this.request = null; |
| this.result = null; |
| } |
| |
| RefactoringStatus _setOptions(EditGetRefactoringParams params) { |
| final refactoring = this.refactoring; |
| if (refactoring is ExtractLocalRefactoring) { |
| var extractOptions = params.options as ExtractLocalVariableOptions; |
| refactoring.name = extractOptions.name; |
| refactoring.extractAll = extractOptions.extractAll; |
| return refactoring.checkName(); |
| } else if (refactoring is ExtractMethodRefactoring) { |
| var extractOptions = params.options as ExtractMethodOptions; |
| refactoring.createGetter = extractOptions.createGetter; |
| refactoring.extractAll = extractOptions.extractAll; |
| refactoring.name = extractOptions.name; |
| refactoring.parameters = extractOptions.parameters; |
| refactoring.returnType = extractOptions.returnType; |
| return refactoring.checkName(); |
| } else if (refactoring is ExtractWidgetRefactoring) { |
| var extractOptions = params.options as ExtractWidgetOptions; |
| refactoring.name = extractOptions.name; |
| return refactoring.checkName(); |
| } else if (refactoring is InlineMethodRefactoring) { |
| var inlineOptions = params.options as InlineMethodOptions; |
| refactoring.deleteSource = inlineOptions.deleteSource; |
| refactoring.inlineAll = inlineOptions.inlineAll; |
| return RefactoringStatus(); |
| } else if (refactoring is MoveFileRefactoring) { |
| var moveOptions = params.options as MoveFileOptions; |
| refactoring.newFile = moveOptions.newFile; |
| return RefactoringStatus(); |
| } else if (refactoring is RenameRefactoring) { |
| var renameOptions = params.options as RenameOptions; |
| refactoring.newName = renameOptions.newName; |
| return refactoring.checkNewName(); |
| } |
| return RefactoringStatus(); |
| } |
| } |
| |
| /// [_RefactoringManager] throws instances of this class internally to stop |
| /// processing in a manager that was reset. |
| class _ResetError {} |