| OLD | NEW |
| (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.nesting; | |
| 6 | |
| 7 /// A single level of expression nesting. | |
| 8 /// | |
| 9 /// When a line is split in the middle of an expression, this tracks the | |
| 10 /// context of where in the expression that split occurs. It ensures that the | |
| 11 /// [LineSplitter] obeys the expression nesting when deciding what column to | |
| 12 /// start lines at when split inside an expression. | |
| 13 /// | |
| 14 /// Each instance of this represents a single level of expression nesting. If we | |
| 15 /// split at to chunks with different levels of nesting, the splitter ensures | |
| 16 /// they each get assigned to different columns. | |
| 17 /// | |
| 18 /// In addition, each level has an indent. This is the number of spaces it is | |
| 19 /// indented relative to the outer expression. It's almost always | |
| 20 /// [Indent.expression], but cascades are special magic snowflakes and use | |
| 21 /// [Indent.cascade]. | |
| 22 class NestingLevel { | |
| 23 /// The nesting level surrounding this one, or `null` if this is represents | |
| 24 /// top level code in a block. | |
| 25 NestingLevel get parent => _parent; | |
| 26 NestingLevel _parent; | |
| 27 | |
| 28 /// The number of characters that this nesting level is indented relative to | |
| 29 /// the containing level. | |
| 30 /// | |
| 31 /// Normally, this is [Indent.expression], but cascades use [Indent.cascade]. | |
| 32 final int indent; | |
| 33 | |
| 34 /// The number of nesting levels surrounding this one. | |
| 35 int get depth { | |
| 36 var result = 0; | |
| 37 var nesting = this; | |
| 38 while (nesting != null) { | |
| 39 result++; | |
| 40 nesting = nesting.parent; | |
| 41 } | |
| 42 | |
| 43 return result - 1; | |
| 44 } | |
| 45 | |
| 46 NestingLevel() : indent = 0; | |
| 47 | |
| 48 NestingLevel._(this._parent, this.indent); | |
| 49 | |
| 50 /// Creates a new deeper level of nesting indented [spaces] more characters | |
| 51 /// that the outer level. | |
| 52 NestingLevel nest(int spaces) => new NestingLevel._(this, spaces); | |
| 53 | |
| 54 /// Gets the relative indentation of the nesting level at [depth]. | |
| 55 int indentAtDepth(int depth) { | |
| 56 // How many levels do we need to walk up to reach [depth]? | |
| 57 var levels = this.depth - depth; | |
| 58 assert(levels >= 0); | |
| 59 | |
| 60 var nesting = this; | |
| 61 for (var i = 0; i < levels; i++) { | |
| 62 nesting = nesting._parent; | |
| 63 } | |
| 64 | |
| 65 return nesting.indent; | |
| 66 } | |
| 67 | |
| 68 /// Discards this level's parent if it is not in [used] (or is not the top | |
| 69 /// level nesting). | |
| 70 void removeUnused(Set<NestingLevel> used) { | |
| 71 // Always keep the top level zero nesting. | |
| 72 if (_parent == null) return; | |
| 73 if (_parent._parent == null) return; | |
| 74 | |
| 75 // Unlink the unused parent from the chain. | |
| 76 if (!used.contains(_parent)) _parent = _parent._parent; | |
| 77 | |
| 78 // Walk up the whole chain. | |
| 79 _parent.removeUnused(used); | |
| 80 } | |
| 81 | |
| 82 String toString() => depth.toString(); | |
| 83 } | |
| 84 | |
| 85 /// Maintains a stack of nested expressions that have currently been split. | |
| 86 /// | |
| 87 /// A single statement may have multiple different levels of indentation based | |
| 88 /// on the expression nesting level at the point where the line is broken. For | |
| 89 /// example: | |
| 90 /// | |
| 91 /// someFunction(argument, argument, | |
| 92 /// innerFunction(argument, | |
| 93 /// innermost), argument); | |
| 94 /// | |
| 95 /// This means that when splitting a line, we need to keep track of the nesting | |
| 96 /// level of the previous line(s) to determine how far the next line must be | |
| 97 /// indented. | |
| 98 /// | |
| 99 /// This class is a persistent collection. Each instance is immutable and | |
| 100 /// methods to modify it return a new collection. | |
| 101 class NestingSplitter { | |
| 102 final NestingSplitter _parent; | |
| 103 | |
| 104 /// The number of characters of indentation for the current nesting. | |
| 105 int get indent => _indent; | |
| 106 final int _indent; | |
| 107 | |
| 108 /// The number of surrounding expression nesting levels. | |
| 109 int get depth => _depth; | |
| 110 final int _depth; | |
| 111 | |
| 112 NestingSplitter() : this._(null, 0, 0); | |
| 113 | |
| 114 NestingSplitter._(this._parent, this._depth, this._indent); | |
| 115 | |
| 116 /// LinePrefixes implement their own value equality to ensure that two | |
| 117 /// prefixes with the same nesting stack are considered equal even if the | |
| 118 /// nesting occurred from different splits. | |
| 119 /// | |
| 120 /// For example, consider these two prefixes with `^` marking where splits | |
| 121 /// have been applied: | |
| 122 /// | |
| 123 /// fn( first, second, ... | |
| 124 /// ^ | |
| 125 /// fn( first, second, ... | |
| 126 /// ^ | |
| 127 /// | |
| 128 /// These are equivalent from the view of the suffix because they have the | |
| 129 /// same nesting stack, even though the nesting came from different tokens. | |
| 130 /// This lets us reuse memoized suffixes more frequently when solving. | |
| 131 bool operator ==(other) { | |
| 132 if (other is! NestingSplitter) return false; | |
| 133 | |
| 134 var self = this; | |
| 135 while (self != null) { | |
| 136 if (self._indent != other._indent) return false; | |
| 137 if (self._depth != other._depth) return false; | |
| 138 self = self._parent; | |
| 139 other = other._parent; | |
| 140 | |
| 141 // They should be the same length. | |
| 142 if ((self == null) != (other == null)) return false; | |
| 143 } | |
| 144 | |
| 145 return true; | |
| 146 } | |
| 147 | |
| 148 int get hashCode { | |
| 149 // TODO(rnystrom): Is it worth iterating through the stack? | |
| 150 return _indent.hashCode ^ _depth.hashCode; | |
| 151 } | |
| 152 | |
| 153 /// Takes this nesting stack and produces all of the new nesting stacks that | |
| 154 /// are possible when followed by [nesting]. | |
| 155 /// | |
| 156 /// This may produce multiple solutions because a non-incremental jump in | |
| 157 /// nesting depth can be sliced up multiple ways. Let's say the prefix is: | |
| 158 /// | |
| 159 /// first(second(third(... | |
| 160 /// | |
| 161 /// The current nesting stack is empty (since we're on the first line). How | |
| 162 /// do we modify it by taking into account the split after `third(`? The | |
| 163 /// simple answer is to just increase the indentation by one level: | |
| 164 /// | |
| 165 /// first(second(third( | |
| 166 /// argumentToThird))); | |
| 167 /// | |
| 168 /// This is correct in most cases, but not all. Consider: | |
| 169 /// | |
| 170 /// first(second(third( | |
| 171 /// argumentToThird), | |
| 172 /// argumentToSecond)); | |
| 173 /// | |
| 174 /// Oops! There's no place for `argumentToSecond` to go. To handle that, the | |
| 175 /// second line needs to be indented one more level to make room for the later | |
| 176 /// line: | |
| 177 /// | |
| 178 /// first(second(third( | |
| 179 /// argumentToThird), | |
| 180 /// argumentToSecond)); | |
| 181 /// | |
| 182 /// It's even possible we may need to do: | |
| 183 /// | |
| 184 /// first(second(third( | |
| 185 /// argumentToThird), | |
| 186 /// argumentToSecond), | |
| 187 /// argumentToFirst); | |
| 188 /// | |
| 189 /// To accommodate those, this returns the list of all possible ways the | |
| 190 /// nesting stack can be modified. | |
| 191 List<NestingSplitter> update(NestingLevel nesting) { | |
| 192 if (nesting.depth == _depth) return [this]; | |
| 193 | |
| 194 // If the new split is less nested than we currently are, pop and discard | |
| 195 // the previous nesting levels. | |
| 196 if (nesting.depth < _depth) { | |
| 197 // Pop items off the stack until we find the level we are now at. | |
| 198 var stack = this; | |
| 199 while (stack != null) { | |
| 200 if (stack._depth == nesting.depth) return [stack]; | |
| 201 stack = stack._parent; | |
| 202 } | |
| 203 | |
| 204 // If we got here, the level wasn't found. That means there is no correct | |
| 205 // stack level to pop to, since the stack skips past our indentation | |
| 206 // level. | |
| 207 return []; | |
| 208 } | |
| 209 | |
| 210 // Going deeper, so try every indentation for every subset of expression | |
| 211 // nesting levels between the old and new one. | |
| 212 return _intermediateDepths(_depth, nesting.depth).map((depths) { | |
| 213 var result = this; | |
| 214 | |
| 215 for (var depth in depths) { | |
| 216 result = new NestingSplitter._( | |
| 217 result, depth, result._indent + nesting.indentAtDepth(depth)); | |
| 218 } | |
| 219 | |
| 220 return new NestingSplitter._( | |
| 221 result, nesting.depth, result._indent + nesting.indent); | |
| 222 }).toList(); | |
| 223 } | |
| 224 | |
| 225 /// Given [min] and [max], generates all of the subsets of numbers in that | |
| 226 /// range (exclusive), including the empty set. | |
| 227 /// | |
| 228 /// This is used to determine what sets of intermediate nesting levels to | |
| 229 /// consider when jumping from a shallow nesting level to a much deeper one. | |
| 230 /// Subsets are generated in order of increasing length. For example, `(2, 6)` | |
| 231 /// yields: | |
| 232 /// | |
| 233 /// [] | |
| 234 /// [3] [4] [5] | |
| 235 /// [3, 4] [3, 5] [4, 5] | |
| 236 /// [3, 4, 5] | |
| 237 /// | |
| 238 /// This ensures the splitter prefers solutions that use the least | |
| 239 /// indentation. | |
| 240 List<List<int>> _intermediateDepths(int min, int max) { | |
| 241 assert(min < max); | |
| 242 | |
| 243 var subsets = [[]]; | |
| 244 | |
| 245 var lastLengthStart = 0; | |
| 246 var lastLengthEnd = subsets.length; | |
| 247 | |
| 248 // Generate subsets in order of increasing length. | |
| 249 for (var length = 1; length <= max - min + 1; length++) { | |
| 250 // Start with each subset containing one fewer element. | |
| 251 for (var i = lastLengthStart; i < lastLengthEnd; i++) { | |
| 252 var previousSubset = subsets[i]; | |
| 253 | |
| 254 var start = | |
| 255 previousSubset.isNotEmpty ? previousSubset.last + 1 : min + 1; | |
| 256 | |
| 257 // Then for each value in the remainer, make a new subset that is the | |
| 258 // union of the shorter subset and that value. | |
| 259 for (var j = start; j < max; j++) { | |
| 260 var subset = previousSubset.toList()..add(j); | |
| 261 subsets.add(subset); | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 // Move on to the next length range. | |
| 266 lastLengthStart = lastLengthEnd; | |
| 267 lastLengthEnd = subsets.length; | |
| 268 } | |
| 269 | |
| 270 return subsets; | |
| 271 } | |
| 272 | |
| 273 /// Shows each indentation level and the nesting depth associated with it. | |
| 274 /// | |
| 275 /// For example: | |
| 276 /// | |
| 277 /// |1|3 | |
| 278 /// | |
| 279 /// Means that the first level of indentation is associated with nesting | |
| 280 /// level one, and the second level of indentation is associated with nesting | |
| 281 /// level three. | |
| 282 String toString() { | |
| 283 var result = ""; | |
| 284 | |
| 285 for (var nesting = this; nesting._depth != 0; nesting = nesting._parent) { | |
| 286 result = "|${nesting._depth}$result"; | |
| 287 } | |
| 288 | |
| 289 return result; | |
| 290 } | |
| 291 } | |
| OLD | NEW |