blob: 379cff527096adc9e60efb3f281565df7b29654c [file] [log] [blame]
// Copyright (c) 2017, 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/protocol/protocol_generated.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/src/generated/source.dart';
// TODO(devoncarew): We should look into not creating any labels until there's
// at least 2 levels of nesting.
/**
* A computer for [CompilationUnit] closing labels.
*/
class DartUnitClosingLabelsComputer {
final LineInfo _lineInfo;
final CompilationUnit _unit;
final Map<int, List<_ClosingLabelWithLineCount>> _closingLabelsByEndLine = {};
DartUnitClosingLabelsComputer(this._lineInfo, this._unit);
/**
* Returns a list of closing labels, not `null`.
*/
List<ClosingLabel> compute() {
_unit.accept(new _DartUnitClosingLabelsComputerVisitor(this));
return _closingLabelsByEndLine.values
.where((l) => l.any((cl) => cl.spannedLines >= 2))
.expand((cls) => cls)
.map((clwlc) => clwlc.label)
.toList();
}
}
class _ClosingLabelWithLineCount {
final ClosingLabel label;
final int spannedLines;
_ClosingLabelWithLineCount(this.label, this.spannedLines);
}
/**
* An AST visitor for [DartUnitClosingLabelsComputer].
*/
class _DartUnitClosingLabelsComputerVisitor
extends RecursiveAstVisitor<Object> {
final DartUnitClosingLabelsComputer computer;
int interpolatedStringsEntered = 0;
_DartUnitClosingLabelsComputerVisitor(this.computer);
@override
Object visitInstanceCreationExpression(InstanceCreationExpression node) {
if (node.argumentList != null) {
var label = node.constructorName.type.name.name;
if (node.constructorName.name != null) {
label += ".${node.constructorName.name.name}";
}
// We override the node used for doing line calculations because otherwise
// constructors that split over multiple lines (but have parens on same
// line) would incorrectly get labels, because node.start on an instance
// creation expression starts at the start of the expression.
_addLabel(node, label, checkLinesUsing: node.argumentList);
}
return super.visitInstanceCreationExpression(node);
}
@override
Object visitListLiteral(ListLiteral node) {
final NodeList<TypeAnnotation> args = node.typeArguments?.arguments;
final String typeName = args != null ? args[0]?.toString() : null;
if (typeName != null) {
_addLabel(node, "<$typeName>[]");
}
return super.visitListLiteral(node);
}
@override
Object visitStringInterpolation(StringInterpolation node) {
interpolatedStringsEntered++;
try {
return super.visitStringInterpolation(node);
} finally {
interpolatedStringsEntered--;
}
}
void _addLabel(AstNode node, String label, {AstNode checkLinesUsing}) {
// Never add labels if we're inside strings.
if (interpolatedStringsEntered > 0) {
return;
}
checkLinesUsing = checkLinesUsing ?? node;
final LineInfo_Location start =
computer._lineInfo.getLocation(checkLinesUsing.offset);
final LineInfo_Location end =
computer._lineInfo.getLocation(checkLinesUsing.end - 1);
final ClosingLabel closingLabel =
new ClosingLabel(node.offset, node.length, label);
final _ClosingLabelWithLineCount labelWithSpan =
new _ClosingLabelWithLineCount(
closingLabel, end.lineNumber - start.lineNumber);
computer._closingLabelsByEndLine
.putIfAbsent(end.lineNumber, () => <_ClosingLabelWithLineCount>[])
.add(labelWithSpan);
}
}