blob: 16c838899dd69224f3fa8fce32dd42b806628bda [file] [log] [blame]
// 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 i = 0; i < accessors.length; i++) {
var result = accessors[i].getValue(target);
if (!result.isValid) {
return '';
}
target = result.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
String toString() => accessors.join('.');
@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);
}