Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(146)

Side by Side Diff: pkg/analyzer_experimental/lib/src/services/formatter_impl.dart

Issue 16562012: Dart formatter babysteps. (Closed) Base URL: http://dart.googlecode.com/svn/branches/bleeding_edge/dart/
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013, 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 formatter_impl;
6
7
8 import 'dart:io';
Bob Nystrom 2013/06/06 22:23:06 Nit, but Nathan and I put organize our imports lik
pquitslund 2013/06/07 19:45:01 Done.
9 import 'package:analyzer_experimental/src/generated/scanner.dart';
10 import 'package:analyzer_experimental/analyzer.dart';
11 import 'package:analyzer_experimental/src/generated/parser.dart';
12 import 'package:analyzer_experimental/src/generated/source.dart';
Bob Nystrom 2013/06/06 22:23:06 These are fine, but you can also use relative impo
pquitslund 2013/06/07 19:45:01 I thought this would lead to woes if I ever wanted
Bob Nystrom 2013/06/10 21:15:46 It's very confusing, unfortunately. The basic rule
13
14
15 /// OS line separator. (TODO: may not be necessary)
messick 2013/06/07 13:51:36 TODO(ldap) everywhere? It probably seems redundant
pquitslund 2013/06/07 19:45:01 Done.
16 const String NEW_LINE = '\n' ; //Platform.pathSeparator;
Bob Nystrom 2013/06/06 22:23:06 Ditch the type annotation.
pquitslund 2013/06/07 19:45:01 Oh my. This is taking some getting used to. :) D
17
18 /// Formatter options.
19 class FormatterOptions {
20
21 /// Create formatter options with defaults derived (where defined) from
22 /// the style guide: <http://www.dartlang.org/articles/style-guide/>.
23 const FormatterOptions({this.initialIndentationLevel:0,
Bob Nystrom 2013/06/06 22:23:06 Space after ":".
pquitslund 2013/06/07 19:45:01 Done. If only I had a formatter to do this for me
24 this.indentPerLevel:2,
25 this.lineSeparator:NEW_LINE,
26 this.pageWidth:80,
27 this.tabSize:2});
28
29 final String lineSeparator;
30 final int initialIndentationLevel;
31 final int indentPerLevel;
32 final int tabSize;
33 final int pageWidth;
34 }
35
36
37 /// Thrown when an error occurs in formatting.
38 class FormatterException implements Exception {
39
40 /// A message describing the error.
41 final String message;
42
43 /// Creates a new FormatterException with an optional error [message].
44 const FormatterException([this.message = '']);
45
46 FormatterException.forError(List<AnalysisError> errors) :
47 // TODO: add descriptive message based on errors
48 message = 'an analysis error occured during format';
49
50 String toString() => 'FormatterException: $message';
51
52 }
53
54 /// Specifies the kind of code snippet to format.
55 class CodeKind {
56
57 final int index;
58
59 const CodeKind(this.index);
60
61 /// A compilation unit snippet.
62 static const COMPILATION_UNIT = const CodeKind(0);
63
64 /// A statement snippet.
65 static const STATEMENT = const CodeKind(1);
66
67 }
68
69 /// Dart source code formatter.
70 abstract class CodeFormatter {
messick 2013/06/07 13:51:36 I'm not clear what your plan is but I wasn't expec
pquitslund 2013/06/07 19:45:01 See response to Bob below.
71
72 factory CodeFormatter([FormatterOptions options = const FormatterOptions()])
73 => new CodeFormatterImpl(options);
74
75 /// Format the specified portion (from [offset] with [length]) of the given
76 /// [source] string, optionally providing an [indentationLevel].
77 String format(CodeKind kind, String source, {int offset, int end,
78 int indentationLevel:0});
Bob Nystrom 2013/06/06 22:23:06 Indent a line continuation +4, not +2.
pquitslund 2013/06/07 19:45:01 Done.
79
80 }
81
82 class CodeFormatterImpl implements CodeFormatter, AnalysisErrorListener {
Bob Nystrom 2013/06/06 22:23:06 Unify this with CodeFormatter. Simpler = better. :
pquitslund 2013/06/07 19:45:01 OK. I *might*. My thinking here is that I really
Brian Wilkerson 2013/06/07 19:55:11 Another alternative would be to not implement Anal
83
84 final FormatterOptions options;
85 final List<AnalysisError> errors;
86
87 CodeFormatterImpl(this.options) : errors = new List<AnalysisError>();
88
89 String format(CodeKind kind, String source, {int offset, int end,
90 int indentationLevel:0}) {
91
92 var start = tokenize(source);
93 _checkForErrors();
94
95 var node = parse(kind, start);
96 _checkForErrors();
97
98 // To be continued...
99
100 return source;
101 }
102
103 ASTNode parse(CodeKind kind, Token start) {
Bob Nystrom 2013/06/06 22:23:06 The style guide would name this "AstNode".
messick 2013/06/07 13:51:36 We should talk to Konstantin to see if we can get
pquitslund 2013/06/07 19:45:01 Aha. Something for Konstantin (or Brian). Thanks
pquitslund 2013/06/07 19:45:01 Good point!
104
105 var parser = new Parser(null, this);
106
107 switch (kind) {
108 case CodeKind.COMPILATION_UNIT:
109 return parser.parseCompilationUnit(start);
110 case CodeKind.STATEMENT:
111 return parser.parseStatement(start);
messick 2013/06/07 13:51:36 At a guess, I'd expect refactoring to want a few m
pquitslund 2013/06/07 19:45:01 Absolutely. Need to crawl before I can walk thoug
112 }
113
114 throw new FormatterException('Unsupported format kind: $kind');
115 }
116
117 _checkForErrors() {
118 if (errors.length > 0) {
119 throw new FormatterException.forError(errors);
120 }
121 }
122
123 void onError(AnalysisError error){
Bob Nystrom 2013/06/06 22:23:06 Space before "{".
pquitslund 2013/06/07 19:45:01 Eagle eye. Fixed!
124 errors.add(error);
125 }
126
127 Token tokenize(String source) {
128 var scanner = new StringScanner(null, source, this);
129 return scanner.tokenize();
Brian Wilkerson 2013/06/06 22:21:58 I noticed that you're testing for new-lines in the
pquitslund 2013/06/07 19:45:01 Cool. Great tip. I'll look into that. Thanks!
130 }
131
132 }
133
134 /// Placeholder class to hold a reference to the Class object representing
135 /// the Dart keyword void.
136 class Void extends Object {
messick 2013/06/07 13:51:36 I hope you'll be able to use Keyword.VOID instead
pquitslund 2013/06/07 19:45:01 The issue is that I really like to use VOID as a t
137
138 }
139
140
141 /// Records a sequence of edits to a source string that will cause the string
142 /// to be formatted when applied.
143 class EditRecorder {
144
145 final FormatterOptions options;
146
147 int column = 0;
148
149 int sourceIndex = 0;
150 String source = '';
151
152 Token currentToken;
153
154 int indentationLevel = 0;
155 int numberOfIndentations = 0;
156
157 bool isIndentNeeded = false;
158
159 EditRecorder(this.options);
160
161 /// Count the number of whitespace chars beginning at the current
162 /// [sourceIndex].
163 int countWhitespace() {
164 var count = 0;
165 for (var i = sourceIndex; i < source.length; ++i) {
166 if (isIndentChar(source[i])) {
167 ++count;
168 } else {
169 break;
170 }
171 }
172 return count;
173 }
174
175 /// Indent.
176 void indent() {
177 indentationLevel += options.indentPerLevel;
messick 2013/06/07 13:51:36 These names are confusing. What's a "level"? inden
pquitslund 2013/06/07 19:45:01 Working on it. Thanks!
178 numberOfIndentations++;
179 }
180
181 /// Test if there is a newline at the given source [index].
182 bool isNewlineAt(int index) {
183 if (index < 0 || index + NEW_LINE.length > source.length) {
184 return false;
185 }
186 for (var i = 0; i < NEW_LINE.length; i++) {
Bob Nystrom 2013/06/06 22:23:06 Do you need to increment index here too?
pquitslund 2013/06/07 19:45:01 Time for some more tests! :)
187 if (source[index] != NEW_LINE[i]) {
188 return false;
189 }
190 }
191 return true;
192 }
193
194 }
195
196 final String SPACE = ' ';
Bob Nystrom 2013/06/06 22:23:06 "final String" -> "const".
pquitslund 2013/06/07 19:45:01 Done.
197
198 bool isIndentChar(String ch) => ch == SPACE; // TODO(pquitslund) also check tab
199
200
201 /// Manages stored [Edit]s.
202 abstract class EditStore {
Brian Wilkerson 2013/06/06 22:21:58 Edits and edit stores sound like a re-usable piece
messick 2013/06/07 13:51:36 I think so, too.
203
204 /// Add an [Edit] that describes a textual [replacement] of a text interval
205 /// starting at the given [offset] spanning the given [length].
206 void addEdit(int offset, int length, String replacement);
207
208 /// Get the index of the current edit (for use in caching location
209 /// information).
210 int getCurrentEditIndex();
211
212 /// Get the underlying sequence of [Edit]s.
213 List<Edit> get edits;
214
215 /// Get the last edit.
216 Edit getLastEdit();
Bob Nystrom 2013/06/06 22:23:06 You'll already have edits.last, so I'd ditch this.
pquitslund 2013/06/07 19:45:01 Right but for some reason I convinced myself that
Bob Nystrom 2013/06/10 21:15:46 They could just do edits.isEmpty to check for that
217
218 /// Add an [Edit] that describes an insertion of text starting at the given
219 /// [offset].
220 void insert(int offset, String insertedString);
221
222 /// Reset cached state.
223 void reset();
224
225 }
226
227 /// Basic un-optimized [EditStore] suitable for subclassing.
228 class BasicEditStore implements EditStore {
Bob Nystrom 2013/06/06 22:23:06 You can unify this with EditStore. If users don't
pquitslund 2013/06/07 19:45:01 Ah yes. Good point! Done.
229
230 final List<Edit> edits = new List<Edit>();
Bob Nystrom 2013/06/06 22:23:06 final edits = <Edit>[];
pquitslund 2013/06/07 19:45:01 Done.
231
232 /// Add the given [Edit] to the end of the edit sequence.
233 void add(Edit edit) {
234 edits.add(edit);
235 }
236
237 void addEdit(int offset, int length, String replacement) {
238 add(new Edit(offset, length, replacement));
239 }
240
241 //TODO(pquitslund): verify that this should be "last" vs. "next"
messick 2013/06/07 13:51:36 I wonder if this comment is in the right place?
pquitslund 2013/06/07 19:45:01 Done.
242 int getCurrentEditIndex() => edits.length - 1;
243
244 Edit getLastEdit() => edits.isEmpty ? null : edits.last;
245
246 void insert(int offset, String insertedString) {
247 addEdit(offset, 0, insertedString);
248 }
249
250 void reset() {
251 edits.clear();
252 }
253
254 String toString() => 'EditStore( ${edits.toString()} )';
255
256 }
Bob Nystrom 2013/06/06 22:23:06 Unindent.
pquitslund 2013/06/07 19:45:01 Nothing escapes you!
257
258
259
260 /// Describes a text edit.
261 class Edit {
262
263 /// The offset at which to apply the edit.
264 final int offset;
265
266 /// The length of the text interval to replace.
267 final int length;
268
269 /// The replacement text.
270 final String replacement;
271
272 /// Create an edit.
273 const Edit(this.offset, this.length, this.replacement);
274
275 /// Create an edit for the given [range].
276 Edit.forRange(SourceRange range, String replacement):
277 this(range.offset, range.length, replacement);
278
279 String toString() => '${offset < 0 ? '(' : 'X('} offset: ${offset} , '
280 'length ${length}, replacement :> ${replacement} <:)';
281
282 }
283
284 /// An AST visitor that drives formatting heuristics.
285 class FormattingEngine extends RecursiveASTVisitor<Void> {
286
287 final FormatterOptions options;
288
289 FormattingEngine(this.options);
290
291 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698