| // 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. |
| |
| library domain.analysis; |
| |
| import 'dart:collection'; |
| |
| import 'package:analysis_server/src/analysis_server.dart'; |
| import 'package:analysis_server/src/computer/computer_hover.dart'; |
| import 'package:analysis_server/src/computer/error.dart'; |
| import 'package:analysis_server/src/constants.dart'; |
| import 'package:analysis_server/src/protocol.dart'; |
| import 'package:analysis_services/constants.dart'; |
| import 'package:analyzer/src/generated/ast.dart'; |
| import 'package:analyzer/src/generated/engine.dart'; |
| |
| |
| /** |
| * Instances of the class [AnalysisDomainHandler] implement a [RequestHandler] |
| * that handles requests in the `analysis` domain. |
| */ |
| class AnalysisDomainHandler implements RequestHandler { |
| /** |
| * The analysis server that is using this handler to process requests. |
| */ |
| final AnalysisServer server; |
| |
| /** |
| * Initialize a newly created handler to handle requests for the given [server]. |
| */ |
| AnalysisDomainHandler(this.server); |
| |
| /** |
| * Implement the `analysis.getErrors` request. |
| */ |
| Response getErrors(Request request) { |
| String file = request.getRequiredParameter(FILE).asString(); |
| server.onFileAnalysisComplete(file).then((_) { |
| Response response = new Response(request.id); |
| AnalysisErrorInfo errorInfo = server.getErrors(file); |
| if (errorInfo == null) { |
| response.setResult(ERRORS, []); |
| } else { |
| response.setResult(ERRORS, engineErrorInfoToJson(errorInfo)); |
| } |
| server.sendResponse(response); |
| }).catchError((message) { |
| if (message is! String) { |
| AnalysisEngine.instance.logger.logError( |
| 'Illegal error message during getErrors: $message'); |
| message = ''; |
| } |
| Response response = new Response.getErrorsError(request, message); |
| response.setResult(ERRORS, []); |
| server.sendResponse(response); |
| }); |
| // delay response |
| return Response.DELAYED_RESPONSE; |
| } |
| |
| /** |
| * Implement the `analysis.getHover` request. |
| */ |
| Response getHover(Request request) { |
| // prepare parameters |
| String file = request.getRequiredParameter(FILE).asString(); |
| int offset = request.getRequiredParameter(OFFSET).asInt(); |
| // prepare hovers |
| List<Hover> hovers = <Hover>[]; |
| List<CompilationUnit> units = server.getResolvedCompilationUnits(file); |
| for (CompilationUnit unit in units) { |
| Hover hoverInformation = |
| new DartUnitHoverComputer(unit, offset).compute(); |
| if (hoverInformation != null) { |
| hovers.add(hoverInformation); |
| } |
| } |
| // send response |
| Response response = new Response(request.id); |
| response.setResult(HOVERS, hovers); |
| return response; |
| } |
| |
| @override |
| Response handleRequest(Request request) { |
| try { |
| String requestName = request.method; |
| if (requestName == ANALYSIS_GET_ERRORS) { |
| return getErrors(request); |
| } else if (requestName == ANALYSIS_GET_HOVER) { |
| return getHover(request); |
| } else if (requestName == ANALYSIS_SET_ANALYSIS_ROOTS) { |
| return setAnalysisRoots(request); |
| } else if (requestName == ANALYSIS_SET_PRIORITY_FILES) { |
| return setPriorityFiles(request); |
| } else if (requestName == ANALYSIS_SET_SUBSCRIPTIONS) { |
| return setSubscriptions(request); |
| } else if (requestName == ANALYSIS_UPDATE_CONTENT) { |
| return updateContent(request); |
| } else if (requestName == ANALYSIS_UPDATE_OPTIONS) { |
| return updateOptions(request); |
| } |
| } on RequestFailure catch (exception) { |
| return exception.response; |
| } |
| return null; |
| } |
| |
| /** |
| * Implement the 'analysis.setAnalysisRoots' request. |
| */ |
| Response setAnalysisRoots(Request request) { |
| // included |
| RequestDatum includedDatum = request.getRequiredParameter(INCLUDED); |
| List<String> includedPaths = includedDatum.asStringList(); |
| // excluded |
| RequestDatum excludedDatum = request.getRequiredParameter(EXCLUDED); |
| List<String> excludedPaths = excludedDatum.asStringList(); |
| // continue in server |
| server.setAnalysisRoots(request.id, includedPaths, excludedPaths); |
| return new Response(request.id); |
| } |
| |
| /** |
| * Implement the 'analysis.setPriorityFiles' request. |
| */ |
| Response setPriorityFiles(Request request) { |
| // files |
| RequestDatum filesDatum = request.getRequiredParameter(FILES); |
| List<String> files = filesDatum.asStringList(); |
| server.setPriorityFiles(request, files); |
| return new Response(request.id); |
| } |
| |
| /** |
| * Implement the 'analysis.setSubscriptions' request. |
| */ |
| Response setSubscriptions(Request request) { |
| // parse subscriptions |
| Map<AnalysisService, Set<String>> subMap; |
| { |
| RequestDatum subDatum = request.getRequiredParameter(SUBSCRIPTIONS); |
| Map<String, List<String>> subStringMap = subDatum.asStringListMap(); |
| subMap = new HashMap<AnalysisService, Set<String>>(); |
| subStringMap.forEach((String serviceName, List<String> paths) { |
| AnalysisService service = |
| Enum2.valueOf(AnalysisService.VALUES, serviceName); |
| if (service == null) { |
| throw new RequestFailure( |
| new Response.unknownAnalysisService(request, serviceName)); |
| } |
| subMap[service] = new HashSet.from(paths); |
| }); |
| } |
| server.setAnalysisSubscriptions(subMap); |
| return new Response(request.id); |
| } |
| |
| /** |
| * Implement the 'analysis.updateContent' request. |
| */ |
| Response updateContent(Request request) { |
| var changes = new HashMap<String, ContentChange>(); |
| RequestDatum filesDatum = request.getRequiredParameter(FILES); |
| for (String file in filesDatum.keys) { |
| RequestDatum changeDatum = filesDatum[file]; |
| ContentChange change = new ContentChange(); |
| switch (changeDatum[TYPE].asString()) { |
| case ADD: |
| change.contentOrReplacement = changeDatum[CONTENT].asString(); |
| break; |
| case CHANGE: |
| change.offset = changeDatum[OFFSET].asInt(); |
| change.length = changeDatum[LENGTH].asInt(); |
| change.contentOrReplacement = changeDatum[REPLACEMENT].asString(); |
| break; |
| case REMOVE: |
| break; |
| default: |
| return new Response.invalidParameter( |
| request, |
| changeDatum[TYPE].path, |
| 'be one of "add", "change", or "remove"'); |
| } |
| changes[file] = change; |
| } |
| server.updateContent(changes); |
| return new Response(request.id); |
| } |
| |
| /** |
| * Implement the 'analysis.updateOptions' request. |
| */ |
| Response updateOptions(Request request) { |
| // options |
| RequestDatum optionsDatum = request.getRequiredParameter(OPTIONS); |
| List<OptionUpdater> updaters = new List<OptionUpdater>(); |
| optionsDatum.forEachMap((String optionName, RequestDatum optionDatum) { |
| if (optionName == ANALYZE_ANGULAR) { |
| bool optionValue = optionDatum.asBool(); |
| updaters.add((AnalysisOptionsImpl options) { |
| options.analyzeAngular = optionValue; |
| }); |
| } else if (optionName == ANALYZE_POLYMER) { |
| bool optionValue = optionDatum.asBool(); |
| updaters.add((AnalysisOptionsImpl options) { |
| options.analyzePolymer = optionValue; |
| }); |
| } else if (optionName == ENABLE_ASYNC) { |
| // TODO(brianwilkerson) Uncomment this when the option is supported. |
| // bool optionValue = optionDatum.asBool(); |
| // updaters.add((AnalysisOptionsImpl options) { |
| // options.enableAsync = optionValue; |
| // }); |
| } else if (optionName == ENABLE_DEFERRED_LOADING) { |
| bool optionValue = optionDatum.asBool(); |
| updaters.add((AnalysisOptionsImpl options) { |
| options.enableDeferredLoading = optionValue; |
| }); |
| } else if (optionName == ENABLE_ENUMS) { |
| // TODO(brianwilkerson) Uncomment this when the option is supported. |
| // bool optionValue = optionDatum.asBool(); |
| // updaters.add((AnalysisOptionsImpl options) { |
| // options.enableEnums = optionValue; |
| // }); |
| } else if (optionName == GENERATE_DART2JS_HINTS) { |
| bool optionValue = optionDatum.asBool(); |
| updaters.add((AnalysisOptionsImpl options) { |
| options.dart2jsHint = optionValue; |
| }); |
| } else if (optionName == GENERATE_HINTS) { |
| bool optionValue = optionDatum.asBool(); |
| updaters.add((AnalysisOptionsImpl options) { |
| options.hint = optionValue; |
| }); |
| } else { |
| throw new RequestFailure( |
| new Response.unknownOptionName(request, optionName)); |
| } |
| }); |
| server.updateOptions(updaters); |
| return new Response(request.id); |
| } |
| } |
| |
| |
| /** |
| * A description of the change to the content of a file. |
| */ |
| class ContentChange { |
| /** |
| * If [offset] and [length] are null, the full content of the file (or null |
| * if the file should be read from the filesystem). |
| * |
| * If [offset] and [length] are non-null, the replacement text which should |
| * take the place of the [length] characters of the file starting at [offset]. |
| */ |
| String contentOrReplacement; |
| |
| int offset; |
| int length; |
| } |