OLD | NEW |
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 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 | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library dart_style.src.line_splitter; | 5 library dart_style.src.line_splitter; |
6 | 6 |
7 import 'dart:math' as math; | 7 import 'dart:math' as math; |
8 | 8 |
9 import 'chunk.dart'; | 9 import 'chunk.dart'; |
10 import 'debug.dart'; | 10 import 'debug.dart'; |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
96 /// return a single string that may contain one or more newline characters. | 96 /// return a single string that may contain one or more newline characters. |
97 /// | 97 /// |
98 /// Returns a two-element list. The first element will be an [int] indicating | 98 /// Returns a two-element list. The first element will be an [int] indicating |
99 /// where in [buffer] the selection start point should appear if it was | 99 /// where in [buffer] the selection start point should appear if it was |
100 /// contained in the formatted list of chunks. Otherwise it will be `null`. | 100 /// contained in the formatted list of chunks. Otherwise it will be `null`. |
101 /// Likewise, the second element will be non-`null` if the selection endpoint | 101 /// Likewise, the second element will be non-`null` if the selection endpoint |
102 /// is within the list of chunks. | 102 /// is within the list of chunks. |
103 List<int> apply(StringBuffer buffer) { | 103 List<int> apply(StringBuffer buffer) { |
104 if (debugFormatter) dumpLine(_chunks, _indent); | 104 if (debugFormatter) dumpLine(_chunks, _indent); |
105 | 105 |
| 106 _flattenNestingLevels(); |
| 107 |
106 var splits = _findBestSplits(new LinePrefix()); | 108 var splits = _findBestSplits(new LinePrefix()); |
107 var selection = [null, null]; | 109 var selection = [null, null]; |
108 | 110 |
109 // Write each chunk and the split after it. | 111 // Write each chunk and the split after it. |
110 buffer.write(" " * (_indent * spacesPerIndent)); | 112 buffer.write(" " * (_indent * spacesPerIndent)); |
111 for (var i = 0; i < _chunks.length; i++) { | 113 for (var i = 0; i < _chunks.length; i++) { |
112 var chunk = _chunks[i]; | 114 var chunk = _chunks[i]; |
113 | 115 |
114 // If this chunk contains one of the selection markers, tell the writer | 116 // If this chunk contains one of the selection markers, tell the writer |
115 // where it ended up in the final output. | 117 // where it ended up in the final output. |
(...skipping 19 matching lines...) Expand all Loading... |
135 // Should have a valid set of splits when we get here. | 137 // Should have a valid set of splits when we get here. |
136 assert(indent != invalidSplits); | 138 assert(indent != invalidSplits); |
137 } else { | 139 } else { |
138 if (chunk.spaceWhenUnsplit) buffer.write(" "); | 140 if (chunk.spaceWhenUnsplit) buffer.write(" "); |
139 } | 141 } |
140 } | 142 } |
141 | 143 |
142 return selection; | 144 return selection; |
143 } | 145 } |
144 | 146 |
| 147 /// Removes any unused nesting levels from the chunks. |
| 148 /// |
| 149 /// The line splitter considers every possible combination of mapping |
| 150 /// indentation to nesting levels when trying to find the best solution. For |
| 151 /// example, it may assign 4 spaces of indentation to level 1, 8 spaces to |
| 152 /// level 3, etc. |
| 153 /// |
| 154 /// It's fairly common for a nesting level to not actually appear at the |
| 155 /// boundary of a chunk. The source visitor may enter more than one level of |
| 156 /// nesting at a point where a split cannot happen. In that case, there's no |
| 157 /// point in trying to assign an indentation level to that nesting level. It |
| 158 /// will never be used because no line will begin at that level of |
| 159 /// indentation. |
| 160 /// |
| 161 /// Worse, if the splitter *does* consider these levels, it can dramatically |
| 162 /// increase solving time. To avoid that, this renumbers all of the nesting |
| 163 /// levels in the chunks to not have any of these unused gaps. |
| 164 void _flattenNestingLevels() { |
| 165 var nestingLevels = _chunks |
| 166 .map((chunk) => chunk.nesting) |
| 167 .where((nesting) => nesting != -1) |
| 168 .toSet() |
| 169 .toList(); |
| 170 nestingLevels.sort(); |
| 171 |
| 172 var nestingMap = {-1: -1}; |
| 173 for (var i = 0; i < nestingLevels.length; i++) { |
| 174 nestingMap[nestingLevels[i]] = i; |
| 175 } |
| 176 |
| 177 for (var chunk in _chunks) { |
| 178 chunk.nesting = nestingMap[chunk.nesting]; |
| 179 } |
| 180 } |
| 181 |
145 /// Finds the best set of splits to apply to the remainder of the line | 182 /// Finds the best set of splits to apply to the remainder of the line |
146 /// following [prefix]. | 183 /// following [prefix]. |
147 SplitSet _findBestSplits(LinePrefix prefix) { | 184 SplitSet _findBestSplits(LinePrefix prefix) { |
148 // Use the memoized result if we have it. | 185 // Use the memoized result if we have it. |
149 if (_bestSplits.containsKey(prefix)) return _bestSplits[prefix]; | 186 if (_bestSplits.containsKey(prefix)) return _bestSplits[prefix]; |
150 | 187 |
151 var indent = prefix.getNextLineIndent(_chunks, _indent); | 188 var indent = prefix.getNextLineIndent(_chunks, _indent); |
152 | 189 |
153 var bestSplits; | 190 var bestSplits; |
154 var lowestCost; | 191 var lowestCost; |
(...skipping 166 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
321 } | 358 } |
322 | 359 |
323 return false; | 360 return false; |
324 } | 361 } |
325 | 362 |
326 /// Gets whether the suffix of the line after index [split] contains a soft | 363 /// Gets whether the suffix of the line after index [split] contains a soft |
327 /// split using [param]. | 364 /// split using [param]. |
328 bool _suffixContainsParam(int split, SplitParam param) { | 365 bool _suffixContainsParam(int split, SplitParam param) { |
329 if (param == null) return false; | 366 if (param == null) return false; |
330 | 367 |
| 368 // TODO(rnystrom): Consider caching the set of params that appear at every |
| 369 // suffix. |
331 for (var i = split + 1; i < _chunks.length; i++) { | 370 for (var i = split + 1; i < _chunks.length; i++) { |
332 if (_chunks[i].isSoftSplit && _chunks[i].param == param) { | 371 if (_chunks[i].isSoftSplit && _chunks[i].param == param) { |
333 return true; | 372 return true; |
334 } | 373 } |
335 } | 374 } |
336 | 375 |
337 return false; | 376 return false; |
338 } | 377 } |
339 | 378 |
340 /// Evaluates the cost (i.e. the relative "badness") of splitting the line | 379 /// Evaluates the cost (i.e. the relative "badness") of splitting the line |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
469 var result = []; | 508 var result = []; |
470 for (var i = 0; i < _splitNesting.length; i++) { | 509 for (var i = 0; i < _splitNesting.length; i++) { |
471 if (_splitNesting[i] != null) { | 510 if (_splitNesting[i] != null) { |
472 result.add("$i:${_splitNesting[i]}"); | 511 result.add("$i:${_splitNesting[i]}"); |
473 } | 512 } |
474 } | 513 } |
475 | 514 |
476 return result.join(" "); | 515 return result.join(" "); |
477 } | 516 } |
478 } | 517 } |
OLD | NEW |