Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 } |
| OLD | NEW |