| Index: pkg/js_ast/lib/src/printer.dart
|
| diff --git a/pkg/js_ast/lib/src/printer.dart b/pkg/js_ast/lib/src/printer.dart
|
| index f362be5646b37857287498760cd5c6115d57283e..4576ee4f786151879c3ac08e16ee070bca9c5df1 100644
|
| --- a/pkg/js_ast/lib/src/printer.dart
|
| +++ b/pkg/js_ast/lib/src/printer.dart
|
| @@ -26,11 +26,24 @@ abstract class JavaScriptPrintingContext {
|
| /// Adds [string] to the output.
|
| void emit(String string);
|
|
|
| - /// Callback immediately before printing [node]. Whitespace may be printed
|
| - /// after this callback before the first non-whitespace character for [node].
|
| - void enterNode(Node node) {}
|
| - /// Callback after printing the last character representing [node].
|
| - void exitNode(Node node) {}
|
| + /// Callback for the start of printing of [node]. [startPosition] is the
|
| + /// position of the first non-whitespace character of [node].
|
| + ///
|
| + /// [enterNode] is called in pre-traversal order.
|
| + void enterNode(Node node, int startPosition) {}
|
| +
|
| + /// Callback for the end of printing of [node]. [startPosition] is the
|
| + /// position of the first non-whitespace character of [node] (also provided
|
| + /// in the [enterNode] callback), [endPosition] is the position immediately
|
| + /// following the last character of [node]. [closingPosition] is the
|
| + /// position of the ending delimiter of [node]. This is only provided for
|
| + /// [Fun] nodes and is `null` otherwise.
|
| + ///
|
| + /// [enterNode] is called in post-traversal order.
|
| + void exitNode(Node node,
|
| + int startPosition,
|
| + int endPosition,
|
| + int closingPosition) {}
|
| }
|
|
|
| /// A simple implementation of [JavaScriptPrintingContext] suitable for tests.
|
| @@ -52,6 +65,7 @@ class Printer implements NodeVisitor {
|
| final DanglingElseVisitor danglingElseVisitor;
|
| final LocalNamer localNamer;
|
|
|
| + int _charCount = 0;
|
| bool inForInit = false;
|
| bool atStatementBegin = false;
|
| bool pendingSemicolon = false;
|
| @@ -98,31 +112,33 @@ class Printer implements NodeVisitor {
|
| _indentLevel--;
|
| }
|
|
|
| -
|
| /// Always emit a newline, even under `enableMinification`.
|
| void forceLine() {
|
| - out("\n");
|
| + out("\n", isWhitespace: true);
|
| }
|
| +
|
| /// Emits a newline for readability.
|
| void lineOut() {
|
| if (!shouldCompressOutput) forceLine();
|
| }
|
| +
|
| void spaceOut() {
|
| - if (!shouldCompressOutput) out(" ");
|
| + if (!shouldCompressOutput) out(" ", isWhitespace: true);
|
| }
|
|
|
| String lastAddedString = null;
|
| +
|
| int get lastCharCode {
|
| if (lastAddedString == null) return 0;
|
| assert(lastAddedString.length != "");
|
| return lastAddedString.codeUnitAt(lastAddedString.length - 1);
|
| }
|
|
|
| - void out(String str) {
|
| + void out(String str, {bool isWhitespace: false}) {
|
| if (str != "") {
|
| if (pendingSemicolon) {
|
| if (!shouldCompressOutput) {
|
| - context.emit(";");
|
| + _emit(";");
|
| } else if (str != "}") {
|
| // We want to output newline instead of semicolon because it makes
|
| // the raw stack traces much easier to read and it also makes line-
|
| @@ -137,19 +153,22 @@ class Printer implements NodeVisitor {
|
| // than newlines because the former doesn't need escaping.
|
| if (options.preferSemicolonToNewlineInMinifiedOutput ||
|
| expressionContinuationRegExp.hasMatch(str)) {
|
| - context.emit(";");
|
| + _emit(";");
|
| } else {
|
| - context.emit("\n");
|
| + _emit("\n");
|
| }
|
| }
|
| }
|
| if (pendingSpace &&
|
| (!shouldCompressOutput || identifierCharacterRegExp.hasMatch(str))) {
|
| - context.emit(" ");
|
| + _emit(" ");
|
| }
|
| pendingSpace = false;
|
| pendingSemicolon = false;
|
| - context.emit(str);
|
| + if (!isWhitespace) {
|
| + enterNode();
|
| + }
|
| + _emit(str);
|
| lastAddedString = str;
|
| }
|
| }
|
| @@ -168,22 +187,50 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| - void outIndent(String str) { indent(); out(str); }
|
| - void outIndentLn(String str) { indent(); outLn(str); }
|
| + void outIndent(String str) {
|
| + indent();
|
| + out(str);
|
| + }
|
| +
|
| + void outIndentLn(String str) {
|
| + indent();
|
| + outLn(str);
|
| + }
|
| +
|
| void indent() {
|
| if (!shouldCompressOutput) {
|
| - out(indentation);
|
| + out(indentation, isWhitespace: true);
|
| }
|
| }
|
|
|
| - visit(Node node) {
|
| - context.enterNode(node);
|
| + EnterExitNode currentNode;
|
| +
|
| + void _emit(String text) {
|
| + context.emit(text);
|
| + _charCount += text.length;
|
| + }
|
| +
|
| + void startNode(Node node) {
|
| + currentNode = new EnterExitNode(currentNode, node);
|
| + }
|
| +
|
| + void enterNode() {
|
| + currentNode.addToNode(context, _charCount);
|
| + }
|
| +
|
| + void endNode(Node node) {
|
| + assert(currentNode.node == node);
|
| + currentNode = currentNode.exitNode(context, _charCount);
|
| + }
|
| +
|
| + void visit(Node node) {
|
| + startNode(node);
|
| node.accept(this);
|
| - context.exitNode(node);
|
| + endNode(node);
|
| }
|
|
|
| - visitCommaSeparated(List<Node> nodes, int hasRequiredType,
|
| - {bool newInForInit, bool newAtStatementBegin}) {
|
| + void visitCommaSeparated(List<Node> nodes, int hasRequiredType,
|
| + {bool newInForInit, bool newAtStatementBegin}) {
|
| for (int i = 0; i < nodes.length; i++) {
|
| if (i != 0) {
|
| atStatementBegin = false;
|
| @@ -196,12 +243,15 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| - visitAll(List<Node> nodes) {
|
| + void visitAll(List<Node> nodes) {
|
| nodes.forEach(visit);
|
| }
|
|
|
| - visitProgram(Program program) {
|
| - visitAll(program.body);
|
| + @override
|
| + void visitProgram(Program program) {
|
| + if (program.body.isNotEmpty) {
|
| + visitAll(program.body);
|
| + }
|
| }
|
|
|
| Statement unwrapBlockIfSingleStatement(Statement body) {
|
| @@ -223,7 +273,7 @@ class Printer implements NodeVisitor {
|
| if (shouldCompressOutput && needsSeparation) {
|
| // If [shouldCompressOutput] is false, then the 'lineOut' will insert
|
| // the separation.
|
| - out(" ");
|
| + out(" ", isWhitespace: true);
|
| } else {
|
| lineOut();
|
| }
|
| @@ -235,18 +285,18 @@ class Printer implements NodeVisitor {
|
|
|
| void blockOutWithoutBraces(Node node) {
|
| if (node is Block) {
|
| - context.enterNode(node);
|
| + startNode(node);
|
| Block block = node;
|
| block.statements.forEach(blockOutWithoutBraces);
|
| - context.exitNode(node);
|
| + endNode(node);
|
| } else {
|
| visit(node);
|
| }
|
| }
|
|
|
| - void blockOut(Block node, {bool shouldIndent, bool needsNewline}) {
|
| + int blockOut(Block node, {bool shouldIndent, bool needsNewline}) {
|
| if (shouldIndent) indent();
|
| - context.enterNode(node);
|
| + startNode(node);
|
| out("{");
|
| lineOut();
|
| indentMore();
|
| @@ -254,22 +304,27 @@ class Printer implements NodeVisitor {
|
| indentLess();
|
| indent();
|
| out("}");
|
| - context.exitNode(node);
|
| + int closingPosition = _charCount - 1;
|
| + endNode(node);
|
| if (needsNewline) lineOut();
|
| + return closingPosition;
|
| }
|
|
|
| - visitBlock(Block block) {
|
| + @override
|
| + void visitBlock(Block block) {
|
| blockOut(block, shouldIndent: true, needsNewline: true);
|
| }
|
|
|
| - visitExpressionStatement(ExpressionStatement expressionStatement) {
|
| + @override
|
| + void visitExpressionStatement(ExpressionStatement node) {
|
| indent();
|
| - visitNestedExpression(expressionStatement.expression, EXPRESSION,
|
| + visitNestedExpression(node.expression, EXPRESSION,
|
| newInForInit: false, newAtStatementBegin: true);
|
| outSemicolonLn();
|
| }
|
|
|
| - visitEmptyStatement(EmptyStatement nop) {
|
| + @override
|
| + void visitEmptyStatement(EmptyStatement node) {
|
| outIndentLn(";");
|
| }
|
|
|
| @@ -313,11 +368,13 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| - visitIf(If node) {
|
| + @override
|
| + void visitIf(If node) {
|
| ifOut(node, true);
|
| }
|
|
|
| - visitFor(For loop) {
|
| + @override
|
| + void visitFor(For loop) {
|
| outIndent("for");
|
| spaceOut();
|
| out("(");
|
| @@ -342,7 +399,8 @@ class Printer implements NodeVisitor {
|
| needsSeparation: false, needsNewline: true);
|
| }
|
|
|
| - visitForIn(ForIn loop) {
|
| + @override
|
| + void visitForIn(ForIn loop) {
|
| outIndent("for");
|
| spaceOut();
|
| out("(");
|
| @@ -357,7 +415,8 @@ class Printer implements NodeVisitor {
|
| needsSeparation: false, needsNewline: true);
|
| }
|
|
|
| - visitWhile(While loop) {
|
| + @override
|
| + void visitWhile(While loop) {
|
| outIndent("while");
|
| spaceOut();
|
| out("(");
|
| @@ -368,7 +427,8 @@ class Printer implements NodeVisitor {
|
| needsSeparation: false, needsNewline: true);
|
| }
|
|
|
| - visitDo(Do loop) {
|
| + @override
|
| + void visitDo(Do loop) {
|
| outIndent("do");
|
| if (blockBody(unwrapBlockIfSingleStatement(loop.body),
|
| needsSeparation: true, needsNewline: false)) {
|
| @@ -385,7 +445,8 @@ class Printer implements NodeVisitor {
|
| outSemicolonLn();
|
| }
|
|
|
| - visitContinue(Continue node) {
|
| + @override
|
| + void visitContinue(Continue node) {
|
| if (node.targetLabel == null) {
|
| outIndent("continue");
|
| } else {
|
| @@ -394,7 +455,8 @@ class Printer implements NodeVisitor {
|
| outSemicolonLn();
|
| }
|
|
|
| - visitBreak(Break node) {
|
| + @override
|
| + void visitBreak(Break node) {
|
| if (node.targetLabel == null) {
|
| outIndent("break");
|
| } else {
|
| @@ -403,7 +465,8 @@ class Printer implements NodeVisitor {
|
| outSemicolonLn();
|
| }
|
|
|
| - visitReturn(Return node) {
|
| + @override
|
| + void visitReturn(Return node) {
|
| if (node.value == null) {
|
| outIndent("return");
|
| } else {
|
| @@ -415,7 +478,8 @@ class Printer implements NodeVisitor {
|
| outSemicolonLn();
|
| }
|
|
|
| - visitDartYield(DartYield node) {
|
| + @override
|
| + void visitDartYield(DartYield node) {
|
| if (node.hasStar) {
|
| outIndent("yield*");
|
| } else {
|
| @@ -427,8 +491,8 @@ class Printer implements NodeVisitor {
|
| outSemicolonLn();
|
| }
|
|
|
| -
|
| - visitThrow(Throw node) {
|
| + @override
|
| + void visitThrow(Throw node) {
|
| outIndent("throw");
|
| pendingSpace = true;
|
| visitNestedExpression(node.expression, EXPRESSION,
|
| @@ -436,7 +500,8 @@ class Printer implements NodeVisitor {
|
| outSemicolonLn();
|
| }
|
|
|
| - visitTry(Try node) {
|
| + @override
|
| + void visitTry(Try node) {
|
| outIndent("try");
|
| blockBody(node.body, needsSeparation: true, needsNewline: false);
|
| if (node.catchPart != null) {
|
| @@ -451,7 +516,8 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| - visitCatch(Catch node) {
|
| + @override
|
| + void visitCatch(Catch node) {
|
| spaceOut();
|
| out("catch");
|
| spaceOut();
|
| @@ -462,7 +528,8 @@ class Printer implements NodeVisitor {
|
| blockBody(node.body, needsSeparation: false, needsNewline: false);
|
| }
|
|
|
| - visitSwitch(Switch node) {
|
| + @override
|
| + void visitSwitch(Switch node) {
|
| outIndent("switch");
|
| spaceOut();
|
| out("(");
|
| @@ -477,7 +544,8 @@ class Printer implements NodeVisitor {
|
| outIndentLn("}");
|
| }
|
|
|
| - visitCase(Case node) {
|
| + @override
|
| + void visitCase(Case node) {
|
| outIndent("case");
|
| pendingSpace = true;
|
| visitNestedExpression(node.expression, EXPRESSION,
|
| @@ -490,7 +558,8 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| - visitDefault(Default node) {
|
| + @override
|
| + void visitDefault(Default node) {
|
| outIndentLn("default:");
|
| if (!node.body.statements.isEmpty) {
|
| indentMore();
|
| @@ -499,7 +568,8 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| - visitLabeledStatement(LabeledStatement node) {
|
| + @override
|
| + void visitLabeledStatement(LabeledStatement node) {
|
| Statement body = unwrapBlockIfSingleStatement(node.body);
|
| // `label: break label;`
|
| // Does not work on IE. The statement is a nop, so replace it by an empty
|
| @@ -514,7 +584,7 @@ class Printer implements NodeVisitor {
|
| blockBody(body, needsSeparation: false, needsNewline: true);
|
| }
|
|
|
| - void functionOut(Fun fun, Node name, VarCollector vars) {
|
| + int functionOut(Fun fun, Node name, VarCollector vars) {
|
| out("function");
|
| if (name != null) {
|
| out(" ");
|
| @@ -533,19 +603,27 @@ class Printer implements NodeVisitor {
|
| case const AsyncModifier.sync():
|
| break;
|
| case const AsyncModifier.async():
|
| - out(' async');
|
| + out(' ', isWhitespace: true);
|
| + out('async');
|
| break;
|
| case const AsyncModifier.syncStar():
|
| - out(' sync*');
|
| + out(' ', isWhitespace: true);
|
| + out('sync*');
|
| break;
|
| case const AsyncModifier.asyncStar():
|
| - out(' async*');
|
| + out(' ', isWhitespace: true);
|
| + out('async*');
|
| break;
|
| }
|
| - blockBody(fun.body, needsSeparation: false, needsNewline: false);
|
| + spaceOut();
|
| + int closingPosition =
|
| + blockOut(fun.body, shouldIndent: false, needsNewline: false);
|
| localNamer.leaveScope();
|
| + return closingPosition;
|
| +
|
| }
|
|
|
| + @override
|
| visitFunctionDeclaration(FunctionDeclaration declaration) {
|
| VarCollector vars = new VarCollector();
|
| vars.visitFunctionDeclaration(declaration);
|
| @@ -580,12 +658,14 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| + @override
|
| visitVariableDeclarationList(VariableDeclarationList list) {
|
| out("var ");
|
| visitCommaSeparated(list.declarations, ASSIGNMENT,
|
| newInForInit: inForInit, newAtStatementBegin: false);
|
| }
|
|
|
| + @override
|
| visitAssignment(Assignment assignment) {
|
| visitNestedExpression(assignment.leftHandSide, LEFT_HAND_SIDE,
|
| newInForInit: inForInit,
|
| @@ -602,10 +682,12 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| + @override
|
| visitVariableInitialization(VariableInitialization initialization) {
|
| visitAssignment(initialization);
|
| }
|
|
|
| + @override
|
| visitConditional(Conditional cond) {
|
| visitNestedExpression(cond.condition, LOGICAL_OR,
|
| newInForInit: inForInit,
|
| @@ -623,6 +705,7 @@ class Printer implements NodeVisitor {
|
| newInForInit: inForInit, newAtStatementBegin: false);
|
| }
|
|
|
| + @override
|
| visitNew(New node) {
|
| out("new ");
|
| visitNestedExpression(node.target, CALL,
|
| @@ -633,6 +716,7 @@ class Printer implements NodeVisitor {
|
| out(")");
|
| }
|
|
|
| + @override
|
| visitCall(Call call) {
|
| visitNestedExpression(call.target, LEFT_HAND_SIDE,
|
| newInForInit: inForInit,
|
| @@ -643,7 +727,8 @@ class Printer implements NodeVisitor {
|
| out(")");
|
| }
|
|
|
| - visitBinary(Binary binary) {
|
| + @override
|
| + void visitBinary(Binary binary) {
|
| Expression left = binary.left;
|
| Expression right = binary.right;
|
| String op = binary.op;
|
| @@ -732,9 +817,9 @@ class Printer implements NodeVisitor {
|
| if (op == "in" || op == "instanceof") {
|
| // There are cases where the space is not required but without further
|
| // analysis we cannot know.
|
| - out(" ");
|
| + out(" ", isWhitespace: true);
|
| out(op);
|
| - out(" ");
|
| + out(" ", isWhitespace: true);
|
| } else {
|
| if (leftSpace) spaceOut();
|
| out(op);
|
| @@ -745,7 +830,8 @@ class Printer implements NodeVisitor {
|
| newAtStatementBegin: false);
|
| }
|
|
|
| - visitPrefix(Prefix unary) {
|
| + @override
|
| + void visitPrefix(Prefix unary) {
|
| String op = unary.op;
|
| switch (op) {
|
| case "delete":
|
| @@ -754,16 +840,16 @@ class Printer implements NodeVisitor {
|
| // There are cases where the space is not required but without further
|
| // analysis we cannot know.
|
| out(op);
|
| - out(" ");
|
| + out(" ", isWhitespace: true);
|
| break;
|
| case "+":
|
| case "++":
|
| - if (lastCharCode == charCodes.$PLUS) out(" ");
|
| + if (lastCharCode == charCodes.$PLUS) out(" ", isWhitespace: true);
|
| out(op);
|
| break;
|
| case "-":
|
| case "--":
|
| - if (lastCharCode == charCodes.$MINUS) out(" ");
|
| + if (lastCharCode == charCodes.$MINUS) out(" ", isWhitespace: true);
|
| out(op);
|
| break;
|
| default:
|
| @@ -773,26 +859,31 @@ class Printer implements NodeVisitor {
|
| newInForInit: inForInit, newAtStatementBegin: false);
|
| }
|
|
|
| - visitPostfix(Postfix postfix) {
|
| + @override
|
| + void visitPostfix(Postfix postfix) {
|
| visitNestedExpression(postfix.argument, LEFT_HAND_SIDE,
|
| newInForInit: inForInit,
|
| newAtStatementBegin: atStatementBegin);
|
| out(postfix.op);
|
| }
|
|
|
| - visitVariableUse(VariableUse ref) {
|
| + @override
|
| + void visitVariableUse(VariableUse ref) {
|
| out(localNamer.getName(ref.name));
|
| }
|
|
|
| - visitThis(This node) {
|
| + @override
|
| + void visitThis(This node) {
|
| out("this");
|
| }
|
|
|
| - visitVariableDeclaration(VariableDeclaration decl) {
|
| + @override
|
| + void visitVariableDeclaration(VariableDeclaration decl) {
|
| out(localNamer.getName(decl.name));
|
| }
|
|
|
| - visitParameter(Parameter param) {
|
| + @override
|
| + void visitParameter(Parameter param) {
|
| out(localNamer.getName(param.name));
|
| }
|
|
|
| @@ -821,7 +912,8 @@ class Printer implements NodeVisitor {
|
| return true;
|
| }
|
|
|
| - visitAccess(PropertyAccess access) {
|
| + @override
|
| + void visitAccess(PropertyAccess access) {
|
| visitNestedExpression(access.receiver, CALL,
|
| newInForInit: inForInit,
|
| newAtStatementBegin: atStatementBegin);
|
| @@ -830,7 +922,7 @@ class Printer implements NodeVisitor {
|
| LiteralString selectorString = selector;
|
| String fieldWithQuotes = selectorString.value;
|
| if (isValidJavaScriptId(fieldWithQuotes)) {
|
| - if (access.receiver is LiteralNumber) out(" ");
|
| + if (access.receiver is LiteralNumber) out(" ", isWhitespace: true);
|
| out(".");
|
| out(fieldWithQuotes.substring(1, fieldWithQuotes.length - 1));
|
| return;
|
| @@ -842,39 +934,49 @@ class Printer implements NodeVisitor {
|
| out("]");
|
| }
|
|
|
| - visitNamedFunction(NamedFunction namedFunction) {
|
| + @override
|
| + void visitNamedFunction(NamedFunction namedFunction) {
|
| VarCollector vars = new VarCollector();
|
| vars.visitNamedFunction(namedFunction);
|
| - functionOut(namedFunction.function, namedFunction.name, vars);
|
| + startNode(namedFunction.function);
|
| + currentNode.closingPosition =
|
| + functionOut(namedFunction.function, namedFunction.name, vars);
|
| + endNode(namedFunction.function);
|
| }
|
|
|
| - visitFun(Fun fun) {
|
| + @override
|
| + void visitFun(Fun fun) {
|
| VarCollector vars = new VarCollector();
|
| vars.visitFun(fun);
|
| - functionOut(fun, null, vars);
|
| + currentNode.closingPosition = functionOut(fun, null, vars);
|
| }
|
|
|
| - visitLiteralBool(LiteralBool node) {
|
| + @override
|
| + void visitLiteralBool(LiteralBool node) {
|
| out(node.value ? "true" : "false");
|
| }
|
|
|
| - visitLiteralString(LiteralString node) {
|
| + @override
|
| + void visitLiteralString(LiteralString node) {
|
| out(node.value);
|
| }
|
|
|
| - visitLiteralNumber(LiteralNumber node) {
|
| + @override
|
| + void visitLiteralNumber(LiteralNumber node) {
|
| int charCode = node.value.codeUnitAt(0);
|
| if (charCode == charCodes.$MINUS && lastCharCode == charCodes.$MINUS) {
|
| - out(" ");
|
| + out(" ", isWhitespace: true);
|
| }
|
| out(node.value);
|
| }
|
|
|
| - visitLiteralNull(LiteralNull node) {
|
| + @override
|
| + void visitLiteralNull(LiteralNull node) {
|
| out("null");
|
| }
|
|
|
| - visitArrayInitializer(ArrayInitializer node) {
|
| + @override
|
| + void visitArrayInitializer(ArrayInitializer node) {
|
| out("[");
|
| List<Expression> elements = node.elements;
|
| for (int i = 0; i < elements.length; i++) {
|
| @@ -897,11 +999,13 @@ class Printer implements NodeVisitor {
|
| out("]");
|
| }
|
|
|
| - visitArrayHole(ArrayHole node) {
|
| + @override
|
| + void visitArrayHole(ArrayHole node) {
|
| throw "Unreachable";
|
| }
|
|
|
| - visitObjectInitializer(ObjectInitializer node) {
|
| + @override
|
| + void visitObjectInitializer(ObjectInitializer node) {
|
| // Print all the properties on one line until we see a function-valued
|
| // property. Ideally, we would use a proper pretty-printer to make the
|
| // decision based on layout.
|
| @@ -928,7 +1032,8 @@ class Printer implements NodeVisitor {
|
| out("}");
|
| }
|
|
|
| - visitProperty(Property node) {
|
| + @override
|
| + void visitProperty(Property node) {
|
| if (node.name is LiteralString) {
|
| LiteralString nameString = node.name;
|
| String name = nameString.value;
|
| @@ -948,11 +1053,13 @@ class Printer implements NodeVisitor {
|
| newInForInit: false, newAtStatementBegin: false);
|
| }
|
|
|
| - visitRegExpLiteral(RegExpLiteral node) {
|
| + @override
|
| + void visitRegExpLiteral(RegExpLiteral node) {
|
| out(node.pattern);
|
| }
|
|
|
| - visitLiteralExpression(LiteralExpression node) {
|
| + @override
|
| + void visitLiteralExpression(LiteralExpression node) {
|
| String template = node.template;
|
| List<Expression> inputs = node.inputs;
|
|
|
| @@ -970,34 +1077,42 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| - visitLiteralStatement(LiteralStatement node) {
|
| + @override
|
| + void visitLiteralStatement(LiteralStatement node) {
|
| outLn(node.code);
|
| }
|
|
|
| - visitInterpolatedNode(InterpolatedNode node) {
|
| + void visitInterpolatedNode(InterpolatedNode node) {
|
| out('#${node.nameOrPosition}');
|
| }
|
|
|
| - visitInterpolatedExpression(InterpolatedExpression node) =>
|
| + @override
|
| + void visitInterpolatedExpression(InterpolatedExpression node) =>
|
| visitInterpolatedNode(node);
|
|
|
| - visitInterpolatedLiteral(InterpolatedLiteral node) =>
|
| + @override
|
| + void visitInterpolatedLiteral(InterpolatedLiteral node) =>
|
| visitInterpolatedNode(node);
|
|
|
| - visitInterpolatedParameter(InterpolatedParameter node) =>
|
| + @override
|
| + void visitInterpolatedParameter(InterpolatedParameter node) =>
|
| visitInterpolatedNode(node);
|
|
|
| - visitInterpolatedSelector(InterpolatedSelector node) =>
|
| + @override
|
| + void visitInterpolatedSelector(InterpolatedSelector node) =>
|
| visitInterpolatedNode(node);
|
|
|
| - visitInterpolatedStatement(InterpolatedStatement node) {
|
| + @override
|
| + void visitInterpolatedStatement(InterpolatedStatement node) {
|
| outLn('#${node.nameOrPosition}');
|
| }
|
|
|
| - visitInterpolatedDeclaration(InterpolatedDeclaration node) {
|
| + @override
|
| + void visitInterpolatedDeclaration(InterpolatedDeclaration node) {
|
| visitInterpolatedNode(node);
|
| }
|
|
|
| + @override
|
| void visitComment(Comment node) {
|
| if (shouldCompressOutput) return;
|
| String comment = node.comment.trim();
|
| @@ -1011,6 +1126,7 @@ class Printer implements NodeVisitor {
|
| }
|
| }
|
|
|
| + @override
|
| void visitAwait(Await node) {
|
| out("await ");
|
| visit(node.expression);
|
| @@ -1025,8 +1141,8 @@ class OrderedSet<T> {
|
| OrderedSet() : set = new Set<T>(), list = <T>[];
|
|
|
| void add(T x) {
|
| - if (!set.contains(x)) {
|
| - set.add(x);
|
| + if (set.add(x)) {
|
| + // [Set.add] returns `true` if 'x' was added.
|
| list.add(x);
|
| }
|
| }
|
| @@ -1274,3 +1390,33 @@ class MinifyRenamer implements LocalNamer {
|
| return newName;
|
| }
|
| }
|
| +
|
| +/// Information pertaining the enter and exit callbacks for [node].
|
| +class EnterExitNode {
|
| + final EnterExitNode parent;
|
| + final Node node;
|
| +
|
| + int startPosition;
|
| + int closingPosition;
|
| +
|
| + EnterExitNode(this.parent, this.node);
|
| +
|
| + void addToNode(JavaScriptPrintingContext context, int position) {
|
| + if (startPosition == null) {
|
| + // [position] is the start position of [node].
|
| + if (parent != null) {
|
| + // This might be the start position of the parent as well.
|
| + parent.addToNode(context, position);
|
| + }
|
| + startPosition = position;
|
| + context.enterNode(node, position);
|
| + }
|
| + }
|
| +
|
| + EnterExitNode exitNode(JavaScriptPrintingContext context, int position) {
|
| + // Enter must happen before exit.
|
| + addToNode(context, position);
|
| + context.exitNode(node, startPosition, position, closingPosition);
|
| + return parent;
|
| + }
|
| +}
|
|
|