Skip to content

Commit fa81dca

Browse files
AtkinsSJKernelDeimos
authored andcommitted
feat(backend): Add tab completion to server console command arguments
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
1 parent e1e76c6 commit fa81dca

File tree

5 files changed

+67
-8
lines changed

5 files changed

+67
-8
lines changed

packages/backend/src/services/CommandService.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,13 @@ class Command {
3737
log.error(err.stack);
3838
}
3939
}
40+
41+
completeArgument(args) {
42+
const completer = this.spec_.completer;
43+
if ( completer )
44+
return completer(args);
45+
return [];
46+
}
4047
}
4148

4249
class CommandService extends BaseService {
@@ -87,6 +94,10 @@ class CommandService extends BaseService {
8794
get commandNames() {
8895
return this.commands_.map(command => command.id);
8996
}
97+
98+
getCommand(id) {
99+
return this.commands_.find(command => command.id === id);
100+
}
90101
}
91102

92103
module.exports = {

packages/backend/src/services/DevConsoleService.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,13 @@ class DevConsoleService extends BaseService {
132132
prompt: 'puter> ',
133133
terminal: true,
134134
completer: line => {
135-
// We only complete service and command names
136-
if ( line.includes(' ') )
137-
return;
135+
if ( line.includes(' ') ) {
136+
const [ commandName, ...args ] = line.split(/\s+/);
137+
const command = commands.getCommand(commandName);
138+
if (!command)
139+
return;
140+
return [ command.completeArgument(args), args[args.length - 1] ];
141+
}
138142

139143
const results = commands.commandNames
140144
.filter(name => name.startsWith(line))

packages/backend/src/services/ParameterService.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,17 @@ class ParameterService extends BaseService {
6868
}
6969

7070
_registerCommands (commands) {
71+
const completeParameterName = (args) => {
72+
// The parameter name is the first argument, so return no results if we're on the second or later.
73+
if (args.length > 1)
74+
return;
75+
const lastArg = args[args.length - 1];
76+
77+
return this.parameters_
78+
.map(parameter => parameter.spec_.id)
79+
.filter(parameterName => parameterName.startsWith(lastArg));
80+
};
81+
7182
commands.registerCommands('params', [
7283
{
7384
id: 'get',
@@ -76,7 +87,8 @@ class ParameterService extends BaseService {
7687
const [name] = args;
7788
const value = await this.get(name);
7889
log.log(value);
79-
}
90+
},
91+
completer: completeParameterName,
8092
},
8193
{
8294
id: 'set',
@@ -86,7 +98,8 @@ class ParameterService extends BaseService {
8698
const parameter = this._get_param(name);
8799
parameter.set(value);
88100
log.log(value);
89-
}
101+
},
102+
completer: completeParameterName,
90103
},
91104
{
92105
id: 'list',

packages/backend/src/services/ScriptService.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,16 @@ class ScriptService extends BaseService {
3131
return;
3232
}
3333
await script.run(ctx, args);
34+
},
35+
completer: (args) => {
36+
// The script name is the first argument, so return no results if we're on the second or later.
37+
if (args.length > 1)
38+
return;
39+
const scriptName = args[args.length - 1];
40+
41+
return this.scripts
42+
.filter(script => scriptName.startsWith(scriptName))
43+
.map(script => script.name);
3444
}
3545
}
3646
]);

packages/backend/src/services/runtime-analysis/AlarmService.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,24 @@ class AlarmService extends BaseService {
308308
}
309309

310310
_register_commands (commands) {
311+
const completeAlarmID = (args) => {
312+
// The alarm ID is the first argument, so return no results if we're on the second or later.
313+
if (args.length > 1)
314+
return;
315+
const lastArg = args[args.length - 1];
316+
317+
const results = [];
318+
for ( const alarm of Object.values(this.alarms) ) {
319+
if ( alarm.id.startsWith(lastArg) ) {
320+
results.push(alarm.id);
321+
}
322+
if ( alarm.short_id?.startsWith(lastArg) ) {
323+
results.push(alarm.short_id);
324+
}
325+
}
326+
return results;
327+
};
328+
311329
commands.registerCommands('alarm', [
312330
{
313331
id: 'list',
@@ -341,7 +359,8 @@ class AlarmService extends BaseService {
341359
for ( const [key, value] of Object.entries(alarm.fields) ) {
342360
log.log(`- ${key}: ${util.inspect(value)}`);
343361
}
344-
}
362+
},
363+
completer: completeAlarmID,
345364
},
346365
{
347366
id: 'clear',
@@ -356,7 +375,8 @@ class AlarmService extends BaseService {
356375
);
357376
}
358377
this.clear(id);
359-
}
378+
},
379+
completer: completeAlarmID,
360380
},
361381
{
362382
id: 'clear-all',
@@ -397,7 +417,8 @@ class AlarmService extends BaseService {
397417
log.log("┃ " + stringify_log_entry(lg));
398418
}
399419
log.log(`┗━━ Logs before: ${alarm.id_string} ━━━━`);
400-
}
420+
},
421+
completer: completeAlarmID,
401422
},
402423
]);
403424
}

0 commit comments

Comments
 (0)