| // 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/value_generator.dart'; |
| import 'package:analysis_server/src/services/correction/util.dart'; |
| import 'package:analyzer/dart/ast/ast.dart'; |
| |
| /// An object used to generate code to be inserted. |
| class CodeTemplate { |
| /// The kind of code that will be generated by this template. |
| final CodeTemplateKind kind; |
| |
| /// The components of the template. |
| final List<TemplateComponent> components; |
| |
| /// Initialize a newly generated code template with the given [kind] and |
| /// [components]. |
| CodeTemplate(this.kind, this.components); |
| |
| String generate(AstNode node, CorrectionUtils utils) { |
| var context = TemplateContext(node, utils); |
| var buffer = StringBuffer(); |
| for (var component in components) { |
| component.appendTo(buffer, context); |
| } |
| return buffer.toString(); |
| } |
| |
| /// Use the [context] to validate that this template will be able to generate |
| /// a value. |
| bool validate(TemplateContext context) { |
| for (var component in components) { |
| if (!component.validate(context)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| } |
| |
| /// The kinds of code that can be generated by a template. |
| enum CodeTemplateKind { |
| expression, |
| statements, |
| } |
| |
| /// An object used to compute some portion of a template. |
| abstract class TemplateComponent { |
| /// Append the text contributed by this component to the given [sink], using |
| /// the [context] to access needed information that isn't already known to |
| /// this component. |
| void appendTo(StringSink sink, TemplateContext context); |
| |
| /// Use the [context] to validate that this component will be able to generate |
| /// a value. |
| bool validate(TemplateContext context); |
| } |
| |
| /// The context in which a template is being evaluated. |
| class TemplateContext { |
| /// The node in the AST that is being transformed. |
| final AstNode node; |
| |
| /// The utilities used to help extract the code associated with various nodes. |
| final CorrectionUtils utils; |
| |
| /// A table mapping variable names to the values of those variables after they |
| /// have been computed. Used to prevent computing the same value multiple |
| /// times. |
| final Map<ValueGenerator, String> _variableValues = {}; |
| |
| /// Initialize a newly created variable support. |
| TemplateContext(this.node, this.utils); |
| |
| /// Return the value of the variable with the given [name]. |
| String valueOf(ValueGenerator generator) { |
| return _variableValues.putIfAbsent(generator, () { |
| return generator.from(this); |
| }); |
| } |
| } |
| |
| /// Literal text within a template. |
| class TemplateText extends TemplateComponent { |
| /// The literal text to be included in the resulting code. |
| final String text; |
| |
| /// Initialize a newly create template text with the given [text]. |
| TemplateText(this.text); |
| |
| @override |
| void appendTo(StringSink sink, TemplateContext context) { |
| sink.write(text); |
| } |
| |
| @override |
| bool validate(TemplateContext context) { |
| return true; |
| } |
| } |
| |
| /// A reference to a variable within a template. |
| class TemplateVariable extends TemplateComponent { |
| /// The generator used to compute the value of the variable. |
| final ValueGenerator generator; |
| |
| /// Initialize a newly created template variable with the given [generator]. |
| TemplateVariable(this.generator); |
| |
| @override |
| void appendTo(StringSink sink, TemplateContext context) { |
| sink.write(context.valueOf(generator)); |
| } |
| |
| @override |
| bool validate(TemplateContext context) { |
| return generator.validate(context); |
| } |
| } |