bitwarden-estensione-browser/src/commands/completion.command.ts

111 lines
3.2 KiB
TypeScript
Raw Normal View History

2020-05-26 15:34:44 +02:00
import * as program from 'commander';
2020-05-26 15:34:44 +02:00
import { Response } from 'jslib/cli/models/response';
import { MessageResponse } from 'jslib/cli/models/response/messageResponse';
interface IOption {
long: string;
short: string;
description: string;
2020-05-26 15:34:44 +02:00
}
2020-05-26 15:34:44 +02:00
interface ICommand {
commands?: ICommand[];
options?: IOption[];
_name: string;
_description: string;
2020-05-26 15:34:44 +02:00
}
const validShells = ['zsh'];
export class CompletionCommand {
async run(cmd: program.Command) {
const shell: typeof validShells[number] = cmd.shell;
if (!shell) {
return Response.badRequest('`shell` was not provided.');
}
if (!validShells.includes(shell)) {
return Response.badRequest('Unsupported shell.');
}
let content = '';
if (shell === 'zsh') {
content = this.zshCompletion('bw', cmd.parent).render();
}
const res = new MessageResponse(content, null);
return Response.success(res);
}
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 {
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
.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]'`,
hasCommands ? '"1: :->cmnds"' : null,
2020-05-26 15:34:44 +02:00
'"*::arg:->args"',
).filter(Boolean);
const commandBlockFunctionParts = [];
if (hasCommands) {
2020-05-26 15:34:44 +02:00
commandBlockFunctionParts.push('local -a commands');
}
if (hasOptions) {
2020-05-26 15:34:44 +02:00
commandBlockFunctionParts.push(`_arguments -C \\\n ${args.join(` \\\n `)}`);
}
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 ')}
)
_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`,
);
}
const commandBlocParts = [
2020-05-26 15:34:44 +02:00
`function _${name} {\n ${commandBlockFunctionParts.join('\n\n ')}\n}`,
];
if (hasCommands) {
commandBlocParts.push(
2020-05-26 15:34:44 +02:00
commands.map((c) => this.renderCommandBlock(`${name}_${c._name}`, c)).join('\n\n'),
);
}
2020-05-26 15:34:44 +02:00
return commandBlocParts.join('\n\n');
}
}