autocomplete 2.0.0
autocomplete: ^2.0.0 copied to clipboard
Fig-style CLI completion specs and runtime (parse command, load spec, get suggestions). Pure Dart
// JSON-first example. Run from dart/:
//
// dart run example/example.dart --trace 'astro d'
// dart run example/example.dart 'git b'
// dart run example/example.dart 'chown '
import 'dart:io';
import 'package:autocomplete/autocomplete.dart';
import 'local_adapter.dart';
Shell _parseShell(String name) {
switch (name.toLowerCase()) {
case 'bash':
return Shell.bash;
case 'zsh':
return Shell.zsh;
case 'fish':
return Shell.fish;
case 'pwsh':
return Shell.pwsh;
case 'powershell':
return Shell.powershell;
case 'cmd':
return Shell.cmd;
case 'xonsh':
return Shell.xonsh;
case 'nushell':
return Shell.nushell;
default:
return Shell.bash;
}
}
void _usage() {
print('Usage: dart run example/example.dart [--trace] '
'[--shell <shell>] <command>');
print("Examples: 'cut -', 'cd ', 'astro d', 'chown '");
}
Future<void> main(List<String> args) async {
String? commandLine;
var shell = Shell.bash;
var trace = false;
for (var i = 0; i < args.length; i++) {
final argument = args[i];
if (argument == '--shell' || argument == '-s') {
if (i + 1 >= args.length) {
_usage();
exitCode = 64;
return;
}
shell = _parseShell(args[++i]);
} else if (argument == '--trace') {
trace = true;
} else if (argument == '-h' || argument == '--help') {
_usage();
return;
} else if (commandLine == null) {
commandLine = argument;
} else {
_usage();
exitCode = 64;
return;
}
}
if (commandLine == null || commandLine.isEmpty) {
_usage();
exitCode = 64;
return;
}
final assetRoot =
File.fromUri(Platform.script).parent.parent.uri.resolve('assets/specs/');
final integrityRoot = File.fromUri(Platform.script).parent.parent
.uri
.resolve('assets/spec-integrity/');
final reads = <String>[];
final handlers = JsonHandlerRegistry();
final store = await registerJsonSpecs(
catalog: builtinJsonSpecCatalog,
integrityReader: (shardPath) =>
File.fromUri(integrityRoot.resolve(shardPath)).readAsBytes(),
resourceReader: (resource) {
reads.add(resource.relativePath);
return File.fromUri(assetRoot.resolve(resource.relativePath))
.readAsBytes();
},
handlers: handlers,
);
final engine = AutocompleteEngine(
adapter: LocalCompleteAdapter(),
registry: store.registry,
ensureSpecLoaded: store.ensureLoaded,
);
if (trace) {
engine.setLogger((message, [error, stackTrace]) {
stderr.writeln(message);
if (error != null) stderr.writeln(' error: $error');
if (stackTrace != null && stackTrace != StackTrace.empty) {
stderr.writeln(' stack: $stackTrace');
}
});
}
try {
final result = await engine.getSuggestions(
commandLine,
Directory.current.path,
shell,
);
if (result == null) {
print('(no spec or no suggestions)');
} else {
if (result.argumentDescription != null) {
print('Argument: ${result.argumentDescription}');
}
for (final suggestion in result.suggestions) {
print('${suggestion.name}\t${suggestion.description ?? ''}');
}
print('(${result.suggestions.length} suggestions, '
'charactersToDrop: ${result.charactersToDrop})');
}
} on JsonSpecFormatException catch (error) {
stderr.writeln('JSON spec cannot be activated: ${error.message}');
exitCode = 1;
}
if (trace) {
print('JSON reads: ${reads.join(', ')}');
for (final unresolved in handlers.unresolvedHandlers) {
print('Unresolved handler: ${unresolved.id} at ${unresolved.path}');
}
}
}