mirror of
https://github.com/dart-lang/sdk
synced 2024-11-02 10:28:02 +00:00
Enable package:pedantic/analysis_options.1.9.0.yaml and fixes.
Change-Id: I44a8236cb4569c8cec03a016c4861e32b0356aa6 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/177201 Reviewed-by: Samuel Rawlins <srawlins@google.com> Commit-Queue: Konstantin Shcheglov <scheglov@google.com>
This commit is contained in:
parent
d683f33f5b
commit
f71639daa1
4 changed files with 214 additions and 362 deletions
|
@ -1,3 +1,5 @@
|
|||
include: package:pedantic/analysis_options.1.9.0.yaml
|
||||
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
||||
|
|
|
@ -2,23 +2,17 @@
|
|||
// 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.
|
||||
|
||||
/**
|
||||
* Tools for manipulating HTML during code generation of analyzer and analysis
|
||||
* server.
|
||||
*/
|
||||
/// Tools for manipulating HTML during code generation of analyzer and analysis
|
||||
/// server.
|
||||
import 'package:html/dom.dart' as dom;
|
||||
|
||||
/**
|
||||
* Make a deep copy of the given HTML nodes.
|
||||
*/
|
||||
/// Make a deep copy of the given HTML nodes.
|
||||
List<dom.Node> cloneHtmlNodes(List<dom.Node> nodes) =>
|
||||
nodes.map((dom.Node node) => node.clone(true)).toList();
|
||||
|
||||
/**
|
||||
* Return true if the given iterable contains only whitespace text nodes.
|
||||
*/
|
||||
/// Return true if the given iterable contains only whitespace text nodes.
|
||||
bool containsOnlyWhitespace(Iterable<dom.Node> nodes) {
|
||||
for (dom.Node node in nodes) {
|
||||
for (var node in nodes) {
|
||||
if (!isWhitespaceNode(node)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -26,11 +20,9 @@ bool containsOnlyWhitespace(Iterable<dom.Node> nodes) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the text contents of the element, ignoring all markup.
|
||||
*/
|
||||
/// Get the text contents of the element, ignoring all markup.
|
||||
String innerText(dom.Element parent) {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
var buffer = StringBuffer();
|
||||
void recurse(dom.Element parent) {
|
||||
for (dom.Node child in parent.nodes) {
|
||||
if (child is dom.Text) {
|
||||
|
@ -45,10 +37,8 @@ String innerText(dom.Element parent) {
|
|||
return buffer.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given node is a text node containing only whitespace, or
|
||||
* a comment.
|
||||
*/
|
||||
/// Return true if the given node is a text node containing only whitespace, or
|
||||
/// a comment.
|
||||
bool isWhitespaceNode(dom.Node node) {
|
||||
if (node is dom.Element) {
|
||||
return false;
|
||||
|
@ -59,48 +49,38 @@ bool isWhitespaceNode(dom.Node node) {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTML element with the given name, attributes, and child nodes.
|
||||
*/
|
||||
/// Create an HTML element with the given name, attributes, and child nodes.
|
||||
dom.Element makeElement(
|
||||
String name, Map<dynamic, String> attributes, List<dom.Node> children) {
|
||||
dom.Element result = new dom.Element.tag(name);
|
||||
var result = dom.Element.tag(name);
|
||||
result.attributes.addAll(attributes);
|
||||
for (dom.Node child in children) {
|
||||
for (var child in children) {
|
||||
result.append(child);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin class for generating HTML.
|
||||
*/
|
||||
/// Mixin class for generating HTML.
|
||||
mixin HtmlGenerator {
|
||||
List<dom.Node> _html = [];
|
||||
|
||||
/**
|
||||
* Add the given [node] to the HTML output.
|
||||
*/
|
||||
/// Add the given [node] to the HTML output.
|
||||
void add(dom.Node node) {
|
||||
_html.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given [nodes] to the HTML output.
|
||||
*/
|
||||
/// Add the given [nodes] to the HTML output.
|
||||
void addAll(Iterable<dom.Node> nodes) {
|
||||
for (dom.Node node in nodes) {
|
||||
for (var node in nodes) {
|
||||
add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute [callback], collecting any code that is output using [write],
|
||||
* [writeln], [add], [addAll] or [element], and return the result as a list
|
||||
* of HTML nodes.
|
||||
*/
|
||||
/// Execute [callback], collecting any code that is output using [write],
|
||||
/// [writeln], [add], [addAll] or [element], and return the result as a list
|
||||
/// of HTML nodes.
|
||||
List<dom.Node> collectHtml(void Function()? callback) {
|
||||
List<dom.Node> oldHtml = _html;
|
||||
var oldHtml = _html;
|
||||
try {
|
||||
_html = <dom.Node>[];
|
||||
if (callback != null) {
|
||||
|
@ -112,25 +92,19 @@ mixin HtmlGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute [callback], wrapping its output in an element with the given
|
||||
* [name] and [attributes].
|
||||
*/
|
||||
/// Execute [callback], wrapping its output in an element with the given
|
||||
/// [name] and [attributes].
|
||||
void element(String name, Map<dynamic, String> attributes,
|
||||
[void Function()? callback]) {
|
||||
add(makeElement(name, attributes, collectHtml(callback)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Output text without ending the current line.
|
||||
*/
|
||||
/// Output text without ending the current line.
|
||||
void write(String text) {
|
||||
_html.add(new dom.Text(text));
|
||||
_html.add(dom.Text(text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Output text, ending the current line.
|
||||
*/
|
||||
/// Output text, ending the current line.
|
||||
void writeln([Object obj = '']) {
|
||||
write('$obj\n');
|
||||
}
|
||||
|
|
|
@ -2,80 +2,57 @@
|
|||
// 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.
|
||||
|
||||
/**
|
||||
* Code for converting HTML into text, for use during code generation of
|
||||
* analyzer and analysis server.
|
||||
*/
|
||||
|
||||
/// Code for converting HTML into text, for use during code generation of
|
||||
/// analyzer and analysis server.
|
||||
import 'package:analyzer_utilities/tools.dart';
|
||||
import 'package:html/dom.dart' as dom;
|
||||
|
||||
final RegExp whitespace = new RegExp(r'\s');
|
||||
final RegExp whitespace = RegExp(r'\s');
|
||||
|
||||
/**
|
||||
* Convert the HTML in [desc] into text, word wrapping at width [width].
|
||||
*
|
||||
* If [javadocStyle] is true, then the output is compatible with Javadoc,
|
||||
* which understands certain HTML constructs.
|
||||
*/
|
||||
/// Convert the HTML in [desc] into text, word wrapping at width [width].
|
||||
///
|
||||
/// If [javadocStyle] is true, then the output is compatible with Javadoc,
|
||||
/// which understands certain HTML constructs.
|
||||
String nodesToText(List<dom.Node> desc, int width, bool javadocStyle,
|
||||
{bool removeTrailingNewLine: false}) {
|
||||
_TextFormatter formatter = new _TextFormatter(width, javadocStyle);
|
||||
{bool removeTrailingNewLine = false}) {
|
||||
var formatter = _TextFormatter(width, javadocStyle);
|
||||
return formatter.collectCode(() {
|
||||
formatter.addAll(desc);
|
||||
formatter.lineBreak(false);
|
||||
}, removeTrailingNewLine: removeTrailingNewLine);
|
||||
}
|
||||
|
||||
/**
|
||||
* Engine that transforms HTML to text. The input HTML is processed one
|
||||
* character at a time, gathering characters into words and words into lines.
|
||||
*/
|
||||
/// Engine that transforms HTML to text. The input HTML is processed one
|
||||
/// character at a time, gathering characters into words and words into lines.
|
||||
class _TextFormatter extends CodeGenerator {
|
||||
/**
|
||||
* Word-wrapping width.
|
||||
*/
|
||||
/// Word-wrapping width.
|
||||
final int width;
|
||||
|
||||
/**
|
||||
* The word currently being gathered.
|
||||
*/
|
||||
/// The word currently being gathered.
|
||||
String word = '';
|
||||
|
||||
/**
|
||||
* The line currently being gathered.
|
||||
*/
|
||||
/// The line currently being gathered.
|
||||
String line = '';
|
||||
|
||||
/**
|
||||
* True if a blank line should be inserted before the next word.
|
||||
*/
|
||||
/// True if a blank line should be inserted before the next word.
|
||||
bool verticalSpaceNeeded = false;
|
||||
|
||||
/**
|
||||
* True if no text has been output yet. This suppresses blank lines.
|
||||
*/
|
||||
/// True if no text has been output yet. This suppresses blank lines.
|
||||
bool atStart = true;
|
||||
|
||||
/**
|
||||
* True if we are processing a <pre> element, thus whitespace should be
|
||||
* preserved.
|
||||
*/
|
||||
/// True if we are processing a <pre> element, thus whitespace should be
|
||||
/// preserved.
|
||||
bool preserveSpaces = false;
|
||||
|
||||
/**
|
||||
* True if the output should be Javadoc compatible.
|
||||
*/
|
||||
/// True if the output should be Javadoc compatible.
|
||||
final bool javadocStyle;
|
||||
|
||||
_TextFormatter(this.width, this.javadocStyle);
|
||||
|
||||
/**
|
||||
* Process an HTML node.
|
||||
*/
|
||||
/// Process an HTML node.
|
||||
void add(dom.Node node) {
|
||||
if (node is dom.Text) {
|
||||
for (String char in node.text.split('')) {
|
||||
for (var char in node.text.split('')) {
|
||||
if (preserveSpaces) {
|
||||
wordBreak();
|
||||
write(escape(char));
|
||||
|
@ -141,7 +118,7 @@ class _TextFormatter extends CodeGenerator {
|
|||
if (javadocStyle) {
|
||||
writeln('<pre>');
|
||||
}
|
||||
bool oldPreserveSpaces = preserveSpaces;
|
||||
var oldPreserveSpaces = preserveSpaces;
|
||||
try {
|
||||
preserveSpaces = true;
|
||||
addAll(node.nodes);
|
||||
|
@ -166,25 +143,21 @@ class _TextFormatter extends CodeGenerator {
|
|||
case 'head':
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unexpected HTML element: ${node.localName}');
|
||||
throw Exception('Unexpected HTML element: ${node.localName}');
|
||||
}
|
||||
} else {
|
||||
throw new Exception('Unexpected HTML: $node');
|
||||
throw Exception('Unexpected HTML: $node');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a list of HTML nodes.
|
||||
*/
|
||||
/// Process a list of HTML nodes.
|
||||
void addAll(List<dom.Node> nodes) {
|
||||
for (dom.Node node in nodes) {
|
||||
for (var node in nodes) {
|
||||
add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape the given character for HTML.
|
||||
*/
|
||||
/// Escape the given character for HTML.
|
||||
String escape(String char) {
|
||||
if (javadocStyle) {
|
||||
switch (char) {
|
||||
|
@ -199,9 +172,7 @@ class _TextFormatter extends CodeGenerator {
|
|||
return char;
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the current word and/or line, if either is in progress.
|
||||
*/
|
||||
/// Terminate the current word and/or line, if either is in progress.
|
||||
void lineBreak(bool gap) {
|
||||
wordBreak();
|
||||
if (line.isNotEmpty) {
|
||||
|
@ -213,9 +184,7 @@ class _TextFormatter extends CodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert vertical space if necessary.
|
||||
*/
|
||||
/// Insert vertical space if necessary.
|
||||
void resolveVerticalSpace() {
|
||||
if (verticalSpaceNeeded) {
|
||||
writeln();
|
||||
|
@ -223,9 +192,7 @@ class _TextFormatter extends CodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Terminate the current word, if a word is in progress.
|
||||
*/
|
||||
/// Terminate the current word, if a word is in progress.
|
||||
void wordBreak() {
|
||||
if (word.isNotEmpty) {
|
||||
atStart = false;
|
||||
|
|
|
@ -2,9 +2,7 @@
|
|||
// 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.
|
||||
|
||||
/**
|
||||
* Tools for generating code in analyzer and analysis server.
|
||||
*/
|
||||
/// Tools for generating code in analyzer and analysis server.
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
|
@ -13,16 +11,14 @@ import 'package:analyzer_utilities/text_formatter.dart';
|
|||
import 'package:html/dom.dart' as dom;
|
||||
import 'package:path/path.dart';
|
||||
|
||||
final RegExp trailingSpacesInLineRegExp = new RegExp(r' +$', multiLine: true);
|
||||
final RegExp trailingWhitespaceRegExp = new RegExp(r'[\n ]+$');
|
||||
final RegExp trailingSpacesInLineRegExp = RegExp(r' +$', multiLine: true);
|
||||
final RegExp trailingWhitespaceRegExp = RegExp(r'[\n ]+$');
|
||||
|
||||
/**
|
||||
* Join the given strings using camelCase. If [doCapitalize] is true, the first
|
||||
* part will be capitalized as well.
|
||||
*/
|
||||
String camelJoin(List<String> parts, {bool doCapitalize: false}) {
|
||||
List<String> upcasedParts = <String>[];
|
||||
for (int i = 0; i < parts.length; i++) {
|
||||
/// Join the given strings using camelCase. If [doCapitalize] is true, the first
|
||||
/// part will be capitalized as well.
|
||||
String camelJoin(List<String> parts, {bool doCapitalize = false}) {
|
||||
var upcasedParts = <String>[];
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
if (i == 0 && !doCapitalize) {
|
||||
upcasedParts.add(parts[i]);
|
||||
} else {
|
||||
|
@ -32,51 +28,38 @@ String camelJoin(List<String> parts, {bool doCapitalize: false}) {
|
|||
return upcasedParts.join();
|
||||
}
|
||||
|
||||
/**
|
||||
* Capitalize and return the passed String.
|
||||
*/
|
||||
/// Capitalize and return the passed String.
|
||||
String capitalize(String string) {
|
||||
return string[0].toUpperCase() + string.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Type of functions used to compute the contents of a set of generated files.
|
||||
* [pkgPath] is the path to the current package.
|
||||
*/
|
||||
typedef Map<String, FileContentsComputer> DirectoryContentsComputer(
|
||||
/// Type of functions used to compute the contents of a set of generated files.
|
||||
/// [pkgPath] is the path to the current package.
|
||||
typedef DirectoryContentsComputer = Map<String, FileContentsComputer> Function(
|
||||
String pkgPath);
|
||||
|
||||
/**
|
||||
* Type of functions used to compute the contents of a generated file.
|
||||
* [pkgPath] is the path to the current package.
|
||||
*/
|
||||
typedef Future<String> FileContentsComputer(String pkgPath);
|
||||
/// Type of functions used to compute the contents of a generated file.
|
||||
/// [pkgPath] is the path to the current package.
|
||||
typedef FileContentsComputer = Future<String> Function(String pkgPath);
|
||||
|
||||
/**
|
||||
* Mixin class for generating code.
|
||||
*/
|
||||
/// Mixin class for generating code.
|
||||
class CodeGenerator {
|
||||
_CodeGeneratorState _state = _CodeGeneratorState();
|
||||
|
||||
/**
|
||||
* Settings that specialize code generation behavior for a given
|
||||
* programming language.
|
||||
*/
|
||||
CodeGeneratorSettings codeGeneratorSettings = new CodeGeneratorSettings();
|
||||
/// Settings that specialize code generation behavior for a given
|
||||
/// programming language.
|
||||
CodeGeneratorSettings codeGeneratorSettings = CodeGeneratorSettings();
|
||||
|
||||
/**
|
||||
* Measure the width of the current indentation level.
|
||||
*/
|
||||
/// Measure the width of the current indentation level.
|
||||
int get indentWidth => _state.nextIndent.length;
|
||||
|
||||
/**
|
||||
* Execute [callback], collecting any code that is output using [write]
|
||||
* or [writeln], and return the result as a string.
|
||||
*/
|
||||
String collectCode(void callback(), {bool removeTrailingNewLine: false}) {
|
||||
_CodeGeneratorState oldState = _state;
|
||||
/// Execute [callback], collecting any code that is output using [write]
|
||||
/// or [writeln], and return the result as a string.
|
||||
String collectCode(void Function() callback,
|
||||
{bool removeTrailingNewLine = false}) {
|
||||
var oldState = _state;
|
||||
try {
|
||||
_state = new _CodeGeneratorState();
|
||||
_state = _CodeGeneratorState();
|
||||
callback();
|
||||
var text =
|
||||
_state.buffer.toString().replaceAll(trailingSpacesInLineRegExp, '');
|
||||
|
@ -90,13 +73,11 @@ class CodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a doc comment based on the HTML in [docs].
|
||||
*
|
||||
* When generating java code, the output is compatible with Javadoc, which
|
||||
* understands certain HTML constructs.
|
||||
*/
|
||||
void docComment(List<dom.Node> docs, {bool removeTrailingNewLine: false}) {
|
||||
/// Generate a doc comment based on the HTML in [docs].
|
||||
///
|
||||
/// When generating java code, the output is compatible with Javadoc, which
|
||||
/// understands certain HTML constructs.
|
||||
void docComment(List<dom.Node> docs, {bool removeTrailingNewLine = false}) {
|
||||
if (containsOnlyWhitespace(docs)) return;
|
||||
|
||||
var startMarker = codeGeneratorSettings.docCommentStartMarker;
|
||||
|
@ -104,8 +85,8 @@ class CodeGenerator {
|
|||
writeln(startMarker);
|
||||
}
|
||||
|
||||
int width = codeGeneratorSettings.commentLineLength;
|
||||
bool javadocStyle = codeGeneratorSettings.languageName == 'java';
|
||||
var width = codeGeneratorSettings.commentLineLength;
|
||||
var javadocStyle = codeGeneratorSettings.languageName == 'java';
|
||||
indentBy(codeGeneratorSettings.docCommentLineLeader, () {
|
||||
write(nodesToText(docs, width - _state.indent.length, javadocStyle,
|
||||
removeTrailingNewLine: removeTrailingNewLine));
|
||||
|
@ -117,29 +98,23 @@ class CodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute [callback], indenting any code it outputs.
|
||||
*/
|
||||
void indent(void callback()) {
|
||||
/// Execute [callback], indenting any code it outputs.
|
||||
void indent(void Function() callback) {
|
||||
indentSpecial(
|
||||
codeGeneratorSettings.indent, codeGeneratorSettings.indent, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute [callback], using [additionalIndent] to indent any code it outputs.
|
||||
*/
|
||||
void indentBy(String additionalIndent, void callback()) =>
|
||||
/// Execute [callback], using [additionalIndent] to indent any code it outputs.
|
||||
void indentBy(String additionalIndent, void Function() callback) =>
|
||||
indentSpecial(additionalIndent, additionalIndent, callback);
|
||||
|
||||
/**
|
||||
* Execute [callback], using [additionalIndent] to indent any code it outputs.
|
||||
* The first line of output is indented by [firstAdditionalIndent] instead of
|
||||
* [additionalIndent].
|
||||
*/
|
||||
void indentSpecial(
|
||||
String firstAdditionalIndent, String additionalIndent, void callback()) {
|
||||
String oldNextIndent = _state.nextIndent;
|
||||
String oldIndent = _state.indent;
|
||||
/// Execute [callback], using [additionalIndent] to indent any code it outputs.
|
||||
/// The first line of output is indented by [firstAdditionalIndent] instead of
|
||||
/// [additionalIndent].
|
||||
void indentSpecial(String firstAdditionalIndent, String additionalIndent,
|
||||
void Function() callback) {
|
||||
var oldNextIndent = _state.nextIndent;
|
||||
var oldIndent = _state.indent;
|
||||
try {
|
||||
_state.nextIndent += firstAdditionalIndent;
|
||||
_state.indent += additionalIndent;
|
||||
|
@ -155,13 +130,13 @@ class CodeGenerator {
|
|||
return;
|
||||
}
|
||||
write(codeGeneratorSettings.lineCommentLineLeader);
|
||||
int width = codeGeneratorSettings.commentLineLength;
|
||||
var width = codeGeneratorSettings.commentLineLength;
|
||||
indentBy(codeGeneratorSettings.lineCommentLineLeader, () {
|
||||
write(nodesToText(docs, width - _state.indent.length, false));
|
||||
});
|
||||
}
|
||||
|
||||
void outputHeader({bool javaStyle: false, String? year = null}) {
|
||||
void outputHeader({bool javaStyle = false, String? year}) {
|
||||
String header;
|
||||
if (codeGeneratorSettings.languageName == 'java') {
|
||||
header = '''
|
||||
|
@ -197,96 +172,74 @@ class CodeGenerator {
|
|||
writeln(header.trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* Output text without ending the current line.
|
||||
*/
|
||||
/// Output text without ending the current line.
|
||||
void write(Object obj) {
|
||||
_state.write(obj.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Output text, ending the current line.
|
||||
*/
|
||||
/// Output text, ending the current line.
|
||||
void writeln([Object obj = '']) {
|
||||
_state.write('$obj\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls several settings of [CodeGenerator].
|
||||
*
|
||||
* The default settings are valid for generating Java and Dart code.
|
||||
*/
|
||||
/// Controls several settings of [CodeGenerator].
|
||||
///
|
||||
/// The default settings are valid for generating Java and Dart code.
|
||||
class CodeGeneratorSettings {
|
||||
/**
|
||||
* Name of the language being generated. Lowercase.
|
||||
*/
|
||||
/// Name of the language being generated. Lowercase.
|
||||
String languageName;
|
||||
|
||||
/**
|
||||
* Marker used in line comments.
|
||||
*/
|
||||
/// Marker used in line comments.
|
||||
String lineCommentLineLeader;
|
||||
|
||||
/**
|
||||
* Start marker for doc comments.
|
||||
*/
|
||||
/// Start marker for doc comments.
|
||||
String? docCommentStartMarker;
|
||||
|
||||
/**
|
||||
* Line leader for body lines in doc comments.
|
||||
*/
|
||||
/// Line leader for body lines in doc comments.
|
||||
String docCommentLineLeader;
|
||||
|
||||
/**
|
||||
* End marker for doc comments.
|
||||
*/
|
||||
/// End marker for doc comments.
|
||||
String? docCommentEndMarker;
|
||||
|
||||
/**
|
||||
* Line length for doc comment lines.
|
||||
*/
|
||||
/// Line length for doc comment lines.
|
||||
int commentLineLength;
|
||||
|
||||
/**
|
||||
* String used for indenting code.
|
||||
*/
|
||||
/// String used for indenting code.
|
||||
String indent;
|
||||
|
||||
CodeGeneratorSettings(
|
||||
{this.languageName: 'java',
|
||||
this.lineCommentLineLeader: '// ',
|
||||
this.docCommentStartMarker: '/**',
|
||||
this.docCommentLineLeader: ' * ',
|
||||
this.docCommentEndMarker: ' */',
|
||||
this.commentLineLength: 99,
|
||||
this.indent: ' '});
|
||||
{this.languageName = 'java',
|
||||
this.lineCommentLineLeader = '// ',
|
||||
this.docCommentStartMarker = '/**',
|
||||
this.docCommentLineLeader = ' * ',
|
||||
this.docCommentEndMarker = ' */',
|
||||
this.commentLineLength = 99,
|
||||
this.indent = ' '});
|
||||
}
|
||||
|
||||
/**
|
||||
* A utility class for invoking dartfmt.
|
||||
*/
|
||||
/// A utility class for invoking dartfmt.
|
||||
class DartFormat {
|
||||
static String get _dartfmtPath {
|
||||
String binName = Platform.isWindows ? 'dartfmt.bat' : 'dartfmt';
|
||||
var binName = Platform.isWindows ? 'dartfmt.bat' : 'dartfmt';
|
||||
for (var loc in [binName, join('dart-sdk', 'bin', binName)]) {
|
||||
var candidatePath = join(dirname(Platform.resolvedExecutable), loc);
|
||||
if (new File(candidatePath).existsSync()) {
|
||||
if (File(candidatePath).existsSync()) {
|
||||
return candidatePath;
|
||||
}
|
||||
}
|
||||
throw new StateError('Could not find dartfmt executable');
|
||||
throw StateError('Could not find dartfmt executable');
|
||||
}
|
||||
|
||||
static void formatFile(File file) {
|
||||
ProcessResult result = Process.runSync(_dartfmtPath, ['-w', file.path]);
|
||||
var result = Process.runSync(_dartfmtPath, ['-w', file.path]);
|
||||
_throwIfExitCode(result);
|
||||
}
|
||||
|
||||
static String formatText(String text) {
|
||||
File file = new File(join(Directory.systemTemp.path, 'gen.dart'));
|
||||
var file = File(join(Directory.systemTemp.path, 'gen.dart'));
|
||||
file.writeAsStringSync(text);
|
||||
ProcessResult result = Process.runSync(_dartfmtPath, ['-w', file.path]);
|
||||
var result = Process.runSync(_dartfmtPath, ['-w', file.path]);
|
||||
_throwIfExitCode(result);
|
||||
return file.readAsStringSync();
|
||||
}
|
||||
|
@ -296,62 +249,52 @@ class DartFormat {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract base class representing behaviors common to generated files and
|
||||
* generated directories.
|
||||
*/
|
||||
/// Abstract base class representing behaviors common to generated files and
|
||||
/// generated directories.
|
||||
abstract class GeneratedContent {
|
||||
/**
|
||||
* Check whether the [output] has the correct contents, and return true if it
|
||||
* does. [pkgPath] is the path to the current package.
|
||||
*/
|
||||
/// Check whether the [output] has the correct contents, and return true if it
|
||||
/// does. [pkgPath] is the path to the current package.
|
||||
Future<bool> check(String pkgPath);
|
||||
|
||||
/**
|
||||
* Replace the [output] with the correct contents. [pkgPath] is the path to
|
||||
* the current package.
|
||||
*/
|
||||
/// Replace the [output] with the correct contents. [pkgPath] is the path to
|
||||
/// the current package.
|
||||
Future<void> generate(String pkgPath);
|
||||
|
||||
/**
|
||||
* Get a [FileSystemEntity] representing the output file or directory.
|
||||
* [pkgPath] is the path to the current package.
|
||||
*/
|
||||
/// Get a [FileSystemEntity] representing the output file or directory.
|
||||
/// [pkgPath] is the path to the current package.
|
||||
FileSystemEntity output(String pkgPath);
|
||||
|
||||
/**
|
||||
* Check that all of the [targets] are up to date. If they are not, print
|
||||
* out a message instructing the user to regenerate them, and exit with a
|
||||
* nonzero error code.
|
||||
*
|
||||
* [pkgPath] is the path to the current package. [generatorPath] is the path
|
||||
* to a .dart script the user may use to regenerate the targets.
|
||||
*
|
||||
* To avoid mistakes when run on Windows, [generatorPath] always uses
|
||||
* POSIX directory separators.
|
||||
*/
|
||||
/// Check that all of the [targets] are up to date. If they are not, print
|
||||
/// out a message instructing the user to regenerate them, and exit with a
|
||||
/// nonzero error code.
|
||||
///
|
||||
/// [pkgPath] is the path to the current package. [generatorPath] is the path
|
||||
/// to a .dart script the user may use to regenerate the targets.
|
||||
///
|
||||
/// To avoid mistakes when run on Windows, [generatorPath] always uses
|
||||
/// POSIX directory separators.
|
||||
static Future<void> checkAll(
|
||||
String pkgPath, String generatorPath, Iterable<GeneratedContent> targets,
|
||||
{List<String> args = const []}) async {
|
||||
bool generateNeeded = false;
|
||||
for (GeneratedContent target in targets) {
|
||||
bool ok = await target.check(pkgPath);
|
||||
var generateNeeded = false;
|
||||
for (var target in targets) {
|
||||
var ok = await target.check(pkgPath);
|
||||
if (!ok) {
|
||||
print("${target.output(pkgPath).absolute}"
|
||||
print('${target.output(pkgPath).absolute}'
|
||||
" doesn't have expected contents.");
|
||||
generateNeeded = true;
|
||||
}
|
||||
}
|
||||
if (generateNeeded) {
|
||||
print('Please regenerate using:');
|
||||
String executable = Platform.executable;
|
||||
String packageRoot = '';
|
||||
var executable = Platform.executable;
|
||||
var packageRoot = '';
|
||||
// ignore: deprecated_member_use
|
||||
if (Platform.packageRoot != null) {
|
||||
// ignore: deprecated_member_use
|
||||
packageRoot = ' --package-root=${Platform.packageRoot}';
|
||||
}
|
||||
String generateScript = normalize(joinAll(posix.split(generatorPath)));
|
||||
var generateScript = normalize(joinAll(posix.split(generatorPath)));
|
||||
print(' $executable$packageRoot $generateScript ${args.join(" ")}');
|
||||
exit(1);
|
||||
} else {
|
||||
|
@ -359,47 +302,39 @@ abstract class GeneratedContent {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate all of the [targets]. [pkgPath] is the path to the current
|
||||
* package.
|
||||
*/
|
||||
/// Regenerate all of the [targets]. [pkgPath] is the path to the current
|
||||
/// package.
|
||||
static Future<void> generateAll(
|
||||
String pkgPath, Iterable<GeneratedContent> targets) async {
|
||||
print("Generating...");
|
||||
for (GeneratedContent target in targets) {
|
||||
print('Generating...');
|
||||
for (var target in targets) {
|
||||
await target.generate(pkgPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a single output directory (either generated code or
|
||||
* generated HTML). No other content should exist in the directory.
|
||||
*/
|
||||
/// Class representing a single output directory (either generated code or
|
||||
/// generated HTML). No other content should exist in the directory.
|
||||
class GeneratedDirectory extends GeneratedContent {
|
||||
/**
|
||||
* The path to the directory that will have the generated content.
|
||||
*/
|
||||
/// The path to the directory that will have the generated content.
|
||||
final String outputDirPath;
|
||||
|
||||
/**
|
||||
* Callback function that computes the directory contents.
|
||||
*/
|
||||
/// Callback function that computes the directory contents.
|
||||
final DirectoryContentsComputer directoryContentsComputer;
|
||||
|
||||
GeneratedDirectory(this.outputDirPath, this.directoryContentsComputer);
|
||||
|
||||
@override
|
||||
Future<bool> check(String pkgPath) async {
|
||||
Directory outputDirectory = output(pkgPath);
|
||||
Map<String, FileContentsComputer> map = directoryContentsComputer(pkgPath);
|
||||
var outputDirectory = output(pkgPath);
|
||||
var map = directoryContentsComputer(pkgPath);
|
||||
try {
|
||||
for (var entry in map.entries) {
|
||||
String file = entry.key;
|
||||
FileContentsComputer fileContentsComputer = entry.value;
|
||||
String expectedContents = await fileContentsComputer(pkgPath);
|
||||
File outputFile = new File(posix.join(outputDirectory.path, file));
|
||||
String actualContents = outputFile.readAsStringSync();
|
||||
var file = entry.key;
|
||||
var fileContentsComputer = entry.value;
|
||||
var expectedContents = await fileContentsComputer(pkgPath);
|
||||
var outputFile = File(posix.join(outputDirectory.path, file));
|
||||
var actualContents = outputFile.readAsStringSync();
|
||||
// Normalize Windows line endings to Unix line endings so that the
|
||||
// comparison doesn't fail on Windows.
|
||||
actualContents = actualContents.replaceAll('\r\n', '\n');
|
||||
|
@ -407,7 +342,7 @@ class GeneratedDirectory extends GeneratedContent {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
int nonHiddenFileCount = 0;
|
||||
var nonHiddenFileCount = 0;
|
||||
outputDirectory
|
||||
.listSync(recursive: false, followLinks: false)
|
||||
.forEach((FileSystemEntity fileSystemEntity) {
|
||||
|
@ -432,7 +367,7 @@ class GeneratedDirectory extends GeneratedContent {
|
|||
|
||||
@override
|
||||
Future<void> generate(String pkgPath) async {
|
||||
Directory outputDirectory = output(pkgPath);
|
||||
var outputDirectory = output(pkgPath);
|
||||
try {
|
||||
// delete the contents of the directory (and the directory itself)
|
||||
outputDirectory.deleteSync(recursive: true);
|
||||
|
@ -444,37 +379,31 @@ class GeneratedDirectory extends GeneratedContent {
|
|||
outputDirectory.createSync(recursive: true);
|
||||
|
||||
// generate all of the files in the directory
|
||||
Map<String, FileContentsComputer> map = directoryContentsComputer(pkgPath);
|
||||
var map = directoryContentsComputer(pkgPath);
|
||||
for (var entry in map.entries) {
|
||||
String file = entry.key;
|
||||
FileContentsComputer fileContentsComputer = entry.value;
|
||||
File outputFile = new File(posix.join(outputDirectory.path, file));
|
||||
var file = entry.key;
|
||||
var fileContentsComputer = entry.value;
|
||||
var outputFile = File(posix.join(outputDirectory.path, file));
|
||||
print(' ${outputFile.path}');
|
||||
String contents = await fileContentsComputer(pkgPath);
|
||||
var contents = await fileContentsComputer(pkgPath);
|
||||
outputFile.writeAsStringSync(contents);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Directory output(String pkgPath) =>
|
||||
new Directory(join(pkgPath, joinAll(posix.split(outputDirPath))));
|
||||
Directory(join(pkgPath, joinAll(posix.split(outputDirPath))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing a single output file (either generated code or generated
|
||||
* HTML).
|
||||
*/
|
||||
/// Class representing a single output file (either generated code or generated
|
||||
/// HTML).
|
||||
class GeneratedFile extends GeneratedContent {
|
||||
/**
|
||||
* The output file to which generated output should be written, relative to
|
||||
* the "tool/spec" directory. This filename uses the posix path separator
|
||||
* ('/') regardless of the OS.
|
||||
*/
|
||||
/// The output file to which generated output should be written, relative to
|
||||
/// the "tool/spec" directory. This filename uses the posix path separator
|
||||
/// ('/') regardless of the OS.
|
||||
final String outputPath;
|
||||
|
||||
/**
|
||||
* Callback function which computes the file.
|
||||
*/
|
||||
/// Callback function which computes the file.
|
||||
final FileContentsComputer computeContents;
|
||||
|
||||
GeneratedFile(this.outputPath, this.computeContents);
|
||||
|
@ -483,13 +412,13 @@ class GeneratedFile extends GeneratedContent {
|
|||
|
||||
@override
|
||||
Future<bool> check(String pkgPath) async {
|
||||
File outputFile = output(pkgPath);
|
||||
String expectedContents = await computeContents(pkgPath);
|
||||
var outputFile = output(pkgPath);
|
||||
var expectedContents = await computeContents(pkgPath);
|
||||
if (isDartFile) {
|
||||
expectedContents = DartFormat.formatText(expectedContents);
|
||||
}
|
||||
try {
|
||||
String actualContents = outputFile.readAsStringSync();
|
||||
var actualContents = outputFile.readAsStringSync();
|
||||
// Normalize Windows line endings to Unix line endings so that the
|
||||
// comparison doesn't fail on Windows.
|
||||
actualContents = actualContents.replaceAll('\r\n', '\n');
|
||||
|
@ -504,9 +433,9 @@ class GeneratedFile extends GeneratedContent {
|
|||
|
||||
@override
|
||||
Future<void> generate(String pkgPath) async {
|
||||
File outputFile = output(pkgPath);
|
||||
var outputFile = output(pkgPath);
|
||||
print(' ${outputFile.path}');
|
||||
String contents = await computeContents(pkgPath);
|
||||
var contents = await computeContents(pkgPath);
|
||||
outputFile.writeAsStringSync(contents);
|
||||
if (isDartFile) {
|
||||
DartFormat.formatFile(outputFile);
|
||||
|
@ -515,41 +444,33 @@ class GeneratedFile extends GeneratedContent {
|
|||
|
||||
@override
|
||||
File output(String pkgPath) =>
|
||||
new File(join(pkgPath, joinAll(posix.split(outputPath))));
|
||||
File(join(pkgPath, joinAll(posix.split(outputPath))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Mixin class for generating HTML representations of code that are suitable
|
||||
* for enclosing inside a <pre> element.
|
||||
*/
|
||||
/// Mixin class for generating HTML representations of code that are suitable
|
||||
/// for enclosing inside a <pre> element.
|
||||
abstract class HtmlCodeGenerator {
|
||||
_HtmlCodeGeneratorState _state = _HtmlCodeGeneratorState();
|
||||
|
||||
/**
|
||||
* Add the given [node] to the HTML output.
|
||||
*/
|
||||
/// Add the given [node] to the HTML output.
|
||||
void add(dom.Node node) {
|
||||
_state.add(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given [nodes] to the HTML output.
|
||||
*/
|
||||
/// Add the given [nodes] to the HTML output.
|
||||
void addAll(Iterable<dom.Node> nodes) {
|
||||
for (dom.Node node in nodes) {
|
||||
for (var node in nodes) {
|
||||
_state.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute [callback], collecting any code that is output using [write],
|
||||
* [writeln], [add], or [addAll], and return the result as a list of DOM
|
||||
* nodes.
|
||||
*/
|
||||
/// Execute [callback], collecting any code that is output using [write],
|
||||
/// [writeln], [add], or [addAll], and return the result as a list of DOM
|
||||
/// nodes.
|
||||
List<dom.Node> collectHtml(void Function()? callback) {
|
||||
_HtmlCodeGeneratorState oldState = _state;
|
||||
var oldState = _state;
|
||||
try {
|
||||
_state = new _HtmlCodeGeneratorState();
|
||||
_state = _HtmlCodeGeneratorState();
|
||||
if (callback != null) {
|
||||
callback();
|
||||
}
|
||||
|
@ -559,20 +480,16 @@ abstract class HtmlCodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute [callback], wrapping its output in an element with the given
|
||||
* [name] and [attributes].
|
||||
*/
|
||||
/// Execute [callback], wrapping its output in an element with the given
|
||||
/// [name] and [attributes].
|
||||
void element(String name, Map<dynamic, String> attributes,
|
||||
[void Function()? callback]) {
|
||||
add(makeElement(name, attributes, collectHtml(callback)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute [callback], indenting any code it outputs by two spaces.
|
||||
*/
|
||||
void indent(void callback()) {
|
||||
String oldIndent = _state.indent;
|
||||
/// Execute [callback], indenting any code it outputs by two spaces.
|
||||
void indent(void Function() callback) {
|
||||
var oldIndent = _state.indent;
|
||||
try {
|
||||
_state.indent += ' ';
|
||||
callback();
|
||||
|
@ -581,33 +498,27 @@ abstract class HtmlCodeGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Output text without ending the current line.
|
||||
*/
|
||||
/// Output text without ending the current line.
|
||||
void write(Object obj) {
|
||||
_state.write(obj.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Output text, ending the current line.
|
||||
*/
|
||||
/// Output text, ending the current line.
|
||||
void writeln([Object obj = '']) {
|
||||
_state.write('$obj\n');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* State used by [CodeGenerator].
|
||||
*/
|
||||
/// State used by [CodeGenerator].
|
||||
class _CodeGeneratorState {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
StringBuffer buffer = StringBuffer();
|
||||
String nextIndent = '';
|
||||
String indent = '';
|
||||
bool indentNeeded = true;
|
||||
|
||||
void write(String text) {
|
||||
List<String> lines = text.split('\n');
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
var lines = text.split('\n');
|
||||
for (var i = 0; i < lines.length; i++) {
|
||||
if (i == lines.length - 1 && lines[i].isEmpty) {
|
||||
break;
|
||||
}
|
||||
|
@ -625,9 +536,7 @@ class _CodeGeneratorState {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* State used by [HtmlCodeGenerator].
|
||||
*/
|
||||
/// State used by [HtmlCodeGenerator].
|
||||
class _HtmlCodeGeneratorState {
|
||||
List<dom.Node> buffer = <dom.Node>[];
|
||||
String indent = '';
|
||||
|
@ -646,15 +555,15 @@ class _HtmlCodeGeneratorState {
|
|||
return;
|
||||
}
|
||||
if (indentNeeded) {
|
||||
buffer.add(new dom.Text(indent));
|
||||
buffer.add(dom.Text(indent));
|
||||
}
|
||||
List<String> lines = text.split('\n');
|
||||
var lines = text.split('\n');
|
||||
if (lines.last.isEmpty) {
|
||||
lines.removeLast();
|
||||
buffer.add(new dom.Text(lines.join('\n$indent') + '\n'));
|
||||
buffer.add(dom.Text(lines.join('\n$indent') + '\n'));
|
||||
indentNeeded = true;
|
||||
} else {
|
||||
buffer.add(new dom.Text(lines.join('\n$indent')));
|
||||
buffer.add(dom.Text(lines.join('\n$indent')));
|
||||
indentNeeded = false;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue