11// ignore_for_file: public_member_api_docs
2+
23import 'dart:async' ;
34
4- import 'package:analyzer/dart/analysis/analysis_context.dart' ;
5- import 'package:analyzer/dart/analysis/analysis_context_collection.dart' ;
5+ import 'package:analyzer/dart/analysis/context_locator.dart' ;
66import 'package:analyzer/dart/analysis/results.dart' ;
77import 'package:analyzer/file_system/file_system.dart' ;
8+ import 'package:analyzer/file_system/physical_file_system.dart' ;
9+ // ignore: implementation_imports
10+ import 'package:analyzer/src/analysis_options/analysis_options_provider.dart' ;
11+ // ignore: implementation_imports
12+ import 'package:analyzer/src/dart/analysis/context_builder.dart' ;
13+ // ignore: implementation_imports
14+ import 'package:analyzer/src/dart/analysis/driver.dart' ;
815import 'package:analyzer_plugin/plugin/plugin.dart' ;
916import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
1017
@@ -14,14 +21,18 @@ import '../analyzers/lint_analyzer/metrics/metrics_list/number_of_parameters_met
1421import '../analyzers/lint_analyzer/metrics/metrics_list/source_lines_of_code/source_lines_of_code_metric.dart' ;
1522import '../config_builder/config_builder.dart' ;
1623import '../config_builder/models/analysis_options.dart' ;
24+ import '../utils/analyzer_utils.dart' ;
25+ import '../utils/yaml_utils.dart' ;
1726import 'analyzer_plugin_utils.dart' ;
1827
28+ final _byteStore = createByteStore (PhysicalResourceProvider .INSTANCE );
29+
1930class AnalyzerPlugin extends ServerPlugin {
2031 static const _analyzer = LintAnalyzer ();
2132
22- final _configs = < String , LintAnalysisConfig > {};
33+ final _configs = < AnalysisDriverGeneric , LintAnalysisConfig > {};
2334
24- AnalysisContextCollection ? _contextCollection ;
35+ var _filesFromSetPriorityFilesRequest = < String > [] ;
2536
2637 @override
2738 String get contactInfo =>
@@ -36,101 +47,172 @@ class AnalyzerPlugin extends ServerPlugin {
3647 @override
3748 String get version => '1.0.0-alpha.0' ;
3849
39- AnalyzerPlugin ({
40- required ResourceProvider resourceProvider,
41- }) : super (resourceProvider: resourceProvider);
50+ AnalyzerPlugin (ResourceProvider provider) : super (provider);
4251
4352 @override
44- Future <void > afterNewContextCollection ({
45- required AnalysisContextCollection contextCollection,
46- }) {
47- _contextCollection = contextCollection;
48-
49- contextCollection.contexts.forEach (_createConfig);
50-
51- return super
52- .afterNewContextCollection (contextCollection: contextCollection);
53+ void contentChanged (String path) {
54+ super .driverForPath (path)? .addFile (path);
5355 }
5456
5557 @override
56- Future <void > analyzeFile ({
57- required AnalysisContext analysisContext,
58- required String path,
59- }) async {
60- try {
61- final resolvedUnit =
62- await analysisContext.currentSession.getResolvedUnit (path);
58+ AnalysisDriverGeneric createAnalysisDriver (plugin.ContextRoot contextRoot) {
59+ final rootPath = contextRoot.root;
60+ final locator =
61+ ContextLocator (resourceProvider: resourceProvider).locateRoots (
62+ includedPaths: [rootPath],
63+ excludedPaths: contextRoot.exclude,
64+ optionsFile: contextRoot.optionsFile,
65+ );
6366
64- if (resolvedUnit is ResolvedUnitResult ) {
65- final analysisErrors = _getErrorsForResolvedUnit (
66- resolvedUnit,
67- analysisContext.contextRoot.root.path,
68- );
67+ if (locator.isEmpty) {
68+ final error = StateError ('Unexpected empty context' );
69+ channel.sendNotification (plugin.PluginErrorParams (
70+ true ,
71+ error.message,
72+ error.stackTrace.toString (),
73+ ).toNotification ());
6974
75+ throw error;
76+ }
77+
78+ final builder = ContextBuilderImpl (resourceProvider: resourceProvider);
79+ final context = builder.createContext (
80+ contextRoot: locator.first,
81+ byteStore: _byteStore,
82+ );
83+ final dartDriver = context.driver;
84+ final config = _createConfig (dartDriver, rootPath);
85+
86+ if (config == null ) {
87+ return dartDriver;
88+ }
89+
90+ // Temporary disable deprecation check
91+ //
92+ // final deprecations = checkConfigDeprecatedOptions(
93+ // config,
94+ // deprecatedOptions,
95+ // contextRoot.optionsFile!,
96+ // );
97+ // if (deprecations.isNotEmpty) {
98+ // channel.sendNotification(plugin.AnalysisErrorsParams(
99+ // contextRoot.optionsFile!,
100+ // deprecations.map((deprecation) => deprecation.error).toList(),
101+ // ).toNotification());
102+ // }
103+
104+ runZonedGuarded (
105+ () {
106+ dartDriver.results.listen ((analysisResult) {
107+ if (analysisResult is ResolvedUnitResult ) {
108+ _processResult (dartDriver, analysisResult);
109+ }
110+ });
111+ },
112+ (e, stackTrace) {
70113 channel.sendNotification (
71- plugin.AnalysisErrorsParams (
72- path,
73- analysisErrors.map ((analysisError) => analysisError.error).toList (),
74- ).toNotification (),
75- );
76- } else {
77- channel.sendNotification (
78- plugin.AnalysisErrorsParams (path, []).toNotification (),
114+ plugin.PluginErrorParams (false , e.toString (), stackTrace.toString ())
115+ .toNotification (),
79116 );
117+ },
118+ );
119+
120+ return dartDriver;
121+ }
122+
123+ @override
124+ Future <plugin.AnalysisSetContextRootsResult > handleAnalysisSetContextRoots (
125+ plugin.AnalysisSetContextRootsParams parameters,
126+ ) async {
127+ final result = await super .handleAnalysisSetContextRoots (parameters);
128+ // The super-call adds files to the driver, so we need to prioritize them so they get analyzed.
129+ _updatePriorityFiles ();
130+
131+ return result;
132+ }
133+
134+ @override
135+ Future <plugin.AnalysisSetPriorityFilesResult > handleAnalysisSetPriorityFiles (
136+ plugin.AnalysisSetPriorityFilesParams parameters,
137+ ) async {
138+ _filesFromSetPriorityFilesRequest = parameters.files;
139+ _updatePriorityFiles ();
140+
141+ return plugin.AnalysisSetPriorityFilesResult ();
142+ }
143+
144+ @override
145+ Future <plugin.EditGetFixesResult > handleEditGetFixes (
146+ plugin.EditGetFixesParams parameters,
147+ ) async {
148+ try {
149+ final driver = driverForPath (parameters.file) as AnalysisDriver ;
150+ // ignore: deprecated_member_use
151+ final analysisResult = await driver.getResult2 (parameters.file);
152+
153+ if (analysisResult is ! ResolvedUnitResult ) {
154+ return plugin.EditGetFixesResult ([]);
80155 }
156+
157+ final fixes = _check (driver, analysisResult).where ((fix) {
158+ final location = fix.error.location;
159+
160+ return location.file == parameters.file &&
161+ location.offset <= parameters.offset &&
162+ parameters.offset <= location.offset + location.length &&
163+ fix.fixes.isNotEmpty;
164+ }).toList ();
165+
166+ return plugin.EditGetFixesResult (fixes);
81167 } on Exception catch (e, stackTrace) {
82168 channel.sendNotification (
83169 plugin.PluginErrorParams (false , e.toString (), stackTrace.toString ())
84170 .toNotification (),
85171 );
172+
173+ return plugin.EditGetFixesResult ([]);
86174 }
87175 }
88176
89- @override
90- Future <plugin. EditGetFixesResult > handleEditGetFixes (
91- plugin. EditGetFixesParams parameters ,
92- ) async {
177+ void _processResult (
178+ AnalysisDriver driver,
179+ ResolvedUnitResult analysisResult ,
180+ ) {
93181 try {
94- final path = parameters.file;
95- final analysisContext = _contextCollection? .contextFor (path);
96- final resolvedUnit =
97- await analysisContext? .currentSession.getResolvedUnit (path);
98-
99- if (analysisContext != null && resolvedUnit is ResolvedUnitResult ) {
100- final analysisErrors = _getErrorsForResolvedUnit (
101- resolvedUnit,
102- analysisContext.contextRoot.root.path,
103- ).where ((analysisError) {
104- final location = analysisError.error.location;
105-
106- return location.file == parameters.file &&
107- location.offset <= parameters.offset &&
108- parameters.offset <= location.offset + location.length &&
109- analysisError.fixes.isNotEmpty;
110- }).toList ();
111-
112- return plugin.EditGetFixesResult (analysisErrors);
182+ if (driver.analysisContext? .contextRoot.isAnalyzed (analysisResult.path) ??
183+ false ) {
184+ final fixes = _check (driver, analysisResult);
185+
186+ channel.sendNotification (
187+ plugin.AnalysisErrorsParams (
188+ analysisResult.path,
189+ fixes.map ((fix) => fix.error).toList (),
190+ ).toNotification (),
191+ );
192+ } else {
193+ channel.sendNotification (
194+ plugin.AnalysisErrorsParams (analysisResult.path, []).toNotification (),
195+ );
113196 }
114197 } on Exception catch (e, stackTrace) {
115198 channel.sendNotification (
116199 plugin.PluginErrorParams (false , e.toString (), stackTrace.toString ())
117200 .toNotification (),
118201 );
119202 }
120-
121- return plugin.EditGetFixesResult ([]);
122203 }
123204
124- Iterable <plugin.AnalysisErrorFixes > _getErrorsForResolvedUnit (
205+ Iterable <plugin.AnalysisErrorFixes > _check (
206+ AnalysisDriver driver,
125207 ResolvedUnitResult analysisResult,
126- String rootPath,
127208 ) {
128209 final result = < plugin.AnalysisErrorFixes > [];
129- final config = _configs[rootPath ];
210+ final config = _configs[driver ];
130211
131212 if (config != null ) {
132- final report =
133- _analyzer.runPluginAnalysis (analysisResult, config, rootPath);
213+ final root = driver.analysisContext? .contextRoot.root.path;
214+
215+ final report = _analyzer.runPluginAnalysis (analysisResult, config, root! );
134216
135217 if (report != null ) {
136218 result.addAll ([
@@ -156,26 +238,69 @@ class AnalyzerPlugin extends ServerPlugin {
156238 return result;
157239 }
158240
159- void _createConfig (AnalysisContext analysisContext) {
160- final rootPath = analysisContext.contextRoot.root.path;
161- final file = analysisContext.contextRoot.optionsFile;
162-
241+ LintAnalysisConfig ? _createConfig (AnalysisDriver driver, String rootPath) {
242+ final file = driver.analysisContext? .contextRoot.optionsFile;
163243 if (file != null && file.exists) {
164- final analysisOptions = analysisOptionsFromContext (analysisContext) ??
165- analysisOptionsFromFilePath (file.parent.path, analysisContext);
166- final config = ConfigBuilder .getLintConfigFromOptions (analysisOptions);
167-
244+ final options = AnalysisOptions (
245+ file.path,
246+ yamlMapToDartMap (
247+ AnalysisOptionsProvider (driver.sourceFactory)
248+ .getOptionsFromFile (file),
249+ ),
250+ );
251+ final config = ConfigBuilder .getLintConfigFromOptions (options);
168252 final lintConfig = ConfigBuilder .getLintAnalysisConfig (
169253 config,
170- analysisOptions .folderPath ?? rootPath,
254+ options .folderPath ?? rootPath,
171255 classMetrics: const [],
172256 functionMetrics: [
173257 NumberOfParametersMetric (config: config.metrics),
174258 SourceLinesOfCodeMetric (config: config.metrics),
175259 ],
176260 );
177261
178- _configs[rootPath] = lintConfig;
262+ _configs[driver] = lintConfig;
263+
264+ return lintConfig;
265+ }
266+
267+ return null ;
268+ }
269+
270+ /// AnalysisDriver doesn't fully resolve files that are added via `addFile` ; they need to be either explicitly requested
271+ /// via `getResult` /etc, or added to `priorityFiles` .
272+ ///
273+ /// This method updates `priorityFiles` on the driver to include:
274+ ///
275+ /// - Any files prioritized by the analysis server via [handleAnalysisSetPriorityFiles]
276+ /// - All other files the driver has been told to analyze via addFile (in [ServerPlugin.handleAnalysisSetContextRoots] )
277+ ///
278+ /// As a result, [_processResult] will get called with resolved units, and thus all of our diagnostics
279+ /// will get run on all files in the repo instead of only the currently open/edited ones!
280+ void _updatePriorityFiles () {
281+ final filesToFullyResolve = {
282+ // Ensure these go first, since they're actually considered priority; ...
283+ ..._filesFromSetPriorityFilesRequest,
284+
285+ // ... all other files need to be analyzed, but don't trump priority
286+ for (final driver2 in driverMap.values)
287+ ...(driver2 as AnalysisDriver ).addedFiles,
288+ };
289+
290+ // From ServerPlugin.handleAnalysisSetPriorityFiles.
291+ final filesByDriver = < AnalysisDriverGeneric , List <String >> {};
292+ for (final file in filesToFullyResolve) {
293+ final contextRoot = contextRootContaining (file);
294+ if (contextRoot != null ) {
295+ // TODO(dkrutskikh): Which driver should we use if there is no context root?
296+ final driver = driverMap[contextRoot];
297+ if (driver != null ) {
298+ filesByDriver.putIfAbsent (driver, () => < String > []).add (file);
299+ }
300+ }
179301 }
302+ filesByDriver.forEach ((driver, files) {
303+ driver.priorityFiles = files;
304+ });
180305 }
181306}
0 commit comments