Index: pkg/analyzer_plugin/lib/plugin/plugin.dart |
diff --git a/pkg/analyzer_plugin/lib/plugin/plugin.dart b/pkg/analyzer_plugin/lib/plugin/plugin.dart |
index 77d056035b06015c127d1abc76d6124464479bf5..a6475ff41bd8559834040c0dbf71c835723a57f3 100644 |
--- a/pkg/analyzer_plugin/lib/plugin/plugin.dart |
+++ b/pkg/analyzer_plugin/lib/plugin/plugin.dart |
@@ -5,7 +5,7 @@ |
import 'package:analyzer/file_system/file_system.dart'; |
import 'package:analyzer/src/dart/analysis/byte_store.dart'; |
import 'package:analyzer/src/dart/analysis/driver.dart' |
- show AnalysisDriverScheduler, PerformanceLog; |
+ show AnalysisDriverGeneric, AnalysisDriverScheduler, PerformanceLog; |
import 'package:analyzer/src/dart/analysis/file_byte_store.dart'; |
import 'package:analyzer/src/dart/analysis/file_state.dart'; |
import 'package:analyzer/src/generated/source.dart'; |
@@ -16,6 +16,7 @@ import 'package:analyzer_plugin/protocol/protocol_generated.dart'; |
import 'package:analyzer_plugin/src/protocol/protocol_internal.dart'; |
import 'package:analyzer_plugin/src/utilities/null_string_sink.dart'; |
import 'package:analyzer_plugin/utilities/subscriptions/subscription_manager.dart'; |
+import 'package:path/src/context.dart'; |
import 'package:pub_semver/pub_semver.dart'; |
/** |
@@ -53,6 +54,13 @@ abstract class ServerPlugin { |
AnalysisDriverScheduler analysisDriverScheduler; |
/** |
+ * A table mapping the current context roots to the analysis driver created |
+ * for that root. |
+ */ |
+ final Map<ContextRoot, AnalysisDriverGeneric> driverMap = |
+ <ContextRoot, AnalysisDriverGeneric>{}; |
+ |
+ /** |
* The performance log used by any analysis drivers that are created. |
*/ |
final PerformanceLog performanceLog = |
@@ -77,9 +85,17 @@ abstract class ServerPlugin { |
*/ |
ServerPlugin(this.resourceProvider) { |
analysisDriverScheduler = new AnalysisDriverScheduler(performanceLog); |
+ analysisDriverScheduler.start(); |
} |
/** |
+ * Return the byte store used by any analysis drivers that are created, or |
+ * `null` if the cache location isn't known because the 'plugin.version' |
+ * request has not yet been received. |
+ */ |
+ ByteStore get byteStore => _byteStore; |
+ |
+ /** |
* Return the communication channel being used to communicate with the |
* analysis server, or `null` if the plugin has not been started. |
*/ |
@@ -115,42 +131,116 @@ abstract class ServerPlugin { |
} |
/** |
+ * Return the context root containing the file at the given [filePath]. |
+ */ |
+ ContextRoot contextRootContaining(String filePath) { |
+ Context pathContext = resourceProvider.pathContext; |
+ |
+ /** |
+ * Return `true` if the given [child] is either the same as or within the |
+ * given [parent]. |
+ */ |
+ bool isOrWithin(String parent, String child) { |
+ return parent == child || pathContext.isWithin(parent, child); |
+ } |
+ |
+ /** |
+ * Return `true` if the given context [root] contains the target [file]. |
+ */ |
+ bool ownsFile(ContextRoot root) { |
+ if (isOrWithin(root.root, filePath)) { |
+ List<String> excludedPaths = root.exclude; |
+ for (String excludedPath in excludedPaths) { |
+ if (isOrWithin(excludedPath, filePath)) { |
+ return false; |
+ } |
+ } |
+ return true; |
+ } |
+ return false; |
+ } |
+ |
+ for (ContextRoot root in driverMap.keys) { |
+ if (ownsFile(root)) { |
+ return root; |
+ } |
+ } |
+ return null; |
+ } |
+ |
+ /** |
+ * Create an analysis driver that can analyze the files within the given |
+ * [contextRoot]. |
+ */ |
+ AnalysisDriverGeneric createAnalysisDriver(ContextRoot contextRoot); |
+ |
+ /** |
* Handle an 'analysis.handleWatchEvents' request. |
*/ |
AnalysisHandleWatchEventsResult handleAnalysisHandleWatchEvents( |
- Map<String, Object> parameters) => |
+ AnalysisHandleWatchEventsParams parameters) => |
null; |
/** |
* Handle an 'analysis.reanalyze' request. |
*/ |
AnalysisReanalyzeResult handleAnalysisReanalyze( |
- Map<String, Object> parameters) => |
+ AnalysisReanalyzeParams parameters) => |
null; |
/** |
* Handle an 'analysis.setContextBuilderOptions' request. |
*/ |
AnalysisSetContextBuilderOptionsResult handleAnalysisSetContextBuilderOptions( |
- Map<String, Object> parameters) => |
+ AnalysisSetContextBuilderOptionsParams parameters) => |
null; |
/** |
* Handle an 'analysis.setContextRoots' request. |
*/ |
AnalysisSetContextRootsResult handleAnalysisSetContextRoots( |
- Map<String, Object> parameters) { |
- // TODO(brianwilkerson) Implement this so that implementors don't have to |
- // figure out how to manage contexts. |
- return null; |
+ AnalysisSetContextRootsParams parameters) { |
+ List<ContextRoot> contextRoots = parameters.roots; |
+ List<ContextRoot> oldRoots = driverMap.keys.toList(); |
+ for (ContextRoot contextRoot in contextRoots) { |
+ if (!oldRoots.remove(contextRoot)) { |
+ // The context is new, so we create a driver for it. Creating the driver |
+ // has the side-effect of adding it to the analysis driver scheduler. |
+ AnalysisDriverGeneric driver = createAnalysisDriver(contextRoot); |
+ driverMap[contextRoot] = driver; |
+ } |
+ } |
+ for (ContextRoot contextRoot in oldRoots) { |
+ // The context has been removed, so we remove its driver. |
+ AnalysisDriverGeneric driver = driverMap.remove(contextRoot); |
+ // The `dispose` method has the side-effect of removing the driver from |
+ // the analysis driver scheduler. |
+ driver.dispose(); |
+ } |
+ return new AnalysisSetContextRootsResult(); |
} |
/** |
* Handle an 'analysis.setPriorityFiles' request. |
*/ |
AnalysisSetPriorityFilesResult handleAnalysisSetPriorityFiles( |
- Map<String, Object> parameters) => |
- new AnalysisSetPriorityFilesResult(); |
+ AnalysisSetPriorityFilesParams parameters) { |
+ List<String> files = parameters.files; |
+ Map<AnalysisDriverGeneric, List<String>> filesByDriver = |
+ <AnalysisDriverGeneric, List<String>>{}; |
+ for (String file in files) { |
+ ContextRoot contextRoot = contextRootContaining(file); |
+ if (contextRoot != null) { |
+ // TODO(brianwilkerson) Which driver should we use if there is no context root? |
+ AnalysisDriverGeneric driver = driverMap[contextRoot]; |
+ filesByDriver.putIfAbsent(driver, () => <String>[]).add(file); |
+ } |
+ } |
+ filesByDriver.forEach((AnalysisDriverGeneric driver, List<String> files) { |
+ driver.priorityFiles = files; |
+ }); |
+ return new AnalysisSetPriorityFilesResult(); |
+ } |
/** |
* Handle an 'analysis.setSubscriptions' request. Most subclasses should not |
@@ -158,11 +248,8 @@ abstract class ServerPlugin { |
* access the list of subscriptions for any given file. |
*/ |
AnalysisSetSubscriptionsResult handleAnalysisSetSubscriptions( |
- Map<String, Object> parameters) { |
- Map<AnalysisService, List<String>> subscriptions = validateParameter( |
- parameters, |
- ANALYSIS_REQUEST_SET_SUBSCRIPTIONS_SUBSCRIPTIONS, |
- 'analysis.setSubscriptions'); |
+ AnalysisSetSubscriptionsParams parameters) { |
+ Map<AnalysisService, List<String>> subscriptions = parameters.subscriptions; |
subscriptionManager.setSubscriptions(subscriptions); |
// TODO(brianwilkerson) Cause any newly subscribed for notifications to be sent. |
return new AnalysisSetSubscriptionsResult(); |
@@ -174,9 +261,8 @@ abstract class ServerPlugin { |
* the current content of overlaid files. |
*/ |
AnalysisUpdateContentResult handleAnalysisUpdateContent( |
- Map<String, Object> parameters) { |
- Map<String, Object> files = validateParameter(parameters, |
- ANALYSIS_REQUEST_UPDATE_CONTENT_FILES, 'analysis.updateContent'); |
+ AnalysisUpdateContentParams parameters) { |
+ Map<String, Object> files = parameters.files; |
files.forEach((String filePath, Object overlay) { |
// We don't need to get the correct URI because only the full path is |
// used by the contentCache. |
@@ -214,14 +300,14 @@ abstract class ServerPlugin { |
* Handle a 'completion.getSuggestions' request. |
*/ |
CompletionGetSuggestionsResult handleCompletionGetSuggestions( |
- Map<String, Object> parameters) => |
+ CompletionGetSuggestionsParams parameters) => |
new CompletionGetSuggestionsResult( |
-1, -1, const <CompletionSuggestion>[]); |
/** |
* Handle an 'edit.getAssists' request. |
*/ |
- EditGetAssistsResult handleEditGetAssists(Map<String, Object> parameters) => |
+ EditGetAssistsResult handleEditGetAssists(EditGetAssistsParams parameters) => |
new EditGetAssistsResult(const <PrioritizedSourceChange>[]); |
/** |
@@ -230,20 +316,20 @@ abstract class ServerPlugin { |
* method [handleEditGetRefactoring]. |
*/ |
EditGetAvailableRefactoringsResult handleEditGetAvailableRefactorings( |
- Map<String, Object> parameters) => |
+ EditGetAvailableRefactoringsParams parameters) => |
new EditGetAvailableRefactoringsResult(const <RefactoringKind>[]); |
/** |
* Handle an 'edit.getFixes' request. |
*/ |
- EditGetFixesResult handleEditGetFixes(Map<String, Object> parameters) => |
+ EditGetFixesResult handleEditGetFixes(EditGetFixesParams parameters) => |
new EditGetFixesResult(const <AnalysisErrorFixes>[]); |
/** |
* Handle an 'edit.getRefactoring' request. |
*/ |
EditGetRefactoringResult handleEditGetRefactoring( |
- Map<String, Object> parameters) => |
+ EditGetRefactoringParams parameters) => |
null; |
/** |
@@ -251,18 +337,16 @@ abstract class ServerPlugin { |
* perform any required clean-up, but cannot prevent the plugin from shutting |
* down. |
*/ |
- PluginShutdownResult handlePluginShutdown(Map<String, Object> parameters) => |
+ PluginShutdownResult handlePluginShutdown(PluginShutdownParams parameters) => |
new PluginShutdownResult(); |
/** |
* Handle a 'plugin.versionCheck' request. |
*/ |
PluginVersionCheckResult handlePluginVersionCheck( |
- Map<String, Object> parameters) { |
- String byteStorePath = validateParameter(parameters, |
- PLUGIN_REQUEST_VERSION_CHECK_BYTESTOREPATH, 'plugin.versionCheck'); |
- String versionString = validateParameter(parameters, |
- PLUGIN_REQUEST_VERSION_CHECK_VERSION, 'plugin.versionCheck'); |
+ PluginVersionCheckParams parameters) { |
+ String byteStorePath = parameters.byteStorePath; |
+ String versionString = parameters.version; |
Version serverVersion = new Version.parse(versionString); |
_byteStore = |
new MemoryCachingByteStore(new FileByteStore(byteStorePath), 64 * M); |
@@ -296,33 +380,11 @@ abstract class ServerPlugin { |
* Start this plugin by listening to the given communication [channel]. |
*/ |
void start(PluginCommunicationChannel channel) { |
- this._channel = channel; |
+ _channel = channel; |
_channel.listen(_onRequest, onError: onError, onDone: onDone); |
} |
/** |
- * Validate that the value in the map of [parameters] at the given [key] is of |
- * the type [T]. If it is, return it. Otherwise throw a [RequestFailure] that |
- * will cause an error to be returned to the server. |
- */ |
- Object/*=T*/ validateParameter/*<T>*/( |
- Map<String, Object> parameters, String key, String requestName) { |
- Object value = parameters[key]; |
- // ignore: type_annotation_generic_function_parameter |
- if (value is Object/*=T*/) { |
- return value; |
- } |
- String message; |
- if (value == null) { |
- message = 'Missing parameter $key in $requestName'; |
- } else { |
- message = 'Invalid value for $key in $requestName (${value.runtimeType})'; |
- } |
- throw new RequestFailure( |
- new RequestError(RequestErrorCode.INVALID_PARAMETER, message)); |
- } |
- |
- /** |
* Compute the response that should be returned for the given [request], or |
* `null` if the response has already been sent. |
*/ |
@@ -330,48 +392,64 @@ abstract class ServerPlugin { |
ResponseResult result = null; |
switch (request.method) { |
case ANALYSIS_REQUEST_HANDLE_WATCH_EVENTS: |
- result = handleAnalysisHandleWatchEvents(request.params); |
+ var params = new AnalysisHandleWatchEventsParams.fromRequest(request); |
+ result = handleAnalysisHandleWatchEvents(params); |
break; |
case ANALYSIS_REQUEST_REANALYZE: |
- result = handleAnalysisReanalyze(request.params); |
+ var params = new AnalysisReanalyzeParams.fromRequest(request); |
+ result = handleAnalysisReanalyze(params); |
break; |
case ANALYSIS_REQUEST_SET_CONTEXT_BUILDER_OPTIONS: |
- result = handleAnalysisSetContextBuilderOptions(request.params); |
+ var params = |
+ new AnalysisSetContextBuilderOptionsParams.fromRequest(request); |
+ result = handleAnalysisSetContextBuilderOptions(params); |
break; |
case ANALYSIS_REQUEST_SET_CONTEXT_ROOTS: |
- result = handleAnalysisSetContextRoots(request.params); |
+ var params = new AnalysisSetContextRootsParams.fromRequest(request); |
+ result = handleAnalysisSetContextRoots(params); |
break; |
case ANALYSIS_REQUEST_SET_PRIORITY_FILES: |
- result = handleAnalysisSetPriorityFiles(request.params); |
+ var params = new AnalysisSetPriorityFilesParams.fromRequest(request); |
+ result = handleAnalysisSetPriorityFiles(params); |
break; |
case ANALYSIS_REQUEST_SET_SUBSCRIPTIONS: |
- result = handleAnalysisSetSubscriptions(request.params); |
+ var params = new AnalysisSetSubscriptionsParams.fromRequest(request); |
+ result = handleAnalysisSetSubscriptions(params); |
break; |
case ANALYSIS_REQUEST_UPDATE_CONTENT: |
- result = handleAnalysisUpdateContent(request.params); |
+ var params = new AnalysisUpdateContentParams.fromRequest(request); |
+ result = handleAnalysisUpdateContent(params); |
break; |
case COMPLETION_REQUEST_GET_SUGGESTIONS: |
- result = handleCompletionGetSuggestions(request.params); |
+ var params = new CompletionGetSuggestionsParams.fromRequest(request); |
+ result = handleCompletionGetSuggestions(params); |
break; |
case EDIT_REQUEST_GET_ASSISTS: |
- result = handleEditGetAssists(request.params); |
+ var params = new EditGetAssistsParams.fromRequest(request); |
+ result = handleEditGetAssists(params); |
break; |
case EDIT_REQUEST_GET_AVAILABLE_REFACTORINGS: |
- result = handleEditGetAvailableRefactorings(request.params); |
+ var params = |
+ new EditGetAvailableRefactoringsParams.fromRequest(request); |
+ result = handleEditGetAvailableRefactorings(params); |
break; |
case EDIT_REQUEST_GET_FIXES: |
- result = handleEditGetFixes(request.params); |
+ var params = new EditGetFixesParams.fromRequest(request); |
+ result = handleEditGetFixes(params); |
break; |
case EDIT_REQUEST_GET_REFACTORING: |
- result = handleEditGetRefactoring(request.params); |
+ var params = new EditGetRefactoringParams.fromRequest(request); |
+ result = handleEditGetRefactoring(params); |
break; |
case PLUGIN_REQUEST_SHUTDOWN: |
- result = handlePluginShutdown(request.params); |
+ var params = new PluginShutdownParams(); |
+ result = handlePluginShutdown(params); |
_channel.sendResponse(result.toResponse(request.id)); |
_channel.close(); |
return null; |
case PLUGIN_REQUEST_VERSION_CHECK: |
- result = handlePluginVersionCheck(request.params); |
+ var params = new PluginVersionCheckParams.fromRequest(request); |
+ result = handlePluginVersionCheck(params); |
break; |
} |
if (result == null) { |