Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(17)

Side by Side Diff: pkg/analysis_server/lib/src/analysis_server.dart

Issue 300023004: Partial implementation of the 'setAnalysisRoots' API. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fixes for review comments. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/analysis_server/lib/src/domain_analysis.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 library analysis.server; 5 library analysis.server;
6 6
7 import 'dart:async'; 7 import 'dart:async';
8 8
9 import 'package:analysis_server/src/analysis_logger.dart'; 9 import 'package:analysis_server/src/analysis_logger.dart';
10 import 'package:analysis_server/src/channel.dart'; 10 import 'package:analysis_server/src/channel.dart';
11 import 'package:analysis_server/src/protocol.dart'; 11 import 'package:analysis_server/src/protocol.dart';
12 import 'package:analysis_server/src/resource.dart';
13 import 'package:analyzer/src/generated/ast.dart';
12 import 'package:analyzer/src/generated/engine.dart'; 14 import 'package:analyzer/src/generated/engine.dart';
13 import 'package:analyzer/src/generated/error.dart'; 15 import 'package:analyzer/src/generated/error.dart';
14 import 'package:analyzer/src/generated/java_core.dart'; 16 import 'package:analyzer/src/generated/java_core.dart';
17 import 'package:analyzer/src/generated/sdk.dart';
18 import 'package:analyzer/src/generated/sdk_io.dart';
19 import 'package:analyzer/src/generated/source_io.dart';
15 20
16 /** 21 /**
17 * Instances of the class [AnalysisServer] implement a server that listens on a 22 * Instances of the class [AnalysisServer] implement a server that listens on a
18 * [CommunicationChannel] for analysis requests and process them. 23 * [CommunicationChannel] for analysis requests and process them.
19 */ 24 */
20 class AnalysisServer { 25 class AnalysisServer {
21 /** 26 /**
22 * The name of the notification of new errors associated with a source. 27 * The name of the notification of new errors associated with a source.
23 */ 28 */
24 static const String ERROR_NOTIFICATION_NAME = 'context.errors'; 29 static const String ERROR_NOTIFICATION_NAME = 'context.errors';
(...skipping 18 matching lines...) Expand all
43 */ 48 */
44 static const String CONNECTED_NOTIFICATION = 'server.connected'; 49 static const String CONNECTED_NOTIFICATION = 'server.connected';
45 50
46 /** 51 /**
47 * The channel from which requests are received and to which responses should 52 * The channel from which requests are received and to which responses should
48 * be sent. 53 * be sent.
49 */ 54 */
50 final ServerCommunicationChannel channel; 55 final ServerCommunicationChannel channel;
51 56
52 /** 57 /**
58 * The [ResourceProvider] using which paths are converted into [Resource]s.
59 */
60 final ResourceProvider resourceProvider;
61
62 /**
53 * A flag indicating whether the server is running. When false, contexts 63 * A flag indicating whether the server is running. When false, contexts
54 * will no longer be added to [contextWorkQueue], and [performTask] will 64 * will no longer be added to [contextWorkQueue], and [performTask] will
55 * discard any tasks it finds on [contextWorkQueue]. 65 * discard any tasks it finds on [contextWorkQueue].
56 */ 66 */
57 bool running; 67 bool running;
58 68
59 /** 69 /**
60 * A list of the request handlers used to handle the requests sent to this 70 * A list of the request handlers used to handle the requests sent to this
61 * server. 71 * server.
62 */ 72 */
63 List<RequestHandler> handlers; 73 List<RequestHandler> handlers;
64 74
65 /** 75 // TODO(scheglov) remove once setAnalysisRoots() is completely implemented
66 * A table mapping context id's to the analysis contexts associated with them. 76 // /**
67 */ 77 // * A table mapping context id's to the analysis contexts associated with the m.
68 final Map<String, AnalysisContext> contextMap = new Map<String, AnalysisContex t>(); 78 // */
79 // final Map<String, AnalysisContext> contextMap = new Map<String, AnalysisCont ext>();
80 //
81 // /**
82 // * A table mapping analysis contexts to the context id's associated with the m.
83 // */
84 // final Map<AnalysisContext, String> contextIdMap = new Map<AnalysisContext, S tring>();
69 85
70 /** 86 /**
71 * A table mapping analysis contexts to the context id's associated with them. 87 * The current default [DartSdk].
72 */ 88 */
73 final Map<AnalysisContext, String> contextIdMap = new Map<AnalysisContext, Str ing>(); 89 DartSdk defaultSdk = DirectoryBasedDartSdk.defaultSdk;
90
91 /**
92 * A table mapping [Folder]s to the [PubFolder]s associated with them.
93 */
94 final Map<Folder, PubFolder> folderMap = <Folder, PubFolder>{};
74 95
75 /** 96 /**
76 * A list of the analysis contexts for which analysis work needs to be 97 * A list of the analysis contexts for which analysis work needs to be
77 * performed. 98 * performed.
78 * 99 *
79 * Invariant: when this list is non-empty, there is exactly one pending call 100 * Invariant: when this list is non-empty, there is exactly one pending call
80 * to [performTask] on the event queue. When this list is empty, there are 101 * to [performTask] on the event queue. When this list is empty, there are
81 * no calls to [performTask] on the event queue. 102 * no calls to [performTask] on the event queue.
82 */ 103 */
83 final List<AnalysisContext> contextWorkQueue = new List<AnalysisContext>(); 104 final List<AnalysisContext> contextWorkQueue = new List<AnalysisContext>();
84 105
85 /** 106 /**
86 * A set of the [ServerService]s to send notifications for. 107 * A set of the [ServerService]s to send notifications for.
87 */ 108 */
88 Set<ServerService> serverServices = new Set<ServerService>(); 109 Set<ServerService> serverServices = new Set<ServerService>();
89 110
90 /** 111 /**
91 * Initialize a newly created server to receive requests from and send 112 * Initialize a newly created server to receive requests from and send
92 * responses to the given [channel]. 113 * responses to the given [channel].
93 */ 114 */
94 AnalysisServer(this.channel) { 115 AnalysisServer(this.channel, this.resourceProvider) {
95 AnalysisEngine.instance.logger = new AnalysisLogger(); 116 AnalysisEngine.instance.logger = new AnalysisLogger();
96 running = true; 117 running = true;
97 Notification notification = new Notification(CONNECTED_NOTIFICATION); 118 Notification notification = new Notification(CONNECTED_NOTIFICATION);
98 channel.sendNotification(notification); 119 channel.sendNotification(notification);
99 channel.listen(handleRequest, onDone: done, onError: error); 120 channel.listen(handleRequest, onDone: done, onError: error);
100 } 121 }
101 122
102 /** 123 /**
103 * If [running] is true, add the given [context] to the list of analysis 124 * If [running] is true, add the given [context] to the list of analysis
104 * contexts for which analysis work needs to be performed, and ensure that 125 * contexts for which analysis work needs to be performed, and ensure that
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
165 contextWorkQueue.clear(); 186 contextWorkQueue.clear();
166 } 187 }
167 if (contextWorkQueue.isEmpty) { 188 if (contextWorkQueue.isEmpty) {
168 // Nothing to do. 189 // Nothing to do.
169 return; 190 return;
170 } 191 }
171 // 192 //
172 // Look for a context that has work to be done and then perform one task. 193 // Look for a context that has work to be done and then perform one task.
173 // 194 //
174 List<ChangeNotice> notices = null; 195 List<ChangeNotice> notices = null;
175 String contextId; 196 // String contextId;
176 try { 197 try {
177 AnalysisContext context = contextWorkQueue[0]; 198 AnalysisContext context = contextWorkQueue[0];
178 contextId = contextIdMap[context]; 199 // contextId = contextIdMap[context];
179 AnalysisResult result = context.performAnalysisTask(); 200 AnalysisResult result = context.performAnalysisTask();
180 notices = result.changeNotices; 201 notices = result.changeNotices;
181 } finally { 202 } finally {
182 if (notices == null) { 203 if (notices == null) {
183 // Either we have no more work to do for this context, or there was an 204 // Either we have no more work to do for this context, or there was an
184 // unhandled exception trying to perform the analysis. In either case, 205 // unhandled exception trying to perform the analysis. In either case,
185 // remove the context form the work queue so we won't try to do more 206 // remove the context form the work queue so we won't try to do more
186 // analysis on it. 207 // analysis on it.
187 contextWorkQueue.removeAt(0); 208 contextWorkQueue.removeAt(0);
188 } 209 }
189 // 210 //
190 // Schedule this method to be run again if there is any more work to be 211 // Schedule this method to be run again if there is any more work to be
191 // done. 212 // done.
192 // 213 //
193 if (!contextWorkQueue.isEmpty) { 214 if (!contextWorkQueue.isEmpty) {
194 _scheduleTask(); 215 _scheduleTask();
195 } 216 }
196 } 217 }
197 if (notices != null) { 218 // TODO(scheglov) implement for [PubFolder]
198 sendNotices(contextId, notices); 219 // if (notices != null) {
220 // sendNotices(contextId, notices);
221 // }
222 }
223
224 // TODO(scheglov) rewrite for the new API.
225 // /**
226 // * Send the information in the given list of notices back to the client.
227 // */
228 // void sendNotices(String contextId, List<ChangeNotice> notices) {
229 // for (int i = 0; i < notices.length; i++) {
230 // ChangeNotice notice = notices[i];
231 // Notification notification = new Notification(ERROR_NOTIFICATION_NAME);
232 // notification.setParameter(CONTEXT_ID_PARAM, contextId);
233 // notification.setParameter(SOURCE_PARAM, notice.source.encoding);
234 // notification.setParameter(ERRORS_PARAM, notice.errors.map(
235 // errorToJson).toList());
236 // sendNotification(notification);
237 // }
238 // }
239
240 /**
241 * Implementation for `server.setAnalysisRoots`.
242 *
243 * TODO(scheglov) implement complete projects/contexts semantics.
244 *
245 * The current implementation is intentionally simplified and expected
246 * that only folders are given each given folder corresponds to the exactly
247 * one context.
248 *
249 * So, we can start working in parallel on adding services and improving
250 * projects/contexts support.
251 */
252 void setAnalysisRoots(String requestId,
253 List<String> includedPaths,
254 List<String> excludedPaths) {
255 // included
256 Set<Folder> includedFolders = new Set<Folder>();
257 for (int i = 0; i < includedPaths.length; i++) {
258 String path = includedPaths[i];
259 Resource resource = resourceProvider.getResource(path);
260 if (resource is Folder) {
261 includedFolders.add(resource);
262 } else {
263 // TODO(scheglov) implemented separate files analysis
264 throw new RequestFailure(
265 new Response.unsupportedFeature(
266 requestId,
267 '$path is not a folder. '
268 'Only support for folder analysis is implemented currently.'));
269 }
270 }
271 // excluded
272 // TODO(scheglov) remove when implemented
273 if (excludedPaths.isNotEmpty) {
274 throw new RequestFailure(
275 new Response.unsupportedFeature(
276 requestId,
277 'Excluded paths are not supported yet'));
278 }
279 Set<Folder> excludedFolders = new Set<Folder>();
280 // diff
281 Set<Folder> currentFolders = new Set<Folder>.from(folderMap.keys);
282 Set<Folder> newFolders = includedFolders.difference(currentFolders);
283 Set<Folder> oldFolders = currentFolders.difference(includedFolders);
284 // remove old contexts
285 for (Folder folder in oldFolders) {
286 // TODO(scheglov) implement
287 }
288 // add new contexts
289 for (Folder folder in newFolders) {
290 PubFolder pubFolder = new PubFolder(defaultSdk, folder);
291 folderMap[folder] = pubFolder;
292 addContextToWorkQueue(pubFolder.context);
199 } 293 }
200 } 294 }
201 295
202 /** 296 /**
203 * Send the information in the given list of notices back to the client. 297 * Return the [AnalysisContext] that is used to analyze the given [path].
298 * Return `null` if there is no such context.
204 */ 299 */
205 void sendNotices(String contextId, List<ChangeNotice> notices) { 300 AnalysisContext test_getAnalysisContext(String path) {
206 for (int i = 0; i < notices.length; i++) { 301 for (Folder folder in folderMap.keys) {
207 ChangeNotice notice = notices[i]; 302 if (path.startsWith(folder.fullName)) {
208 Notification notification = new Notification(ERROR_NOTIFICATION_NAME); 303 return folderMap[folder].context;
209 notification.setParameter(CONTEXT_ID_PARAM, contextId); 304 }
210 notification.setParameter(SOURCE_PARAM, notice.source.encoding);
211 notification.setParameter(ERRORS_PARAM, notice.errors.map(
212 errorToJson).toList());
213 sendNotification(notification);
214 } 305 }
306 return null;
307 }
308
309 /**
310 * Return the [CompilationUnit] of the Dart file with the given [path].
311 * Return `null` if the file is not a part of any context.
312 */
313 CompilationUnit test_getResolvedCompilationUnit(String path) {
314 // prepare AnalysisContext
315 AnalysisContext context = test_getAnalysisContext(path);
316 if (context == null) {
317 return null;
318 }
319 // prepare sources
320 File file = resourceProvider.getResource(path);
321 Source unitSource = file.createSource(UriKind.FILE_URI);
322 List<Source> librarySources = context.getLibrariesContaining(unitSource);
323 if (librarySources.isEmpty) {
324 return null;
325 }
326 // get a resolved unit
327 return context.getResolvedCompilationUnit2(unitSource, librarySources[0]);
215 } 328 }
216 329
217 static Map<String, Object> errorToJson(AnalysisError analysisError) { 330 static Map<String, Object> errorToJson(AnalysisError analysisError) {
218 // TODO(paulberry): move this function into the AnalysisError class. 331 // TODO(paulberry): move this function into the AnalysisError class.
219 332
220 // TODO(paulberry): we really shouldn't be exposing errorCode.ordinal 333 // TODO(paulberry): we really shouldn't be exposing errorCode.ordinal
221 // outside the analyzer, since the ordinal numbers change whenever we 334 // outside the analyzer, since the ordinal numbers change whenever we
222 // regenerate the analysis engine. 335 // regenerate the analysis engine.
223 Map<String, Object> result = { 336 Map<String, Object> result = {
224 'source': analysisError.source.encoding, 337 'source': analysisError.source.encoding,
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
258 static const AnalysisService OUTLINE = const AnalysisService('OUTLINE', 3); 371 static const AnalysisService OUTLINE = const AnalysisService('OUTLINE', 3);
259 372
260 static const List<AnalysisService> VALUES = 373 static const List<AnalysisService> VALUES =
261 const [ERRORS, HIGHLIGHTS, NAVIGATION, OUTLINE]; 374 const [ERRORS, HIGHLIGHTS, NAVIGATION, OUTLINE];
262 375
263 const AnalysisService(String name, int ordinal) : super(name, ordinal); 376 const AnalysisService(String name, int ordinal) : super(name, ordinal);
264 } 377 }
265 378
266 379
267 /** 380 /**
381 * Instances of [PubFolder] represents a [Folder] with a Pub `pubspec.yaml`.
382 *
383 * TODO(scheglov) implement complete projects/contexts semantics.
384 *
385 * This class is intentionally simplified to serve as a base to start working
386 * on services while work on complete semantics is being done in parallel.
387 */
388 class PubFolder {
389 /**
390 * The root [Folder] of this [PubFolder].
391 */
392 final Folder _folder;
393
394 /**
395 * The `pubspec.yaml` file in [_folder].
396 */
397 File _pubspecFile;
398
399 /**
400 * The [AnalysisContext] of this [_folder].
401 */
402 AnalysisContext _context;
403
404 PubFolder(DartSdk sdk, this._folder) {
405 // prepare pubspec.yaml
406 _pubspecFile = _folder.getChild('pubspec.yaml');
407 if (!_pubspecFile.exists) {
408 throw new ArgumentError('$_pubspecFile does not exist');
409 }
410 // create AnalysisContext
411 _context = AnalysisEngine.instance.createAnalysisContext();
412 // TODO(scheglov) replace FileUriResolver with an Resource based resolver
413 // TODO(scheglov) create packages resolver
414 _context.sourceFactory = new SourceFactory([
415 new DartUriResolver(sdk),
416 new FileUriResolver(),
417 // new PackageUriResolver(),
418 ]);
419 // add folder files
420 {
421 ChangeSet changeSet = new ChangeSet();
422 _addSourceFiles(changeSet, _folder);
423 _context.applyChanges(changeSet);
424 }
425 }
426
427 /**
428 * Return the [AnalysisContext] of this folder.
429 */
430 AnalysisContext get context => _context;
431
432 /**
433 * Resursively adds all Dart and HTML files to the [changeSet].
434 */
435 static void _addSourceFiles(ChangeSet changeSet, Folder folder) {
436 List<Resource> children = folder.getChildren();
437 for (Resource child in children) {
438 if (child is File) {
439 String fileName = child.shortName;
440 if (AnalysisEngine.isDartFileName(fileName)
441 || AnalysisEngine.isHtmlFileName(fileName)) {
442 Source source = child.createSource(UriKind.FILE_URI);
443 changeSet.addedSource(source);
444 }
445 } else if (child is Folder) {
446 _addSourceFiles(changeSet, child);
447 }
448 }
449 }
450 }
451
452
453 /**
268 * An enumeration of the services provided by the server domain. 454 * An enumeration of the services provided by the server domain.
269 */ 455 */
270 class ServerService extends Enum2<ServerService> { 456 class ServerService extends Enum2<ServerService> {
271 static const ServerService STATUS = const ServerService('STATUS', 0); 457 static const ServerService STATUS = const ServerService('STATUS', 0);
272 458
273 static const List<ServerService> VALUES = const [STATUS]; 459 static const List<ServerService> VALUES = const [STATUS];
274 460
275 const ServerService(String name, int ordinal) : super(name, ordinal); 461 const ServerService(String name, int ordinal) : super(name, ordinal);
276 } 462 }
OLDNEW
« no previous file with comments | « no previous file | pkg/analysis_server/lib/src/domain_analysis.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698