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 | | /// Internal debugging utilities.
|
6 | | library dart_style.src.debug;
|
7 | |
|
8 | | import 'dart:math' as math;
|
9 | |
|
10 | | import 'chunk.dart';
|
11 | | import 'line_splitting/rule_set.dart';
|
12 | |
|
13 | | /// Set this to `true` to turn on diagnostic output while building chunks.
|
14 | | bool traceChunkBuilder = false;
|
15 | |
|
16 | | /// Set this to `true` to turn on diagnostic output while writing lines.
|
17 | | bool traceLineWriter = false;
|
18 | |
|
19 | | /// Set this to `true` to turn on diagnostic output while line splitting.
|
20 | | bool traceSplitter = false;
|
21 | |
|
22 | | bool useAnsiColors = false;
|
23 | |
|
24 | | const unicodeSection = "\u00a7";
|
25 | | const unicodeMidDot = "\u00b7";
|
26 | |
|
27 | | /// The whitespace prefixing each line of output.
|
28 | | String _indent = "";
|
29 | |
|
30 | 0 | void indent() {
|
31 | 0 | _indent = " $_indent";
|
32 | | }
|
33 | |
|
34 | 0 | void unindent() {
|
35 | 0 | _indent = _indent.substring(2);
|
36 | | }
|
37 | |
|
38 | | /// Constants for ANSI color escape codes.
|
39 | | final _gray = _color("\u001b[1;30m");
|
40 | | final _green = _color("\u001b[32m");
|
41 | | final _none = _color("\u001b[0m");
|
42 | | final _bold = _color("\u001b[1m");
|
43 | |
|
44 | | /// Prints [message] to stdout with each line correctly indented.
|
45 | 0 | void log([message]) {
|
46 | | if (message == null) {
|
47 | 0 | print("");
|
48 | | return;
|
49 | | }
|
50 | |
|
51 | 0 | print(_indent + message.toString().replaceAll("\n", "\n$_indent"));
|
52 | | }
|
53 | |
|
54 | | /// Wraps [message] in gray ANSI escape codes if enabled.
|
55 | 0 | String gray(message) => "$_gray$message$_none";
|
56 | |
|
57 | | /// Wraps [message] in green ANSI escape codes if enabled.
|
58 | 0 | String green(message) => "$_green$message$_none";
|
59 | |
|
60 | | /// Wraps [message] in bold ANSI escape codes if enabled.
|
61 | 0 | String bold(message) => "$_bold$message$_none";
|
62 | |
|
63 | | /// Prints [chunks] to stdout, one chunk per line, with detailed information
|
64 | | /// about each chunk.
|
65 | 0 | void dumpChunks(int start, List<Chunk> chunks) {
|
66 | 0 | if (chunks.skip(start).isEmpty) return;
|
67 | |
|
68 | | // Show the spans as vertical bands over their range (unless there are too
|
69 | | // many).
|
70 | 0 | var spanSet = new Set<Span>();
|
71 | 0 | addSpans(List<Chunk> chunks) {
|
72 | 0 | for (var chunk in chunks) {
|
73 | 0 | spanSet.addAll(chunk.spans);
|
74 | |
|
75 | 0 | if (chunk.isBlock) addSpans(chunk.block.chunks);
|
76 | | }
|
77 | | }
|
78 | |
|
79 | 0 | addSpans(chunks);
|
80 | |
|
81 | 0 | var spans = spanSet.toList();
|
82 | | var rules =
|
83 | 0 | chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
|
84 | |
|
85 | 0 | var rows = <List<String>>[];
|
86 | |
|
87 | 0 | addChunk(List<Chunk> chunks, String prefix, int index) {
|
88 | 0 | var row = <String>[];
|
89 | 0 | row.add("$prefix$index:");
|
90 | |
|
91 | 0 | var chunk = chunks[index];
|
92 | 0 | if (chunk.text.length > 70) {
|
93 | 0 | row.add(chunk.text.substring(0, 70));
|
94 | | } else {
|
95 | 0 | row.add(chunk.text);
|
96 | | }
|
97 | |
|
98 | 0 | if (spans.length <= 20) {
|
99 | | var spanBars = "";
|
100 | 0 | for (var span in spans) {
|
101 | 0 | if (chunk.spans.contains(span)) {
|
102 | 0 | if (index == 0 || !chunks[index - 1].spans.contains(span)) {
|
103 | 0 | if (span.cost == 1) {
|
104 | 0 | spanBars += "╖";
|
105 | | } else {
|
106 | 0 | spanBars += span.cost.toString();
|
107 | | }
|
108 | | } else {
|
109 | 0 | spanBars += "║";
|
110 | | }
|
111 | | } else {
|
112 | 0 | if (index > 0 && chunks[index - 1].spans.contains(span)) {
|
113 | 0 | spanBars += "╜";
|
114 | | } else {
|
115 | 0 | spanBars += " ";
|
116 | | }
|
117 | | }
|
118 | | }
|
119 | 0 | row.add(spanBars);
|
120 | | }
|
121 | |
|
122 | 0 | writeIf(predicate, String callback()) {
|
123 | | if (predicate) {
|
124 | 0 | row.add(callback());
|
125 | | } else {
|
126 | 0 | row.add("");
|
127 | | }
|
128 | | }
|
129 | |
|
130 | 0 | if (chunk.rule == null) {
|
131 | 0 | row.add("");
|
132 | 0 | row.add("(no rule)");
|
133 | 0 | row.add("");
|
134 | | } else {
|
135 | 0 | writeIf(chunk.rule.cost != 0, () => "\$${chunk.rule.cost}");
|
136 | |
|
137 | 0 | var ruleString = chunk.rule.toString();
|
138 | 0 | if (chunk.rule.isHardened) ruleString += "!";
|
139 | 0 | row.add(ruleString);
|
140 | |
|
141 | | var constrainedRules =
|
142 | 0 | chunk.rule.constrainedRules.toSet().intersection(rules);
|
143 | 0 | writeIf(constrainedRules.isNotEmpty,
|
144 | 0 | () => "-> ${constrainedRules.join(" ")}");
|
145 | | }
|
146 | |
|
147 | 0 | writeIf(chunk.indent != null && chunk.indent != 0,
|
148 | 0 | () => "indent ${chunk.indent}");
|
149 | |
|
150 | 0 | writeIf(chunk.nesting != null && chunk.nesting.indent != 0,
|
151 | 0 | () => "nest ${chunk.nesting}");
|
152 | |
|
153 | 0 | writeIf(chunk.flushLeft != null && chunk.flushLeft, () => "flush");
|
154 | |
|
155 | 0 | writeIf(chunk.canDivide, () => "divide");
|
156 | |
|
157 | 0 | rows.add(row);
|
158 | |
|
159 | 0 | if (chunk.isBlock) {
|
160 | 0 | for (var j = 0; j < chunk.block.chunks.length; j++) {
|
161 | 0 | addChunk(chunk.block.chunks, "$prefix$index.", j);
|
162 | | }
|
163 | | }
|
164 | | }
|
165 | |
|
166 | 0 | for (var i = start; i < chunks.length; i++) {
|
167 | 0 | addChunk(chunks, "", i);
|
168 | | }
|
169 | |
|
170 | 0 | var rowWidths = new List.filled(rows.first.length, 0);
|
171 | 0 | for (var row in rows) {
|
172 | 0 | for (var i = 0; i < row.length; i++) {
|
173 | 5 | rowWidths[i] = math.max(rowWidths[i], row[i].length);
|
174 | | }
|
175 | | }
|
176 | |
|
177 | 0 | var buffer = new StringBuffer();
|
178 | 0 | for (var row in rows) {
|
179 | 0 | for (var i = 0; i < row.length; i++) {
|
180 | 0 | var cell = row[i].padRight(rowWidths[i]);
|
181 | |
|
182 | 0 | if (i != 1) cell = gray(cell);
|
183 | |
|
184 | 0 | buffer.write(cell);
|
185 | 0 | buffer.write(" ");
|
186 | | }
|
187 | |
|
188 | 0 | buffer.writeln();
|
189 | | }
|
190 | |
|
191 | 0 | print(buffer.toString());
|
192 | | }
|
193 | |
|
194 | | /// Shows all of the constraints between the rules used by [chunks].
|
195 | 0 | void dumpConstraints(List<Chunk> chunks) {
|
196 | | var rules =
|
197 | 0 | chunks.map((chunk) => chunk.rule).where((rule) => rule != null).toSet();
|
198 | |
|
199 | 0 | for (var rule in rules) {
|
200 | 0 | var constrainedValues = [];
|
201 | 0 | for (var value = 0; value < rule.numValues; value++) {
|
202 | 0 | var constraints = [];
|
203 | 0 | for (var other in rules) {
|
204 | 0 | if (rule == other) continue;
|
205 | |
|
206 | 0 | var constraint = rule.constrain(value, other);
|
207 | | if (constraint != null) {
|
208 | 0 | constraints.add("$other->$constraint");
|
209 | | }
|
210 | | }
|
211 | |
|
212 | 0 | if (constraints.isNotEmpty) {
|
213 | 0 | constrainedValues.add("$value:(${constraints.join(' ')})");
|
214 | | }
|
215 | | }
|
216 | |
|
217 | 0 | log("$rule ${constrainedValues.join(' ')}");
|
218 | | }
|
219 | | }
|
220 | |
|
221 | | /// Convert the line to a [String] representation.
|
222 | | ///
|
223 | | /// It will determine how best to split it into multiple lines of output and
|
224 | | /// return a single string that may contain one or more newline characters.
|
225 | 0 | void dumpLines(List<Chunk> chunks, int firstLineIndent, SplitSet splits) {
|
226 | 0 | var buffer = new StringBuffer();
|
227 | |
|
228 | 0 | writeIndent(indent) => buffer.write(gray("| " * (indent ~/ 2)));
|
229 | |
|
230 | 0 | writeChunksUnsplit(List<Chunk> chunks) {
|
231 | 0 | for (var chunk in chunks) {
|
232 | 0 | buffer.write(chunk.text);
|
233 | 0 | if (chunk.spaceWhenUnsplit) buffer.write(" ");
|
234 | |
|
235 | | // Recurse into the block.
|
236 | 0 | if (chunk.isBlock) writeChunksUnsplit(chunk.block.chunks);
|
237 | | }
|
238 | | }
|
239 | |
|
240 | 0 | writeIndent(firstLineIndent);
|
241 | |
|
242 | 0 | for (var i = 0; i < chunks.length - 1; i++) {
|
243 | 0 | var chunk = chunks[i];
|
244 | 0 | buffer.write(chunk.text);
|
245 | |
|
246 | 0 | if (splits.shouldSplitAt(i)) {
|
247 | 0 | for (var j = 0; j < (chunk.isDouble ? 2 : 1); j++) {
|
248 | 0 | buffer.writeln();
|
249 | 0 | writeIndent(splits.getColumn(i));
|
250 | | }
|
251 | | } else {
|
252 | 0 | if (chunk.isBlock) writeChunksUnsplit(chunk.block.chunks);
|
253 | |
|
254 | 0 | if (chunk.spaceWhenUnsplit) buffer.write(" ");
|
255 | | }
|
256 | | }
|
257 | |
|
258 | 0 | buffer.write(chunks.last.text);
|
259 | 0 | log(buffer);
|
260 | | }
|
261 | |
|
262 | 0 | String _color(String ansiEscape) => useAnsiColors ? ansiEscape : "";
|