| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2014, 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 library computer.navigation; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 | |
| 9 import 'package:analysis_server/src/protocol_server.dart' as protocol; | |
| 10 import 'package:analyzer/src/generated/ast.dart'; | |
| 11 import 'package:analyzer/src/generated/element.dart'; | |
| 12 import 'package:analyzer/src/generated/scanner.dart'; | |
| 13 import 'package:analyzer/src/generated/source.dart'; | |
| 14 | |
| 15 /** | |
| 16 * A computer for navigation regions in a Dart [CompilationUnit]. | |
| 17 */ | |
| 18 class DartUnitNavigationComputer { | |
| 19 final List<String> files = <String>[]; | |
| 20 final Map<String, int> fileMap = new HashMap<String, int>(); | |
| 21 final List<protocol.NavigationTarget> targets = <protocol.NavigationTarget>[]; | |
| 22 final Map<Element, int> targetMap = new HashMap<Element, int>(); | |
| 23 final List<protocol.NavigationRegion> regions = <protocol.NavigationRegion>[]; | |
| 24 | |
| 25 /** | |
| 26 * Computes [regions], [targets] and [files]. | |
| 27 */ | |
| 28 void compute(AstNode node) { | |
| 29 node.accept(new _DartUnitNavigationComputerVisitor(this)); | |
| 30 } | |
| 31 | |
| 32 int _addFile(String file) { | |
| 33 int index = fileMap[file]; | |
| 34 if (index == null) { | |
| 35 index = files.length; | |
| 36 files.add(file); | |
| 37 fileMap[file] = index; | |
| 38 } | |
| 39 return index; | |
| 40 } | |
| 41 | |
| 42 void _addRegion(int offset, int length, Element element) { | |
| 43 if (element is FieldFormalParameterElement) { | |
| 44 element = (element as FieldFormalParameterElement).field; | |
| 45 } | |
| 46 if (element == null || element == DynamicElementImpl.instance) { | |
| 47 return; | |
| 48 } | |
| 49 if (element.location == null) { | |
| 50 return; | |
| 51 } | |
| 52 int targetIndex = _addTarget(element); | |
| 53 regions | |
| 54 .add(new protocol.NavigationRegion(offset, length, <int>[targetIndex])); | |
| 55 } | |
| 56 | |
| 57 void _addRegion_nodeStart_nodeEnd(AstNode a, AstNode b, Element element) { | |
| 58 int offset = a.offset; | |
| 59 int length = b.end - offset; | |
| 60 _addRegion(offset, length, element); | |
| 61 } | |
| 62 | |
| 63 void _addRegion_tokenStart_nodeEnd(Token a, AstNode b, Element element) { | |
| 64 int offset = a.offset; | |
| 65 int length = b.end - offset; | |
| 66 _addRegion(offset, length, element); | |
| 67 } | |
| 68 | |
| 69 void _addRegionForNode(AstNode node, Element element) { | |
| 70 int offset = node.offset; | |
| 71 int length = node.length; | |
| 72 _addRegion(offset, length, element); | |
| 73 } | |
| 74 | |
| 75 void _addRegionForToken(Token token, Element element) { | |
| 76 int offset = token.offset; | |
| 77 int length = token.length; | |
| 78 _addRegion(offset, length, element); | |
| 79 } | |
| 80 | |
| 81 int _addTarget(Element element) { | |
| 82 int index = targetMap[element]; | |
| 83 if (index == null) { | |
| 84 index = targets.length; | |
| 85 protocol.NavigationTarget target = | |
| 86 protocol.newNavigationTarget_fromElement(element, _addFile); | |
| 87 targets.add(target); | |
| 88 targetMap[element] = index; | |
| 89 } | |
| 90 return index; | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 class _DartUnitNavigationComputerVisitor extends RecursiveAstVisitor { | |
| 95 final DartUnitNavigationComputer computer; | |
| 96 | |
| 97 _DartUnitNavigationComputerVisitor(this.computer); | |
| 98 | |
| 99 @override | |
| 100 visitAssignmentExpression(AssignmentExpression node) { | |
| 101 _safelyVisit(node.leftHandSide); | |
| 102 computer._addRegionForToken(node.operator, node.bestElement); | |
| 103 _safelyVisit(node.rightHandSide); | |
| 104 } | |
| 105 | |
| 106 @override | |
| 107 visitBinaryExpression(BinaryExpression node) { | |
| 108 _safelyVisit(node.leftOperand); | |
| 109 computer._addRegionForToken(node.operator, node.bestElement); | |
| 110 _safelyVisit(node.rightOperand); | |
| 111 } | |
| 112 | |
| 113 @override | |
| 114 visitCompilationUnit(CompilationUnit unit) { | |
| 115 // prepare top-level nodes sorted by their offsets | |
| 116 List<AstNode> nodes = <AstNode>[]; | |
| 117 nodes.addAll(unit.directives); | |
| 118 nodes.addAll(unit.declarations); | |
| 119 nodes.sort((a, b) { | |
| 120 return a.offset - b.offset; | |
| 121 }); | |
| 122 // visit sorted nodes | |
| 123 for (AstNode node in nodes) { | |
| 124 node.accept(this); | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 @override | |
| 129 visitConstructorDeclaration(ConstructorDeclaration node) { | |
| 130 // associate constructor with "T" or "T.name" | |
| 131 { | |
| 132 AstNode firstNode = node.returnType; | |
| 133 AstNode lastNode = node.name; | |
| 134 if (lastNode == null) { | |
| 135 lastNode = firstNode; | |
| 136 } | |
| 137 if (firstNode != null && lastNode != null) { | |
| 138 computer._addRegion_nodeStart_nodeEnd( | |
| 139 firstNode, lastNode, node.element); | |
| 140 } | |
| 141 } | |
| 142 super.visitConstructorDeclaration(node); | |
| 143 } | |
| 144 | |
| 145 @override | |
| 146 visitConstructorName(ConstructorName node) { | |
| 147 AstNode parent = node.parent; | |
| 148 if (parent is InstanceCreationExpression && | |
| 149 parent.constructorName == node) { | |
| 150 _addConstructorName(parent, node); | |
| 151 } else if (parent is ConstructorDeclaration && | |
| 152 parent.redirectedConstructor == node) { | |
| 153 _addConstructorName(node, node); | |
| 154 } | |
| 155 } | |
| 156 | |
| 157 @override | |
| 158 visitExportDirective(ExportDirective node) { | |
| 159 ExportElement exportElement = node.element; | |
| 160 if (exportElement != null) { | |
| 161 Element libraryElement = exportElement.exportedLibrary; | |
| 162 _addUriDirectiveRegion(node, libraryElement); | |
| 163 } | |
| 164 super.visitExportDirective(node); | |
| 165 } | |
| 166 | |
| 167 @override | |
| 168 visitImportDirective(ImportDirective node) { | |
| 169 ImportElement importElement = node.element; | |
| 170 if (importElement != null) { | |
| 171 Element libraryElement = importElement.importedLibrary; | |
| 172 _addUriDirectiveRegion(node, libraryElement); | |
| 173 } | |
| 174 super.visitImportDirective(node); | |
| 175 } | |
| 176 | |
| 177 @override | |
| 178 visitIndexExpression(IndexExpression node) { | |
| 179 super.visitIndexExpression(node); | |
| 180 computer._addRegionForToken(node.rightBracket, node.bestElement); | |
| 181 } | |
| 182 | |
| 183 @override | |
| 184 visitPartDirective(PartDirective node) { | |
| 185 _addUriDirectiveRegion(node, node.element); | |
| 186 super.visitPartDirective(node); | |
| 187 } | |
| 188 | |
| 189 @override | |
| 190 visitPartOfDirective(PartOfDirective node) { | |
| 191 computer._addRegion_tokenStart_nodeEnd( | |
| 192 node.keyword, node.libraryName, node.element); | |
| 193 super.visitPartOfDirective(node); | |
| 194 } | |
| 195 | |
| 196 @override | |
| 197 visitPostfixExpression(PostfixExpression node) { | |
| 198 super.visitPostfixExpression(node); | |
| 199 computer._addRegionForToken(node.operator, node.bestElement); | |
| 200 } | |
| 201 | |
| 202 @override | |
| 203 visitPrefixExpression(PrefixExpression node) { | |
| 204 computer._addRegionForToken(node.operator, node.bestElement); | |
| 205 super.visitPrefixExpression(node); | |
| 206 } | |
| 207 | |
| 208 @override | |
| 209 visitSimpleIdentifier(SimpleIdentifier node) { | |
| 210 if (node.parent is ConstructorDeclaration) { | |
| 211 return; | |
| 212 } | |
| 213 Element element = node.bestElement; | |
| 214 computer._addRegionForNode(node, element); | |
| 215 } | |
| 216 | |
| 217 @override | |
| 218 visitSuperConstructorInvocation(SuperConstructorInvocation node) { | |
| 219 Element element = node.staticElement; | |
| 220 if (element != null && element.isSynthetic) { | |
| 221 element = element.enclosingElement; | |
| 222 } | |
| 223 // add region | |
| 224 SimpleIdentifier name = node.constructorName; | |
| 225 if (name != null) { | |
| 226 computer._addRegion_nodeStart_nodeEnd(node, name, element); | |
| 227 } else { | |
| 228 computer._addRegionForToken(node.superKeyword, element); | |
| 229 } | |
| 230 // process arguments | |
| 231 _safelyVisit(node.argumentList); | |
| 232 } | |
| 233 | |
| 234 void _addConstructorName(AstNode parent, ConstructorName node) { | |
| 235 Element element = node.staticElement; | |
| 236 if (element == null) { | |
| 237 return; | |
| 238 } | |
| 239 // if a synthetic constructor, navigate to the class | |
| 240 if (element.isSynthetic) { | |
| 241 element = element.enclosingElement; | |
| 242 } | |
| 243 // add regions | |
| 244 TypeName typeName = node.type; | |
| 245 computer._addRegionForNode(typeName.name, element); | |
| 246 // <TypeA, TypeB> | |
| 247 TypeArgumentList typeArguments = typeName.typeArguments; | |
| 248 if (typeArguments != null) { | |
| 249 typeArguments.accept(this); | |
| 250 } | |
| 251 // optional "name" | |
| 252 if (node.name != null) { | |
| 253 computer._addRegionForNode(node.name, element); | |
| 254 } | |
| 255 } | |
| 256 | |
| 257 /** | |
| 258 * If the source of the given [element] (referenced by the [node]) exists, | |
| 259 * then add the navigation region from the [node] to the [element]. | |
| 260 */ | |
| 261 void _addUriDirectiveRegion(UriBasedDirective node, Element element) { | |
| 262 if (element != null) { | |
| 263 Source source = element.source; | |
| 264 if (element.context.exists(source)) { | |
| 265 computer._addRegionForNode(node.uri, element); | |
| 266 } | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 void _safelyVisit(AstNode node) { | |
| 271 if (node != null) { | |
| 272 node.accept(this); | |
| 273 } | |
| 274 } | |
| 275 } | |
| OLD | NEW |