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

Unified Diff: pkg/analysis_server/lib/src/analysis_server.dart

Issue 725143004: Format and sort analyzer and analysis_server packages. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: pkg/analysis_server/lib/src/analysis_server.dart
diff --git a/pkg/analysis_server/lib/src/analysis_server.dart b/pkg/analysis_server/lib/src/analysis_server.dart
index f256834850bc5edc557f0a4ebbe7387d8c4a5d41..145c000d09a215624e9d7fcf452aafccd4a04233 100644
--- a/pkg/analysis_server/lib/src/analysis_server.dart
+++ b/pkg/analysis_server/lib/src/analysis_server.dart
@@ -7,137 +7,28 @@ library analysis.server;
import 'dart:async';
import 'dart:collection';
-import 'package:analyzer/file_system/file_system.dart';
import 'package:analysis_server/src/analysis_logger.dart';
import 'package:analysis_server/src/channel/channel.dart';
import 'package:analysis_server/src/context_manager.dart';
-import 'package:analysis_server/src/operation/operation_analysis.dart';
import 'package:analysis_server/src/operation/operation.dart';
+import 'package:analysis_server/src/operation/operation_analysis.dart';
import 'package:analysis_server/src/operation/operation_queue.dart';
import 'package:analysis_server/src/protocol.dart' hide Element;
+import 'package:analysis_server/src/services/correction/namespace.dart';
+import 'package:analysis_server/src/services/index/index.dart';
+import 'package:analysis_server/src/services/search/search_engine.dart';
+import 'package:analyzer/file_system/file_system.dart';
import 'package:analyzer/source/package_map_provider.dart';
import 'package:analyzer/src/generated/ast.dart';
+import 'package:analyzer/src/generated/element.dart';
import 'package:analyzer/src/generated/engine.dart';
-import 'package:analyzer/src/generated/source.dart';
+import 'package:analyzer/src/generated/java_engine.dart';
import 'package:analyzer/src/generated/sdk.dart';
+import 'package:analyzer/src/generated/source.dart';
import 'package:analyzer/src/generated/source_io.dart';
-import 'package:analyzer/src/generated/java_engine.dart';
-import 'package:analysis_server/src/services/index/index.dart';
-import 'package:analysis_server/src/services/search/search_engine.dart';
-import 'package:analyzer/src/generated/element.dart';
-import 'package:analysis_server/src/services/correction/namespace.dart';
-
-
-class ServerContextManager extends ContextManager {
- final AnalysisServer analysisServer;
-
- /**
- * The default options used to create new analysis contexts.
- */
- AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
-
- /**
- * The controller for sending [ContextsChangedEvent]s.
- */
- StreamController<ContextsChangedEvent> _onContextsChangedController;
-
- ServerContextManager(this.analysisServer, ResourceProvider resourceProvider,
- PackageMapProvider packageMapProvider)
- : super(resourceProvider, packageMapProvider) {
- _onContextsChangedController = new StreamController<ContextsChangedEvent>();
- }
-
- /**
- * The stream that is notified when contexts are added or removed.
- */
- Stream<ContextsChangedEvent> get onContextsChanged =>
- _onContextsChangedController.stream;
-
- @override
- void addContext(Folder folder, UriResolver packageUriResolver) {
- AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
- analysisServer.folderMap[folder] = context;
- context.sourceFactory = _createSourceFactory(packageUriResolver);
- context.analysisOptions = new AnalysisOptionsImpl.con1(defaultOptions);
- _onContextsChangedController.add(
- new ContextsChangedEvent(added: [context]));
- analysisServer.schedulePerformAnalysisOperation(context);
- }
-
- @override
- void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) {
- AnalysisContext context = analysisServer.folderMap[contextFolder];
- if (context != null) {
- context.applyChanges(changeSet);
- analysisServer.schedulePerformAnalysisOperation(context);
- }
- }
-
- @override
- void removeContext(Folder folder) {
- AnalysisContext context = analysisServer.folderMap.remove(folder);
- if (analysisServer.index != null) {
- analysisServer.index.removeContext(context);
- }
- _onContextsChangedController.add(
- new ContextsChangedEvent(removed: [context]));
- analysisServer.sendContextAnalysisDoneNotifications(
- context,
- AnalysisDoneReason.CONTEXT_REMOVED);
- }
-
- @override
- void updateContextPackageUriResolver(Folder contextFolder,
- UriResolver packageUriResolver) {
- AnalysisContext context = analysisServer.folderMap[contextFolder];
- context.sourceFactory = _createSourceFactory(packageUriResolver);
- _onContextsChangedController.add(
- new ContextsChangedEvent(changed: [context]));
- analysisServer.schedulePerformAnalysisOperation(context);
- }
-
- /**
- * Set up a [SourceFactory] that resolves packages using the given
- * [packageUriResolver].
- */
- SourceFactory _createSourceFactory(UriResolver packageUriResolver) {
- List<UriResolver> resolvers = <UriResolver>[
- new DartUriResolver(analysisServer.defaultSdk),
- new ResourceUriResolver(resourceProvider),
- packageUriResolver];
- return new SourceFactory(resolvers);
- }
-}
-
-
-/**
- * A [ContextsChangedEvent] indicate what contexts were added or removed.
- *
- * No context should be added to the event more than once. It does not make
- * sense, for example, for a context to be both added and removed.
- */
-class ContextsChangedEvent {
-
- /**
- * The contexts that were added to the server.
- */
- final List<AnalysisContext> added;
- /**
- * The contexts that were changed.
- */
- final List<AnalysisContext> changed;
-
- /**
- * The contexts that were removed from the server.
- */
- final List<AnalysisContext> removed;
- ContextsChangedEvent({
- this.added: AnalysisContext.EMPTY_LIST,
- this.changed: AnalysisContext.EMPTY_LIST,
- this.removed: AnalysisContext.EMPTY_LIST});
-}
+typedef void OptionUpdater(AnalysisOptionsImpl options);
/**
@@ -311,107 +202,9 @@ class AnalysisServer {
}
/**
- * If the given notice applies to a file contained within an analysis root,
- * notify interested parties that the file has been (at least partially)
- * analyzed.
- */
- void fileAnalyzed(ChangeNotice notice) {
- if (contextDirectoryManager.isInAnalysisRoot(notice.source.fullName)) {
- _onFileAnalyzedController.add(notice);
- }
- }
-
- /**
- * Schedules execution of the given [ServerOperation].
- */
- void scheduleOperation(ServerOperation operation) {
- addOperation(operation);
- if (!performOperationPending) {
- _schedulePerformOperation();
- }
- }
-
- /**
- * Schedules analysis of the given context.
- */
- void schedulePerformAnalysisOperation(AnalysisContext context) {
- _onAnalysisStartedController.add(context);
- scheduleOperation(new PerformAnalysisOperation(context, false));
- }
-
- /**
- * Send the given [notification] to the client.
- */
- void sendNotification(Notification notification) {
- channel.sendNotification(notification);
- }
-
- /**
- * Send the given [response] to the client.
- */
- void sendResponse(Response response) {
- channel.sendResponse(response);
- }
-
- /**
- * Set the priority files to the given [files].
- */
- void setPriorityFiles(Request request, List<String> files) {
- Map<AnalysisContext, List<Source>> sourceMap =
- new HashMap<AnalysisContext, List<Source>>();
- List<String> unanalyzed = new List<String>();
- files.forEach((file) {
- AnalysisContext analysisContext = getAnalysisContext(file);
- if (analysisContext == null) {
- unanalyzed.add(file);
- } else {
- List<Source> sourceList = sourceMap[analysisContext];
- if (sourceList == null) {
- sourceList = <Source>[];
- sourceMap[analysisContext] = sourceList;
- }
- sourceList.add(getSource(file));
- }
- });
- if (unanalyzed.isNotEmpty) {
- StringBuffer buffer = new StringBuffer();
- buffer.writeAll(unanalyzed, ', ');
- throw new RequestFailure(
- new Response.unanalyzedPriorityFiles(request, buffer.toString()));
- }
- folderMap.forEach((Folder folder, AnalysisContext context) {
- List<Source> sourceList = sourceMap[context];
- if (sourceList == null) {
- sourceList = Source.EMPTY_ARRAY;
- }
- context.analysisPriorityOrder = sourceList;
- });
- }
-
- /**
- * Use the given updaters to update the values of the options in every
- * existing analysis context.
+ * The stream that is notified when analysis is complete.
*/
- void updateOptions(List<OptionUpdater> optionUpdaters) {
- //
- // Update existing contexts.
- //
- folderMap.forEach((Folder folder, AnalysisContext context) {
- AnalysisOptionsImpl options =
- new AnalysisOptionsImpl.con1(context.analysisOptions);
- optionUpdaters.forEach((OptionUpdater optionUpdater) {
- optionUpdater(options);
- });
- context.analysisOptions = options;
- });
- //
- // Update the defaults used to create new contexts.
- //
- AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions;
- optionUpdaters.forEach((OptionUpdater optionUpdater) {
- optionUpdater(options);
- });
- }
+ Stream get onAnalysisComplete => _onAnalysisCompleteController.stream;
/**
* The stream that is notified when analysis of a context is started.
@@ -421,11 +214,6 @@ class AnalysisServer {
}
/**
- * The stream that is notified when analysis is complete.
- */
- Stream get onAnalysisComplete => _onAnalysisCompleteController.stream;
-
- /**
* The stream that is notified when a single file has been analyzed.
*/
Stream get onFileAnalyzed => _onFileAnalyzedController.stream;
@@ -454,31 +242,222 @@ class AnalysisServer {
running = false;
}
-// TODO(brianwilkerson) Add the following method after 'prioritySources' has
-// been added to InternalAnalysisContext.
-// /**
-// * Return a list containing the full names of all of the sources that are
-// * priority sources.
-// */
-// List<String> getPriorityFiles() {
-// List<String> priorityFiles = new List<String>();
-// folderMap.values.forEach((ContextDirectory directory) {
-// InternalAnalysisContext context = directory.context;
-// context.prioritySources.forEach((Source source) {
-// priorityFiles.add(source.fullName);
-// });
-// });
-// return priorityFiles;
-// }
-
/**
- * Handle a [request] that was read from the communication channel.
+ * If the given notice applies to a file contained within an analysis root,
+ * notify interested parties that the file has been (at least partially)
+ * analyzed.
*/
- void handleRequest(Request request) {
- runZoned(() {
- int count = handlers.length;
- for (int i = 0; i < count; i++) {
- try {
+ void fileAnalyzed(ChangeNotice notice) {
+ if (contextDirectoryManager.isInAnalysisRoot(notice.source.fullName)) {
+ _onFileAnalyzedController.add(notice);
+ }
+ }
+
+ /**
+ * Return the [AnalysisContext] that is used to analyze the given [path].
+ * Return `null` if there is no such context.
+ */
+ AnalysisContext getAnalysisContext(String path) {
+ // try to find a containing context
+ for (Folder folder in folderMap.keys) {
+ if (folder.contains(path)) {
+ return folderMap[folder];
+ }
+ }
+ // check if there is a context that analyzed this source
+ Source source = getSource(path);
+ for (AnalysisContext context in folderMap.values) {
+ SourceKind kind = context.getKindOf(source);
+ if (kind != null) {
+ return context;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the [AnalysisContext]s that are being used to analyze the analysis
+ * roots.
+ */
+ Iterable<AnalysisContext> getAnalysisContexts() {
+ return folderMap.values;
+ }
+
+ /**
+ * Returns [Element]s at the given [offset] of the given [file].
+ *
+ * May be empty if cannot be resolved, but not `null`.
+ */
+ List<Element> getElementsAtOffset(String file, int offset) {
+ List<AstNode> nodes = getNodesAtOffset(file, offset);
+ return getElementsOfNodes(nodes, offset);
+ }
+
+ /**
+ * Returns [Element]s of the given [nodes].
+ *
+ * May be empty if not resolved, but not `null`.
+ */
+ List<Element> getElementsOfNodes(List<AstNode> nodes, int offset) {
+ List<Element> elements = <Element>[];
+ for (AstNode node in nodes) {
+ if (node is SimpleIdentifier && node.parent is LibraryIdentifier) {
+ node = node.parent;
+ }
+ if (node is LibraryIdentifier) {
+ node = node.parent;
+ }
+ Element element = ElementLocator.locateWithOffset(node, offset);
+ if (node is SimpleIdentifier && element is PrefixElement) {
+ element = getImportElement(node);
+ }
+ if (element != null) {
+ elements.add(element);
+ }
+ }
+ return elements;
+ }
+
+ /**
+ * Return an analysis error info containing the array of all of the errors and
+ * the line info associated with [file].
+ *
+ * Returns `null` if [file] does not belong to any [AnalysisContext], or the
+ * file does not exist.
+ *
+ * The array of errors will be empty if there are no errors in [file]. The
+ * errors contained in the array can be incomplete.
+ *
+ * This method does not wait for all errors to be computed, and returns just
+ * the current state.
+ */
+ AnalysisErrorInfo getErrors(String file) {
+ // prepare AnalysisContext
+ AnalysisContext context = getAnalysisContext(file);
+ if (context == null) {
+ return null;
+ }
+ // prepare Source
+ Source source = getSource(file);
+ if (context.getKindOf(source) == SourceKind.UNKNOWN) {
+ return null;
+ }
+ // get errors for the file
+ return context.getErrors(source);
+ }
+
+ /**
+ * Returns resolved [AstNode]s at the given [offset] of the given [file].
+ *
+ * May be empty, but not `null`.
+ */
+ List<AstNode> getNodesAtOffset(String file, int offset) {
+ List<CompilationUnit> units = getResolvedCompilationUnits(file);
+ List<AstNode> nodes = <AstNode>[];
+ for (CompilationUnit unit in units) {
+ AstNode node = new NodeLocator.con1(offset).searchWithin(unit);
+ if (node != null) {
+ nodes.add(node);
+ }
+ }
+ return nodes;
+ }
+
+// TODO(brianwilkerson) Add the following method after 'prioritySources' has
+// been added to InternalAnalysisContext.
+// /**
+// * Return a list containing the full names of all of the sources that are
+// * priority sources.
+// */
+// List<String> getPriorityFiles() {
+// List<String> priorityFiles = new List<String>();
+// folderMap.values.forEach((ContextDirectory directory) {
+// InternalAnalysisContext context = directory.context;
+// context.prioritySources.forEach((Source source) {
+// priorityFiles.add(source.fullName);
+// });
+// });
+// return priorityFiles;
+// }
+
+ /**
+ * Returns resolved [CompilationUnit]s of the Dart file with the given [path].
+ *
+ * May be empty, but not `null`.
+ */
+ List<CompilationUnit> getResolvedCompilationUnits(String path) {
+ List<CompilationUnit> units = <CompilationUnit>[];
+ // prepare AnalysisContext
+ AnalysisContext context = getAnalysisContext(path);
+ if (context == null) {
+ return units;
+ }
+ // add a unit for each unit/library combination
+ Source unitSource = getSource(path);
+ List<Source> librarySources = context.getLibrariesContaining(unitSource);
+ for (Source librarySource in librarySources) {
+ CompilationUnit unit =
+ context.resolveCompilationUnit2(unitSource, librarySource);
+ if (unit != null) {
+ units.add(unit);
+ }
+ }
+ // done
+ return units;
+ }
+
+ /**
+ * Returns the [CompilationUnit] of the Dart file with the given [path] that
+ * should be used to resend notifications for already resolved unit.
+ * Returns `null` if the file is not a part of any context, library has not
+ * been yet resolved, or any problem happened.
+ */
+ CompilationUnit getResolvedCompilationUnitToResendNotification(String path) {
+ // prepare AnalysisContext
+ AnalysisContext context = getAnalysisContext(path);
+ if (context == null) {
+ return null;
+ }
+ // prepare sources
+ Source unitSource = getSource(path);
+ List<Source> librarySources = context.getLibrariesContaining(unitSource);
+ if (librarySources.isEmpty) {
+ return null;
+ }
+ // if library has not been resolved yet, the unit will be resolved later
+ Source librarySource = librarySources[0];
+ if (context.getLibraryElement(librarySource) == null) {
+ return null;
+ }
+ // if library has been already resolved, resolve unit
+ return context.resolveCompilationUnit2(unitSource, librarySource);
+ }
+
+ /**
+ * Return the [Source] of the Dart file with the given [path].
+ */
+ Source getSource(String path) {
+ // try SDK
+ {
+ Uri uri = resourceProvider.pathContext.toUri(path);
+ Source sdkSource = defaultSdk.fromFileUri(uri);
+ if (sdkSource != null) {
+ return sdkSource;
+ }
+ }
+ // file-based source
+ File file = resourceProvider.getResource(path);
+ return file.createSource();
+ }
+
+ /**
+ * Handle a [request] that was read from the communication channel.
+ */
+ void handleRequest(Request request) {
+ runZoned(() {
+ int count = handlers.length;
+ for (int i = 0; i < count; i++) {
+ try {
Response response = handlers[i].handleRequest(request);
if (response == Response.DELAYED_RESPONSE) {
return;
@@ -514,15 +493,6 @@ class AnalysisServer {
}
/**
- * Returns `true` if errors should be reported for [file] with the given
- * absolute path.
- */
- bool shouldSendErrorsNotificationFor(String file) {
- // TODO(scheglov) add support for the "--no-error-notification" flag.
- return contextDirectoryManager.isInAnalysisRoot(file);
- }
-
- /**
* Return `true` if analysis is complete.
*/
bool isAnalysisComplete() {
@@ -538,6 +508,39 @@ class AnalysisServer {
}
/**
+ * Returns a [Future] completing when [file] has been completely analyzed, in
+ * particular, all its errors have been computed. The future is completed
+ * with an [AnalysisDoneReason] indicating what caused the file's analysis to
+ * be considered complete.
+ *
+ * If the given file doesn't belong to any context, null is returned.
+ *
+ * TODO(scheglov) this method should be improved.
+ *
+ * 1. The analysis context should be told to analyze this particular file ASAP.
+ *
+ * 2. We should complete the future as soon as the file is analyzed (not wait
+ * until the context is completely finished)
+ */
+ Future<AnalysisDoneReason> onFileAnalysisComplete(String file) {
+ // prepare AnalysisContext
+ AnalysisContext context = getAnalysisContext(file);
+ if (context == null) {
+ return null;
+ }
+ // schedule context analysis
+ schedulePerformAnalysisOperation(context);
+ // associate with the context completer
+ Completer<AnalysisDoneReason> completer =
+ contextAnalysisDoneCompleters[context];
+ if (completer == null) {
+ completer = new Completer<AnalysisDoneReason>();
+ contextAnalysisDoneCompleters[context] = completer;
+ }
+ return completer.future;
+ }
+
+ /**
* Perform the next available [ServerOperation].
*/
void performOperation() {
@@ -593,8 +596,53 @@ class AnalysisServer {
}
/**
- * Send status notification to the client. The `operation` is the operation
- * being performed or `null` if analysis is complete.
+ * Schedules execution of the given [ServerOperation].
+ */
+ void scheduleOperation(ServerOperation operation) {
+ addOperation(operation);
+ if (!performOperationPending) {
+ _schedulePerformOperation();
+ }
+ }
+
+ /**
+ * Schedules analysis of the given context.
+ */
+ void schedulePerformAnalysisOperation(AnalysisContext context) {
+ _onAnalysisStartedController.add(context);
+ scheduleOperation(new PerformAnalysisOperation(context, false));
+ }
+
+ /**
+ * This method is called when analysis of the given [AnalysisContext] is
+ * done.
+ */
+ void sendContextAnalysisDoneNotifications(AnalysisContext context,
+ AnalysisDoneReason reason) {
+ Completer<AnalysisDoneReason> completer =
+ contextAnalysisDoneCompleters.remove(context);
+ if (completer != null) {
+ completer.complete(reason);
+ }
+ }
+
+ /**
+ * Send the given [notification] to the client.
+ */
+ void sendNotification(Notification notification) {
+ channel.sendNotification(notification);
+ }
+
+ /**
+ * Send the given [response] to the client.
+ */
+ void sendResponse(Response response) {
+ channel.sendResponse(response);
+ }
+
+ /**
+ * Send status notification to the client. The `operation` is the operation
+ * being performed or `null` if analysis is complete.
*/
void sendStatusNotification(ServerOperation operation) {
// Only send status when subscribed.
@@ -638,51 +686,6 @@ class AnalysisServer {
}
/**
- * Implementation for `analysis.updateContent`.
- */
- void updateContent(String id, Map<String, dynamic> changes) {
- changes.forEach((file, change) {
- AnalysisContext analysisContext = getAnalysisContext(file);
- // TODO(paulberry): handle the case where a file is referred to by more
- // than one context (e.g package A depends on package B using a local
- // path, user has both packages open for editing in separate contexts,
- // and user modifies a file in package B).
- if (analysisContext != null) {
- Source source = getSource(file);
- if (change is AddContentOverlay) {
- analysisContext.setContents(source, change.content);
- } else if (change is ChangeContentOverlay) {
- // TODO(paulberry): an error should be generated if source is not
- // currently in the content cache.
- TimestampedData<String> oldContents =
- analysisContext.getContents(source);
- String newContents;
- try {
- newContents =
- SourceEdit.applySequence(oldContents.data, change.edits);
- } on RangeError {
- throw new RequestFailure(
- new Response(
- id,
- error: new RequestError(
- RequestErrorCode.INVALID_OVERLAY_CHANGE,
- 'Invalid overlay change')));
- }
- // TODO(paulberry): to aid in incremental processing it would be
- // better to use setChangedContents.
- analysisContext.setContents(source, newContents);
- } else if (change is RemoveContentOverlay) {
- analysisContext.setContents(source, null);
- } else {
- // Protocol parsing should have ensured that we never get here.
- throw new AnalysisException('Illegal change type');
- }
- schedulePerformAnalysisOperation(analysisContext);
- }
- });
- }
-
- /**
* Implementation for `analysis.setSubscriptions`.
*/
void setAnalysisSubscriptions(Map<AnalysisService,
@@ -736,239 +739,127 @@ class AnalysisServer {
}
/**
- * Return the [AnalysisContext]s that are being used to analyze the analysis
- * roots.
- */
- Iterable<AnalysisContext> getAnalysisContexts() {
- return folderMap.values;
- }
-
- /**
- * Return the [AnalysisContext] that is used to analyze the given [path].
- * Return `null` if there is no such context.
+ * Set the priority files to the given [files].
*/
- AnalysisContext getAnalysisContext(String path) {
- // try to find a containing context
- for (Folder folder in folderMap.keys) {
- if (folder.contains(path)) {
- return folderMap[folder];
- }
- }
- // check if there is a context that analyzed this source
- Source source = getSource(path);
- for (AnalysisContext context in folderMap.values) {
- SourceKind kind = context.getKindOf(source);
- if (kind != null) {
- return context;
+ void setPriorityFiles(Request request, List<String> files) {
+ Map<AnalysisContext, List<Source>> sourceMap =
+ new HashMap<AnalysisContext, List<Source>>();
+ List<String> unanalyzed = new List<String>();
+ files.forEach((file) {
+ AnalysisContext analysisContext = getAnalysisContext(file);
+ if (analysisContext == null) {
+ unanalyzed.add(file);
+ } else {
+ List<Source> sourceList = sourceMap[analysisContext];
+ if (sourceList == null) {
+ sourceList = <Source>[];
+ sourceMap[analysisContext] = sourceList;
+ }
+ sourceList.add(getSource(file));
}
+ });
+ if (unanalyzed.isNotEmpty) {
+ StringBuffer buffer = new StringBuffer();
+ buffer.writeAll(unanalyzed, ', ');
+ throw new RequestFailure(
+ new Response.unanalyzedPriorityFiles(request, buffer.toString()));
}
- return null;
- }
-
- /**
- * Return the [Source] of the Dart file with the given [path].
- */
- Source getSource(String path) {
- // try SDK
- {
- Uri uri = resourceProvider.pathContext.toUri(path);
- Source sdkSource = defaultSdk.fromFileUri(uri);
- if (sdkSource != null) {
- return sdkSource;
+ folderMap.forEach((Folder folder, AnalysisContext context) {
+ List<Source> sourceList = sourceMap[context];
+ if (sourceList == null) {
+ sourceList = Source.EMPTY_ARRAY;
}
- }
- // file-based source
- File file = resourceProvider.getResource(path);
- return file.createSource();
- }
-
- /**
- * Returns the [CompilationUnit] of the Dart file with the given [path] that
- * should be used to resend notifications for already resolved unit.
- * Returns `null` if the file is not a part of any context, library has not
- * been yet resolved, or any problem happened.
- */
- CompilationUnit getResolvedCompilationUnitToResendNotification(String path) {
- // prepare AnalysisContext
- AnalysisContext context = getAnalysisContext(path);
- if (context == null) {
- return null;
- }
- // prepare sources
- Source unitSource = getSource(path);
- List<Source> librarySources = context.getLibrariesContaining(unitSource);
- if (librarySources.isEmpty) {
- return null;
- }
- // if library has not been resolved yet, the unit will be resolved later
- Source librarySource = librarySources[0];
- if (context.getLibraryElement(librarySource) == null) {
- return null;
- }
- // if library has been already resolved, resolve unit
- return context.resolveCompilationUnit2(unitSource, librarySource);
- }
-
- /**
- * Return an analysis error info containing the array of all of the errors and
- * the line info associated with [file].
- *
- * Returns `null` if [file] does not belong to any [AnalysisContext], or the
- * file does not exist.
- *
- * The array of errors will be empty if there are no errors in [file]. The
- * errors contained in the array can be incomplete.
- *
- * This method does not wait for all errors to be computed, and returns just
- * the current state.
- */
- AnalysisErrorInfo getErrors(String file) {
- // prepare AnalysisContext
- AnalysisContext context = getAnalysisContext(file);
- if (context == null) {
- return null;
- }
- // prepare Source
- Source source = getSource(file);
- if (context.getKindOf(source) == SourceKind.UNKNOWN) {
- return null;
- }
- // get errors for the file
- return context.getErrors(source);
+ context.analysisPriorityOrder = sourceList;
+ });
}
/**
- * Returns resolved [CompilationUnit]s of the Dart file with the given [path].
- *
- * May be empty, but not `null`.
+ * Returns `true` if errors should be reported for [file] with the given
+ * absolute path.
*/
- List<CompilationUnit> getResolvedCompilationUnits(String path) {
- List<CompilationUnit> units = <CompilationUnit>[];
- // prepare AnalysisContext
- AnalysisContext context = getAnalysisContext(path);
- if (context == null) {
- return units;
- }
- // add a unit for each unit/library combination
- Source unitSource = getSource(path);
- List<Source> librarySources = context.getLibrariesContaining(unitSource);
- for (Source librarySource in librarySources) {
- CompilationUnit unit =
- context.resolveCompilationUnit2(unitSource, librarySource);
- if (unit != null) {
- units.add(unit);
- }
- }
- // done
- return units;
+ bool shouldSendErrorsNotificationFor(String file) {
+ // TODO(scheglov) add support for the "--no-error-notification" flag.
+ return contextDirectoryManager.isInAnalysisRoot(file);
}
- /**
- * Returns resolved [AstNode]s at the given [offset] of the given [file].
- *
- * May be empty, but not `null`.
- */
- List<AstNode> getNodesAtOffset(String file, int offset) {
- List<CompilationUnit> units = getResolvedCompilationUnits(file);
- List<AstNode> nodes = <AstNode>[];
- for (CompilationUnit unit in units) {
- AstNode node = new NodeLocator.con1(offset).searchWithin(unit);
- if (node != null) {
- nodes.add(node);
- }
+ void shutdown() {
+ running = false;
+ if (index != null) {
+ index.clear();
+ index.stop();
}
- return nodes;
- }
-
- /**
- * Returns [Element]s at the given [offset] of the given [file].
- *
- * May be empty if cannot be resolved, but not `null`.
- */
- List<Element> getElementsAtOffset(String file, int offset) {
- List<AstNode> nodes = getNodesAtOffset(file, offset);
- return getElementsOfNodes(nodes, offset);
+ // Defer closing the channel so that the shutdown response can be sent.
+ new Future(channel.close);
}
/**
- * Returns [Element]s of the given [nodes].
- *
- * May be empty if not resolved, but not `null`.
+ * Implementation for `analysis.updateContent`.
*/
- List<Element> getElementsOfNodes(List<AstNode> nodes, int offset) {
- List<Element> elements = <Element>[];
- for (AstNode node in nodes) {
- if (node is SimpleIdentifier && node.parent is LibraryIdentifier) {
- node = node.parent;
- }
- if (node is LibraryIdentifier) {
- node = node.parent;
- }
- Element element = ElementLocator.locateWithOffset(node, offset);
- if (node is SimpleIdentifier && element is PrefixElement) {
- element = getImportElement(node);
- }
- if (element != null) {
- elements.add(element);
+ void updateContent(String id, Map<String, dynamic> changes) {
+ changes.forEach((file, change) {
+ AnalysisContext analysisContext = getAnalysisContext(file);
+ // TODO(paulberry): handle the case where a file is referred to by more
+ // than one context (e.g package A depends on package B using a local
+ // path, user has both packages open for editing in separate contexts,
+ // and user modifies a file in package B).
+ if (analysisContext != null) {
+ Source source = getSource(file);
+ if (change is AddContentOverlay) {
+ analysisContext.setContents(source, change.content);
+ } else if (change is ChangeContentOverlay) {
+ // TODO(paulberry): an error should be generated if source is not
+ // currently in the content cache.
+ TimestampedData<String> oldContents =
+ analysisContext.getContents(source);
+ String newContents;
+ try {
+ newContents =
+ SourceEdit.applySequence(oldContents.data, change.edits);
+ } on RangeError {
+ throw new RequestFailure(
+ new Response(
+ id,
+ error: new RequestError(
+ RequestErrorCode.INVALID_OVERLAY_CHANGE,
+ 'Invalid overlay change')));
+ }
+ // TODO(paulberry): to aid in incremental processing it would be
+ // better to use setChangedContents.
+ analysisContext.setContents(source, newContents);
+ } else if (change is RemoveContentOverlay) {
+ analysisContext.setContents(source, null);
+ } else {
+ // Protocol parsing should have ensured that we never get here.
+ throw new AnalysisException('Illegal change type');
+ }
+ schedulePerformAnalysisOperation(analysisContext);
}
- }
- return elements;
- }
-
- /**
- * Returns a [Future] completing when [file] has been completely analyzed, in
- * particular, all its errors have been computed. The future is completed
- * with an [AnalysisDoneReason] indicating what caused the file's analysis to
- * be considered complete.
- *
- * If the given file doesn't belong to any context, null is returned.
- *
- * TODO(scheglov) this method should be improved.
- *
- * 1. The analysis context should be told to analyze this particular file ASAP.
- *
- * 2. We should complete the future as soon as the file is analyzed (not wait
- * until the context is completely finished)
- */
- Future<AnalysisDoneReason> onFileAnalysisComplete(String file) {
- // prepare AnalysisContext
- AnalysisContext context = getAnalysisContext(file);
- if (context == null) {
- return null;
- }
- // schedule context analysis
- schedulePerformAnalysisOperation(context);
- // associate with the context completer
- Completer<AnalysisDoneReason> completer =
- contextAnalysisDoneCompleters[context];
- if (completer == null) {
- completer = new Completer<AnalysisDoneReason>();
- contextAnalysisDoneCompleters[context] = completer;
- }
- return completer.future;
+ });
}
/**
- * This method is called when analysis of the given [AnalysisContext] is
- * done.
+ * Use the given updaters to update the values of the options in every
+ * existing analysis context.
*/
- void sendContextAnalysisDoneNotifications(AnalysisContext context,
- AnalysisDoneReason reason) {
- Completer<AnalysisDoneReason> completer =
- contextAnalysisDoneCompleters.remove(context);
- if (completer != null) {
- completer.complete(reason);
- }
- }
-
- void shutdown() {
- running = false;
- if (index != null) {
- index.clear();
- index.stop();
- }
- // Defer closing the channel so that the shutdown response can be sent.
- new Future(channel.close);
+ void updateOptions(List<OptionUpdater> optionUpdaters) {
+ //
+ // Update existing contexts.
+ //
+ folderMap.forEach((Folder folder, AnalysisContext context) {
+ AnalysisOptionsImpl options =
+ new AnalysisOptionsImpl.con1(context.analysisOptions);
+ optionUpdaters.forEach((OptionUpdater optionUpdater) {
+ optionUpdater(options);
+ });
+ context.analysisOptions = options;
+ });
+ //
+ // Update the defaults used to create new contexts.
+ //
+ AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions;
+ optionUpdaters.forEach((OptionUpdater optionUpdater) {
+ optionUpdater(options);
+ });
}
/**
@@ -1007,4 +898,111 @@ class AnalysisServer {
}
}
-typedef void OptionUpdater(AnalysisOptionsImpl options);
+
+/**
+ * A [ContextsChangedEvent] indicate what contexts were added or removed.
+ *
+ * No context should be added to the event more than once. It does not make
+ * sense, for example, for a context to be both added and removed.
+ */
+class ContextsChangedEvent {
+
+ /**
+ * The contexts that were added to the server.
+ */
+ final List<AnalysisContext> added;
+
+ /**
+ * The contexts that were changed.
+ */
+ final List<AnalysisContext> changed;
+
+ /**
+ * The contexts that were removed from the server.
+ */
+ final List<AnalysisContext> removed;
+
+ ContextsChangedEvent({this.added: AnalysisContext.EMPTY_LIST, this.changed:
+ AnalysisContext.EMPTY_LIST, this.removed: AnalysisContext.EMPTY_LIST});
+}
+
+class ServerContextManager extends ContextManager {
+ final AnalysisServer analysisServer;
+
+ /**
+ * The default options used to create new analysis contexts.
+ */
+ AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
+
+ /**
+ * The controller for sending [ContextsChangedEvent]s.
+ */
+ StreamController<ContextsChangedEvent> _onContextsChangedController;
+
+ ServerContextManager(this.analysisServer, ResourceProvider resourceProvider,
+ PackageMapProvider packageMapProvider)
+ : super(resourceProvider, packageMapProvider) {
+ _onContextsChangedController = new StreamController<ContextsChangedEvent>();
+ }
+
+ /**
+ * The stream that is notified when contexts are added or removed.
+ */
+ Stream<ContextsChangedEvent> get onContextsChanged =>
+ _onContextsChangedController.stream;
+
+ @override
+ void addContext(Folder folder, UriResolver packageUriResolver) {
+ AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
+ analysisServer.folderMap[folder] = context;
+ context.sourceFactory = _createSourceFactory(packageUriResolver);
+ context.analysisOptions = new AnalysisOptionsImpl.con1(defaultOptions);
+ _onContextsChangedController.add(
+ new ContextsChangedEvent(added: [context]));
+ analysisServer.schedulePerformAnalysisOperation(context);
+ }
+
+ @override
+ void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) {
+ AnalysisContext context = analysisServer.folderMap[contextFolder];
+ if (context != null) {
+ context.applyChanges(changeSet);
+ analysisServer.schedulePerformAnalysisOperation(context);
+ }
+ }
+
+ @override
+ void removeContext(Folder folder) {
+ AnalysisContext context = analysisServer.folderMap.remove(folder);
+ if (analysisServer.index != null) {
+ analysisServer.index.removeContext(context);
+ }
+ _onContextsChangedController.add(
+ new ContextsChangedEvent(removed: [context]));
+ analysisServer.sendContextAnalysisDoneNotifications(
+ context,
+ AnalysisDoneReason.CONTEXT_REMOVED);
+ }
+
+ @override
+ void updateContextPackageUriResolver(Folder contextFolder,
+ UriResolver packageUriResolver) {
+ AnalysisContext context = analysisServer.folderMap[contextFolder];
+ context.sourceFactory = _createSourceFactory(packageUriResolver);
+ _onContextsChangedController.add(
+ new ContextsChangedEvent(changed: [context]));
+ analysisServer.schedulePerformAnalysisOperation(context);
+ }
+
+ /**
+ * Set up a [SourceFactory] that resolves packages using the given
+ * [packageUriResolver].
+ */
+ SourceFactory _createSourceFactory(UriResolver packageUriResolver) {
+ List<UriResolver> resolvers = <UriResolver>[
+ new DartUriResolver(analysisServer.defaultSdk),
+ new ResourceUriResolver(resourceProvider),
+ packageUriResolver];
+ return new SourceFactory(resolvers);
+ }
+}
« no previous file with comments | « pkg/analysis_server/lib/src/analysis_manager.dart ('k') | pkg/analysis_server/lib/src/channel/byte_stream_channel.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698