OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, 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 import 'dart:io'; | |
6 | |
7 import 'package:analyzer/analyzer.dart'; | |
8 import 'package:analyzer/dart/ast/token.dart'; | |
9 import 'package:analyzer/src/dart/ast/token.dart'; | |
10 import 'package:analyzer/src/dart/scanner/reader.dart'; | |
11 import 'package:analyzer/src/dart/scanner/scanner.dart'; | |
12 import 'package:analyzer/src/generated/parser.dart' show Parser; | |
13 import 'package:analyzer/src/string_source.dart' show StringSource; | |
14 import 'package:path/path.dart' as p; | |
15 | |
16 final _identifier = new RegExp(r'^([(_|$)a-zA-Z]+([_a-zA-Z0-9])*)$'); | |
17 | |
18 final _lowerCamelCase = | |
19 new RegExp(r'^(_)*[?$a-z][a-z0-9?$]*([A-Z][a-z0-9?$]*)*$'); | |
20 | |
21 final _lowerCaseUnderScore = new RegExp(r'^([a-z]+([_]?[a-z0-9]+)*)+$'); | |
22 | |
23 final _lowerCaseUnderScoreWithDots = | |
24 new RegExp(r'^[a-z][_a-z0-9]*(\.[a-z][_a-z0-9]*)*$'); | |
25 | |
26 final _pubspec = new RegExp(r'^[_]?pubspec\.yaml$'); | |
27 | |
28 final _underscores = new RegExp(r'^[_]+$'); | |
29 | |
30 /// Create a library name prefix based on [libraryPath], [projectRoot] and | |
31 /// current [packageName]. | |
32 String createLibraryNamePrefix( | |
33 {String libraryPath, String projectRoot, String packageName}) { | |
34 // Use the posix context to canonicalize separators (`\`). | |
35 var libraryDirectory = p.posix.dirname(libraryPath); | |
36 var path = p.posix.relative(libraryDirectory, from: projectRoot); | |
37 // Drop 'lib/'. | |
38 var segments = p.split(path); | |
39 if (segments[0] == 'lib') { | |
40 path = p.posix.joinAll(segments.sublist(1)); | |
41 } | |
42 // Replace separators. | |
43 path = path.replaceAll('/', '.'); | |
44 // Add separator if needed. | |
45 if (path.isNotEmpty) { | |
46 path = '.$path'; | |
47 } | |
48 | |
49 return '$packageName$path'; | |
50 } | |
51 | |
52 /// Returns `true` if this [fileName] is a Dart file. | |
53 bool isDartFileName(String fileName) => fileName.endsWith('.dart'); | |
54 | |
55 /// Returns `true` if this [name] is a legal Dart identifier. | |
56 bool isIdentifier(String name) => _identifier.hasMatch(name); | |
57 | |
58 /// Returns `true` of the given [name] is composed only of `_`s. | |
59 bool isJustUnderscores(String name) => _underscores.hasMatch(name); | |
60 | |
61 /// Returns `true` if this [id] is `lowerCamelCase`. | |
62 bool isLowerCamelCase(String id) => | |
63 id.length == 1 && isUpperCase(id.codeUnitAt(0)) || | |
64 id == '_' || | |
65 _lowerCamelCase.hasMatch(id); | |
66 | |
67 /// Returns `true` if this [id] is `lower_camel_case_with_underscores`. | |
68 bool isLowerCaseUnderScore(String id) => _lowerCaseUnderScore.hasMatch(id); | |
69 | |
70 /// Returns `true` if this [id] is `lower_camel_case_with_underscores_or.dots`. | |
71 bool isLowerCaseUnderScoreWithDots(String id) => | |
72 _lowerCaseUnderScoreWithDots.hasMatch(id); | |
73 | |
74 /// Returns `true` if this [fileName] is a Pubspec file. | |
75 bool isPubspecFileName(String fileName) => _pubspec.hasMatch(fileName); | |
76 | |
77 /// Returns `true` if the given code unit [c] is upper case. | |
78 bool isUpperCase(int c) => c >= 0x40 && c <= 0x5A; | |
79 | |
80 class Spelunker { | |
81 final String path; | |
82 final IOSink sink; | |
83 Spelunker(this.path, {IOSink sink}) : this.sink = sink ?? stdout; | |
84 | |
85 void spelunk() { | |
86 var contents = new File(path).readAsStringSync(); | |
87 | |
88 var errorListener = new _ErrorListener(); | |
89 | |
90 var reader = new CharSequenceReader(contents); | |
91 var stringSource = new StringSource(contents, path); | |
92 var scanner = new Scanner(stringSource, reader, errorListener); | |
93 var startToken = scanner.tokenize(); | |
94 | |
95 errorListener.throwIfErrors(); | |
96 | |
97 var parser = new Parser(stringSource, errorListener); | |
98 var node = parser.parseCompilationUnit(startToken); | |
99 | |
100 errorListener.throwIfErrors(); | |
101 | |
102 var visitor = new _SourceVisitor(sink); | |
103 node.accept(visitor); | |
104 } | |
105 } | |
106 | |
107 class _ErrorListener implements AnalysisErrorListener { | |
108 final errors = <AnalysisError>[]; | |
109 | |
110 @override | |
111 void onError(AnalysisError error) { | |
112 errors.add(error); | |
113 } | |
114 | |
115 void throwIfErrors() { | |
116 if (errors.isNotEmpty) { | |
117 throw new Exception(errors); | |
118 } | |
119 } | |
120 } | |
121 | |
122 class _SourceVisitor extends GeneralizingAstVisitor { | |
123 int indent = 0; | |
124 | |
125 final IOSink sink; | |
126 _SourceVisitor(this.sink); | |
127 | |
128 String asString(AstNode node) => | |
129 typeInfo(node.runtimeType) + ' [${node.toString()}]'; | |
130 | |
131 List<CommentToken> getPrecedingComments(Token token) { | |
132 var comments = <CommentToken>[]; | |
133 var comment = token.precedingComments; | |
134 while (comment is CommentToken) { | |
135 comments.add(comment); | |
136 comment = comment.next; | |
137 } | |
138 return comments; | |
139 } | |
140 | |
141 String getTrailingComment(AstNode node) { | |
142 var successor = node.endToken.next; | |
143 if (successor != null) { | |
144 var precedingComments = successor.precedingComments; | |
145 if (precedingComments != null) { | |
146 return precedingComments.toString(); | |
147 } | |
148 } | |
149 return ''; | |
150 } | |
151 | |
152 String typeInfo(Type type) => type.toString(); | |
153 | |
154 @override | |
155 visitNode(AstNode node) { | |
156 write(node); | |
157 | |
158 ++indent; | |
159 node.visitChildren(this); | |
160 --indent; | |
161 return null; | |
162 } | |
163 | |
164 write(AstNode node) { | |
165 //EOL comments | |
166 var comments = getPrecedingComments(node.beginToken); | |
167 comments.forEach((c) => sink.writeln('${" " * indent}$c')); | |
168 | |
169 sink.writeln( | |
170 '${" " * indent}${asString(node)} ${getTrailingComment(node)}'); | |
171 } | |
172 } | |
OLD | NEW |