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(); | 106 var nestingDepth = _flattenNestingLevels(); |
107 | |
108 // Hack. The formatter doesn't handle formatting very deeply nested code | |
109 // well. It can make performance spiral into a pit of sadness. Fortunately, | |
110 // we only tend to see expressions pathologically deeply nested in | |
111 // generated code that isn't read by humans much anyway. To avoid burning | |
112 // too much time on these, harden any splits containing more than a certain | |
113 // level of nesting. | |
114 // | |
115 // The number here was chosen empirically based on formatting the repo. It | |
116 // was picked to get the best performance while affecting the minimum amount | |
117 // of results. | |
118 // TODO(rnystrom): Do something smarter. | |
pquitslund
2015/03/11 00:09:50
Best comment I've read all year.
Bob Nystrom
2015/03/11 00:14:17
:D
| |
119 if (nestingDepth > 9) { | |
120 for (var chunk in _chunks) { | |
121 if (chunk.param != null && nestingDepth - chunk.nesting > 9) { | |
122 chunk.harden(); | |
123 } | |
124 } | |
125 } | |
107 | 126 |
108 var splits = _findBestSplits(new LinePrefix()); | 127 var splits = _findBestSplits(new LinePrefix()); |
128 | |
109 var selection = [null, null]; | 129 var selection = [null, null]; |
110 | 130 |
111 // Write each chunk and the split after it. | 131 // Write each chunk and the split after it. |
112 buffer.write(" " * (_indent * spacesPerIndent)); | 132 buffer.write(" " * (_indent * spacesPerIndent)); |
113 for (var i = 0; i < _chunks.length; i++) { | 133 for (var i = 0; i < _chunks.length; i++) { |
114 var chunk = _chunks[i]; | 134 var chunk = _chunks[i]; |
115 | 135 |
116 // If this chunk contains one of the selection markers, tell the writer | 136 // If this chunk contains one of the selection markers, tell the writer |
117 // where it ended up in the final output. | 137 // where it ended up in the final output. |
118 if (chunk.selectionStart != null) { | 138 if (chunk.selectionStart != null) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
154 /// It's fairly common for a nesting level to not actually appear at the | 174 /// 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 | 175 /// 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 | 176 /// 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 | 177 /// 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 | 178 /// will never be used because no line will begin at that level of |
159 /// indentation. | 179 /// indentation. |
160 /// | 180 /// |
161 /// Worse, if the splitter *does* consider these levels, it can dramatically | 181 /// Worse, if the splitter *does* consider these levels, it can dramatically |
162 /// increase solving time. To avoid that, this renumbers all of the nesting | 182 /// 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. | 183 /// levels in the chunks to not have any of these unused gaps. |
164 void _flattenNestingLevels() { | 184 /// |
185 /// Returns the number of distinct nesting levels remaining after flattening. | |
186 /// This may be zero if the chunks have no nesting (i.e. just statement-level | |
187 /// indentation). | |
188 int _flattenNestingLevels() { | |
165 var nestingLevels = _chunks | 189 var nestingLevels = _chunks |
166 .map((chunk) => chunk.nesting) | 190 .map((chunk) => chunk.nesting) |
167 .where((nesting) => nesting != -1) | 191 .where((nesting) => nesting != -1) |
168 .toSet() | 192 .toSet() |
169 .toList(); | 193 .toList(); |
170 nestingLevels.sort(); | 194 nestingLevels.sort(); |
171 | 195 |
172 var nestingMap = {-1: -1}; | 196 var nestingMap = {-1: -1}; |
173 for (var i = 0; i < nestingLevels.length; i++) { | 197 for (var i = 0; i < nestingLevels.length; i++) { |
174 nestingMap[nestingLevels[i]] = i; | 198 nestingMap[nestingLevels[i]] = i; |
175 } | 199 } |
176 | 200 |
177 for (var chunk in _chunks) { | 201 for (var chunk in _chunks) { |
178 chunk.nesting = nestingMap[chunk.nesting]; | 202 chunk.nesting = nestingMap[chunk.nesting]; |
179 } | 203 } |
204 | |
205 return nestingLevels.length; | |
180 } | 206 } |
181 | 207 |
182 /// Finds the best set of splits to apply to the remainder of the line | 208 /// Finds the best set of splits to apply to the remainder of the line |
183 /// following [prefix]. | 209 /// following [prefix]. |
184 SplitSet _findBestSplits(LinePrefix prefix) { | 210 SplitSet _findBestSplits(LinePrefix prefix) { |
185 // Use the memoized result if we have it. | 211 // Use the memoized result if we have it. |
186 if (_bestSplits.containsKey(prefix)) return _bestSplits[prefix]; | 212 if (_bestSplits.containsKey(prefix)) return _bestSplits[prefix]; |
187 | 213 |
188 var indent = prefix.getNextLineIndent(_chunks, _indent); | 214 var indent = prefix.getNextLineIndent(_chunks, _indent); |
189 | 215 |
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
508 var result = []; | 534 var result = []; |
509 for (var i = 0; i < _splitNesting.length; i++) { | 535 for (var i = 0; i < _splitNesting.length; i++) { |
510 if (_splitNesting[i] != null) { | 536 if (_splitNesting[i] != null) { |
511 result.add("$i:${_splitNesting[i]}"); | 537 result.add("$i:${_splitNesting[i]}"); |
512 } | 538 } |
513 } | 539 } |
514 | 540 |
515 return result.join(" "); | 541 return result.join(" "); |
516 } | 542 } |
517 } | 543 } |
OLD | NEW |