OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 package com.google.dart.compiler.parser; |
| 6 |
| 7 import com.google.common.collect.Lists; |
| 8 import com.google.dart.compiler.DartCompilerListener; |
| 9 import com.google.dart.compiler.Source; |
| 10 import com.google.dart.compiler.ast.ASTVisitor; |
| 11 import com.google.dart.compiler.ast.DartComment; |
| 12 import com.google.dart.compiler.ast.DartDeclaration; |
| 13 import com.google.dart.compiler.ast.DartField; |
| 14 import com.google.dart.compiler.ast.DartMethodDefinition; |
| 15 import com.google.dart.compiler.ast.DartNode; |
| 16 import com.google.dart.compiler.ast.DartUnit; |
| 17 import com.google.dart.compiler.common.SourceInfo; |
| 18 import com.google.dart.compiler.metrics.CompilerMetrics; |
| 19 |
| 20 import java.util.ArrayList; |
| 21 import java.util.Collections; |
| 22 import java.util.Comparator; |
| 23 import java.util.List; |
| 24 |
| 25 /** |
| 26 * A parser for Dart that records comment positions. |
| 27 */ |
| 28 public class DartParserCommentsHelper { |
| 29 |
| 30 static class CommentParserContext extends DartScannerParserContext { |
| 31 |
| 32 private List<int[]> commentLocs; |
| 33 |
| 34 CommentParserContext(Source source, String code, DartCompilerListener listen
er, |
| 35 CompilerMetrics metrics) { |
| 36 super(source, code, listener, metrics); |
| 37 } |
| 38 |
| 39 List<int[]> getCommentLocs() { |
| 40 return commentLocs; |
| 41 } |
| 42 |
| 43 @Override |
| 44 protected DartScanner createScanner(String sourceCode) { |
| 45 commentLocs = Lists.newArrayList(); |
| 46 return new CommentScanner(sourceCode); |
| 47 } |
| 48 |
| 49 private class CommentScanner extends DartScanner { |
| 50 |
| 51 CommentScanner(String sourceCode) { |
| 52 super(sourceCode); |
| 53 } |
| 54 |
| 55 @Override |
| 56 protected void recordCommentLocation(int start, int stop, int line, int co
l) { |
| 57 int size = commentLocs.size(); |
| 58 if (size > 0) { |
| 59 // the parser may re-scan lookahead tokens |
| 60 // fortunately, comments are always scanned as comments |
| 61 int[] loc = commentLocs.get(size - 1); |
| 62 if (start <= loc[0] && stop <= loc[1]) { |
| 63 return; |
| 64 } |
| 65 } |
| 66 commentLocs.add(new int[] {start, stop}); |
| 67 } |
| 68 } |
| 69 } |
| 70 |
| 71 static void addComments(DartUnit unit, Source source, String sourceCode, List<
int[]> commentLocs) { |
| 72 for (int[] loc : commentLocs) { |
| 73 int start = loc[0]; |
| 74 int length = loc[1] - start; |
| 75 DartComment.Style style = getCommentStyle(sourceCode, start); |
| 76 unit.getComments().add(new DartComment(source, start, length, style)); |
| 77 } |
| 78 List<DartComment> comments = unit.getComments(); |
| 79 if (comments != null) { |
| 80 assignDartComments(unit, sourceCode, comments); |
| 81 } |
| 82 } |
| 83 |
| 84 private static void assignDartComments(DartUnit unit, String sourceCode, |
| 85 List<DartComment> comments) { |
| 86 // Collect the AST nodes in a list. |
| 87 final List<DartNode> astNodes = new ArrayList<DartNode>(); |
| 88 unit.accept(new ASTVisitor<DartNode>() { |
| 89 @Override |
| 90 public DartNode visitDeclaration(DartDeclaration<?> node) { |
| 91 astNodes.add(node); |
| 92 // Avoid NPE in visitors because of missing part. |
| 93 try { |
| 94 super.visitDeclaration(node); |
| 95 } catch (NullPointerException e) { |
| 96 } |
| 97 // No result. |
| 98 return null; |
| 99 } |
| 100 }); |
| 101 |
| 102 // Collect all the nodes in one list. |
| 103 List<DartNode> nodes = new ArrayList<DartNode>(); |
| 104 |
| 105 nodes.addAll(comments); |
| 106 nodes.addAll(astNodes); |
| 107 |
| 108 // Sort the nodes by their position in the source file. |
| 109 Collections.sort(nodes, new Comparator<DartNode>() { |
| 110 @Override |
| 111 public int compare(DartNode node1, DartNode node2) { |
| 112 return node1.getSourceInfo().getOffset() - node2.getSourceInfo().getOffs
et(); |
| 113 } |
| 114 }); |
| 115 |
| 116 // Assign dart docs to their associated DartDeclarations. |
| 117 for (int i = 0; i < nodes.size(); i++) { |
| 118 DartNode node = nodes.get(i); |
| 119 if (node instanceof DartComment) { |
| 120 DartComment comment = (DartComment) node; |
| 121 // prepare next declaration |
| 122 DartDeclaration<?> decl = null; |
| 123 { |
| 124 int delta = 1; |
| 125 while (i + delta < nodes.size()) { |
| 126 DartNode next = nodes.get(i + delta); |
| 127 // skip all comments |
| 128 if (next instanceof DartComment) { |
| 129 delta++; |
| 130 continue; |
| 131 } |
| 132 // declaration found |
| 133 if (next instanceof DartDeclaration) { |
| 134 decl = (DartDeclaration<?>) next; |
| 135 if (!commentContainedBySibling(comment, decl)) { |
| 136 if (i + 2 < nodes.size()) { |
| 137 decl = adjustDartdocTarget(next, nodes.get(i + 2)); |
| 138 } |
| 139 } |
| 140 break; |
| 141 } |
| 142 // something other than declaration |
| 143 break; |
| 144 } |
| 145 } |
| 146 // apply comment to declaration |
| 147 if (decl != null) { |
| 148 String commentStr = sourceCode.substring(comment.getSourceInfo().getOf
fset(), |
| 149 comment.getSourceInfo().getEnd()); |
| 150 // may be @Metadata |
| 151 if (commentStr.contains("@deprecated")) { |
| 152 decl.setMetadata(decl.getMetadata().makeDeprecated()); |
| 153 } |
| 154 if (commentStr.contains("@override")) { |
| 155 decl.setMetadata(decl.getMetadata().makeOverride()); |
| 156 } |
| 157 // DartDoc |
| 158 if (comment.isDartDoc()) { |
| 159 decl.setDartDoc(comment); |
| 160 } |
| 161 } |
| 162 } |
| 163 } |
| 164 } |
| 165 |
| 166 private static DartDeclaration<?> adjustDartdocTarget(DartNode currentNode, Da
rtNode nextNode) { |
| 167 if (currentNode instanceof DartField && nextNode instanceof DartMethodDefini
tion) { |
| 168 if (currentNode.getSourceInfo().equals(nextNode.getSourceInfo())) { |
| 169 return (DartDeclaration<?>) nextNode; |
| 170 } |
| 171 } |
| 172 |
| 173 return (DartDeclaration<?>) currentNode; |
| 174 } |
| 175 |
| 176 /** |
| 177 * DartC creates both a DartField and a DartMethodDefinition for getters and s
etters. They have |
| 178 * the same source location; we want to assign the DartDoc to the method defin
ition and not the |
| 179 * field. |
| 180 */ |
| 181 private static boolean commentContainedBySibling(DartComment comment, DartDecl
aration<?> node) { |
| 182 for (DartNode child : getChildren(node.getParent())) { |
| 183 if (child != node && !(child instanceof DartComment)) { |
| 184 if (isContainedBy(comment, child)) { |
| 185 return true; |
| 186 } |
| 187 } |
| 188 } |
| 189 |
| 190 return false; |
| 191 } |
| 192 |
| 193 private static List<DartNode> getChildren(DartNode parent) { |
| 194 final List<DartNode> children = new ArrayList<DartNode>(); |
| 195 |
| 196 parent.visitChildren(new ASTVisitor<DartNode>() { |
| 197 @Override |
| 198 public DartNode visitNode(DartNode node) { |
| 199 children.add(node); |
| 200 return null; |
| 201 } |
| 202 }); |
| 203 |
| 204 return children; |
| 205 } |
| 206 |
| 207 private static boolean isContainedBy(DartNode node, DartNode containedByNode)
{ |
| 208 SourceInfo nodeSource = node.getSourceInfo(); |
| 209 SourceInfo containedBySource = containedByNode.getSourceInfo(); |
| 210 int nodeEnd = nodeSource.getOffset() + nodeSource.getLength(); |
| 211 int containedByEnd = containedBySource.getOffset() + containedBySource.getLe
ngth(); |
| 212 return nodeSource.getOffset() >= containedBySource.getOffset() && nodeEnd <=
containedByEnd; |
| 213 } |
| 214 |
| 215 /** |
| 216 * Return the style of the comment in the given string. |
| 217 * |
| 218 * @param sourceString the source containing the comment |
| 219 * @param commentStart the location of the comment in the source |
| 220 * @return the style of the comment in the given string |
| 221 */ |
| 222 private static DartComment.Style getCommentStyle(String sourceString, int comm
entStart) { |
| 223 boolean hasMore1 = commentStart + 1 < sourceString.length(); |
| 224 boolean hasMore2 = commentStart + 2 < sourceString.length(); |
| 225 if (hasMore1 && sourceString.charAt(commentStart + 1) == '/') { |
| 226 return DartComment.Style.END_OF_LINE; |
| 227 } else if (hasMore2 && sourceString.charAt(commentStart + 2) == '*') { |
| 228 return DartComment.Style.DART_DOC; |
| 229 } |
| 230 return DartComment.Style.BLOCK; |
| 231 } |
| 232 } |
OLD | NEW |