From 5e601ba185ea2cd3397bea8f7362b33e70c35c47 Mon Sep 17 00:00:00 2001 From: Orestis <orestis.malaspinas@pm.me> Date: Mon, 19 Feb 2024 22:06:28 +0100 Subject: [PATCH] added all 3 completion commands --- NodeApp/src/commander/CommanderApp.ts | 6 +- .../commander/completion/CompletionCommand.ts | 25 +++++++ .../subcommands/CompletionBashCommand.ts | 61 ++++++++++++++++ .../subcommands/CompletionFishCommand.ts | 56 +++++++++++++++ .../subcommands/CompletionZshCommand.ts | 69 +++++++++++++++++++ NodeApp/src/helpers/AutoCompletionHelper.ts | 17 +++-- 6 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 NodeApp/src/commander/completion/CompletionCommand.ts create mode 100644 NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts create mode 100644 NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts create mode 100644 NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts index e70ce93..b729c95 100644 --- a/NodeApp/src/commander/CommanderApp.ts +++ b/NodeApp/src/commander/CommanderApp.ts @@ -9,7 +9,7 @@ import { stateConfigFile } from '../config/ConfigFiles'; import semver from 'semver/preload'; import { version } from '../config/Version'; import Config from '../config/Config'; -import { writeBashCompletion, writeFishCompletion } from '../helpers/AutoCompletionHelper'; +import CompletionCommand from './completion/CompletionCommand'; class CommanderApp { @@ -44,8 +44,7 @@ class CommanderApp { this.registerCommands(); - writeBashCompletion(this.program, "bash_completion.sh") - writeFishCompletion(this.program, "dojo.fish") + this.program.on this.program.parse(); } @@ -92,6 +91,7 @@ https://gitedu.hesge.ch/dojo_project/projects/ui/dojocli/-/releases/Latest`, { SessionCommand.registerOnCommand(this.program); AssignmentCommand.registerOnCommand(this.program); ExerciseCommand.registerOnCommand(this.program); + CompletionCommand.registerOnCommand(this.program); } } diff --git a/NodeApp/src/commander/completion/CompletionCommand.ts b/NodeApp/src/commander/completion/CompletionCommand.ts new file mode 100644 index 0000000..11df30f --- /dev/null +++ b/NodeApp/src/commander/completion/CompletionCommand.ts @@ -0,0 +1,25 @@ +import CommanderCommand from '../CommanderCommand'; +import CompletionBashCommand from './subcommands/CompletionBashCommand'; +import CompletionFishCommand from './subcommands/CompletionFishCommand'; +import CompletionZshCommand from './subcommands/CompletionZshCommand'; + + +class CompletionCommand extends CommanderCommand { + protected commandName: string = 'completion'; + + protected defineCommand() { + this.command + .description('Generate completions for bash, fish, or zsh'); + } + + protected defineSubCommands() { + CompletionBashCommand.registerOnCommand(this.command); + CompletionFishCommand.registerOnCommand(this.command); + CompletionZshCommand.registerOnCommand(this.command); + } + + protected async commandAction(): Promise<void> { } +} + + +export default new CompletionCommand(); \ No newline at end of file diff --git a/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts new file mode 100644 index 0000000..5dfde48 --- /dev/null +++ b/NodeApp/src/commander/completion/subcommands/CompletionBashCommand.ts @@ -0,0 +1,61 @@ +import { existsSync, writeFileSync } from 'fs'; +import inquirer from 'inquirer'; +import CommanderCommand from '../../CommanderCommand'; +import { generateBashCompletion, getRoot } from '../../../helpers/AutoCompletionHelper'; + +class CompletionBashCommand extends CommanderCommand { + protected commandName: string = 'bash'; + + protected default_filename: string = './dojo_bash_completion.sh' + + writeFile(filename: string) { + const installInstructions: string = ` +The easiest way to install the completion is to append the content of the +generated file to the end of the '~/.bash_completion' file or to +overwrite it, if it only contains the 'dojo' completion. + +This can be performed by either + +cat ${filename} > ~/.bash_completion # overwrites .bash_completion +cat ${filename} >> ~/.bash_completion # appends to .bash_completion + +For more details: <https://github.com/scop/bash-completion/blob/master/README.md> +` + try { + writeFileSync(filename, generateBashCompletion(getRoot(this.command))) + console.log(`Bash completion successfully written.`) + console.log(`${installInstructions}`) + } catch (error) { + console.log(`Error: ${error}, failed to write ${filename}`) + } + } + + protected defineCommand() { + this.command + .description('generate bash completion') + .option(`-f, --file <filename>', 'complete path of the filename where the bash completion will be stored (default to ${this.default_filename}).`) + .action(this.commandAction.bind(this)); + } + + protected async commandAction(options: { file?: string }): Promise<void> { + const filename = options.file ?? this.default_filename + if (existsSync(filename)) { + // File exists in path + const confirm: boolean = (await inquirer.prompt({ + name: 'confirm', + message: `${options.file} is going to be overwritten. Are you sure?`, + type: 'confirm', + default: false + })).confirm; + + if (confirm) { + this.writeFile(filename) + } + } else { + this.writeFile(filename) + } + } +} + + +export default new CompletionBashCommand(); \ No newline at end of file diff --git a/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts new file mode 100644 index 0000000..b6ad117 --- /dev/null +++ b/NodeApp/src/commander/completion/subcommands/CompletionFishCommand.ts @@ -0,0 +1,56 @@ +import { existsSync, writeFileSync } from 'fs'; +import { generateFishCompletion, getRoot } from '../../../helpers/AutoCompletionHelper'; +import CommanderCommand from '../../CommanderCommand'; +import inquirer from 'inquirer'; + + +class CompletionFishCommand extends CommanderCommand { + protected commandName: string = 'fish'; + + protected default_filename: string = './dojo.fish' + + writeFile(filename: string) { + const installInstructions: string = ` +The easiest way to install the completion is to copy the ${filename} into the +~/.config/fish/completions directory + +cp -i ${filename} ~/.config/fish/completions # interactive cp to avoid accidents +` + try { + writeFileSync(filename, generateFishCompletion(getRoot(this.command))) + console.log(`Bash completion successfully written.`) + console.log(`${installInstructions}`) + } catch (error) { + console.log(`Error: ${error}, failed to write ${filename}`) + } + } + + protected defineCommand() { + this.command + .description('generate fish completion') + .option('-f, --file <filename>', 'filename where the bash completion will be stored (default to ./dojo.fish).') + .action(this.commandAction.bind(this)); + } + + protected async commandAction(options: { file?: string }): Promise<void> { + const filename = options.file ?? this.default_filename + if (existsSync(filename)) { + // File exists in path + const confirm: boolean = (await inquirer.prompt({ + name: 'confirm', + message: `${options.file} is going to be overwritten. Are you sure?`, + type: 'confirm', + default: false + })).confirm; + + if (confirm) { + this.writeFile(filename) + } + } else { + this.writeFile(filename) + } + } +} + + +export default new CompletionFishCommand(); \ No newline at end of file diff --git a/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts b/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts new file mode 100644 index 0000000..d162dc9 --- /dev/null +++ b/NodeApp/src/commander/completion/subcommands/CompletionZshCommand.ts @@ -0,0 +1,69 @@ +import { existsSync, writeFileSync } from 'fs'; +import CommanderCommand from '../../CommanderCommand'; +import { generateBashCompletion, getRoot } from '../../../helpers/AutoCompletionHelper'; +import inquirer from 'inquirer'; + + + +class CompletionZshCommand extends CommanderCommand { + protected commandName: string = 'zsh'; + + protected default_filename: string = './dojo_bash_completion.sh' + + writeFile(filename: string) { + const installInstructions: string = ` +The easiest way to install the completion is to append the content of the +generated file to the end of the '~/.bash_completion' file or to +overwrite it, if it only contains the 'dojo' completion. + +This can be performed by either + +cat ${filename} > ~/.bash_completion # overwrites .bash_completion +cat ${filename} >> ~/.bash_completion # appends to .bash_completion + +For more details: <https://github.com/scop/bash-completion/blob/master/README.md> + +Next add the following lines to your ~/.zprofile file: + +autoload -U +X compinit && compinit +autoload -U +X bashcompinit && bashcompinit +source .bash_completion +` + try { + writeFileSync(filename, generateBashCompletion(getRoot(this.command))) + console.log(`Bash completion successfully written.`) + console.log(`${installInstructions}`) + } catch (error) { + console.log(`Error: ${error}, failed to write ${filename}`) + } + } + + protected async commandAction(options: { file?: string }): Promise<void> { + const filename = options.file ?? this.default_filename + if (existsSync(filename)) { + // File exists in path + const confirm: boolean = (await inquirer.prompt({ + name: 'confirm', + message: `${options.file} is going to be overwritten. Are you sure?`, + type: 'confirm', + default: false + })).confirm; + + if (confirm) { + this.writeFile(filename) + } + } else { + this.writeFile(filename) + } + } + + protected defineCommand() { + this.command + .description('generate zsh completion, which is derived from the bash completion') + .option('-f, --file <filename>', 'bash completion filename (defaults to ./dojo_bash_completion.sh).') + .action(this.commandAction.bind(this)); + } +} + + +export default new CompletionZshCommand(); \ No newline at end of file diff --git a/NodeApp/src/helpers/AutoCompletionHelper.ts b/NodeApp/src/helpers/AutoCompletionHelper.ts index 2e1f42e..3af7148 100644 --- a/NodeApp/src/helpers/AutoCompletionHelper.ts +++ b/NodeApp/src/helpers/AutoCompletionHelper.ts @@ -53,6 +53,15 @@ function computeHeight(cmd: Command | null): number { return height } +// Computes the maximum number of commands until the root is reached +export function getRoot(cmd: Command): Command { + if (cmd.parent == null) { + return cmd + } else { + return getRoot(cmd.parent) + } +} + function getOptions(cmd: Command): string { // we remove <args>, [command], and , from option lines return cmd.options.filter(opt => !opt.hidden).map(opt => @@ -93,7 +102,7 @@ function generateBashSubCommands(cmd: Command, current: number, maxDepth: number } } -export function writeBashCompletion(root: Command, filename: string) { +export function generateBashCompletion(root: Command): string { const depth = computeDepth(root) let data = addLine(0, '#/usr/bin/env bash\nfunction _dojo_completions()') @@ -110,7 +119,7 @@ export function writeBashCompletion(root: Command, filename: string) { + addLine(0, '}') + addLine(0, 'complete -F _dojo_completions dojo') - writeFileSync(filename, data); + return data } const prefix = 'complete -f -c dojo -n \'__fish_dojo_using_commands' @@ -134,7 +143,7 @@ function optionsToString(cmd: Command): string { }).join('\n').concat('\n') } -export function writeFishCompletion(root: Command, filename: string) { +export function generateFishCompletion(root: Command): string { const commands = flatten(root) const data = fishFunction.concat( @@ -149,7 +158,7 @@ export function writeFishCompletion(root: Command, filename: string) { }).join('\n').concat('\n')).join('') ) - writeFileSync(filename, data); + return data } -- GitLab