blob: 8158825722adde5d193d862730641424c533f158 [file] [log] [blame]
import 'dart:io';
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'util.dart';
/// The string 'completion' used to denote that arguments provided to an app are
/// for command completion.
///
/// The expected arg format is: completion -- {process name} {rest of current
/// args}
const String completionCommandName = 'completion';
const _compPointVar = 'COMP_POINT';
void tryCompletion(
List<String> args,
List<String> Function(List<String> args, String compLine, int compPoint)
completer, {
@Deprecated('Useful for testing, but do not release with this set.')
bool? logFile,
}) {
if (logFile ?? false) {
final logFile = File('_completion.log');
void logLine(String content) {
logFile.writeAsStringSync('$content\n', mode: FileMode.writeOnlyAppend);
}
logLine(' *' * 50);
Logger.root.onRecord.listen((e) {
final loggerName = e.loggerName.split('.');
if (loggerName.isNotEmpty && loggerName.first == 'completion') {
loggerName.removeAt(0);
assert(e.level == Level.INFO);
logLine(
'${loggerName.join('.').padLeft(Tag.longestTagLength)} '
'${e.message}',
);
}
});
}
String scriptName;
try {
scriptName = p.basename(Platform.script.toFilePath());
} on FileSystemException {
// Script may not have a current working directory.
scriptName = '<unknown>';
} on UnsupportedError // ignore: avoid_catching_errors
{
scriptName = '<unknown>';
}
log('Checking for completion on script:\t$scriptName');
if (args.length >= 3 && args[0] == completionCommandName && args[1] == '--') {
try {
log('Starting completion');
log('All args: $args');
log('completion-reported exe: ${args[2]}');
final env = Platform.environment;
// There are 3 interesting env parameters passed by the completion logic
// COMP_LINE: the full contents of the completion
final compLine = env['COMP_LINE'];
if (compLine == null) {
throw StateError('Environment variable COMP_LINE must be set');
}
// COMP_CWORD: number of words. Also might be nice
// COMP_POINT: where the cursor is on the completion line
final compPointValue = env[_compPointVar];
if (compPointValue == null || compPointValue.isEmpty) {
throw StateError(
'Environment variable $_compPointVar must be set and non-empty');
}
final compPoint = int.tryParse(compPointValue);
if (compPoint == null) {
throw FormatException('Could not parse $_compPointVar value '
'"$compPointValue" into an integer');
}
final trimmedArgs = args.sublist(3);
log('input args: ${helpfulToString(trimmedArgs)}');
final completions = completer(trimmedArgs, compLine, compPoint);
log('completions: ${helpfulToString(completions)}');
for (final comp in completions) {
print(comp);
}
exit(0);
} catch (ex, stack) {
log('An error occurred while attemping completion');
log(ex);
log(stack);
exit(1);
}
}
log('Completion params not found');
}