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

Side by Side Diff: pkg/analyzer/lib/src/codegen/tools.dart

Issue 2742333005: Move some of analyzer's code generation utilities into front_end. (Closed)
Patch Set: Created 3 years, 9 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
« no previous file with comments | « pkg/analysis_server/tool/spec/to_html.dart ('k') | pkg/analyzer/tool/summary/check_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file 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 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 /** 5 /**
6 * Tools for generating code in analyzer and analysis server. 6 * Tools for generating code in analyzer and analysis server.
7 */ 7 */
8 library analyzer.src.codegen.tools; 8 library analyzer.src.codegen.tools;
9 9
10 import 'dart:io';
11
12 import 'package:analyzer/src/codegen/html.dart'; 10 import 'package:analyzer/src/codegen/html.dart';
13 import 'package:analyzer/src/codegen/text_formatter.dart'; 11 import 'package:analyzer/src/codegen/text_formatter.dart';
14 import 'package:html/dom.dart' as dom; 12 import 'package:html/dom.dart' as dom;
15 import 'package:path/path.dart';
16 13
17 final RegExp trailingSpacesInLineRegExp = new RegExp(r' +$', multiLine: true); 14 final RegExp trailingSpacesInLineRegExp = new RegExp(r' +$', multiLine: true);
18 final RegExp trailingWhitespaceRegExp = new RegExp(r'[\n ]+$'); 15 final RegExp trailingWhitespaceRegExp = new RegExp(r'[\n ]+$');
19 16
20 /** 17 /**
21 * Join the given strings using camelCase. If [doCapitalize] is true, the first 18 * Join the given strings using camelCase. If [doCapitalize] is true, the first
22 * part will be capitalized as well. 19 * part will be capitalized as well.
23 */ 20 */
24 String camelJoin(List<String> parts, {bool doCapitalize: false}) { 21 String camelJoin(List<String> parts, {bool doCapitalize: false}) {
25 List<String> upcasedParts = <String>[]; 22 List<String> upcasedParts = <String>[];
26 for (int i = 0; i < parts.length; i++) { 23 for (int i = 0; i < parts.length; i++) {
27 if (i == 0 && !doCapitalize) { 24 if (i == 0 && !doCapitalize) {
28 upcasedParts.add(parts[i]); 25 upcasedParts.add(parts[i]);
29 } else { 26 } else {
30 upcasedParts.add(capitalize(parts[i])); 27 upcasedParts.add(capitalize(parts[i]));
31 } 28 }
32 } 29 }
33 return upcasedParts.join(); 30 return upcasedParts.join();
34 } 31 }
35 32
36 /** 33 /**
37 * Capitalize and return the passed String. 34 * Capitalize and return the passed String.
38 */ 35 */
39 String capitalize(String string) { 36 String capitalize(String string) {
40 return string[0].toUpperCase() + string.substring(1); 37 return string[0].toUpperCase() + string.substring(1);
41 } 38 }
42 39
43 /** 40 /**
44 * Type of functions used to compute the contents of a set of generated files.
45 * [pkgPath] is the path to the current package.
46 */
47 typedef Map<String, FileContentsComputer> DirectoryContentsComputer(
48 String pkgPath);
49
50 /**
51 * Type of functions used to compute the contents of a generated file.
52 * [pkgPath] is the path to the current package.
53 */
54 typedef String FileContentsComputer(String pkgPath);
55
56 /**
57 * Mixin class for generating code. 41 * Mixin class for generating code.
58 */ 42 */
59 class CodeGenerator { 43 class CodeGenerator {
60 _CodeGeneratorState _state; 44 _CodeGeneratorState _state;
61 45
62 /** 46 /**
63 * Settings that specialize code generation behavior for a given 47 * Settings that specialize code generation behavior for a given
64 * programming language. 48 * programming language.
65 */ 49 */
66 CodeGeneratorSettings codeGeneratorSettings = new CodeGeneratorSettings(); 50 CodeGeneratorSettings codeGeneratorSettings = new CodeGeneratorSettings();
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after
259 {this.languageName: 'java', 243 {this.languageName: 'java',
260 this.lineCommentLineLeader: '// ', 244 this.lineCommentLineLeader: '// ',
261 this.docCommentStartMarker: '/**', 245 this.docCommentStartMarker: '/**',
262 this.docCommentLineLeader: ' * ', 246 this.docCommentLineLeader: ' * ',
263 this.docCommentEndMarker: ' */', 247 this.docCommentEndMarker: ' */',
264 this.commentLineLength: 99, 248 this.commentLineLength: 99,
265 this.indent: ' '}); 249 this.indent: ' '});
266 } 250 }
267 251
268 /** 252 /**
269 * Abstract base class representing behaviors common to generated files and
270 * generated directories.
271 */
272 abstract class GeneratedContent {
273 /**
274 * Check whether the [output] has the correct contents, and return true if it
275 * does. [pkgPath] is the path to the current package.
276 */
277 bool check(String pkgPath);
278
279 /**
280 * Replace the [output] with the correct contents. [pkgPath] is the path to
281 * the current package.
282 */
283 void generate(String pkgPath);
284
285 /**
286 * Get a [FileSystemEntity] representing the output file or directory.
287 * [pkgPath] is the path to the current package.
288 */
289 FileSystemEntity output(String pkgPath);
290
291 /**
292 * Check that all of the [targets] are up to date. If they are not, print
293 * out a message instructing the user to regenerate them, and exit with a
294 * nonzero error code.
295 *
296 * [pkgPath] is the path to the current package. [generatorRelPath] is the
297 * path to a .dart script the user may use to regenerate the targets.
298 *
299 * To avoid mistakes when run on Windows, [generatorRelPath] always uses
300 * POSIX directory separators.
301 */
302 static void checkAll(String pkgPath, String generatorRelPath,
303 Iterable<GeneratedContent> targets) {
304 bool generateNeeded = false;
305 for (GeneratedContent target in targets) {
306 if (!target.check(pkgPath)) {
307 print(
308 '${target.output(pkgPath).absolute} does not have expected contents. ');
309 generateNeeded = true;
310 }
311 }
312 if (generateNeeded) {
313 print('Please regenerate using:');
314 String executable = Platform.executable;
315 String packageRoot = '';
316 if (Platform.packageRoot != null) {
317 packageRoot = ' --package-root=${Platform.packageRoot}';
318 }
319 String generateScript =
320 join(pkgPath, joinAll(posix.split(generatorRelPath)));
321 print(' $executable$packageRoot $generateScript');
322 exit(1);
323 } else {
324 print('All generated files up to date.');
325 }
326 }
327
328 /**
329 * Regenerate all of the [targets]. [pkgPath] is the path to the current
330 * package.
331 */
332 static void generateAll(String pkgPath, Iterable<GeneratedContent> targets) {
333 for (GeneratedContent target in targets) {
334 target.generate(pkgPath);
335 }
336 }
337 }
338
339 /**
340 * Class representing a single output directory (either generated code or
341 * generated HTML). No other content should exist in the directory.
342 */
343 class GeneratedDirectory extends GeneratedContent {
344 /**
345 * The path to the directory that will have the generated content.
346 */
347 final String outputDirPath;
348
349 /**
350 * Callback function that computes the directory contents.
351 */
352 final DirectoryContentsComputer directoryContentsComputer;
353
354 GeneratedDirectory(this.outputDirPath, this.directoryContentsComputer);
355
356 @override
357 bool check(String pkgPath) {
358 Directory outputDirectory = output(pkgPath);
359 Map<String, FileContentsComputer> map = directoryContentsComputer(pkgPath);
360 try {
361 for (String file in map.keys) {
362 FileContentsComputer fileContentsComputer = map[file];
363 String expectedContents = fileContentsComputer(pkgPath);
364 File outputFile = new File(posix.join(outputDirectory.path, file));
365 String actualContents = outputFile.readAsStringSync();
366 // Normalize Windows line endings to Unix line endings so that the
367 // comparison doesn't fail on Windows.
368 actualContents = actualContents.replaceAll('\r\n', '\n');
369 if (expectedContents != actualContents) {
370 return false;
371 }
372 }
373 int nonHiddenFileCount = 0;
374 outputDirectory
375 .listSync(recursive: false, followLinks: false)
376 .forEach((FileSystemEntity fileSystemEntity) {
377 if (fileSystemEntity is File &&
378 !basename(fileSystemEntity.path).startsWith('.')) {
379 nonHiddenFileCount++;
380 }
381 });
382 if (nonHiddenFileCount != map.length) {
383 // The number of files generated doesn't match the number we expected to
384 // generate.
385 return false;
386 }
387 } catch (e) {
388 // There was a problem reading the file (most likely because it didn't
389 // exist). Treat that the same as if the file doesn't have the expected
390 // contents.
391 return false;
392 }
393 return true;
394 }
395
396 @override
397 void generate(String pkgPath) {
398 Directory outputDirectory = output(pkgPath);
399 try {
400 // delete the contents of the directory (and the directory itself)
401 outputDirectory.deleteSync(recursive: true);
402 } catch (e) {
403 // Error caught while trying to delete the directory, this can happen if
404 // it didn't yet exist.
405 }
406 // re-create the empty directory
407 outputDirectory.createSync(recursive: true);
408
409 // generate all of the files in the directory
410 Map<String, FileContentsComputer> map = directoryContentsComputer(pkgPath);
411 map.forEach((String file, FileContentsComputer fileContentsComputer) {
412 File outputFile = new File(posix.join(outputDirectory.path, file));
413 outputFile.writeAsStringSync(fileContentsComputer(pkgPath));
414 });
415 }
416
417 @override
418 Directory output(String pkgPath) =>
419 new Directory(join(pkgPath, joinAll(posix.split(outputDirPath))));
420 }
421
422 /**
423 * Class representing a single output file (either generated code or generated
424 * HTML).
425 */
426 class GeneratedFile extends GeneratedContent {
427 /**
428 * The output file to which generated output should be written, relative to
429 * the "tool/spec" directory. This filename uses the posix path separator
430 * ('/') regardless of the OS.
431 */
432 final String outputPath;
433
434 /**
435 * Callback function which computes the file.
436 */
437 final FileContentsComputer computeContents;
438
439 GeneratedFile(this.outputPath, this.computeContents);
440
441 @override
442 bool check(String pkgPath) {
443 File outputFile = output(pkgPath);
444 String expectedContents = computeContents(pkgPath);
445 try {
446 String actualContents = outputFile.readAsStringSync();
447 // Normalize Windows line endings to Unix line endings so that the
448 // comparison doesn't fail on Windows.
449 actualContents = actualContents.replaceAll('\r\n', '\n');
450 return expectedContents == actualContents;
451 } catch (e) {
452 // There was a problem reading the file (most likely because it didn't
453 // exist). Treat that the same as if the file doesn't have the expected
454 // contents.
455 return false;
456 }
457 }
458
459 @override
460 void generate(String pkgPath) {
461 output(pkgPath).writeAsStringSync(computeContents(pkgPath));
462 }
463
464 @override
465 File output(String pkgPath) =>
466 new File(join(pkgPath, joinAll(posix.split(outputPath))));
467 }
468
469 /**
470 * Mixin class for generating HTML representations of code that are suitable 253 * Mixin class for generating HTML representations of code that are suitable
471 * for enclosing inside a <pre> element. 254 * for enclosing inside a <pre> element.
472 */ 255 */
473 abstract class HtmlCodeGenerator { 256 abstract class HtmlCodeGenerator {
474 _HtmlCodeGeneratorState _state; 257 _HtmlCodeGeneratorState _state;
475 258
476 /** 259 /**
477 * Add the given [node] to the HTML output. 260 * Add the given [node] to the HTML output.
478 */ 261 */
479 void add(dom.Node node) { 262 void add(dom.Node node) {
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
600 if (lines.last.isEmpty) { 383 if (lines.last.isEmpty) {
601 lines.removeLast(); 384 lines.removeLast();
602 buffer.add(new dom.Text(lines.join('\n$indent') + '\n')); 385 buffer.add(new dom.Text(lines.join('\n$indent') + '\n'));
603 indentNeeded = true; 386 indentNeeded = true;
604 } else { 387 } else {
605 buffer.add(new dom.Text(lines.join('\n$indent'))); 388 buffer.add(new dom.Text(lines.join('\n$indent')));
606 indentNeeded = false; 389 indentNeeded = false;
607 } 390 }
608 } 391 }
609 } 392 }
OLDNEW
« no previous file with comments | « pkg/analysis_server/tool/spec/to_html.dart ('k') | pkg/analyzer/tool/summary/check_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698