platform/packages/jael/jael2/bin/jaelfmt.dart
2021-05-15 17:38:15 +08:00

127 lines
3.7 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:args/args.dart';
import 'package:jael/jael.dart';
var argParser = ArgParser()
..addOption('line-length',
abbr: 'l',
help: 'The maximum length of a single line. Longer lines will wrap.',
defaultsTo: '80')
..addOption('stdin-name',
help: 'The filename to print when an error occurs in standard input.',
defaultsTo: '<stdin>')
..addOption('tab-size',
help: 'The number of spaces to output where a TAB would be inserted.',
defaultsTo: '2')
..addFlag('dry-run',
abbr: 'n',
help:
'Print the names of files that would be changed, without actually overwriting them.',
negatable: false)
..addFlag('help',
abbr: 'h', help: 'Print this usage information.', negatable: false)
..addFlag('insert-spaces',
help: 'Insert spaces instead of TAB character.', defaultsTo: true)
..addFlag('overwrite',
abbr: 'w',
help: 'Overwrite input files with formatted output.',
negatable: false);
main(List<String> args) async {
try {
var argResults = argParser.parse(args);
if (argResults['help'] as bool) {
stdout..writeln('Formatter for Jael templates.')..writeln();
printUsage(stdout);
return;
}
if (argResults.rest.isEmpty) {
var text = await stdin.transform(utf8.decoder).join();
var result =
await format(argResults['stdin-name'] as String?, text, argResults);
if (result != null) print(result);
} else {
for (var arg in argResults.rest) {
await formatPath(arg, argResults);
}
}
} on ArgParserException catch (e) {
stderr..writeln(e.message)..writeln();
printUsage(stderr);
exitCode = 65;
}
}
void printUsage(IOSink sink) {
sink
..writeln('Usage: jaelfmt [options...] [files or directories...]')
..writeln()
..writeln('Options:')
..writeln(argParser.usage);
}
Future<void> formatPath(String path, ArgResults argResults) async {
var stat = await FileStat.stat(path);
await formatStat(stat, path, argResults);
}
Future<void> formatStat(
FileStat stat, String path, ArgResults argResults) async {
switch (stat.type) {
case FileSystemEntityType.directory:
await for (var entity in Directory(path).list()) {
await formatStat(await entity.stat(), entity.path, argResults);
}
break;
case FileSystemEntityType.file:
if (path.endsWith('.jael')) await formatFile(File(path), argResults);
break;
case FileSystemEntityType.link:
var link = await Link(path).resolveSymbolicLinks();
await formatPath(link, argResults);
break;
default:
throw 'No file or directory found at "$path".';
}
}
Future<void> formatFile(File file, ArgResults argResults) async {
var content = await file.readAsString();
var formatted = format(file.path, content, argResults);
if (formatted == null) return;
if (argResults['overwrite'] as bool) {
if (formatted != content) {
if (argResults['dry-run'] as bool) {
print('Would have formatted ${file.path}');
} else {
file.writeAsStringSync(formatted);
print('Formatted ${file.path}');
}
} else {
print('Unchanged ${file.path}');
}
} else {
print(formatted);
}
}
String? format(String? filename, String content, ArgResults argResults) {
var errored = false;
var doc = parseDocument(content, sourceUrl: filename, onError: (e) {
stderr.writeln(e);
errored = true;
});
if (errored) return null;
var fmt = JaelFormatter(
int.parse(argResults['tab-size'] as String),
argResults['insert-spaces'] as bool?,
int.parse(argResults['line-length'] as String));
if (doc == null) {
return null;
}
return fmt.apply(doc);
}