Add support to pass the location of the analysis options file to plugins

R=mfairhurst@google.com

Review-Url: https://codereview.chromium.org/2947743002 .
This commit is contained in:
Brian Wilkerson 2017-06-20 07:41:02 -07:00
parent 1594006758
commit 24996fbfe7
9 changed files with 157 additions and 83 deletions

View file

@ -750,30 +750,6 @@ class ContextManagerImpl implements ContextManager {
}
}
/**
* Return the options from the analysis options file in the given [folder]
* if exists, or in one of the parent folders, or `null` if no analysis
* options file is found or if the contents of the file are not valid YAML.
*/
Map<String, Object> readOptions(Folder folder, Packages packages) {
try {
Map<String, List<Folder>> packageMap =
new ContextBuilder(resourceProvider, null, null)
.convertPackagesToMap(packages);
List<UriResolver> resolvers = <UriResolver>[
new ResourceUriResolver(resourceProvider),
new PackageMapUriResolver(resourceProvider, packageMap),
];
SourceFactory sourceFactory =
new SourceFactory(resolvers, packages, resourceProvider);
return new AnalysisOptionsProvider(sourceFactory)
.getOptions(folder, crawlUp: true);
} catch (_) {
// Parse errors are reported by GenerateOptionsErrorsTask.
}
return null;
}
@override
void refresh(List<Resource> roots) {
// Destroy old contexts
@ -1118,6 +1094,23 @@ class ContextManagerImpl implements ContextManager {
}
}
/**
* Create an object that can be used to find and read the analysis options
* file for code being analyzed using the given [packages].
*/
AnalysisOptionsProvider _createAnalysisOptionsProvider(Packages packages) {
Map<String, List<Folder>> packageMap =
new ContextBuilder(resourceProvider, null, null)
.convertPackagesToMap(packages);
List<UriResolver> resolvers = <UriResolver>[
new ResourceUriResolver(resourceProvider),
new PackageMapUriResolver(resourceProvider, packageMap),
];
SourceFactory sourceFactory =
new SourceFactory(resolvers, packages, resourceProvider);
return new AnalysisOptionsProvider(sourceFactory);
}
/**
* Create a new empty context associated with [folder], having parent
* [parent] and using [packagesFile] to resolve package URI's.
@ -1130,8 +1123,18 @@ class ContextManagerImpl implements ContextManager {
ContextInfo info = new ContextInfo(this, parent, folder, packagesFile,
normalizedPackageRoots[folder.path], disposition);
Map<String, Object> optionMap =
readOptions(info.folder, disposition.packages);
File optionsFile = null;
Map<String, Object> optionMap = null;
try {
AnalysisOptionsProvider provider =
_createAnalysisOptionsProvider(disposition.packages);
optionsFile = provider.getOptionsFile(info.folder, crawlUp: true);
if (optionsFile != null) {
optionMap = provider.getOptionsFromFile(optionsFile);
}
} catch (_) {
// Parse errors are reported elsewhere.
}
AnalysisOptions options =
new AnalysisOptionsImpl.from(defaultContextOptions);
applyToAnalysisOptions(options, optionMap);
@ -1143,8 +1146,13 @@ class ContextManagerImpl implements ContextManager {
pathContext.isWithin(includedPath, excludedPath))
.toList();
processOptionsForDriver(info, options, optionMap);
info.analysisDriver = callbacks.addAnalysisDriver(
folder, new ContextRoot(folder.path, containedExcludedPaths), options);
ContextRoot contextRoot =
new ContextRoot(folder.path, containedExcludedPaths);
if (optionsFile != null) {
contextRoot.optionsFilePath = optionsFile.path;
}
info.analysisDriver =
callbacks.addAnalysisDriver(folder, contextRoot, options);
return info;
}

View file

@ -222,8 +222,9 @@ abstract class PluginInfo {
if (currentSession != null) {
AnalysisSetContextRootsParams params = new AnalysisSetContextRootsParams(
contextRoots
.map((analyzer.ContextRoot contextRoot) =>
new ContextRoot(contextRoot.root, contextRoot.exclude))
.map((analyzer.ContextRoot contextRoot) => new ContextRoot(
contextRoot.root, contextRoot.exclude,
optionsFile: contextRoot.optionsFilePath))
.toList());
currentSession.sendRequest(params);
}

View file

@ -119,11 +119,21 @@ class DiscoveredPluginInfoTest {
}
test_addContextRoot() {
String optionsFilePath = '/pkg1/analysis_options.yaml';
ContextRoot contextRoot1 = new ContextRoot('/pkg1', []);
contextRoot1.optionsFilePath = optionsFilePath;
PluginSession session = new PluginSession(plugin);
TestServerCommunicationChannel channel =
new TestServerCommunicationChannel(session);
plugin.currentSession = session;
plugin.addContextRoot(contextRoot1);
expect(plugin.contextRoots, [contextRoot1]);
plugin.addContextRoot(contextRoot1);
expect(plugin.contextRoots, [contextRoot1]);
List<Request> sentRequests = channel.sentRequests;
expect(sentRequests, hasLength(1));
List<Map> roots = sentRequests[0].params['roots'];
expect(roots[0]['optionsFile'], optionsFilePath);
}
test_creation() {

View file

@ -30,7 +30,16 @@ class AnalysisOptionsProvider {
/// and remove the include directive from the resulting options map.
/// Return an empty options map if the file does not exist.
Map<String, YamlNode> getOptions(Folder root, {bool crawlUp: false}) {
Resource resource;
return getOptionsFromFile(getOptionsFile(root, crawlUp: crawlUp));
}
/// Return the analysis options file from which options should be read, or
/// `null` if there is no analysis options file for code in the given [root].
///
/// The given [root] directory will be searched first. If no file is found and
/// if [crawlUp] is `true`, then enclosing directories will be searched.
File getOptionsFile(Folder root, {bool crawlUp: false}) {
Resource resource = null;
for (Folder folder = root; folder != null; folder = folder.parent) {
resource = folder.getChild(AnalysisEngine.ANALYSIS_OPTIONS_FILE);
if (resource.exists) {
@ -41,7 +50,7 @@ class AnalysisOptionsProvider {
break;
}
}
return getOptionsFromFile(resource);
return resource is File ? resource : null;
}
/// Provide the options found in [file].

View file

@ -141,8 +141,8 @@ a:focus, a:hover {
"id": String
"method": "plugin.versionCheck"
"params": {
"<b>byteStorePath</b>": String
"<b>sdkPath</b>": String
"<b>byteStorePath</b>": <a href="#type_FilePath">FilePath</a>
"<b>sdkPath</b>": <a href="#type_FilePath">FilePath</a>
"<b>version</b>": String
}
}</pre><br><pre>response: {
@ -162,13 +162,13 @@ a:focus, a:hover {
</p>
<h4>parameters:</h4><dl><dt class="field"><b>byteStorePath: String</b></dt><dd>
<h4>parameters:</h4><dl><dt class="field"><b>byteStorePath: <a href="#type_FilePath">FilePath</a></b></dt><dd>
<p>
The path to the directory containing the on-disk byte store that is to
be used by any analysis drivers that are created.
</p>
</dd><dt class="field"><b>sdkPath: String</b></dt><dd>
</dd><dt class="field"><b>sdkPath: <a href="#type_FilePath">FilePath</a></b></dt><dd>
<p>
The path to the directory containing the SDK that is to be used by any
@ -1369,14 +1369,14 @@ a:focus, a:hover {
The options used to build an analysis context.
</p>
<dl><dt class="field"><b>dartSdkSummaryPath: String<span style="color:#999999"> (optional)</span></b></dt><dd>
<dl><dt class="field"><b>dartSdkSummaryPath: <a href="#type_FilePath">FilePath</a><span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
The file path of the file containing the summary of the SDK that
should be used to "analyze" the SDK. The field will be omitted if the
summary should be found in the SDK.
</p>
</dd><dt class="field"><b>defaultAnalysisOptionsFilePath: List&lt;String&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
</dd><dt class="field"><b>defaultAnalysisOptionsFilePath: List&lt;<a href="#type_FilePath">FilePath</a>&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
The file path of the analysis options file that should be used in
@ -1391,7 +1391,7 @@ a:focus, a:hover {
The field will be omitted if no additional variables need to be
declared.
</p>
</dd><dt class="field"><b>defaultPackageFilePath: List&lt;String&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
</dd><dt class="field"><b>defaultPackageFilePath: List&lt;<a href="#type_FilePath">FilePath</a>&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
The file path of the .packages file that should be used in place of
@ -1399,7 +1399,7 @@ a:focus, a:hover {
mechanism. The field will be omitted if the normal lookup mechanism
should be used.
</p>
</dd><dt class="field"><b>defaultPackagesDirectoryPath: List&lt;String&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
</dd><dt class="field"><b>defaultPackagesDirectoryPath: List&lt;<a href="#type_FilePath">FilePath</a>&gt;<span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
The file path of the packages directory that should be used in place
@ -1412,18 +1412,24 @@ a:focus, a:hover {
A description of an analysis context.
</p>
<dl><dt class="field"><b>root: String</b></dt><dd>
<dl><dt class="field"><b>root: <a href="#type_FilePath">FilePath</a></b></dt><dd>
<p>
The absolute path of the root directory containing the files to be
analyzed.
</p>
</dd><dt class="field"><b>exclude: List&lt;String&gt;</b></dt><dd>
</dd><dt class="field"><b>exclude: List&lt;<a href="#type_FilePath">FilePath</a>&gt;</b></dt><dd>
<p>
A list of the absolute paths of files and directories within the root
directory that should not be analyzed.
</p>
</dd><dt class="field"><b>optionsFile: <a href="#type_FilePath">FilePath</a><span style="color:#999999"> (optional)</span></b></dt><dd>
<p>
The absolute path of the analysis options file that should be used to
control the analysis of the files in the context.
</p>
</dd></dl></dd><dt class="typeDefinition"><a name="type_Element">Element: object</a></dt><dd>
<p>
Information about an element (something that can be declared in code).
@ -2221,7 +2227,7 @@ a:focus, a:hover {
<p>
The type of change represented by this event.
</p>
</dd><dt class="field"><b>path: String</b></dt><dd>
</dd><dt class="field"><b>path: <a href="#type_FilePath">FilePath</a></b></dt><dd>
<p>
The absolute path of the file or directory that changed.

View file

@ -2354,11 +2354,11 @@ class CompletionGetSuggestionsResult implements ResponseResult {
* ContextBuilderOptions
*
* {
* "dartSdkSummaryPath": optional String
* "defaultAnalysisOptionsFilePath": optional List<String>
* "dartSdkSummaryPath": optional FilePath
* "defaultAnalysisOptionsFilePath": optional List<FilePath>
* "declaredVariables": optional Map<String, String>
* "defaultPackageFilePath": optional List<String>
* "defaultPackagesDirectoryPath": optional List<String>
* "defaultPackageFilePath": optional List<FilePath>
* "defaultPackagesDirectoryPath": optional List<FilePath>
* }
*
* Clients may not extend, implement or mix-in this class.
@ -2579,8 +2579,9 @@ class ContextBuilderOptions implements HasToJson {
* ContextRoot
*
* {
* "root": String
* "exclude": List<String>
* "root": FilePath
* "exclude": List<FilePath>
* "optionsFile": optional FilePath
* }
*
* Clients may not extend, implement or mix-in this class.
@ -2590,6 +2591,8 @@ class ContextRoot implements HasToJson {
List<String> _exclude;
String _optionsFile;
/**
* The absolute path of the root directory containing the files to be
* analyzed.
@ -2620,9 +2623,24 @@ class ContextRoot implements HasToJson {
this._exclude = value;
}
ContextRoot(String root, List<String> exclude) {
/**
* The absolute path of the analysis options file that should be used to
* control the analysis of the files in the context.
*/
String get optionsFile => _optionsFile;
/**
* The absolute path of the analysis options file that should be used to
* control the analysis of the files in the context.
*/
void set optionsFile(String value) {
this._optionsFile = value;
}
ContextRoot(String root, List<String> exclude, {String optionsFile}) {
this.root = root;
this.exclude = exclude;
this.optionsFile = optionsFile;
}
factory ContextRoot.fromJson(
@ -2644,7 +2662,12 @@ class ContextRoot implements HasToJson {
} else {
throw jsonDecoder.mismatch(jsonPath, "exclude");
}
return new ContextRoot(root, exclude);
String optionsFile;
if (json.containsKey("optionsFile")) {
optionsFile = jsonDecoder.decodeString(
jsonPath + ".optionsFile", json["optionsFile"]);
}
return new ContextRoot(root, exclude, optionsFile: optionsFile);
} else {
throw jsonDecoder.mismatch(jsonPath, "ContextRoot", json);
}
@ -2655,6 +2678,9 @@ class ContextRoot implements HasToJson {
Map<String, dynamic> result = {};
result["root"] = root;
result["exclude"] = exclude;
if (optionsFile != null) {
result["optionsFile"] = optionsFile;
}
return result;
}
@ -2665,7 +2691,8 @@ class ContextRoot implements HasToJson {
bool operator ==(other) {
if (other is ContextRoot) {
return root == other.root &&
listEqual(exclude, other.exclude, (String a, String b) => a == b);
listEqual(exclude, other.exclude, (String a, String b) => a == b) &&
optionsFile == other.optionsFile;
}
return false;
}
@ -2675,6 +2702,7 @@ class ContextRoot implements HasToJson {
int hash = 0;
hash = JenkinsSmiHash.combine(hash, root.hashCode);
hash = JenkinsSmiHash.combine(hash, exclude.hashCode);
hash = JenkinsSmiHash.combine(hash, optionsFile.hashCode);
return JenkinsSmiHash.finish(hash);
}
}
@ -5439,8 +5467,8 @@ class PluginShutdownResult implements ResponseResult {
* plugin.versionCheck params
*
* {
* "byteStorePath": String
* "sdkPath": String
* "byteStorePath": FilePath
* "sdkPath": FilePath
* "version": String
* }
*
@ -6441,7 +6469,7 @@ class RequestErrorCode implements Enum {
*
* {
* "type": WatchEventType
* "path": String
* "path": FilePath
* }
*
* Clients may not extend, implement or mix-in this class.

View file

@ -31,12 +31,12 @@ abstract class IntegrationTestMixin {
*
* Parameters
*
* byteStorePath: String
* byteStorePath: FilePath
*
* The path to the directory containing the on-disk byte store that is to
* be used by any analysis drivers that are created.
*
* sdkPath: String
* sdkPath: FilePath
*
* The path to the directory containing the SDK that is to be used by any
* analysis drivers that are created.

View file

@ -209,32 +209,34 @@ final Matcher isCompletionSuggestionKind =
* ContextBuilderOptions
*
* {
* "dartSdkSummaryPath": optional String
* "defaultAnalysisOptionsFilePath": optional List<String>
* "dartSdkSummaryPath": optional FilePath
* "defaultAnalysisOptionsFilePath": optional List<FilePath>
* "declaredVariables": optional Map<String, String>
* "defaultPackageFilePath": optional List<String>
* "defaultPackagesDirectoryPath": optional List<String>
* "defaultPackageFilePath": optional List<FilePath>
* "defaultPackagesDirectoryPath": optional List<FilePath>
* }
*/
final Matcher isContextBuilderOptions = new LazyMatcher(
() => new MatchesJsonObject("ContextBuilderOptions", null, optionalFields: {
"dartSdkSummaryPath": isString,
"defaultAnalysisOptionsFilePath": isListOf(isString),
"dartSdkSummaryPath": isFilePath,
"defaultAnalysisOptionsFilePath": isListOf(isFilePath),
"declaredVariables": isMapOf(isString, isString),
"defaultPackageFilePath": isListOf(isString),
"defaultPackagesDirectoryPath": isListOf(isString)
"defaultPackageFilePath": isListOf(isFilePath),
"defaultPackagesDirectoryPath": isListOf(isFilePath)
}));
/**
* ContextRoot
*
* {
* "root": String
* "exclude": List<String>
* "root": FilePath
* "exclude": List<FilePath>
* "optionsFile": optional FilePath
* }
*/
final Matcher isContextRoot = new LazyMatcher(() => new MatchesJsonObject(
"ContextRoot", {"root": isString, "exclude": isListOf(isString)}));
"ContextRoot", {"root": isFilePath, "exclude": isListOf(isFilePath)},
optionalFields: {"optionsFile": isFilePath}));
/**
* Element
@ -865,11 +867,11 @@ final Matcher isSourceFileEdit = new LazyMatcher(() => new MatchesJsonObject(
*
* {
* "type": WatchEventType
* "path": String
* "path": FilePath
* }
*/
final Matcher isWatchEvent = new LazyMatcher(() => new MatchesJsonObject(
"WatchEvent", {"type": isWatchEventType, "path": isString}));
"WatchEvent", {"type": isWatchEventType, "path": isFilePath}));
/**
* WatchEventType
@ -1438,14 +1440,17 @@ final Matcher isPluginShutdownResult = isNull;
* plugin.versionCheck params
*
* {
* "byteStorePath": String
* "sdkPath": String
* "byteStorePath": FilePath
* "sdkPath": FilePath
* "version": String
* }
*/
final Matcher isPluginVersionCheckParams = new LazyMatcher(() =>
new MatchesJsonObject("plugin.versionCheck params",
{"byteStorePath": isString, "sdkPath": isString, "version": isString}));
new MatchesJsonObject("plugin.versionCheck params", {
"byteStorePath": isFilePath,
"sdkPath": isFilePath,
"version": isString
}));
/**
* plugin.versionCheck result

View file

@ -39,14 +39,14 @@
</p>
<params>
<field name="byteStorePath">
<ref>String</ref>
<ref>FilePath</ref>
<p>
The path to the directory containing the on-disk byte store that is to
be used by any analysis drivers that are created.
</p>
</field>
<field name="sdkPath">
<ref>String</ref>
<ref>FilePath</ref>
<p>
The path to the directory containing the SDK that is to be used by any
analysis drivers that are created.
@ -913,7 +913,7 @@
</p>
<object>
<field name="dartSdkSummaryPath" optional="true">
<ref>String</ref>
<ref>FilePath</ref>
<p>
The file path of the file containing the summary of the SDK that
should be used to "analyze" the SDK. The field will be omitted if the
@ -922,7 +922,7 @@
</field>
<field name="defaultAnalysisOptionsFilePath" optional="true">
<list>
<ref>String</ref>
<ref>FilePath</ref>
</list>
<p>
The file path of the analysis options file that should be used in
@ -959,7 +959,7 @@
-->
<field name="defaultPackageFilePath" optional="true">
<list>
<ref>String</ref>
<ref>FilePath</ref>
</list>
<p>
The file path of the .packages file that should be used in place of
@ -970,7 +970,7 @@
</field>
<field name="defaultPackagesDirectoryPath" optional="true">
<list>
<ref>String</ref>
<ref>FilePath</ref>
</list>
<p>
The file path of the packages directory that should be used in place
@ -987,7 +987,7 @@
</p>
<object>
<field name="root">
<ref>String</ref>
<ref>FilePath</ref>
<p>
The absolute path of the root directory containing the files to be
analyzed.
@ -995,13 +995,20 @@
</field>
<field name="exclude">
<list>
<ref>String</ref>
<ref>FilePath</ref>
</list>
<p>
A list of the absolute paths of files and directories within the root
directory that should not be analyzed.
</p>
</field>
<field name="optionsFile" optional="true">
<ref>FilePath</ref>
<p>
The absolute path of the analysis options file that should be used to
control the analysis of the files in the context.
</p>
</field>
</object>
</type>
<type name="PrioritizedSourceChange">
@ -1233,7 +1240,7 @@
</p>
</field>
<field name="path">
<ref>String</ref>
<ref>FilePath</ref>
<p>
The absolute path of the file or directory that changed.
</p>