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

Side by Side Diff: dart_style/lib/src/chunk.dart

Issue 1400473008: Roll Observatory packages and add a roll script (Closed) Base URL: git@github.com:dart-lang/observatory_pub_packages.git@master
Patch Set: Created 5 years, 2 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
« no previous file with comments | « dart_style/lib/src/call_chain_visitor.dart ('k') | dart_style/lib/src/chunk_builder.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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 dart_style.src.chunk;
6
7 import 'fast_hash.dart';
8 import 'nesting_level.dart';
9 import 'rule/rule.dart';
10
11 /// Tracks where a selection start or end point may appear in some piece of
12 /// text.
13 abstract class Selection {
14 /// The chunk of text.
15 String get text;
16
17 /// The offset from the beginning of [text] where the selection starts, or
18 /// `null` if the selection does not start within this chunk.
19 int get selectionStart => _selectionStart;
20 int _selectionStart;
21
22 /// The offset from the beginning of [text] where the selection ends, or
23 /// `null` if the selection does not start within this chunk.
24 int get selectionEnd => _selectionEnd;
25 int _selectionEnd;
26
27 /// Sets [selectionStart] to be [start] characters into [text].
28 void startSelection(int start) {
29 _selectionStart = start;
30 }
31
32 /// Sets [selectionStart] to be [fromEnd] characters from the end of [text].
33 void startSelectionFromEnd(int fromEnd) {
34 _selectionStart = text.length - fromEnd;
35 }
36
37 /// Sets [selectionEnd] to be [end] characters into [text].
38 void endSelection(int end) {
39 _selectionEnd = end;
40 }
41
42 /// Sets [selectionEnd] to be [fromEnd] characters from the end of [text].
43 void endSelectionFromEnd(int fromEnd) {
44 _selectionEnd = text.length - fromEnd;
45 }
46 }
47
48 /// A chunk of non-breaking output text terminated by a hard or soft newline.
49 ///
50 /// Chunks are created by [LineWriter] and fed into [LineSplitter]. Each
51 /// contains some text, along with the data needed to tell how the next line
52 /// should be formatted and how desireable it is to split after the chunk.
53 ///
54 /// Line splitting after chunks comes in a few different forms.
55 ///
56 /// * A "hard" split is a mandatory newline. The formatted output will contain
57 /// at least one newline after the chunk's text.
58 /// * A "soft" split is a discretionary newline. If a line doesn't fit within
59 /// the page width, one or more soft splits may be turned into newlines to
60 /// wrap the line to fit within the bounds. If a soft split is not turned
61 /// into a newline, it may instead appear as a space or zero-length string
62 /// in the output, depending on [spaceWhenUnsplit].
63 /// * A "double" split expands to two newlines. In other words, it leaves a
64 /// blank line in the output. Hard or soft splits may be doubled. This is
65 /// determined by [isDouble].
66 ///
67 /// A split controls the leading spacing of the subsequent line, both
68 /// block-based [indent] and expression-wrapping-based [nesting].
69 class Chunk extends Selection {
70 /// The literal text output for the chunk.
71 String get text => _text;
72 String _text;
73
74 /// The number of characters of indentation from the left edge of the block
75 /// that contains this chunk.
76 ///
77 /// For top level chunks that are not inside any block, this also includes
78 /// leading indentation.
79 int get indent => _indent;
80 int _indent;
81
82 /// The expression nesting level following this chunk.
83 ///
84 /// This is used to determine how much to increase the indentation when a
85 /// line starts after this chunk. A single statement may be indented multiple
86 /// times if the splits occur in more deeply nested expressions, for example:
87 ///
88 /// // 40 columns |
89 /// someFunctionName(argument, argument,
90 /// argument, anotherFunction(argument,
91 /// argument));
92 NestingLevel get nesting => _nesting;
93 NestingLevel _nesting;
94
95 /// If this chunk marks the beginning of a block, these are the chunks
96 /// contained in the block.
97 final blockChunks = <Chunk>[];
98
99 /// Whether it's valid to add more text to this chunk or not.
100 ///
101 /// Chunks are built up by adding text and then "capped off" by having their
102 /// split information set by calling [handleSplit]. Once the latter has been
103 /// called, no more text should be added to the chunk since it would appear
104 /// *before* the split.
105 bool get canAddText => _rule == null;
106
107 /// The [Rule] that controls when a split should occur after this chunk.
108 ///
109 /// Multiple splits may share a [Rule].
110 Rule get rule => _rule;
111 Rule _rule;
112
113 /// Whether this chunk is always followed by a newline or whether the line
114 /// splitter may choose to keep the next chunk on the same line.
115 bool get isHardSplit => _rule is HardSplitRule;
116
117 /// Whether or not an extra blank line should be output after this chunk if
118 /// it's split.
119 ///
120 /// Internally, this can be either `true`, `false`, or `null`. The latter is
121 /// an indeterminate state that lets later modifications to the split decide
122 /// whether it should be double or not.
123 ///
124 /// However, this getter does not expose that. It will return `false` if the
125 /// chunk is still indeterminate.
126 bool get isDouble => _isDouble != null ? _isDouble : false;
127 bool _isDouble;
128
129 /// If `true`, then the line after this chunk should always be at column
130 /// zero regardless of any indentation or expression nesting.
131 ///
132 /// Used for multi-line strings and commented out code.
133 bool get flushLeft => _flushLeft;
134 bool _flushLeft = false;
135
136 /// If `true`, then the line after this chunk and its contained block should
137 /// be flush left.
138 bool get flushLeftAfter {
139 if (blockChunks.isEmpty) return _flushLeft;
140
141 return blockChunks.last.flushLeftAfter;
142 }
143
144 /// Whether this chunk should append an extra space if it does not split.
145 ///
146 /// This is `true`, for example, in a chunk that ends with a ",".
147 bool get spaceWhenUnsplit => _spaceWhenUnsplit;
148 bool _spaceWhenUnsplit = false;
149
150 /// Whether this chunk marks the end of a range of chunks that can be line
151 /// split independently of the following chunks.
152 bool get canDivide {
153 // Have to call markDivide() before accessing this.
154 assert(_canDivide != null);
155 return _canDivide;
156 }
157
158 bool _canDivide;
159
160 /// The number of characters in this chunk when unsplit.
161 int get length => _text.length + (spaceWhenUnsplit ? 1 : 0);
162
163 /// The unsplit length of all of this chunk's block contents.
164 ///
165 /// Does not include this chunk's own length, just the length of its child
166 /// block chunks (recursively).
167 int get unsplitBlockLength {
168 var length = 0;
169 for (var chunk in blockChunks) {
170 length += chunk.length + chunk.unsplitBlockLength;
171 }
172
173 return length;
174 }
175
176 /// The [Span]s that contain this chunk.
177 final spans = <Span>[];
178
179 /// Creates a new chunk starting with [_text].
180 Chunk(this._text);
181
182 /// Discard the split for the chunk and put it back into the state where more
183 /// text can be appended.
184 void allowText() {
185 _rule = null;
186 }
187
188 /// Append [text] to the end of the split's text.
189 void appendText(String text) {
190 assert(canAddText);
191 _text += text;
192 }
193
194 /// Forces this soft split to become a hard split.
195 ///
196 /// This is called on the soft splits owned by a rule that decides to harden
197 /// when it finds out another hard split occurs within its chunks.
198 void harden() {
199 _rule = new HardSplitRule();
200 spans.clear();
201 }
202
203 /// Finishes off this chunk with the given [rule] and split information.
204 ///
205 /// This may be called multiple times on the same split since the splits
206 /// produced by walking the source and the splits coming from comments and
207 /// preserved whitespace often overlap. When that happens, this has logic to
208 /// combine that information into a single split.
209 void applySplit(Rule rule, int indent, NestingLevel nesting,
210 {bool flushLeft, bool spaceWhenUnsplit, bool isDouble}) {
211 if (flushLeft == null) flushLeft = false;
212 if (spaceWhenUnsplit == null) spaceWhenUnsplit = false;
213 if (isHardSplit || rule is HardSplitRule) {
214 // A hard split always wins.
215 _rule = rule;
216 } else if (_rule == null) {
217 // If the chunk hasn't been initialized yet, just inherit the rule.
218 _rule = rule;
219 }
220
221 // Last split settings win.
222 _flushLeft = flushLeft;
223 _nesting = nesting;
224 _indent = indent;
225
226 _spaceWhenUnsplit = spaceWhenUnsplit;
227
228 // Pin down the double state, if given and we haven't already.
229 if (_isDouble == null) _isDouble = isDouble;
230 }
231
232 // Mark whether this chunk can divide the range of chunks.
233 void markDivide(canDivide) {
234 // Should only do this once.
235 assert(_canDivide == null);
236
237 _canDivide = canDivide;
238 }
239
240 String toString() {
241 var parts = [];
242
243 if (text.isNotEmpty) parts.add(text);
244
245 if (_indent != null) parts.add("indent:$_indent");
246 if (spaceWhenUnsplit) parts.add("space");
247 if (_isDouble) parts.add("double");
248 if (_flushLeft) parts.add("flush");
249
250 if (_rule == null) {
251 parts.add("(no split)");
252 } else if (isHardSplit) {
253 parts.add("hard");
254 } else {
255 parts.add(rule.toString());
256
257 if (_rule.outerRules.isNotEmpty) {
258 parts.add("-> ${_rule.outerRules.join(' ')}");
259 }
260 }
261
262 return parts.join(" ");
263 }
264 }
265
266 /// Constants for the cost heuristics used to determine which set of splits is
267 /// most desirable.
268 class Cost {
269 /// The cost of splitting after the `=>` in a lambda or arrow-bodied member.
270 ///
271 /// We make this zero because there is already a span around the entire body
272 /// and we generally do prefer splitting after the `=>` over other places.
273 static const arrow = 0;
274
275 /// The default cost.
276 ///
277 /// This isn't zero because we want to ensure all splitting has *some* cost,
278 /// otherwise, the formatter won't try to keep things on one line at all.
279 /// Most splits and spans use this. Greater costs tend to come from a greater
280 /// number of nested spans.
281 static const normal = 1;
282
283 /// Splitting after a "=" both for assignment and initialization.
284 static const assignment = 2;
285
286 /// Splitting before the first argument when it happens to be a function
287 /// expression with a block body.
288 static const firstBlockArgument = 2;
289
290 /// The series of positional arguments.
291 static const positionalArguments = 2;
292
293 /// Splitting inside the brackets of a list with only one element.
294 static const singleElementList = 2;
295
296 /// Splitting the internals of collection literal arguments.
297 ///
298 /// Used to prefer splitting at the argument boundary over splitting the
299 /// collection contents.
300 static const splitCollections = 2;
301
302 /// Splitting before a type argument or type parameter.
303 static const typeArgument = 4;
304 }
305
306 /// The in-progress state for a [Span] that has been started but has not yet
307 /// been completed.
308 class OpenSpan {
309 /// Index of the first chunk contained in this span.
310 int get start => _start;
311 int _start;
312
313 /// The cost applied when the span is split across multiple lines or `null`
314 /// if the span is for a multisplit.
315 final int cost;
316
317 OpenSpan(this._start, this.cost);
318
319 String toString() => "OpenSpan($start, \$$cost)";
320 }
321
322 /// Delimits a range of chunks that must end up on the same line to avoid an
323 /// additional cost.
324 ///
325 /// These are used to encourage the line splitter to try to keep things
326 /// together, like parameter lists and binary operator expressions.
327 ///
328 /// This is a wrapper around the cost so that spans have unique identities.
329 /// This way we can correctly avoid paying the cost multiple times if the same
330 /// span is split by multiple chunks.
331 class Span extends FastHash {
332 /// The cost applied when the span is split across multiple lines or `null`
333 /// if the span is for a multisplit.
334 final int cost;
335
336 Span(this.cost);
337
338 String toString() => "$id\$$cost";
339 }
340
341 /// A comment in the source, with a bit of information about the surrounding
342 /// whitespace.
343 class SourceComment extends Selection {
344 /// The text of the comment, including `//`, `/*`, and `*/`.
345 final String text;
346
347 /// The number of newlines between the comment or token preceding this comment
348 /// and the beginning of this one.
349 ///
350 /// Will be zero if the comment is a trailing one.
351 int linesBefore;
352
353 /// Whether this comment is a line comment.
354 final bool isLineComment;
355
356 /// Whether this comment starts at column one in the source.
357 ///
358 /// Comments that start at the start of the line will not be indented in the
359 /// output. This way, commented out chunks of code do not get erroneously
360 /// re-indented.
361 final bool flushLeft;
362
363 /// Whether this comment is an inline block comment.
364 bool get isInline => linesBefore == 0 && !isLineComment;
365
366 SourceComment(this.text, this.linesBefore,
367 {this.isLineComment, this.flushLeft});
368 }
OLDNEW
« no previous file with comments | « dart_style/lib/src/call_chain_visitor.dart ('k') | dart_style/lib/src/chunk_builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698