Skip to content
This repository has been archived by the owner on May 25, 2022. It is now read-only.

add completion generation for zsh #137

Merged
merged 1 commit into from
May 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 135 additions & 0 deletions src/commands/completion.command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import * as program from "commander";
import { Response } from "jslib/cli/models/response";
import { MessageResponse } from "jslib/cli/models/response/messageResponse";

type Option = {
long: string;
short: string;
description: string;
};

type Command = {
commands?: Command[];
options?: Option[];
_name: string;
_description: string;
};

const zshCompletion = (rootName: string, rootCommand: Command) => {
const renderCommandBlock = (name: string, command: Command): string => {
const { commands = [], options = [] } = command;
const hasOptions = options.length > 0;
const hasCommands = commands.length > 0;

const _arguments = options
.map(({ long, short, description }) => {
const aliases = [short, long].filter(Boolean);

const OPTS = aliases.join(",");

const DESCRIPTION = `[${description.replace("'", `'"'"'`)}]`;

return aliases.length > 1
? `'(${aliases.join(" ")})'{${OPTS}}'${DESCRIPTION}'`
: `'${OPTS}${DESCRIPTION}'`;
})
.concat(
`'(-h --help)'{-h,--help}'[output usage information]'`,
hasCommands ? '"1: :->cmnds"' : null,
'"*::arg:->args"'
)
.filter(Boolean);

const commandBlockFunctionParts = [];

if (hasCommands) {
commandBlockFunctionParts.push("local -a commands");
}

if (hasOptions) {
commandBlockFunctionParts.push(
`_arguments -C \\\n ${_arguments.join(` \\\n `)}`
);
}

if (hasCommands) {
commandBlockFunctionParts.push(
`case $state in
cmnds)
commands=(
${commands
.map(({ _name, _description }) => `"${_name}:${_description}"`)
.join("\n ")}
)
_describe "command" commands
;;
esac

case "$words[1]" in
${commands
.map(({ _name }) =>
[`${_name})`, `_${name}_${_name}`, ";;"].join("\n ")
)
.join("\n ")}
esac`
);
}

const commandBlocParts = [
`function _${name} {\n ${commandBlockFunctionParts.join(
"\n\n "
)}\n}`,
];

if (hasCommands) {
commandBlocParts.push(
commands
.map((command) =>
renderCommandBlock(`${name}_${command._name}`, command)
)
.join("\n\n")
);
}

return commandBlocParts.join("\n\n");
};

const render = () => {
return [
`#compdef _${rootName} ${rootName}`,
"",
renderCommandBlock(rootName, rootCommand),
].join("\n");
};

return {
render,
};
};

const validShells = ["zsh"];

export class CompletionCommand {
constructor() {}

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 = zshCompletion("bw", cmd.parent).render();
}

const res = new MessageResponse(content, null);
return Response.success(res);
}
}
22 changes: 22 additions & 0 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import { ShareCommand } from './commands/share.command';
import { SyncCommand } from './commands/sync.command';
import { UnlockCommand } from './commands/unlock.command';

import { CompletionCommand } from './commands/completion.command';

import { LogoutCommand } from 'jslib/cli/commands/logout.command';
import { UpdateCommand } from 'jslib/cli/commands/update.command';

Expand Down Expand Up @@ -660,6 +662,26 @@ export class Program extends BaseProgram {
this.processResponse(response);
});

program
.command('completion')
.description('Generate shell completions.')
.option('--shell <shell>', 'Shell to generate completions for.')
.on('--help', () => {
writeLn('\n Notes:');
writeLn('');
writeLn(' Valid shells are `zsh`.')
writeLn('');
writeLn(' Examples:');
writeLn('');
writeLn(' bw completion --shell zsh');
writeLn('', true);
})
.action(async (cmd: program.Command) => {
const command = new CompletionCommand();
const response = await command.run(cmd);
this.processResponse(response);
})

program
.parse(process.argv);

Expand Down