diff --git a/NodeApp/src/commander/CommanderApp.ts b/NodeApp/src/commander/CommanderApp.ts index e4d16320e81c2c3555d988008372c6567f72f9f2..e70ce933f692f7cf8b4cac50fdd0b571cb5e4d11 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 {getBashCompletion, getFishCompletion} from '../helpers/AutoCompletionHelper'; +import { writeBashCompletion, writeFishCompletion } from '../helpers/AutoCompletionHelper'; class CommanderApp { @@ -44,8 +44,8 @@ class CommanderApp { this.registerCommands(); - getBashCompletion(this.program, "bash_completion.sh") - getFishCompletion(this.program, "dojo.fish") + writeBashCompletion(this.program, "bash_completion.sh") + writeFishCompletion(this.program, "dojo.fish") this.program.parse(); } diff --git a/NodeApp/src/helpers/AutoCompletionHelper.ts b/NodeApp/src/helpers/AutoCompletionHelper.ts index 34c5d7e3d7196fcbd382291a3f16c11237400ed5..0e3fbdb228aca95721443c2a9981ad22d25ed261 100644 --- a/NodeApp/src/helpers/AutoCompletionHelper.ts +++ b/NodeApp/src/helpers/AutoCompletionHelper.ts @@ -1,131 +1,36 @@ import { Command } from 'commander'; import { writeFileSync } from 'fs'; -// The following code should create a bash completion automatically from the Commander -// CLI library. The file should look something like that (it looks at the time -// this comment is written). +const fishFunction = ` +function __fish_dojo_using_commands + set cmd (commandline -opc) + set num_cmd (count $cmd) + if [ $num_cmd -eq $argv[1] ] + for i in (seq 1 (math $num_cmd)) + if [ $argv[(math $i+1)] != $cmd[$i] ] + return 1 + end + end + return 0 + end + return 1 +end -// #/usr/bin/env bash -// function _dojo_completions() -// { -// latest="${COMP_WORDS[$COMP_CWORD]}" -// parent="${COMP_WORDS[$COMP_CWORD - 1]}" -// grand_parent="${COMP_WORDS[$COMP_CWORD - 2]}" -// words="" -// case "${parent}" in -// dojo) -// words="session assignment exercise -V --version -H --host --help -h" -// ;; -// session) -// words="login logout test --help -h" -// ;; -// login) -// words="-c --cli --help -h" -// ;; -// logout) -// words="-f --force --help -h" -// ;; -// assignment) -// words="create check run publish unpublish --help -h" -// ;; -// create) -// case "${grand_parent}" in -// assignment) -// words="-n --name -i --members_id -u --members_username -t --template -c --clone --help -h" -// ;; -// *) -// ;; -// esac -// case "${grand_parent}" in -// exercise) -// words="-a --assignment -i --members_id -u --members_username -c --clone --help -h" -// ;; -// *) -// ;; -// esac -// ;; -// check) -// words="-p --path -v --verbose -w --super-verbose --help -h" -// ;; -// run) -// case "${grand_parent}" in -// assignment) -// words="-p --path -v --verbose -w --super-verbose --help -h" -// ;; -// *) -// ;; -// esac -// case "${grand_parent}" in -// exercise) -// words="-p --path -v --verbose -w --super-verbose --help -h" -// ;; -// *) -// ;; -// esac -// ;; -// publish) -// words="-f --force --help -h" -// ;; -// unpublish) -// words="-f --force --help -h" -// ;; -// exercise) -// words="create run --help -h" -// ;; -// *) -// ;; -// esac -// COMPREPLY=($(compgen -W "$words" -- $latest)) -// return 0 -// } +complete -f -c dojo +` function isLeaf(cmd: Command): boolean { return cmd.commands.length == 0 } -function search(cmd: Command, cmdName: string): Array<Command> { - if (isLeaf(cmd)) { - if (cmd.name() != cmdName) { - return [] - } else { - return [cmd] - } - } else if (cmd.name() == cmdName) { - return cmd.commands.flatMap(st => search(st, cmdName)).concat(cmd) - } else { - return cmd.commands.flatMap(st => search(st, cmdName)) - } -} - -function flatten(cmd: Command): Array<Command> { - if (isLeaf(cmd)) { - return [cmd] +function computeDepth(cmd: Command | undefined): number { + if (cmd === undefined) { + return 0 } else { - return cmd.commands - .map(child => flatten(child)) - .reduce((acc, cmd) => acc.concat(cmd), [cmd]) + return 1 + cmd.commands.map(subCmd => computeDepth(subCmd)).reduce((acc, depth) => depth > acc ? depth : acc, 0) } } -function addWordsGrandParents(cmd: Command): string { - let parentWords = "" - let ident = 3 - if (cmd.parent !== null) { - parentWords += openCase(ident, 'grand_parent') - ident += 1 - parentWords += addLine(ident, `${cmd.parent.name()})`) - ident += 1 - } - parentWords += addLine(ident, `words="${commandsAndOptionsToString(cmd)}"`) - ident -= 1 - parentWords += addLine(ident, ';;') - if (cmd.parent !== null) { - ident -= 1 - parentWords += `${closeCase(ident)}` - } - return parentWords -} - function getOptions(cmd: Command): string { // we remove <args>, [command], and , from option lines return cmd.options.filter(opt => !opt.hidden).map(opt => @@ -137,90 +42,223 @@ function commandsAndOptionsToString(cmd: Command): string { return cmd.commands.map(c => c.name()).join(" ").concat(' ' + getOptions(cmd)).trim().concat(' --help -h').trim() } -function openCase(identLevel: number, pattern: string): string { - return `${' '.repeat(identLevel)}case "\${${pattern}}" in\n` -} - -function closeCase(identLevel: number): string { - return `${' '.repeat(identLevel + 1)}*)\n` - + `${' '.repeat(identLevel + 1)};;\n` - + `${' '.repeat(identLevel)}esac\n` -} - function addLine(identLevel: number, pattern: string): string { return `${' '.repeat(identLevel)}${pattern}\n` } -const fishFunctions = `### Fish completions for dojo ### -function __fish_dojo_needs_command - set cmd (commandline -opc) - if [ (count $cmd) -eq 1 -a $cmd[1] = dojo ] - return 0 - end - return 1 -end +function generateBashSubCommands(cmd: Command, current: number, maxDepth: number, ident: number): string { + if (current == maxDepth) { + return addLine(ident, `case "\${COMP_WORDS[$COMP_CWORD - ${maxDepth - current + 1}]}" in`) + + addLine(ident + 1, `${cmd.name()})`) + + addLine(ident + 2, `words="${commandsAndOptionsToString(cmd)}"`) + + addLine(ident + 1, ';;') + + addLine(ident + 1, '*)') + + addLine(ident + 1, ';;') + + addLine(ident, 'esac') -function __fish_dojo_using_command - set cmd (commandline -opc) - set num_cmd (count $cmd) - if [ $num_cmd -eq 2 ] - if [ $argv[(math $num_cmd - 1)] = $cmd[$num_cmd] ] - return 0 - end - end - return 1 -end - -function __fish_dojo_using_two_commands - set cmd (commandline -opc) - set num_cmd (count $cmd) - if [ $num_cmd -gt 3 ] - if [ $argv[(math $num_cmd - 2)] = $cmd[(math $num_cmd - 1)] -a $argv[(math $num_cmd - 1)] = $cmd[$num_cmd] ] - return 0 - end - end - return 1 -end -` - -export function getFishCompletion(root: Command, filename: string) { - const commands = Array.from(new Set(flatten(root).filter(cmd => !isLeaf(cmd)).map(cmd => cmd.name()))).map(name => search(root, name)) - let data = fishFunctions - for (const node of commands) { - const cmd = node[0] - data += addLine(0, `complete -f -c dojo -n __fish_dojo_needs_command ${cmd.name()} -a "${commandsAndOptionsToString(cmd)}"`) + } else { + let data = addLine(ident, `case "\${COMP_WORDS[$COMP_CWORD - ${maxDepth - current + 1}]}" in`) + + addLine(ident + 1, `${cmd.name()})`) + cmd.commands.forEach(subCmd => { + data += generateBashSubCommands(subCmd, current + 1, maxDepth, ident + 2) + }) + data += + addLine(ident + 1, ';;') + + addLine(ident + 1, '*)') + + addLine(ident + 1, ';;') + + addLine(ident, 'esac') + return data } +} +export function writeBashCompletion(root: Command, filename: string) { + const depth = computeDepth(root) + let data = + addLine(0, '#/usr/bin/env bash\nfunction _dojo_completions()') + + addLine(0, '{') + + addLine(1, 'latest="${COMP_WORDS[$COMP_CWORD]}"') + for (let i = 1; i <= depth; i++) { + data += addLine(1, `${i == 1 ? 'if' : 'elif'} [ $COMP_CWORD -eq ${depth - i + 1} ]`) + + addLine(1, 'then') + data += generateBashSubCommands(root, i, depth, 2) + } + data += addLine(1, 'fi') + + addLine(1, 'COMPREPLY=($(compgen -W "$words" -- $latest))') + + addLine(1, 'return 0') + + addLine(0, '}') + + addLine(0, 'complete -F _dojo_completions dojo') writeFileSync(filename, data); } +const prefix = 'complete -f -c dojo -n \'__fish_dojo_using_commands' + +function generateFishSubCommands(cmd: Command, crtDepth: number): string { + let data = '' + for (const arg of cmd.commands) { + data += `${prefix} ${crtDepth} ${cmd.name()}' -a ${arg.name()} -d "${arg.description()}"\n` + } + return data +} + +export function writeFishCompletion(root: Command, filename: string) { + const depth = computeDepth(root) -export function getBashCompletion(root: Command, filename: string) { let data = - `${addLine(0, '#/usr/bin/env bash\nfunction _dojo_completions()')}` - + `${addLine(0, '{')}${addLine(1, 'latest="${COMP_WORDS[$COMP_CWORD]}"')}` - + `${addLine(1, 'parent="${COMP_WORDS[$COMP_CWORD - 1]}"')}` - + `${addLine(1, 'grand_parent="${COMP_WORDS[$COMP_CWORD - 2]}"')}` - + `${addLine(1, 'words=""')}` - + `${openCase(1, "parent")}` - const commands = Array.from(new Set(flatten(root).map(cmd => cmd.name()))).map(name => search(root, name)) - for (const cmdNode of commands) { - const cmd = cmdNode[0] - data += addLine(2, `${cmd.name()})`) - if (cmd !== undefined && cmdNode.length == 1) { - data += addLine(3, `words="${commandsAndOptionsToString(cmd)}"`) - } else if (cmd !== undefined && cmdNode.length > 1) { - cmdNode.forEach(n => data += addWordsGrandParents(n)) - } - data += addLine(2, ';;') + fishFunction + for (let i = 1; i <= depth; i++) { + data += generateFishSubCommands(root, i) } - data += - `${closeCase(1)}` - + `${addLine(1, 'COMPREPLY=($(compgen -W "$words" -- $latest))')}` - + `${addLine(1, 'return 0')}` - + `${addLine(0, '}')}` - + `${addLine(0, 'complete -F _dojo_completions dojo')}` writeFileSync(filename, data); } + + + +// The following code should create a bash completion automatically from the Commander +// CLI library. The file should look something like that (it looks at the time +// this comment is written). + +// #/usr/bin/env bash +// function _dojo_completions() +// { +// latest="${COMP_WORDS[$COMP_CWORD]}" +// if [ $COMP_CWORD -eq 3 ] +// then +// case "${COMP_WORDS[$COMP_CWORD - 3]}" in +// dojo) +// case "${COMP_WORDS[$COMP_CWORD - 2]}" in +// session) +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// login) +// words="-c --cli --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// logout) +// words="-f --force --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// test) +// words="--help -h" +// ;; +// *) +// ;; +// esac +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 2]}" in +// assignment) +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// create) +// words="-n --name -i --members_id -u --members_username -t --template -c --clone --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// check) +// words="-p --path -v --verbose -w --super-verbose --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// run) +// words="-p --path -v --verbose -w --super-verbose --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// publish) +// words="-f --force --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// unpublish) +// words="-f --force --help -h" +// ;; +// *) +// ;; +// esac +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 2]}" in +// exercise) +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// create) +// words="-a --assignment -i --members_id -u --members_username -c --clone --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// run) +// words="-p --path -v --verbose -w --super-verbose --help -h" +// ;; +// *) +// ;; +// esac +// ;; +// *) +// ;; +// esac +// ;; +// *) +// ;; +// esac +// elif [ $COMP_CWORD -eq 2 ] +// then +// case "${COMP_WORDS[$COMP_CWORD - 2]}" in +// dojo) +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// session) +// words="login logout test --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// assignment) +// words="create check run publish unpublish --help -h" +// ;; +// *) +// ;; +// esac +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// exercise) +// words="create run --help -h" +// ;; +// *) +// ;; +// esac +// ;; +// *) +// ;; +// esac +// elif [ $COMP_CWORD -eq 1 ] +// then +// case "${COMP_WORDS[$COMP_CWORD - 1]}" in +// dojo) +// words="session assignment exercise -V --version -H --host --help -h" +// ;; +// *) +// ;; +// esac +// fi +// COMPREPLY=($(compgen -W "$words" -- $latest)) +// return 0 +// } +// complete -F _dojo_completions dojo