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; |
+ } |
+} |