Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(322)

Side by Side Diff: pkg/status_file/lib/status_file.dart

Issue 2988383002: A minimal status file formatter and canonicalizer. (Closed)
Patch Set: Created 3 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2017, 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 import 'dart:io'; 5 import 'dart:io';
6 6
7 import 'package:path/path.dart' as p; 7 import 'package:path/path.dart' as p;
8 8
9 import 'environment.dart'; 9 import 'environment.dart';
10 import 'expectation.dart'; 10 import 'expectation.dart';
(...skipping 27 matching lines...) Expand all
38 /// 38 ///
39 /// Each entry is a glob-like file path followed by a colon and then a 39 /// Each entry is a glob-like file path followed by a colon and then a
40 /// comma-separated list of [Expectation]s. The path may point to an individual 40 /// comma-separated list of [Expectation]s. The path may point to an individual
41 /// file, or a directory, in which case it applies to all files under that path. 41 /// file, or a directory, in which case it applies to all files under that path.
42 /// 42 ///
43 /// Entries may also appear before any section header, in which case they 43 /// Entries may also appear before any section header, in which case they
44 /// always apply. 44 /// always apply.
45 class StatusFile { 45 class StatusFile {
46 final String path; 46 final String path;
47 final List<StatusSection> sections = []; 47 final List<StatusSection> sections = [];
48 final List<String> _comments = [];
49
50 int _lineCount = 0;
48 51
49 StatusFile(this.path); 52 StatusFile(this.path);
50 53
51 /// Parses the status file at [path]. 54 /// Parses the status file at [path].
52 /// 55 ///
53 /// Throws a [SyntaxError] if the file could not be parsed. 56 /// Throws a [SyntaxError] if the file could not be parsed.
54 StatusFile.read(this.path) { 57 StatusFile.read(this.path) {
55 var lines = new File(path).readAsLinesSync(); 58 var lines = new File(path).readAsLinesSync();
59 _comments.length = lines.length + 1;
56 60
57 // The current section whose rules are being parsed. 61 // The current section whose rules are being parsed.
58 StatusSection section; 62 StatusSection section;
59 63
60 var lineNumber = 0;
61
62 for (var line in lines) { 64 for (var line in lines) {
63 lineNumber++; 65 _lineCount++;
64 66
65 fail(String message, [List<String> errors]) { 67 fail(String message, [List<String> errors]) {
66 throw new SyntaxError(_shortPath, lineNumber, line, message, errors); 68 throw new SyntaxError(_shortPath, _lineCount, line, message, errors);
67 } 69 }
68 70
69 // Strip off the comment and whitespace. 71 // Strip off the comment and whitespace.
70 var source = line; 72 var source = line;
71 var comment = ""; 73 var comment = "";
72 var hashIndex = line.indexOf('#'); 74 var hashIndex = line.indexOf('#');
73 if (hashIndex >= 0) { 75 if (hashIndex >= 0) {
74 source = line.substring(0, hashIndex); 76 source = line.substring(0, hashIndex);
75 comment = line.substring(hashIndex); 77 comment = line.substring(hashIndex + 1);
78 _comments[_lineCount] = comment;
76 } 79 }
77 source = source.trim(); 80 source = source.trim();
78 81
79 // Ignore empty (or comment-only) lines. 82 // Ignore empty (or comment-only) lines.
80 if (source.isEmpty) continue; 83 if (source.isEmpty) continue;
81 84
82 // See if we are starting a new section. 85 // See if we are starting a new section.
83 var match = _sectionPattern.firstMatch(source); 86 var match = _sectionPattern.firstMatch(source);
84 if (match != null) { 87 if (match != null) {
85 try { 88 try {
86 var condition = Expression.parse(match[1].trim()); 89 var condition = Expression.parse(match[1].trim());
87 section = new StatusSection(condition, lineNumber); 90 section = new StatusSection(condition, _lineCount);
88 sections.add(section); 91 sections.add(section);
89 } on FormatException { 92 } on FormatException {
90 fail("Status expression syntax error"); 93 fail("Status expression syntax error");
91 } 94 }
92 continue; 95 continue;
93 } 96 }
94 97
95 // Otherwise, it should be a new entry under the current section. 98 // Otherwise, it should be a new entry under the current section.
96 match = _entryPattern.firstMatch(source); 99 match = _entryPattern.firstMatch(source);
97 if (match != null) { 100 if (match != null) {
(...skipping 12 matching lines...) Expand all
110 var issue = _issueNumber(comment); 113 var issue = _issueNumber(comment);
111 114
112 // If we haven't found a section header yet, create an implicit section 115 // If we haven't found a section header yet, create an implicit section
113 // that matches everything. 116 // that matches everything.
114 if (section == null) { 117 if (section == null) {
115 section = new StatusSection(null, -1); 118 section = new StatusSection(null, -1);
116 sections.add(section); 119 sections.add(section);
117 } 120 }
118 121
119 section.entries 122 section.entries
120 .add(new StatusEntry(path, lineNumber, expectations, issue)); 123 .add(new StatusEntry(path, _lineCount, expectations, issue));
121 continue; 124 continue;
122 } 125 }
123 126
124 fail("Unrecognized input"); 127 fail("Unrecognized input");
125 } 128 }
126 } 129 }
127 130
128 bool get isEmpty => sections.isEmpty; 131 bool get isEmpty => sections.isEmpty;
129 132
130 /// Validates that the variables and values used in all of the section 133 /// Validates that the variables and values used in all of the section
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
171 buffer.write("${entry.path}: ${entry.expectations.join(', ')}"); 174 buffer.write("${entry.path}: ${entry.expectations.join(', ')}");
172 if (entry.issue != null) buffer.write(" # Issue ${entry.issue}"); 175 if (entry.issue != null) buffer.write(" # Issue ${entry.issue}");
173 buffer.writeln(); 176 buffer.writeln();
174 } 177 }
175 178
176 buffer.writeln(); 179 buffer.writeln();
177 } 180 }
178 181
179 return buffer.toString(); 182 return buffer.toString();
180 } 183 }
184
185 /// Serialize the status file to a string.
186 ///
187 /// Unlike [toString()], this preserves comments and gives a "canonical"
188 /// rendering of the status file that can be saved back to disc.
189 String serialize() {
190 var buffer = new StringBuffer();
191
192 var lastLine = 0;
193 var needBlankLine = false;
194
195 void writeLine(String text, int line) {
196 var comment = _comments[line];
197 if (text == null && comment == null) {
198 // There's no comment on this line, so it's blank.
199 needBlankLine = true;
200 return;
201 }
202
203 if (needBlankLine) buffer.writeln();
204 needBlankLine = false;
205
206 if (text != null) {
207 buffer.write(text);
208 }
209
210 if (comment != null) {
211 if (text != null) buffer.write(" ");
212 buffer.write("#$comment");
213 }
214
215 buffer.writeln();
216 }
217
218 void writeText(String text, int line) {
219 if (line != null) {
220 while (++lastLine < line) {
221 writeLine(null, lastLine);
222 }
223 }
224
225 writeLine(text, line);
226 }
227
228 for (var section in sections) {
229 if (section.condition != null) {
230 writeText("[ ${section.condition} ]", section.lineNumber);
231 }
232
233 for (var entry in section.entries) {
Bill Hesse 2017/08/03 14:24:07 Not in the printing, but as a separate stage, I th
Bob Nystrom 2017/08/03 20:28:49 Yeah, I thought of that after I sent this out. I m
234 writeText("${entry.path}: ${entry.expectations.join(', ')}",
235 entry.lineNumber);
236 }
237
238 needBlankLine = true;
239 }
240
241 // Write any trailing comments.
242 while (++lastLine <= _lineCount) {
243 writeLine(null, lastLine);
244 }
245
246 return buffer.toString();
247 }
181 } 248 }
182 249
183 /// One section in a status file. 250 /// One section in a status file.
184 /// 251 ///
185 /// Contains the condition from the header that begins the section, then all of 252 /// Contains the condition from the header that begins the section, then all of
186 /// the entries within the section. 253 /// the entries within the section.
187 class StatusSection { 254 class StatusSection {
188 /// The expression that determines when this section is applied. 255 /// The expression that determines when this section is applied.
189 /// 256 ///
190 /// May be `null` for paths that appear before any section header in the file. 257 /// May be `null` for paths that appear before any section header in the file.
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
233 300
234 if (errors != null) { 301 if (errors != null) {
235 for (var error in errors) { 302 for (var error in errors) {
236 buffer.writeln("- ${error.replaceAll('\n', '\n ')}"); 303 buffer.writeln("- ${error.replaceAll('\n', '\n ')}");
237 } 304 }
238 } 305 }
239 306
240 return buffer.toString().trimRight(); 307 return buffer.toString().trimRight();
241 } 308 }
242 } 309 }
OLDNEW
« no previous file with comments | « pkg/status_file/lib/src/expression.dart ('k') | pkg/status_file/test/status_expression_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698