2020-05-26 15:34:44 +02:00
|
|
|
import * as program from 'commander';
|
2020-05-26 15:17:41 +02:00
|
|
|
|
2021-06-07 19:25:55 +02:00
|
|
|
import { Response } from 'jslib-node/cli/models/response';
|
|
|
|
import { MessageResponse } from 'jslib-node/cli/models/response/messageResponse';
|
2020-05-26 15:34:44 +02:00
|
|
|
|
|
|
|
interface IOption {
|
2021-02-03 18:44:33 +01:00
|
|
|
long?: string;
|
|
|
|
short?: string;
|
2020-05-26 15:17:41 +02:00
|
|
|
description: string;
|
2020-05-26 15:34:44 +02:00
|
|
|
}
|
2020-05-26 15:17:41 +02:00
|
|
|
|
2020-05-26 15:34:44 +02:00
|
|
|
interface ICommand {
|
|
|
|
commands?: ICommand[];
|
|
|
|
options?: IOption[];
|
2020-05-26 15:17:41 +02:00
|
|
|
_name: string;
|
|
|
|
_description: string;
|
2020-05-26 15:34:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const validShells = ['zsh'];
|
|
|
|
|
|
|
|
export class CompletionCommand {
|
2021-02-03 18:44:33 +01:00
|
|
|
async run(options: program.OptionValues) {
|
|
|
|
const shell: typeof validShells[number] = options.shell;
|
2020-05-26 15:34:44 +02:00
|
|
|
|
|
|
|
if (!shell) {
|
|
|
|
return Response.badRequest('`shell` was not provided.');
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!validShells.includes(shell)) {
|
|
|
|
return Response.badRequest('Unsupported shell.');
|
|
|
|
}
|
|
|
|
|
|
|
|
let content = '';
|
|
|
|
|
|
|
|
if (shell === 'zsh') {
|
2021-02-03 18:44:33 +01:00
|
|
|
content = this.zshCompletion('bw', program as any as ICommand).render();
|
2020-05-26 15:34:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const res = new MessageResponse(content, null);
|
|
|
|
return Response.success(res);
|
|
|
|
}
|
2020-05-26 15:17:41 +02:00
|
|
|
|
2020-05-26 15:34:44 +02:00
|
|
|
private zshCompletion(rootName: string, rootCommand: ICommand) {
|
|
|
|
return {
|
|
|
|
render: () => {
|
|
|
|
return [
|
|
|
|
`#compdef _${rootName} ${rootName}`,
|
|
|
|
'',
|
|
|
|
this.renderCommandBlock(rootName, rootCommand),
|
|
|
|
].join('\n');
|
|
|
|
},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
private renderCommandBlock(name: string, command: ICommand): string {
|
2020-05-26 15:17:41 +02:00
|
|
|
const { commands = [], options = [] } = command;
|
|
|
|
const hasOptions = options.length > 0;
|
|
|
|
const hasCommands = commands.length > 0;
|
|
|
|
|
2020-05-26 15:34:44 +02:00
|
|
|
const args = options
|
2020-05-26 15:17:41 +02:00
|
|
|
.map(({ long, short, description }) => {
|
|
|
|
const aliases = [short, long].filter(Boolean);
|
2020-05-26 15:34:44 +02:00
|
|
|
const opts = aliases.join(',');
|
|
|
|
const desc = `[${description.replace(`'`, `'"'"'`)}]`;
|
|
|
|
return aliases.length > 1 ? `'(${aliases.join(' ')})'{${opts}}'${desc}'` : `'${opts}${desc}'`;
|
|
|
|
}).concat(`'(-h --help)'{-h,--help}'[output usage information]'`,
|
2020-05-26 15:17:41 +02:00
|
|
|
hasCommands ? '"1: :->cmnds"' : null,
|
2020-05-26 15:34:44 +02:00
|
|
|
'"*::arg:->args"',
|
|
|
|
).filter(Boolean);
|
2020-05-26 15:17:41 +02:00
|
|
|
|
|
|
|
const commandBlockFunctionParts = [];
|
|
|
|
|
|
|
|
if (hasCommands) {
|
2020-05-26 15:34:44 +02:00
|
|
|
commandBlockFunctionParts.push('local -a commands');
|
2020-05-26 15:17:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hasOptions) {
|
2020-05-26 15:34:44 +02:00
|
|
|
commandBlockFunctionParts.push(`_arguments -C \\\n ${args.join(` \\\n `)}`);
|
2020-05-26 15:17:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hasCommands) {
|
|
|
|
commandBlockFunctionParts.push(
|
|
|
|
`case $state in
|
|
|
|
cmnds)
|
|
|
|
commands=(
|
2020-05-26 15:34:44 +02:00
|
|
|
${commands.map(({ _name, _description }) => `"${_name}:${_description}"`).join('\n ')}
|
2020-05-26 15:17:41 +02:00
|
|
|
)
|
|
|
|
_describe "command" commands
|
|
|
|
;;
|
|
|
|
esac
|
|
|
|
|
|
|
|
case "$words[1]" in
|
2020-05-26 15:34:44 +02:00
|
|
|
${commands.map(({ _name }) => [`${_name})`, `_${name}_${_name}`, ';;'].join('\n ')).join('\n ')}
|
|
|
|
esac`,
|
2020-05-26 15:17:41 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
const commandBlocParts = [
|
2020-05-26 15:34:44 +02:00
|
|
|
`function _${name} {\n ${commandBlockFunctionParts.join('\n\n ')}\n}`,
|
2020-05-26 15:17:41 +02:00
|
|
|
];
|
|
|
|
|
|
|
|
if (hasCommands) {
|
|
|
|
commandBlocParts.push(
|
2021-02-04 05:51:59 +01:00
|
|
|
commands.map(c => this.renderCommandBlock(`${name}_${c._name}`, c)).join('\n\n'),
|
2020-05-26 15:17:41 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2020-05-26 15:34:44 +02:00
|
|
|
return commandBlocParts.join('\n\n');
|
2020-05-26 15:17:41 +02:00
|
|
|
}
|
|
|
|
}
|