| Index: compiler/java/com/google/dart/compiler/parser/DartParserCommentsHelper.java
|
| diff --git a/compiler/java/com/google/dart/compiler/parser/DartParserCommentsHelper.java b/compiler/java/com/google/dart/compiler/parser/DartParserCommentsHelper.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..6633dc506207eb6ef82ebfcdd89955c33013006f
|
| --- /dev/null
|
| +++ b/compiler/java/com/google/dart/compiler/parser/DartParserCommentsHelper.java
|
| @@ -0,0 +1,232 @@
|
| +// Copyright (c) 2012, 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.
|
| +
|
| +package com.google.dart.compiler.parser;
|
| +
|
| +import com.google.common.collect.Lists;
|
| +import com.google.dart.compiler.DartCompilerListener;
|
| +import com.google.dart.compiler.Source;
|
| +import com.google.dart.compiler.ast.ASTVisitor;
|
| +import com.google.dart.compiler.ast.DartComment;
|
| +import com.google.dart.compiler.ast.DartDeclaration;
|
| +import com.google.dart.compiler.ast.DartField;
|
| +import com.google.dart.compiler.ast.DartMethodDefinition;
|
| +import com.google.dart.compiler.ast.DartNode;
|
| +import com.google.dart.compiler.ast.DartUnit;
|
| +import com.google.dart.compiler.common.SourceInfo;
|
| +import com.google.dart.compiler.metrics.CompilerMetrics;
|
| +
|
| +import java.util.ArrayList;
|
| +import java.util.Collections;
|
| +import java.util.Comparator;
|
| +import java.util.List;
|
| +
|
| +/**
|
| + * A parser for Dart that records comment positions.
|
| + */
|
| +public class DartParserCommentsHelper {
|
| +
|
| + static class CommentParserContext extends DartScannerParserContext {
|
| +
|
| + private List<int[]> commentLocs;
|
| +
|
| + CommentParserContext(Source source, String code, DartCompilerListener listener,
|
| + CompilerMetrics metrics) {
|
| + super(source, code, listener, metrics);
|
| + }
|
| +
|
| + List<int[]> getCommentLocs() {
|
| + return commentLocs;
|
| + }
|
| +
|
| + @Override
|
| + protected DartScanner createScanner(String sourceCode) {
|
| + commentLocs = Lists.newArrayList();
|
| + return new CommentScanner(sourceCode);
|
| + }
|
| +
|
| + private class CommentScanner extends DartScanner {
|
| +
|
| + CommentScanner(String sourceCode) {
|
| + super(sourceCode);
|
| + }
|
| +
|
| + @Override
|
| + protected void recordCommentLocation(int start, int stop, int line, int col) {
|
| + int size = commentLocs.size();
|
| + if (size > 0) {
|
| + // the parser may re-scan lookahead tokens
|
| + // fortunately, comments are always scanned as comments
|
| + int[] loc = commentLocs.get(size - 1);
|
| + if (start <= loc[0] && stop <= loc[1]) {
|
| + return;
|
| + }
|
| + }
|
| + commentLocs.add(new int[] {start, stop});
|
| + }
|
| + }
|
| + }
|
| +
|
| + static void addComments(DartUnit unit, Source source, String sourceCode, List<int[]> commentLocs) {
|
| + for (int[] loc : commentLocs) {
|
| + int start = loc[0];
|
| + int length = loc[1] - start;
|
| + DartComment.Style style = getCommentStyle(sourceCode, start);
|
| + unit.getComments().add(new DartComment(source, start, length, style));
|
| + }
|
| + List<DartComment> comments = unit.getComments();
|
| + if (comments != null) {
|
| + assignDartComments(unit, sourceCode, comments);
|
| + }
|
| + }
|
| +
|
| + private static void assignDartComments(DartUnit unit, String sourceCode,
|
| + List<DartComment> comments) {
|
| + // Collect the AST nodes in a list.
|
| + final List<DartNode> astNodes = new ArrayList<DartNode>();
|
| + unit.accept(new ASTVisitor<DartNode>() {
|
| + @Override
|
| + public DartNode visitDeclaration(DartDeclaration<?> node) {
|
| + astNodes.add(node);
|
| + // Avoid NPE in visitors because of missing part.
|
| + try {
|
| + super.visitDeclaration(node);
|
| + } catch (NullPointerException e) {
|
| + }
|
| + // No result.
|
| + return null;
|
| + }
|
| + });
|
| +
|
| + // Collect all the nodes in one list.
|
| + List<DartNode> nodes = new ArrayList<DartNode>();
|
| +
|
| + nodes.addAll(comments);
|
| + nodes.addAll(astNodes);
|
| +
|
| + // Sort the nodes by their position in the source file.
|
| + Collections.sort(nodes, new Comparator<DartNode>() {
|
| + @Override
|
| + public int compare(DartNode node1, DartNode node2) {
|
| + return node1.getSourceInfo().getOffset() - node2.getSourceInfo().getOffset();
|
| + }
|
| + });
|
| +
|
| + // Assign dart docs to their associated DartDeclarations.
|
| + for (int i = 0; i < nodes.size(); i++) {
|
| + DartNode node = nodes.get(i);
|
| + if (node instanceof DartComment) {
|
| + DartComment comment = (DartComment) node;
|
| + // prepare next declaration
|
| + DartDeclaration<?> decl = null;
|
| + {
|
| + int delta = 1;
|
| + while (i + delta < nodes.size()) {
|
| + DartNode next = nodes.get(i + delta);
|
| + // skip all comments
|
| + if (next instanceof DartComment) {
|
| + delta++;
|
| + continue;
|
| + }
|
| + // declaration found
|
| + if (next instanceof DartDeclaration) {
|
| + decl = (DartDeclaration<?>) next;
|
| + if (!commentContainedBySibling(comment, decl)) {
|
| + if (i + 2 < nodes.size()) {
|
| + decl = adjustDartdocTarget(next, nodes.get(i + 2));
|
| + }
|
| + }
|
| + break;
|
| + }
|
| + // something other than declaration
|
| + break;
|
| + }
|
| + }
|
| + // apply comment to declaration
|
| + if (decl != null) {
|
| + String commentStr = sourceCode.substring(comment.getSourceInfo().getOffset(),
|
| + comment.getSourceInfo().getEnd());
|
| + // may be @Metadata
|
| + if (commentStr.contains("@deprecated")) {
|
| + decl.setMetadata(decl.getMetadata().makeDeprecated());
|
| + }
|
| + if (commentStr.contains("@override")) {
|
| + decl.setMetadata(decl.getMetadata().makeOverride());
|
| + }
|
| + // DartDoc
|
| + if (comment.isDartDoc()) {
|
| + decl.setDartDoc(comment);
|
| + }
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + private static DartDeclaration<?> adjustDartdocTarget(DartNode currentNode, DartNode nextNode) {
|
| + if (currentNode instanceof DartField && nextNode instanceof DartMethodDefinition) {
|
| + if (currentNode.getSourceInfo().equals(nextNode.getSourceInfo())) {
|
| + return (DartDeclaration<?>) nextNode;
|
| + }
|
| + }
|
| +
|
| + return (DartDeclaration<?>) currentNode;
|
| + }
|
| +
|
| + /**
|
| + * DartC creates both a DartField and a DartMethodDefinition for getters and setters. They have
|
| + * the same source location; we want to assign the DartDoc to the method definition and not the
|
| + * field.
|
| + */
|
| + private static boolean commentContainedBySibling(DartComment comment, DartDeclaration<?> node) {
|
| + for (DartNode child : getChildren(node.getParent())) {
|
| + if (child != node && !(child instanceof DartComment)) {
|
| + if (isContainedBy(comment, child)) {
|
| + return true;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return false;
|
| + }
|
| +
|
| + private static List<DartNode> getChildren(DartNode parent) {
|
| + final List<DartNode> children = new ArrayList<DartNode>();
|
| +
|
| + parent.visitChildren(new ASTVisitor<DartNode>() {
|
| + @Override
|
| + public DartNode visitNode(DartNode node) {
|
| + children.add(node);
|
| + return null;
|
| + }
|
| + });
|
| +
|
| + return children;
|
| + }
|
| +
|
| + private static boolean isContainedBy(DartNode node, DartNode containedByNode) {
|
| + SourceInfo nodeSource = node.getSourceInfo();
|
| + SourceInfo containedBySource = containedByNode.getSourceInfo();
|
| + int nodeEnd = nodeSource.getOffset() + nodeSource.getLength();
|
| + int containedByEnd = containedBySource.getOffset() + containedBySource.getLength();
|
| + return nodeSource.getOffset() >= containedBySource.getOffset() && nodeEnd <= containedByEnd;
|
| + }
|
| +
|
| + /**
|
| + * Return the style of the comment in the given string.
|
| + *
|
| + * @param sourceString the source containing the comment
|
| + * @param commentStart the location of the comment in the source
|
| + * @return the style of the comment in the given string
|
| + */
|
| + private static DartComment.Style getCommentStyle(String sourceString, int commentStart) {
|
| + boolean hasMore1 = commentStart + 1 < sourceString.length();
|
| + boolean hasMore2 = commentStart + 2 < sourceString.length();
|
| + if (hasMore1 && sourceString.charAt(commentStart + 1) == '/') {
|
| + return DartComment.Style.END_OF_LINE;
|
| + } else if (hasMore2 && sourceString.charAt(commentStart + 2) == '*') {
|
| + return DartComment.Style.DART_DOC;
|
| + }
|
| + return DartComment.Style.BLOCK;
|
| + }
|
| +}
|
|
|