Add autocomplete command

This commit is contained in:
Anton Medvedev 2020-10-07 11:54:08 +02:00
parent f2a0f34fb1
commit da851a27ca
2 changed files with 132 additions and 0 deletions

View File

@ -0,0 +1,130 @@
<?php
/* (c) Anton Medvedev <anton@medv.io>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Deployer\Console;
use InvalidArgumentException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class AutocompleteCommand extends Command
{
protected function configure()
{
$this
->setName('autocomplete')
->setDescription('Add CLI autocomplete')
->setDefinition(array(
new InputOption('shell', null, InputOption::VALUE_REQUIRED, 'Shell type ("bash" or "zsh")', isset($_SERVER['SHELL']) ? basename($_SERVER['SHELL'], '.exe') : null),
));
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
$shell = $input->getOption('shell');
if (!in_array($shell, ['bash', 'zsh'])) {
throw new InvalidArgumentException("Completion is only available for bash and zsh, \"{$shell}\" given.");
}
$output->write($this->$shell());
return 0;
}
private function bash()
{
return <<<'BASH'
_dep()
{
local cur script com opts
COMPREPLY=()
_get_comp_words_by_ref -n : cur words
# for an alias, get the real script behind it
if [[ $(type -t ${words[0]}) == "alias" ]]; then
script=$(alias ${words[0]} | sed -E "s/alias ${words[0]}='(.*)'/\1/")
else
script=${words[0]}
fi
# lookup for command
for word in ${words[@]:1}; do
if [[ $word != -* ]]; then
com=$word
break
fi
done
# completing for an option
if [[ ${cur} == --* ]] ; then
opts=$script
[[ -n $com ]] && opts=$opts" -h "$com
opts=$($opts --no-ansi 2>/dev/null | sed -n '/Options/,/^$/p' | sed -e '1d;$d' | sed 's/[^--]*\(--.*\)/\1/' | sed -En 's/[^ ]*(-(-[[:alnum:]]+){1,}).*/\1/p' | awk '{$1=$1};1'; exit ${PIPESTATUS[0]});
[[ $? -eq 0 ]] || return 0;
COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
__ltrim_colon_completions "$cur"
return 0
fi
# completing for a command
if [[ $cur == $com ]]; then
coms=$($script list --raw 2>/dev/null | awk '{print $1}'; exit ${PIPESTATUS[0]})
[[ $? -eq 0 ]] || return 0;
COMPREPLY=($(compgen -W "${coms}" -- ${cur}))
__ltrim_colon_completions "$cur"
return 0;
fi
}
complete -o default -F _dep dep
BASH;
}
private function zsh()
{
return <<<'ZSH'
_dep()
{
local state com cur commands options
cur=${words[${#words[@]}]}
# lookup for command
for word in ${words[@]:1}; do
if [[ $word != -* ]]; then
com=$word
break
fi
done
[[ ${cur} == --* ]] && state="option"
[[ $cur == $com ]] && state="command"
case $state in
command)
commands=("${(@f)$(${words[1]} list --no-ansi --raw 2>/dev/null | awk '{ gsub(/:/, "\\:", $1); print }' | awk '{if (NF>1) print $1 ":" substr($0, index($0,$2)); else print $1}')}")
_describe 'command' commands
;;
option)
options=("${(@f)$(${words[1]} -h ${words[2]} --no-ansi 2>/dev/null | sed -n '/Options/,/^$/p' | sed -e '1d;$d' | sed 's/[^--]*\(--.*\)/\1/' | sed -En 's/[^ ]*(-(-[[:alnum:]]+){1,})[[:space:]]+(.*)/\1:\3/p' | awk '{$1=$1};1')}")
_describe 'option' options
;;
*)
# fallback to file completion
_arguments '*:file:_files'
esac
}
compdef _dep dep
ZSH;
}
}

View File

@ -14,6 +14,7 @@ use Deployer\Component\ProcessRunner\Printer;
use Deployer\Component\ProcessRunner\ProcessRunner;
use Deployer\Component\Ssh\Client;
use Deployer\Configuration\Configuration;
use Deployer\Console\AutocompleteCommand;
use Deployer\Console\CommandEvent;
use Deployer\Console\ConnectCommand;
use Deployer\Console\DiceCommand;
@ -203,6 +204,7 @@ class Deployer extends Container
public function init()
{
$this->addTaskCommands();
$this->getConsole()->add(new AutocompleteCommand());
$this->getConsole()->add(new ConnectCommand($this));
$this->getConsole()->add(new WorkerCommand($this));
$this->getConsole()->add(new DiceCommand());