Skip to content

Commit

Permalink
feat(backend): Add tab completion to server console command arguments
Browse files Browse the repository at this point in the history
A command can optionally define a `completer(args)` function. It takes
an array of strings for the current arguments, and returns an array of
completion results for the last argument.

Includes a few completers:
- alarm:info/clear/inspect completes the first argument as an alarm id
  or short_id
- script:run completes the first argument as a script name
- params:get/set completes the first argument as a parameter name
  • Loading branch information
AtkinsSJ authored and KernelDeimos committed Jun 28, 2024
1 parent e1e76c6 commit fa81dca
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 8 deletions.
11 changes: 11 additions & 0 deletions packages/backend/src/services/CommandService.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ class Command {
log.error(err.stack);
}
}

completeArgument(args) {
const completer = this.spec_.completer;
if ( completer )
return completer(args);
return [];
}
}

class CommandService extends BaseService {
Expand Down Expand Up @@ -87,6 +94,10 @@ class CommandService extends BaseService {
get commandNames() {
return this.commands_.map(command => command.id);
}

getCommand(id) {
return this.commands_.find(command => command.id === id);
}
}

module.exports = {
Expand Down
10 changes: 7 additions & 3 deletions packages/backend/src/services/DevConsoleService.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,13 @@ class DevConsoleService extends BaseService {
prompt: 'puter> ',
terminal: true,
completer: line => {
// We only complete service and command names
if ( line.includes(' ') )
return;
if ( line.includes(' ') ) {
const [ commandName, ...args ] = line.split(/\s+/);
const command = commands.getCommand(commandName);
if (!command)
return;
return [ command.completeArgument(args), args[args.length - 1] ];
}

const results = commands.commandNames
.filter(name => name.startsWith(line))
Expand Down
17 changes: 15 additions & 2 deletions packages/backend/src/services/ParameterService.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,17 @@ class ParameterService extends BaseService {
}

_registerCommands (commands) {
const completeParameterName = (args) => {
// The parameter name is the first argument, so return no results if we're on the second or later.
if (args.length > 1)
return;
const lastArg = args[args.length - 1];

return this.parameters_
.map(parameter => parameter.spec_.id)
.filter(parameterName => parameterName.startsWith(lastArg));
};

commands.registerCommands('params', [
{
id: 'get',
Expand All @@ -76,7 +87,8 @@ class ParameterService extends BaseService {
const [name] = args;
const value = await this.get(name);
log.log(value);
}
},
completer: completeParameterName,
},
{
id: 'set',
Expand All @@ -86,7 +98,8 @@ class ParameterService extends BaseService {
const parameter = this._get_param(name);
parameter.set(value);
log.log(value);
}
},
completer: completeParameterName,
},
{
id: 'list',
Expand Down
10 changes: 10 additions & 0 deletions packages/backend/src/services/ScriptService.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,16 @@ class ScriptService extends BaseService {
return;
}
await script.run(ctx, args);
},
completer: (args) => {
// The script name is the first argument, so return no results if we're on the second or later.
if (args.length > 1)
return;
const scriptName = args[args.length - 1];

return this.scripts
.filter(script => scriptName.startsWith(scriptName))
.map(script => script.name);
}
}
]);
Expand Down
27 changes: 24 additions & 3 deletions packages/backend/src/services/runtime-analysis/AlarmService.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,24 @@ class AlarmService extends BaseService {
}

_register_commands (commands) {
const completeAlarmID = (args) => {
// The alarm ID is the first argument, so return no results if we're on the second or later.
if (args.length > 1)
return;
const lastArg = args[args.length - 1];

const results = [];
for ( const alarm of Object.values(this.alarms) ) {
if ( alarm.id.startsWith(lastArg) ) {
results.push(alarm.id);
}
if ( alarm.short_id?.startsWith(lastArg) ) {
results.push(alarm.short_id);
}
}
return results;
};

commands.registerCommands('alarm', [
{
id: 'list',
Expand Down Expand Up @@ -341,7 +359,8 @@ class AlarmService extends BaseService {
for ( const [key, value] of Object.entries(alarm.fields) ) {
log.log(`- ${key}: ${util.inspect(value)}`);
}
}
},
completer: completeAlarmID,
},
{
id: 'clear',
Expand All @@ -356,7 +375,8 @@ class AlarmService extends BaseService {
);
}
this.clear(id);
}
},
completer: completeAlarmID,
},
{
id: 'clear-all',
Expand Down Expand Up @@ -397,7 +417,8 @@ class AlarmService extends BaseService {
log.log("┃ " + stringify_log_entry(lg));
}
log.log(`┗━━ Logs before: ${alarm.id_string} ━━━━`);
}
},
completer: completeAlarmID,
},
]);
}
Expand Down

0 comments on commit fa81dca

Please sign in to comment.