1 | | // Copyright (c) 2015, 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_builder;
|
6 | |
|
7 | | import 'nesting_level.dart';
|
8 | | import 'whitespace.dart';
|
9 | |
|
10 | | /// Keeps track of block indentation and expression nesting while the source
|
11 | | /// code is being visited and the chunks are being built.
|
12 | | ///
|
13 | | /// This class requires (and verifies) that indentation and nesting are
|
14 | | /// stratified from each other. Expression nesting is always inside block
|
15 | | /// indentation, which means it is an error to try to change the block
|
16 | | /// indentation while any expression nesting is in effect.
|
17 | | class NestingBuilder {
|
18 | | /// The block indentation levels.
|
19 | | ///
|
20 | | /// This is tracked as a stack of numbers, each of which is the total number
|
21 | | /// of spaces of block indentation. We only store the stack of previous
|
22 | | /// levels as a convenience to the caller: it spares you from having to pass
|
23 | | /// the unindent amount to [unindent()].
|
24 | | final List<int> _stack = [0];
|
25 | |
|
26 | | /// When not `null`, the expression nesting after the next token is written.
|
27 | | ///
|
28 | | /// When the nesting is increased, we don't want it to take effect until
|
29 | | /// after at least one token has been written. That ensures that comments
|
30 | | /// appearing before the first token are correctly indented. For example, a
|
31 | | /// binary operator expression increases the nesting before the first operand
|
32 | | /// to ensure any splits within the left operand are handled correctly. If we
|
33 | | /// changed the nesting level immediately, then code like:
|
34 | | ///
|
35 | | /// {
|
36 | | /// // comment
|
37 | | /// foo + bar;
|
38 | | /// }
|
39 | | ///
|
40 | | /// would incorrectly get indented because the line comment adds a split which
|
41 | | /// would take the nesting level of the binary operator into account even
|
42 | | /// though we haven't written any of its tokens yet.
|
43 | | ///
|
44 | | /// Likewise, when nesting is decreased, we may want to defer that until
|
45 | | /// we've written the next token to handle uncommon cases like:
|
46 | | ///
|
47 | | /// do // comment
|
48 | | /// {
|
49 | | /// ...
|
50 | | /// }
|
51 | | ///
|
52 | | /// Here, if we discard the expression nesting before we reach the "{", then
|
53 | | /// it won't get indented as it should.
|
54 | | NestingLevel _pendingNesting;
|
55 | |
|
56 | | /// The current number of characters of block indentation.
|
57 | 9 | int get indentation => _stack.last;
|
58 | |
|
59 | | /// The current nesting, ignoring any pending nesting.
|
60 | 6 | NestingLevel get nesting => _nesting;
|
61 | | NestingLevel _nesting = new NestingLevel();
|
62 | |
|
63 | | /// The current nesting, including any pending nesting.
|
64 | 3 | NestingLevel get currentNesting =>
|
65 | 7 | _pendingNesting != null ? _pendingNesting : _nesting;
|
66 | |
|
67 | | /// The top "nesting level" that represents no expression nesting for the
|
68 | | /// current block.
|
69 | 0 | NestingLevel get blockNesting {
|
70 | | // Walk the nesting levels until we bottom out.
|
71 | 0 | var result = _nesting;
|
72 | 0 | while (result.parent != null) {
|
73 | 0 | result = result.parent;
|
74 | | }
|
75 | | return result;
|
76 | | }
|
77 | |
|
78 | | /// Creates a new indentation level [spaces] deeper than the current one.
|
79 | | ///
|
80 | | /// If omitted, [spaces] defaults to [Indent.block].
|
81 | 3 | void indent([int spaces]) {
|
82 | | if (spaces == null) spaces = Indent.block;
|
83 | |
|
84 | | // Indentation should only change outside of nesting.
|
85 | 3 | assert(_pendingNesting == null);
|
86 | 9 | assert(_nesting.indent == 0);
|
87 | |
|
88 | 15 | _stack.add(_stack.last + spaces);
|
89 | | }
|
90 | |
|
91 | | /// Discards the most recent indentation level.
|
92 | 2 | void unindent() {
|
93 | | // Indentation should only change outside of nesting.
|
94 | 2 | assert(_pendingNesting == null);
|
95 | 6 | assert(_nesting.indent == 0);
|
96 | |
|
97 | | // If this fails, an unindent() call did not have a preceding indent() call.
|
98 | 4 | assert(_stack.isNotEmpty);
|
99 | |
|
100 | 4 | _stack.removeLast();
|
101 | | }
|
102 | |
|
103 | | /// Begins a new expression nesting level [indent] deeper than the current
|
104 | | /// one if it splits.
|
105 | | ///
|
106 | | /// Expressions that are more nested will get increased indentation when split
|
107 | | /// if the previous line has a lower level of nesting.
|
108 | | ///
|
109 | | /// If [indent] is omitted, defaults to [Indent.expression].
|
110 | 3 | void nest([int indent]) {
|
111 | | if (indent == null) indent = Indent.expression;
|
112 | |
|
113 | 3 | if (_pendingNesting != null) {
|
114 | 9 | _pendingNesting = _pendingNesting.nest(indent);
|
115 | | } else {
|
116 | 9 | _pendingNesting = _nesting.nest(indent);
|
117 | | }
|
118 | | }
|
119 | |
|
120 | | /// Discards the most recent level of expression nesting.
|
121 | 3 | void unnest() {
|
122 | 3 | if (_pendingNesting != null) {
|
123 | 1 | _pendingNesting = _pendingNesting.parent;
|
124 | | } else {
|
125 | 9 | _pendingNesting = _nesting.parent;
|
126 | | }
|
127 | |
|
128 | | // If this fails, an unnest() call did not have a preceding nest() call.
|
129 | 3 | assert(_pendingNesting != null);
|
130 | | }
|
131 | |
|
132 | | /// Applies any pending nesting now that we are ready for it to take effect.
|
133 | 3 | void commitNesting() {
|
134 | 3 | if (_pendingNesting == null) return;
|
135 | |
|
136 | 6 | _nesting = _pendingNesting;
|
137 | 3 | _pendingNesting = null;
|
138 | | }
|
139 | | }
|