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

Side by Side Diff: observatory_pub_packages/analyzer/src/services/writer.dart

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

Powered by Google App Engine
This is Rietveld 408576698