| 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.outline; |
| 6 |
| 7 import 'package:analysis_server/src/constants.dart'; |
| 8 import 'package:analyzer/src/generated/ast.dart'; |
| 9 |
| 10 |
| 11 /** |
| 12 * A computer for [Outline]s in a Dart [CompilationUnit]. |
| 13 */ |
| 14 class DartUnitOutlineComputer { |
| 15 static String _UNITTEST_LIBRARY = "unittest"; |
| 16 |
| 17 final CompilationUnit _unit; |
| 18 |
| 19 List<Map<String, Object>> _regions = <Map<String, Object>>[]; |
| 20 |
| 21 DartUnitOutlineComputer(this._unit); |
| 22 |
| 23 /** |
| 24 * Returns the computed outlines, not `null`. |
| 25 */ |
| 26 Map<String, Object> compute() { |
| 27 _Outline unitOutline = _newUnitOutline(); |
| 28 for (CompilationUnitMember unitMember in _unit.declarations) { |
| 29 if (unitMember is ClassDeclaration) { |
| 30 ClassDeclaration classDeclartion = unitMember; |
| 31 _Outline classOutline = _newClassOutline(unitOutline, classDeclartion); |
| 32 for (ClassMember classMember in classDeclartion.members) { |
| 33 if (classMember is ConstructorDeclaration) { |
| 34 ConstructorDeclaration constructorDeclaration = classMember; |
| 35 _newConstructorOutline(classOutline, constructorDeclaration); |
| 36 } |
| 37 if (classMember is FieldDeclaration) { |
| 38 FieldDeclaration fieldDeclaration = classMember; |
| 39 VariableDeclarationList fields = fieldDeclaration.fields; |
| 40 if (fields != null) { |
| 41 TypeName fieldType = fields.type; |
| 42 String fieldTypeName = fieldType != null ? fieldType.toSource() :
""; |
| 43 for (VariableDeclaration field in fields.variables) { |
| 44 _newVariableOutline(classOutline, fieldTypeName, _OutlineKind.FI
ELD, field, fieldDeclaration.isStatic); |
| 45 } |
| 46 } |
| 47 } |
| 48 if (classMember is MethodDeclaration) { |
| 49 MethodDeclaration methodDeclaration = classMember; |
| 50 _newMethodOutline(classOutline, methodDeclaration); |
| 51 } |
| 52 } |
| 53 } |
| 54 if (unitMember is TopLevelVariableDeclaration) { |
| 55 TopLevelVariableDeclaration fieldDeclaration = unitMember; |
| 56 VariableDeclarationList fields = fieldDeclaration.variables; |
| 57 if (fields != null) { |
| 58 TypeName fieldType = fields.type; |
| 59 String fieldTypeName = fieldType != null ? fieldType.toSource() : ""; |
| 60 for (VariableDeclaration field in fields.variables) { |
| 61 _newVariableOutline(unitOutline, fieldTypeName, _OutlineKind.TOP_LEV
EL_VARIABLE, field, false); |
| 62 } |
| 63 } |
| 64 } |
| 65 if (unitMember is FunctionDeclaration) { |
| 66 FunctionDeclaration functionDeclaration = unitMember; |
| 67 _newFunctionOutline(unitOutline, functionDeclaration); |
| 68 } |
| 69 if (unitMember is ClassTypeAlias) { |
| 70 ClassTypeAlias alias = unitMember; |
| 71 _newClassTypeAlias(unitOutline, alias); |
| 72 } |
| 73 if (unitMember is FunctionTypeAlias) { |
| 74 FunctionTypeAlias alias = unitMember; |
| 75 _newFunctionTypeAliasOutline(unitOutline, alias); |
| 76 } |
| 77 } |
| 78 return unitOutline.toJson(); |
| 79 } |
| 80 |
| 81 void _addLocalFunctionOutlines(_Outline parent, FunctionBody body) { |
| 82 body.accept(new _LocalFunctionOutlinesVisitor(this, parent)); |
| 83 } |
| 84 |
| 85 /** |
| 86 * Returns the [AstNode]'s source region. |
| 87 */ |
| 88 _SourceRegion _getSourceRegion(AstNode node) { |
| 89 int endOffset = node.end; |
| 90 // prepare position of the node among its siblings |
| 91 int firstOffset; |
| 92 List<AstNode> siblings; |
| 93 AstNode parent = node.parent; |
| 94 // field |
| 95 if (parent is VariableDeclarationList) { |
| 96 VariableDeclarationList variableList = parent as VariableDeclarationList; |
| 97 List<VariableDeclaration> variables = variableList.variables; |
| 98 int variableIndex = variables.indexOf(node); |
| 99 if (variableIndex == variables.length - 1) { |
| 100 endOffset = variableList.parent.end; |
| 101 } |
| 102 if (variableIndex == 0) { |
| 103 node = parent.parent; |
| 104 parent = node.parent; |
| 105 } else if (variableIndex >= 1) { |
| 106 firstOffset = variables[variableIndex - 1].end; |
| 107 return new _SourceRegion(firstOffset, endOffset - firstOffset); |
| 108 } |
| 109 } |
| 110 // unit or class member |
| 111 if (parent is CompilationUnit) { |
| 112 firstOffset = 0; |
| 113 siblings = (parent as CompilationUnit).declarations; |
| 114 } else if (parent is ClassDeclaration) { |
| 115 ClassDeclaration classDeclaration = parent as ClassDeclaration; |
| 116 firstOffset = classDeclaration.leftBracket.end; |
| 117 siblings = classDeclaration.members; |
| 118 } else { |
| 119 int offset = node.offset; |
| 120 return new _SourceRegion(offset, endOffset - offset); |
| 121 } |
| 122 // first child: [endOfParent, endOfNode] |
| 123 int index = siblings.indexOf(node); |
| 124 if (index == 0) { |
| 125 return new _SourceRegion(firstOffset, endOffset - firstOffset); |
| 126 } |
| 127 // not first child: [endOfPreviousSibling, endOfNode] |
| 128 int prevSiblingEnd = siblings[index - 1].end; |
| 129 return new _SourceRegion(prevSiblingEnd, endOffset - prevSiblingEnd); |
| 130 } |
| 131 |
| 132 _Outline _newClassOutline(_Outline parent, ClassDeclaration classDeclaration)
{ |
| 133 SimpleIdentifier nameNode = classDeclaration.name; |
| 134 String name = nameNode.name; |
| 135 _SourceRegion sourceRegion = _getSourceRegion(classDeclaration); |
| 136 _Outline outline = new _Outline( |
| 137 _OutlineKind.CLASS, name, |
| 138 nameNode.offset, nameNode.length, |
| 139 sourceRegion.offset, sourceRegion.length, |
| 140 classDeclaration.isAbstract, false, |
| 141 null, null); |
| 142 parent.children.add(outline); |
| 143 return outline; |
| 144 } |
| 145 |
| 146 void _newClassTypeAlias(_Outline parent, ClassTypeAlias alias) { |
| 147 SimpleIdentifier nameNode = alias.name; |
| 148 String name = nameNode.name; |
| 149 _SourceRegion sourceRegion = _getSourceRegion(alias); |
| 150 _Outline outline = new _Outline( |
| 151 _OutlineKind.CLASS_TYPE_ALIAS, name, |
| 152 nameNode.offset, nameNode.length, |
| 153 sourceRegion.offset, sourceRegion.length, |
| 154 alias.isAbstract, false, |
| 155 null, null); |
| 156 parent.children.add(outline); |
| 157 } |
| 158 |
| 159 void _newConstructorOutline(_Outline parent, ConstructorDeclaration constructo
r) { |
| 160 Identifier returnType = constructor.returnType; |
| 161 String name = returnType.name; |
| 162 int offset = returnType.offset; |
| 163 int length = returnType.length; |
| 164 SimpleIdentifier constructorNameNode = constructor.name; |
| 165 if (constructorNameNode != null) { |
| 166 String constructorName = constructorNameNode.name; |
| 167 name += ".${constructorName}"; |
| 168 offset = constructorNameNode.offset; |
| 169 length = constructorNameNode.length; |
| 170 } |
| 171 _SourceRegion sourceRegion = _getSourceRegion(constructor); |
| 172 FormalParameterList parameters = constructor.parameters; |
| 173 String parametersStr = parameters != null ? parameters.toSource() : ""; |
| 174 _Outline outline = new _Outline( |
| 175 _OutlineKind.CONSTRUCTOR, name, |
| 176 offset, length, |
| 177 sourceRegion.offset, sourceRegion.length, |
| 178 false, false, |
| 179 parametersStr, null); |
| 180 parent.children.add(outline); |
| 181 _addLocalFunctionOutlines(outline, constructor.body); |
| 182 } |
| 183 |
| 184 void _newFunctionOutline(_Outline parent, FunctionDeclaration function) { |
| 185 TypeName returnType = function.returnType; |
| 186 SimpleIdentifier nameNode = function.name; |
| 187 String name = nameNode.name; |
| 188 FunctionExpression functionExpression = function.functionExpression; |
| 189 FormalParameterList parameters = functionExpression.parameters; |
| 190 _OutlineKind kind; |
| 191 if (function.isGetter) { |
| 192 kind = _OutlineKind.GETTER; |
| 193 } else if (function.isSetter) { |
| 194 kind = _OutlineKind.SETTER; |
| 195 } else { |
| 196 kind = _OutlineKind.FUNCTION; |
| 197 } |
| 198 _SourceRegion sourceRegion = _getSourceRegion(function); |
| 199 String parametersStr = parameters != null ? parameters.toSource() : ""; |
| 200 String returnTypeStr = returnType != null ? returnType.toSource() : ""; |
| 201 _Outline outline = new _Outline( |
| 202 kind, name, |
| 203 nameNode.offset, nameNode.length, |
| 204 sourceRegion.offset, sourceRegion.length, |
| 205 false, false, |
| 206 parametersStr, returnTypeStr); |
| 207 parent.children.add(outline); |
| 208 _addLocalFunctionOutlines(outline, functionExpression.body); |
| 209 } |
| 210 |
| 211 void _newFunctionTypeAliasOutline(_Outline parent, FunctionTypeAlias alias) { |
| 212 TypeName returnType = alias.returnType; |
| 213 SimpleIdentifier nameNode = alias.name; |
| 214 String name = nameNode.name; |
| 215 _SourceRegion sourceRegion = _getSourceRegion(alias); |
| 216 FormalParameterList parameters = alias.parameters; |
| 217 String parametersStr = parameters != null ? parameters.toSource() : ""; |
| 218 String returnTypeStr = returnType != null ? returnType.toSource() : ""; |
| 219 _Outline outline = new _Outline( |
| 220 _OutlineKind.FUNCTION_TYPE_ALIAS, name, |
| 221 nameNode.offset, nameNode.length, |
| 222 sourceRegion.offset, sourceRegion.length, |
| 223 false, false, |
| 224 parametersStr, returnTypeStr); |
| 225 parent.children.add(outline); |
| 226 } |
| 227 |
| 228 void _newMethodOutline(_Outline parent, MethodDeclaration method) { |
| 229 TypeName returnType = method.returnType; |
| 230 SimpleIdentifier nameNode = method.name; |
| 231 String name = nameNode.name; |
| 232 FormalParameterList parameters = method.parameters; |
| 233 _OutlineKind kind; |
| 234 if (method.isGetter) { |
| 235 kind = _OutlineKind.GETTER; |
| 236 } else if (method.isSetter) { |
| 237 kind = _OutlineKind.SETTER; |
| 238 } else { |
| 239 kind = _OutlineKind.METHOD; |
| 240 } |
| 241 _SourceRegion sourceRegion = _getSourceRegion(method); |
| 242 String parametersStr = parameters != null ? parameters.toSource() : ""; |
| 243 String returnTypeStr = returnType != null ? returnType.toSource() : ""; |
| 244 _Outline outline = new _Outline( |
| 245 kind, name, |
| 246 nameNode.offset, nameNode.length, |
| 247 sourceRegion.offset, sourceRegion.length, |
| 248 method.isAbstract, method.isStatic, |
| 249 parametersStr, returnTypeStr); |
| 250 parent.children.add(outline); |
| 251 _addLocalFunctionOutlines(outline, method.body); |
| 252 } |
| 253 |
| 254 _Outline _newUnitOutline() { |
| 255 return new _Outline( |
| 256 _OutlineKind.COMPILATION_UNIT, null, |
| 257 _unit.offset, _unit.length, |
| 258 _unit.offset, _unit.length, |
| 259 false, false, |
| 260 null, null); |
| 261 } |
| 262 |
| 263 void _newVariableOutline(_Outline parent, String typeName, _OutlineKind kind,
VariableDeclaration variable, bool isStatic) { |
| 264 SimpleIdentifier nameNode = variable.name; |
| 265 String name = nameNode.name; |
| 266 _SourceRegion sourceRegion = _getSourceRegion(variable); |
| 267 _Outline outline = new _Outline( |
| 268 kind, name, |
| 269 nameNode.offset, nameNode.length, |
| 270 sourceRegion.offset, sourceRegion.length, |
| 271 false, isStatic, |
| 272 null, typeName); |
| 273 parent.children.add(outline); |
| 274 } |
| 275 } |
| 276 |
| 277 |
| 278 class _LocalFunctionOutlinesVisitor extends RecursiveAstVisitor { |
| 279 final DartUnitOutlineComputer outlineComputer; |
| 280 |
| 281 _Outline parent; |
| 282 |
| 283 _LocalFunctionOutlinesVisitor(this.outlineComputer, this.parent); |
| 284 |
| 285 @override |
| 286 visitFunctionDeclaration(FunctionDeclaration node) { |
| 287 outlineComputer._newFunctionOutline(parent, node); |
| 288 } |
| 289 } |
| 290 |
| 291 |
| 292 |
| 293 /** |
| 294 * A range of characters. |
| 295 */ |
| 296 class _SourceRegion { |
| 297 final int offset; |
| 298 final int length; |
| 299 _SourceRegion(this.offset, this.length); |
| 300 } |
| 301 |
| 302 |
| 303 /** |
| 304 * Element outline kinds. |
| 305 */ |
| 306 class _OutlineKind { |
| 307 static const _OutlineKind CLASS = const _OutlineKind('CLASS'); |
| 308 static const _OutlineKind CLASS_TYPE_ALIAS = const _OutlineKind('CLASS_TYPE_AL
IAS'); |
| 309 static const _OutlineKind COMPILATION_UNIT = const _OutlineKind('COMPILATION_U
NIT'); |
| 310 static const _OutlineKind CONSTRUCTOR = const _OutlineKind('CONSTRUCTOR'); |
| 311 static const _OutlineKind GETTER = const _OutlineKind('GETTER'); |
| 312 static const _OutlineKind FIELD = const _OutlineKind('FIELD'); |
| 313 static const _OutlineKind FUNCTION = const _OutlineKind('FUNCTION'); |
| 314 static const _OutlineKind FUNCTION_TYPE_ALIAS = const _OutlineKind('FUNCTION_T
YPE_ALIAS'); |
| 315 static const _OutlineKind LIBRARY = const _OutlineKind('LIBRARY'); |
| 316 static const _OutlineKind METHOD = const _OutlineKind('METHOD'); |
| 317 static const _OutlineKind SETTER = const _OutlineKind('SETTER'); |
| 318 static const _OutlineKind TOP_LEVEL_VARIABLE = const _OutlineKind('TOP_LEVEL_V
ARIABLE'); |
| 319 static const _OutlineKind UNKNOWN = const _OutlineKind('UNKNOWN'); |
| 320 static const _OutlineKind UNIT_TEST_CASE = const _OutlineKind('UNIT_TEST_CASE'
); |
| 321 static const _OutlineKind UNIT_TEST_GROUP = const _OutlineKind('UNIT_TEST_GROU
P'); |
| 322 |
| 323 final String name; |
| 324 |
| 325 const _OutlineKind(this.name); |
| 326 } |
| 327 |
| 328 |
| 329 class _Outline { |
| 330 static const List<_Outline> EMPTY_ARRAY = const <_Outline>[]; |
| 331 |
| 332 _Outline parent; |
| 333 final _OutlineKind kind; |
| 334 final String name; |
| 335 final int nameOffset; |
| 336 final int nameLength; |
| 337 final int elementOffset; |
| 338 final int elementLength; |
| 339 final bool isAbstract; |
| 340 final bool isStatic; |
| 341 final String arguments; |
| 342 final String returnType; |
| 343 final List<_Outline> children = <_Outline>[]; |
| 344 |
| 345 _Outline(this.kind, this.name, |
| 346 this.nameOffset, this.nameLength, |
| 347 this.elementOffset, this.elementLength, |
| 348 this.isAbstract, this.isStatic, |
| 349 this.arguments, this.returnType); |
| 350 |
| 351 Map<String, Object> toJson() { |
| 352 return { |
| 353 KIND: kind.name, |
| 354 NAME: name, |
| 355 NAME_OFFSET: nameOffset, |
| 356 NAME_LENGTH: nameLength, |
| 357 ELEMENT_OFFSET: elementOffset, |
| 358 ELEMENT_LENGTH: elementLength, |
| 359 IS_ABSTRACT: isAbstract, |
| 360 IS_STATIC: isStatic, |
| 361 ARGUMENTS: arguments, |
| 362 RETURN_TYPE: returnType, |
| 363 CHILDREN: children.map((child) => child.toJson()).toList(growable: false) |
| 364 }; |
| 365 } |
| 366 } |
| OLD | NEW |