blob: ac57af98176560a237cbb268bb7a1c4ffb985159 [file] [log] [blame]
import 'dart:async';
import 'package:analyzer/dart/analysis/results.dart';
import 'package:analyzer/file_system/file_system.dart';
// ignore: implementation_imports
import 'package:analyzer/src/context/builder.dart';
// ignore: implementation_imports
import 'package:analyzer/src/context/context_root.dart';
// ignore: implementation_imports
import 'package:analyzer/src/dart/analysis/driver.dart';
import 'package:analyzer_plugin/plugin/plugin.dart';
import 'package:analyzer_plugin/protocol/protocol_common.dart' as plugin;
import 'package:analyzer_plugin/protocol/protocol_generated.dart' as plugin;
import 'package:built_value_generator/src/plugin/checker.dart';
/// Analyzer plugin for built_value.
///
/// Surfaces the same errors as the generator at compile time, with fixes
/// where possible.
class BuiltValueAnalyzerPlugin extends ServerPlugin {
final Checker checker = Checker();
BuiltValueAnalyzerPlugin(ResourceProvider provider) : super(provider);
@override
AnalysisDriverGeneric createAnalysisDriver(plugin.ContextRoot contextRoot) {
var root = ContextRoot(contextRoot.root, contextRoot.exclude,
pathContext: resourceProvider.pathContext)
..optionsFilePath = contextRoot.optionsFile;
var contextBuilder = ContextBuilder(resourceProvider, sdkManager, null)
..analysisDriverScheduler = analysisDriverScheduler
..byteStore = byteStore
..performanceLog = performanceLog
..fileContentOverlay = fileContentOverlay;
var result = contextBuilder.buildDriver(root);
result.results.listen(_processResult);
return result;
}
@override
List<String> get fileGlobsToAnalyze => const ['*.dart'];
@override
String get name => 'Built Value';
// This is the protocol version, not the plugin version. It must match the
// version of the `analyzer_plugin` package.
@override
String get version => '1.0.0-alpha.0';
@override
String get contactInfo => 'https://github.com/google/built_value.dart/issues';
/// Computes errors based on an analysis result and notifies the analyzer.
// ignore: deprecated_member_use
void _processResult(ResolveResult analysisResult) {
try {
// If there is no relevant analysis result, notify the analyzer of no errors.
if (analysisResult.unit == null ||
analysisResult.libraryElement == null) {
channel.sendNotification(
plugin.AnalysisErrorsParams(analysisResult.path, [])
.toNotification());
} else {
// If there is something to analyze, do so and notify the analyzer.
// Note that notifying with an empty set of errors is important as
// this clears errors if they were fixed.
final checkResult = checker.check(analysisResult.libraryElement);
channel.sendNotification(plugin.AnalysisErrorsParams(
analysisResult.path, checkResult.keys.toList())
.toNotification());
}
} catch (e, stackTrace) {
// Notify the analyzer that an exception happened.
channel.sendNotification(
plugin.PluginErrorParams(false, e.toString(), stackTrace.toString())
.toNotification());
}
}
@override
void contentChanged(String path) {
super.driverForPath(path).addFile(path);
}
@override
Future<plugin.EditGetFixesResult> handleEditGetFixes(
plugin.EditGetFixesParams parameters) async {
try {
final analysisResult =
await (driverForPath(parameters.file) as AnalysisDriver)
.getResult(parameters.file);
// Get errors and fixes for the file.
final checkResult = checker.check(analysisResult.libraryElement);
// Return any fixes that are for the expected file.
final fixes = <plugin.AnalysisErrorFixes>[];
for (var error in checkResult.keys) {
if (error.location.file == parameters.file &&
checkResult[error].change.edits.single.edits.isNotEmpty) {
fixes.add(
plugin.AnalysisErrorFixes(error, fixes: [checkResult[error]]));
}
}
return plugin.EditGetFixesResult(fixes);
} catch (e, stackTrace) {
// Notify the analyzer that an exception happened.
channel.sendNotification(
plugin.PluginErrorParams(false, e.toString(), stackTrace.toString())
.toNotification());
return plugin.EditGetFixesResult([]);
}
}
}