| // Copyright (c) 2020, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:analysis_server/src/services/correction/fix/data_driven/accessor.dart'; |
| import 'package:analysis_server/src/services/correction/fix/data_driven/code_template.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| import 'package:analyzer/dart/element/type.dart'; |
| import 'package:analyzer_plugin/utilities/change_builder/change_builder_dart.dart'; |
| import 'package:analyzer_plugin/utilities/range_factory.dart'; |
| |
| /// Use a specified argument from an invocation as the value of a template |
| /// variable. |
| class CodeFragment extends ValueGenerator { |
| /// The accessor used to access the code fragment. |
| final List<Accessor> accessors; |
| |
| /// Initialize a newly created extractor to extract a code fragment. |
| CodeFragment(this.accessors); |
| |
| @override |
| String evaluateIn(TemplateContext context) { |
| Object target = context.node; |
| for (var accessor in accessors) { |
| target = accessor.getValue(target).result; |
| } |
| if (target is AstNode) { |
| return context.utils.getRangeText(range.node(target)); |
| } else if (target is DartType) { |
| // TODO(brianwilkerson) If we end up needing it, figure out how to convert |
| // a type into valid code. |
| throw UnsupportedError('Unexpected result of ${target.runtimeType}'); |
| } else { |
| throw UnsupportedError('Unexpected result of ${target.runtimeType}'); |
| } |
| } |
| |
| @override |
| bool validate(TemplateContext context) { |
| Object target = context.node; |
| for (var accessor in accessors) { |
| var result = accessor.getValue(target); |
| if (!result.isValid) { |
| return false; |
| } |
| target = result.result; |
| } |
| return true; |
| } |
| |
| @override |
| void writeOn(DartEditBuilder builder, TemplateContext context) { |
| Object target = context.node; |
| for (var accessor in accessors) { |
| target = accessor.getValue(target).result; |
| } |
| if (target is AstNode) { |
| builder.write(context.utils.getRangeText(range.node(target))); |
| } else if (target is DartType) { |
| builder.writeType(target); |
| } else { |
| throw UnsupportedError('Unexpected result of ${target.runtimeType}'); |
| } |
| } |
| } |
| |
| /// Use a name that might need to be imported from a different library as the |
| /// value of a template variable. |
| class ImportedName extends ValueGenerator { |
| /// The URIs of the libraries from which the name can be imported. |
| final List<Uri> uris; |
| |
| /// The name to be used. |
| final String name; |
| |
| ImportedName(this.uris, this.name); |
| |
| @override |
| String evaluateIn(TemplateContext context) { |
| return name; |
| } |
| |
| @override |
| bool validate(TemplateContext context) { |
| // TODO(brianwilkerson) Validate that the import can be added. |
| return true; |
| } |
| |
| @override |
| void writeOn(DartEditBuilder builder, TemplateContext context) { |
| builder.writeImportedName(uris, name); |
| } |
| } |
| |
| /// An object used to generate the value of a template variable. |
| abstract class ValueGenerator { |
| /// Return the value generated by this generator, using the [context] to |
| /// access needed information that isn't already known to this generator. |
| String evaluateIn(TemplateContext context); |
| |
| /// Use the [context] to validate that this generator will be able to generate |
| /// a value. |
| bool validate(TemplateContext context); |
| |
| /// Write the value generated by this generator to the given [builder], using |
| /// the [context] to access needed information that isn't already known to |
| /// this generator. |
| void writeOn(DartEditBuilder builder, TemplateContext context); |
| } |