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

Side by Side Diff: pkg/analyzer/lib/src/services/writer.dart

Issue 1001403002: Remove the old formatter from analyzer. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 5 years, 9 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 source_writer;
6
7 import 'dart:math' as math;
8
9 class Line {
10 final List<LineToken> tokens = <LineToken>[];
11 final bool useTabs;
12 final int spacesPerIndent;
13 final int indentLevel;
14 final LinePrinter printer;
15
16 Line({this.indentLevel: 0, this.useTabs: false, this.spacesPerIndent: 2,
17 this.printer: const SimpleLinePrinter()}) {
18 if (indentLevel > 0) {
19 indent(indentLevel);
20 }
21 }
22
23 void addSpace() {
24 addSpaces(1);
25 }
26
27 void addSpaces(int n, {breakWeight: DEFAULT_SPACE_WEIGHT}) {
28 tokens.add(new SpaceToken(n, breakWeight: breakWeight));
29 }
30
31 void addToken(LineToken token) {
32 tokens.add(token);
33 }
34
35 void clear() {
36 tokens.clear();
37 }
38
39 bool isEmpty() => tokens.isEmpty;
40
41 bool isWhitespace() =>
42 tokens.every((tok) => tok is SpaceToken || tok is TabToken);
43
44 void indent(int n) {
45 tokens.insert(
46 0, useTabs ? new TabToken(n) : new SpaceToken(n * spacesPerIndent));
47 }
48
49 String toString() => printer.printLine(this);
50 }
51
52 /// Base class for line printers
53 abstract class LinePrinter {
54 const LinePrinter();
55
56 /// Convert this [line] to a [String] representation.
57 String printLine(Line line);
58 }
59
60 typedef String Indenter(int n);
61
62 /// A simple line breaking [LinePrinter]
63 class SimpleLineBreaker extends LinePrinter {
64 static final NO_OP_INDENTER = (n) => '';
65
66 final chunks = <Chunk>[];
67 final int maxLength;
68 Indenter indenter;
69
70 SimpleLineBreaker(this.maxLength, [this.indenter]) {
71 if (indenter == null) {
72 indenter = NO_OP_INDENTER;
73 }
74 }
75
76 String printLine(Line line) {
77 var buf = new StringBuffer();
78 var chunks = breakLine(line);
79 for (var i = 0; i < chunks.length; ++i) {
80 var chunk = chunks[i];
81 if (i > 0) {
82 buf.write(indent(chunk, chunk.indent));
83 } else {
84 buf.write(chunk);
85 }
86 }
87 return buf.toString();
88 }
89
90 String indent(Chunk chunk, int level) {
91 return '\n' + indenter(level) + chunk.toString();
92 }
93
94 List<Chunk> breakLine(Line line) {
95 List<LineToken> tokens = preprocess(line.tokens);
96 List<Chunk> chunks = <Chunk>[
97 new Chunk(line.indentLevel, maxLength, tokens)
98 ];
99 // try SINGLE_SPACE_WEIGHT
100 {
101 Chunk chunk = chunks[0];
102 if (chunk.length > maxLength) {
103 for (int i = 0; i < tokens.length; i++) {
104 LineToken token = tokens[i];
105 if (token is SpaceToken && token.breakWeight == SINGLE_SPACE_WEIGHT) {
106 var beforeChunk = chunk.subChunk(chunk.indent, 0, i);
107 var restChunk = chunk.subChunk(chunk.indent + 2, i + 1);
108 // check if 'init' in 'var v = init;' fits a line
109 if (restChunk.length < maxLength) {
110 return [beforeChunk, restChunk];
111 }
112 // check if 'var v = method(' in 'var v = method(args)' does not fit
113 int weight = chunk.findMinSpaceWeight();
114 if (chunk.getLengthToSpaceWithWeight(weight) > maxLength) {
115 chunks = [beforeChunk, restChunk];
116 }
117 // done anyway
118 break;
119 }
120 }
121 }
122 }
123 // other spaces
124 while (true) {
125 List<Chunk> newChunks = <Chunk>[];
126 bool hasChanges = false;
127 for (Chunk chunk in chunks) {
128 tokens = chunk.tokens;
129 if (chunk.length > maxLength) {
130 if (chunk.hasAnySpace()) {
131 int weight = chunk.findMinSpaceWeight();
132 int newIndent = chunk.indent;
133 if (weight == DEFAULT_SPACE_WEIGHT) {
134 int start = 0;
135 int length = 0;
136 for (int i = 0; i < tokens.length; i++) {
137 LineToken token = tokens[i];
138 if (token is SpaceToken &&
139 token.breakWeight == weight &&
140 i < tokens.length - 1) {
141 LineToken nextToken = tokens[i + 1];
142 if (length + token.length + nextToken.length > maxLength) {
143 newChunks.add(chunk.subChunk(newIndent, start, i));
144 newIndent = chunk.indent + 2;
145 start = i + 1;
146 length = 0;
147 continue;
148 }
149 }
150 length += token.length;
151 }
152 if (start < tokens.length) {
153 newChunks.add(chunk.subChunk(newIndent, start));
154 }
155 } else {
156 int start = 0;
157 for (int i = 0; i < tokens.length; i++) {
158 LineToken token = tokens[i];
159 if (token is SpaceToken && token.breakWeight == weight) {
160 newChunks.add(chunk.subChunk(newIndent, start, i));
161 newIndent = chunk.indent + 2;
162 start = i + 1;
163 }
164 }
165 if (start < tokens.length) {
166 newChunks.add(chunk.subChunk(newIndent, start));
167 }
168 }
169 } else {
170 newChunks.add(chunk);
171 }
172 } else {
173 newChunks.add(chunk);
174 }
175 if (newChunks.length > chunks.length) {
176 hasChanges = true;
177 }
178 }
179 if (!hasChanges) {
180 break;
181 }
182 chunks = newChunks;
183 }
184 return chunks;
185 }
186
187 static List<LineToken> preprocess(List<LineToken> tok) {
188 var tokens = <LineToken>[];
189 var curr;
190
191 tok.forEach((token) {
192 if (token is! SpaceToken) {
193 if (curr == null) {
194 curr = token;
195 } else {
196 curr = merge(curr, token);
197 }
198 } else {
199 if (isNonbreaking(token)) {
200 curr = merge(curr, token);
201 } else {
202 if (curr != null) {
203 tokens.add(curr);
204 curr = null;
205 }
206 tokens.add(token);
207 }
208 }
209 });
210
211 if (curr != null) {
212 tokens.add(curr);
213 }
214
215 return tokens;
216 }
217
218 static bool isNonbreaking(SpaceToken token) =>
219 token.breakWeight == UNBREAKABLE_SPACE_WEIGHT;
220
221 static LineToken merge(LineToken first, LineToken second) =>
222 new LineToken(first.value + second.value);
223 }
224
225 /// Test if this [string] contains only whitespace characters
226 bool isWhitespace(String string) => string.codeUnits
227 .every((c) => c == 0x09 || c == 0x20 || c == 0x0A || c == 0x0D);
228
229 /// Special token indicating a line start
230 final LINE_START = new SpaceToken(0);
231
232 const DEFAULT_SPACE_WEIGHT = UNBREAKABLE_SPACE_WEIGHT - 1;
233 /// The weight of a space after '=' in variable declaration or assignment
234 const SINGLE_SPACE_WEIGHT = UNBREAKABLE_SPACE_WEIGHT - 2;
235 const UNBREAKABLE_SPACE_WEIGHT = 100000000;
236
237 /// Simple non-breaking printer
238 class SimpleLinePrinter extends LinePrinter {
239 const SimpleLinePrinter();
240
241 String printLine(Line line) {
242 var buffer = new StringBuffer();
243 line.tokens.forEach((tok) => buffer.write(tok.toString()));
244 return buffer.toString();
245 }
246 }
247
248 /// Describes a piece of text in a [Line].
249 abstract class LineText {
250 int get length;
251 }
252
253 /// A working piece of text used in calculating line breaks
254 class Chunk {
255 final int indent;
256 final int maxLength;
257 final List<LineToken> tokens = <LineToken>[];
258
259 Chunk(this.indent, this.maxLength, [List<LineToken> tokens]) {
260 this.tokens.addAll(tokens);
261 }
262
263 int get length {
264 return tokens.fold(0, (len, token) => len + token.length);
265 }
266
267 int getLengthToSpaceWithWeight(int weight) {
268 int length = 0;
269 for (LineToken token in tokens) {
270 if (token is SpaceToken && token.breakWeight == weight) {
271 break;
272 }
273 length += token.length;
274 }
275 return length;
276 }
277
278 bool fits(LineToken a, LineToken b) {
279 return length + a.length + a.length <= maxLength;
280 }
281
282 void add(LineToken token) {
283 tokens.add(token);
284 }
285
286 bool hasInitializerSpace() {
287 return tokens.any((token) {
288 return token is SpaceToken && token.breakWeight == SINGLE_SPACE_WEIGHT;
289 });
290 }
291
292 bool hasAnySpace() {
293 return tokens.any((token) => token is SpaceToken);
294 }
295
296 int findMinSpaceWeight() {
297 int minWeight = UNBREAKABLE_SPACE_WEIGHT;
298 for (var token in tokens) {
299 if (token is SpaceToken) {
300 minWeight = math.min(minWeight, token.breakWeight);
301 }
302 }
303 return minWeight;
304 }
305
306 Chunk subChunk(int indentLevel, int start, [int end]) {
307 List<LineToken> subTokens = tokens.sublist(start, end);
308 return new Chunk(indentLevel, maxLength, subTokens);
309 }
310
311 String toString() => tokens.join();
312 }
313
314 class LineToken implements LineText {
315 final String value;
316
317 LineToken(this.value);
318
319 String toString() => value;
320
321 int get length => lengthLessNewlines(value);
322
323 int lengthLessNewlines(String str) =>
324 str.endsWith('\n') ? str.length - 1 : str.length;
325 }
326
327 class SpaceToken extends LineToken {
328 final int breakWeight;
329
330 SpaceToken(int n, {this.breakWeight: DEFAULT_SPACE_WEIGHT})
331 : super(getSpaces(n));
332 }
333
334 class TabToken extends LineToken {
335 TabToken(int n) : super(getTabs(n));
336 }
337
338 class NewlineToken extends LineToken {
339 NewlineToken(String value) : super(value);
340 }
341
342 class SourceWriter {
343 final StringBuffer buffer = new StringBuffer();
344 Line currentLine;
345
346 final String lineSeparator;
347 int indentCount = 0;
348 final int spacesPerIndent;
349 final bool useTabs;
350
351 LinePrinter linePrinter;
352 LineToken _lastToken;
353
354 SourceWriter({this.indentCount: 0, this.lineSeparator: NEW_LINE,
355 this.useTabs: false, this.spacesPerIndent: 2, int maxLineLength: 80}) {
356 if (maxLineLength > 0) {
357 linePrinter = new SimpleLineBreaker(maxLineLength, (n) => getIndentString(
358 n, useTabs: useTabs, spacesPerIndent: spacesPerIndent));
359 } else {
360 linePrinter = new SimpleLinePrinter();
361 }
362 currentLine = newLine();
363 }
364
365 LineToken get lastToken => _lastToken;
366
367 _addToken(LineToken token) {
368 _lastToken = token;
369 currentLine.addToken(token);
370 }
371
372 void indent() {
373 ++indentCount;
374 // Rather than fiddle with deletions/insertions just start fresh
375 if (currentLine.isWhitespace()) {
376 currentLine = newLine();
377 }
378 }
379
380 void newline() {
381 if (currentLine.isWhitespace()) {
382 currentLine.tokens.clear();
383 }
384 _addToken(new NewlineToken(this.lineSeparator));
385 buffer.write(currentLine.toString());
386 currentLine = newLine();
387 }
388
389 void newlines(int num) {
390 while (num-- > 0) {
391 newline();
392 }
393 }
394
395 void write(String string) {
396 var lines = string.split(lineSeparator);
397 var length = lines.length;
398 for (int i = 0; i < length; i++) {
399 var line = lines[i];
400 _addToken(new LineToken(line));
401 if (i != length - 1) {
402 newline();
403 // no indentation for multi-line strings
404 currentLine.clear();
405 }
406 }
407 }
408
409 void writeln(String s) {
410 write(s);
411 newline();
412 }
413
414 void space() {
415 spaces(1);
416 }
417
418 void spaces(n, {breakWeight: DEFAULT_SPACE_WEIGHT}) {
419 currentLine.addSpaces(n, breakWeight: breakWeight);
420 }
421
422 void unindent() {
423 --indentCount;
424 // Rather than fiddle with deletions/insertions just start fresh
425 if (currentLine.isWhitespace()) {
426 currentLine = newLine();
427 }
428 }
429
430 Line newLine() => new Line(
431 indentLevel: indentCount,
432 useTabs: useTabs,
433 spacesPerIndent: spacesPerIndent,
434 printer: linePrinter);
435
436 String toString() {
437 var source = new StringBuffer(buffer.toString());
438 if (!currentLine.isWhitespace()) {
439 source.write(currentLine);
440 }
441 return source.toString();
442 }
443 }
444
445 const NEW_LINE = '\n';
446 const SPACE = ' ';
447 const SPACES = const [
448 '',
449 ' ',
450 ' ',
451 ' ',
452 ' ',
453 ' ',
454 ' ',
455 ' ',
456 ' ',
457 ' ',
458 ' ',
459 ' ',
460 ' ',
461 ' ',
462 ' ',
463 ' ',
464 ' ',
465 ];
466 const TABS = const [
467 '',
468 '\t',
469 '\t\t',
470 '\t\t\t',
471 '\t\t\t\t',
472 '\t\t\t\t\t',
473 '\t\t\t\t\t\t',
474 '\t\t\t\t\t\t\t',
475 '\t\t\t\t\t\t\t\t',
476 '\t\t\t\t\t\t\t\t\t',
477 '\t\t\t\t\t\t\t\t\t\t',
478 '\t\t\t\t\t\t\t\t\t\t\t',
479 '\t\t\t\t\t\t\t\t\t\t\t\t',
480 '\t\t\t\t\t\t\t\t\t\t\t\t\t',
481 '\t\t\t\t\t\t\t\t\t\t\t\t\t\t',
482 ];
483
484 String getIndentString(int indentWidth,
485 {bool useTabs: false, int spacesPerIndent: 2}) =>
486 useTabs ? getTabs(indentWidth) : getSpaces(indentWidth * spacesPerIndent);
487
488 String getSpaces(int n) => n < SPACES.length ? SPACES[n] : repeat(' ', n);
489
490 String getTabs(int n) => n < TABS.length ? TABS[n] : repeat('\t', n);
491
492 String repeat(String ch, int times) {
493 var sb = new StringBuffer();
494 for (var i = 0; i < times; ++i) {
495 sb.write(ch);
496 }
497 return sb.toString();
498 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698