| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library domain.execution; |
| 6 |
| 7 import 'package:analysis_server/src/analysis_server.dart'; |
| 8 import 'package:analysis_server/src/constants.dart'; |
| 9 import 'package:analysis_server/src/protocol.dart'; |
| 10 import 'dart:collection'; |
| 11 import 'package:analyzer/src/generated/engine.dart'; |
| 12 import 'package:analyzer/src/generated/source.dart'; |
| 13 |
| 14 /** |
| 15 * Instances of the class [ExecutionDomainHandler] implement a [RequestHandler] |
| 16 * that handles requests in the `execution` domain. |
| 17 */ |
| 18 class ExecutionDomainHandler implements RequestHandler { |
| 19 /** |
| 20 * The analysis server that is using this handler to process requests. |
| 21 */ |
| 22 final AnalysisServer server; |
| 23 |
| 24 /** |
| 25 * The next execution context identifier to be returned. |
| 26 */ |
| 27 int nextContextId = 0; |
| 28 |
| 29 /** |
| 30 * A table mapping execution context id's to the root of the context. |
| 31 */ |
| 32 Map<String, String> contextMap = new HashMap<String, String>(); |
| 33 |
| 34 /** |
| 35 * The listener used to send notifications when |
| 36 */ |
| 37 LaunchDataNotificationListener launchDataListener; |
| 38 |
| 39 /** |
| 40 * Initialize a newly created handler to handle requests for the given [server
]. |
| 41 */ |
| 42 ExecutionDomainHandler(this.server); |
| 43 |
| 44 /** |
| 45 * Implement the `execution.createContext` request. |
| 46 */ |
| 47 Response createContext(Request request) { |
| 48 String file = |
| 49 new ExecutionCreateContextParams.fromRequest(request).contextRoot; |
| 50 String contextId = (nextContextId++).toString(); |
| 51 contextMap[contextId] = file; |
| 52 return new ExecutionCreateContextResult(contextId).toResponse(request.id); |
| 53 } |
| 54 |
| 55 /** |
| 56 * Implement the `execution.deleteContext` request. |
| 57 */ |
| 58 Response deleteContext(Request request) { |
| 59 String contextId = new ExecutionDeleteContextParams.fromRequest(request).id; |
| 60 contextMap.remove(contextId); |
| 61 return new ExecutionDeleteContextResult().toResponse(request.id); |
| 62 } |
| 63 |
| 64 @override |
| 65 Response handleRequest(Request request) { |
| 66 try { |
| 67 String requestName = request.method; |
| 68 if (requestName == EXECUTION_CREATE_CONTEXT) { |
| 69 return createContext(request); |
| 70 } else if (requestName == EXECUTION_DELETE_CONTEXT) { |
| 71 return deleteContext(request); |
| 72 } else if (requestName == EXECUTION_MAP_URI) { |
| 73 return mapUri(request); |
| 74 } else if (requestName == EXECUTION_SET_SUBSCRIPTIONS) { |
| 75 return setSubscriptions(request); |
| 76 } |
| 77 } on RequestFailure catch (exception) { |
| 78 return exception.response; |
| 79 } |
| 80 return null; |
| 81 } |
| 82 |
| 83 /** |
| 84 * Implement the 'execution.mapUri' request. |
| 85 */ |
| 86 Response mapUri(Request request) { |
| 87 ExecutionMapUriParams params = |
| 88 new ExecutionMapUriParams.fromRequest(request); |
| 89 String contextId = params.id; |
| 90 String path = contextMap[contextId]; |
| 91 if (path == null) { |
| 92 return new Response.invalidParameter( |
| 93 request, |
| 94 'id', |
| 95 'There is no execution context with an id of $contextId'); |
| 96 } |
| 97 AnalysisContext context = server.getAnalysisContext(path); |
| 98 if (params.file != null) { |
| 99 if (params.uri != null) { |
| 100 return new Response.invalidParameter( |
| 101 request, |
| 102 'file', |
| 103 'Either file or uri must be provided, but not both'); |
| 104 } |
| 105 Source source = server.getSource(path); |
| 106 // TODO(brianwilkerson) Figure out how to perform the mapping. |
| 107 String uri = ''; |
| 108 return new ExecutionMapUriResult(uri: uri).toResponse(request.id); |
| 109 } else if (params.uri != null) { |
| 110 // TODO(brianwilkerson) Figure out how to perform the mapping. |
| 111 String file = ''; |
| 112 return new ExecutionMapUriResult(file: file).toResponse(request.id); |
| 113 } |
| 114 return new Response.invalidParameter( |
| 115 request, |
| 116 'file', |
| 117 'Either file or uri must be provided'); |
| 118 } |
| 119 |
| 120 /** |
| 121 * Implement the 'execution.setSubscriptions' request. |
| 122 */ |
| 123 Response setSubscriptions(Request request) { |
| 124 List<ExecutionService> subscriptions = |
| 125 new ExecutionSetSubscriptionsParams.fromRequest(request).subscriptions; |
| 126 if (subscriptions.contains(ExecutionService.LAUNCH_DATA)) { |
| 127 if (launchDataListener == null) { |
| 128 launchDataListener = new LaunchDataNotificationListener(server); |
| 129 server.addAnalysisServerListener(launchDataListener); |
| 130 if (server.isAnalysisComplete()) { |
| 131 launchDataListener.analysisComplete(); |
| 132 } |
| 133 } |
| 134 } else { |
| 135 if (launchDataListener != null) { |
| 136 server.removeAnalysisServerListener(launchDataListener); |
| 137 launchDataListener = null; |
| 138 } |
| 139 } |
| 140 return new ExecutionSetSubscriptionsResult().toResponse(request.id); |
| 141 } |
| 142 } |
| 143 |
| 144 /** |
| 145 * Instances of the class [LaunchDataNotificationListener] listen for analysis |
| 146 * to be complete and then notify the client of the launch data that has been |
| 147 * computed. |
| 148 */ |
| 149 class LaunchDataNotificationListener implements AnalysisServerListener { |
| 150 /** |
| 151 * The analysis server used to send notifications. |
| 152 */ |
| 153 final AnalysisServer server; |
| 154 |
| 155 /** |
| 156 * Initialize a newly created listener to send notifications through the given |
| 157 * [server] when analysis is complete. |
| 158 */ |
| 159 LaunchDataNotificationListener(this.server); |
| 160 |
| 161 @override |
| 162 void analysisComplete() { |
| 163 List<ExecutableFile> executables = []; |
| 164 Map<String, List<String>> dartToHtml = new HashMap<String, List<String>>(); |
| 165 Map<String, List<String>> htmlToDart = new HashMap<String, List<String>>(); |
| 166 for (AnalysisContext context in server.getAnalysisContexts()) { |
| 167 List<Source> clientSources = context.launchableClientLibrarySources; |
| 168 List<Source> serverSources = context.launchableServerLibrarySources; |
| 169 for (Source source in clientSources) { |
| 170 ExecutableKind kind = ExecutableKind.CLIENT; |
| 171 if (serverSources.remove(source)) { |
| 172 kind = ExecutableKind.EITHER; |
| 173 } |
| 174 executables.add(new ExecutableFile(source.fullName, kind)); |
| 175 } |
| 176 for (Source source in serverSources) { |
| 177 executables.add( |
| 178 new ExecutableFile(source.fullName, ExecutableKind.SERVER)); |
| 179 } |
| 180 |
| 181 for (Source librarySource in context.librarySources) { |
| 182 List<Source> files = context.getHtmlFilesReferencing(librarySource); |
| 183 if (files.isNotEmpty) { |
| 184 // TODO(brianwilkerson) Handle the case where the same library is |
| 185 // being analyzed in multiple contexts. |
| 186 dartToHtml[librarySource.fullName] = getFullNames(files); |
| 187 } |
| 188 } |
| 189 |
| 190 for (Source htmlSource in context.htmlSources) { |
| 191 List<Source> libraries = |
| 192 context.getLibrariesReferencedFromHtml(htmlSource); |
| 193 if (libraries.isNotEmpty) { |
| 194 // TODO(brianwilkerson) Handle the case where the same HTML file is |
| 195 // being analyzed in multiple contexts. |
| 196 htmlToDart[htmlSource.fullName] = getFullNames(libraries); |
| 197 } |
| 198 } |
| 199 } |
| 200 server.sendNotification( |
| 201 new ExecutionLaunchDataParams( |
| 202 executables, |
| 203 dartToHtml, |
| 204 htmlToDart).toNotification()); |
| 205 } |
| 206 |
| 207 List<String> getFullNames(List<Source> sources) { |
| 208 return sources.map((Source source) => source.fullName).toList(); |
| 209 } |
| 210 } |
| OLD | NEW |