Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Add shell autocomplete generation command #260

Merged
merged 2 commits into from
Jul 29, 2016
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ script:
- ./typo3cms install:setup --non-interactive --database-user-name="root" --database-host-name="localhost" --database-port="3306" --database-name="travis_test" --admin-user-name="admin" --admin-password="password" --site-name="Travis Install" --site-setup-type="createsite"
- echo 'select uid,title from pages limit 1' | ./typo3cms database:import | sed 's/[[:blank:]]//g' | grep 1Home
- ./typo3cms help
- ./typo3cms help:autocomplete
- ./typo3cms help:autocomplete bash
- ./typo3cms help:autocomplete zsh
- ./typo3cms backend:lock
- ./typo3cms backend:unlock
- ./typo3cms backend:lockforeditors
Expand Down
117 changes: 112 additions & 5 deletions Classes/Command/HelpCommandController.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class HelpCommandController extends CommandController
*
* @var string
*/
private $version = '3.2.3';
private $version = '3.3.0';

/**
* @var \Helhum\Typo3Console\Mvc\Cli\CommandManager
Expand All @@ -41,7 +41,7 @@ class HelpCommandController extends CommandController
/**
* @var Command[]
*/
protected $commands = array();
protected $commands = [];

/**
* Help
Expand Down Expand Up @@ -116,8 +116,8 @@ protected function displayHelpForCommand(\TYPO3\CMS\Extbase\Mvc\Cli\Command $com
$this->outputLine();
$this->outputLine('<comment>Usage:</comment>');
$this->outputLine(' ' . $usage);
$argumentDescriptions = array();
$optionDescriptions = array();
$argumentDescriptions = [];
$optionDescriptions = [];
if ($command->hasArguments()) {
foreach ($commandArgumentDefinitions as $commandArgumentDefinition) {
$argumentDescription = $commandArgumentDefinition->getDescription();
Expand Down Expand Up @@ -152,7 +152,7 @@ protected function displayHelpForCommand(\TYPO3\CMS\Extbase\Mvc\Cli\Command $com
}
}
$relatedCommandIdentifiers = $command->getRelatedCommandIdentifiers();
if ($relatedCommandIdentifiers !== array()) {
if ($relatedCommandIdentifiers !== []) {
$this->outputLine();
$this->outputLine('<comment>Related Commands:</comment>');
foreach ($relatedCommandIdentifiers as $commandIdentifier) {
Expand Down Expand Up @@ -186,6 +186,113 @@ public function errorCommand(\TYPO3\CMS\Extbase\Mvc\Exception\CommandException $
$this->outputLine('or <info>help</info> <command> for a detailed description of the corresponding command.');
}

/**
* Generate shell auto complete script
*
* Inspired by and copied code from https://github.com/bamarni/symfony-console-autocomplete
* See https://github.com/bamarni/symfony-console-autocomplete/blob/master/README.md
* for a description how to install the script in your system.
*
* @param string $shell "bash" or "zsh"
* @param array $aliases Aliases for the typo3cms command
* @throws \TYPO3\CMS\Extbase\Mvc\Exception\StopActionException
*/
public function autoCompleteCommand($shell = 'bash', array $aliases = [])
{
if (!in_array($shell, ['zsh', 'bash'], true)) {
$this->output->outputLine('<error>Shell can only be "bash" or "zsh"</error>');
$this->quit(1);
}
$this->buildCommandsIndex();
$commandsDescriptions = [];
$commandsOptionsDescriptions = [];
$commandsOptions = [];
$commands = [];
foreach ($this->commands as $commandIdentifier => $command) {
$commands[] = $commandIdentifier;
$commandsDescriptions[$commandIdentifier] = $command->getShortDescription();
$commandsOptionsDescriptions[$commandIdentifier] = [];
if ($command->hasArguments()) {
$commandOptions = [];
foreach ($command->getArgumentDefinitions() as $commandArgumentDefinition) {
if (!$commandArgumentDefinition->isRequired()) {
$name = $commandArgumentDefinition->getDashedName();
$commandOptions[] = $name;
}
}
$commandsOptions[$commandIdentifier] = $commandOptions;
}
}
$switchCaseStatementTemplate = 'opts="${opts} %%COMMAND_OPTIONS%%"';
if ('zsh' === $shell) {
$switchCaseStatementTemplate = 'opts+=(%%COMMAND_OPTIONS%%)';
}
// generate the switch content
$switchCaseTemplate = <<<SWITCHCASE
%%COMMAND%%)
$switchCaseStatementTemplate
;;
SWITCHCASE;

$switchContent = '';
$zsh_describe = function ($value, $description = null)
{
$value = '"' . str_replace(':', '\\:', $value);
if (!empty($description)) {
$value .= ':' . escapeshellcmd($description);
}

return $value . '"';
};
foreach ($commandsOptions as $command => $options) {
if (empty($options)) {
continue;
}
if ('zsh' === $shell) {
$options = array_map(function ($option) use ($command, $commandsOptionsDescriptions, $zsh_describe) {
return $zsh_describe($option, $commandsOptionsDescriptions[$command][$option]);
}, $options);
}

$switchContent .= str_replace(
array('%%COMMAND%%', '%%COMMAND_OPTIONS%%'),
array($command, implode(' ', $options)),
$switchCaseTemplate
)."\n ";
}
$switchContent = rtrim($switchContent, ' ');

// dump
$template = file_get_contents(__DIR__ . '/../../Resources/Private/AutocompleteTemplates/cached.' . $shell . '.tpl');
$script = 'typo3cms';
$tools = array($script);

if ($aliases) {
$aliases = array_filter(preg_split('/\s+/', implode(' ', $aliases)));
$tools = array_unique(array_merge($tools, $aliases));
}

if ('zsh' === $shell) {
$commands = array_map(function ($command) use ($commandsDescriptions, $zsh_describe) {
return $zsh_describe($command, $commandsDescriptions[$command]);
}, $commands);

$tools = array_map(function ($v) use ($script) {
return "compdef _$script $v";
}, $tools);
} else {
$tools = array_map(function ($v) use ($script) {
return "complete -o default -F _$script $v";
}, $tools);
}

$this->output->output(str_replace(
array('%%SCRIPT%%', '%%COMMANDS%%', '%%SHARED_OPTIONS%%', '%%SWITCH_CONTENT%%', '%%TOOLS%%'),
array($script, implode(' ', $commands), '', $switchContent, implode("\n", $tools)),
$template
));
}

/**
* Builds an index of available commands. For each of them a Command object is
* added to the commands array of this class.
Expand Down
4 changes: 2 additions & 2 deletions Documentation/Settings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
conf.py:
copyright: 2016
project: TYPO3 Console
version: 3.2.3
release: 3.2.3
version: 3.3.0
release: 3.3.0
latex_documents:
- - Index
- typo3_console.tex
Expand Down
49 changes: 49 additions & 0 deletions Resources/Private/AutocompleteTemplates/cached.bash.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
#!/usr/bin/env bash

_%%SCRIPT%%()
{
local cur script coms opts com
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="%%SHARED_OPTIONS%%"

case "$com" in
%%SWITCH_CONTENT%%
esac

COMPREPLY=($(compgen -W "${opts}" -- ${cur}))
__ltrim_colon_completions "$cur"

return 0;
fi

# completing for a command
if [[ $cur == $com ]]; then
coms="%%COMMANDS%%"

COMPREPLY=($(compgen -W "${coms}" -- ${cur}))
__ltrim_colon_completions "$cur"

return 0
fi
}

%%TOOLS%%
42 changes: 42 additions & 0 deletions Resources/Private/AutocompleteTemplates/cached.zsh.tpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env zsh

_%%SCRIPT%%()
{
local state com cur

cur=${words[${#words[@]}]}

# lookup for command
for word in ${words[@]:1}; do
if [[ $word != -* ]]; then
com=$word
break
fi
done

if [[ ${cur} == --* ]]; then
state="option"
opts=(%%SHARED_OPTIONS%%)
elif [[ $cur == $com ]]; then
state="command"
coms=(%%COMMANDS%%)
fi

case $state in
command)
_describe 'command' coms
;;
option)
case "$com" in
%%SWITCH_CONTENT%%
esac

_describe 'option' opts
;;
*)
# fallback to file completion
_arguments '*:file:_files'
esac
}

%%TOOLS%%
2 changes: 1 addition & 1 deletion Resources/Private/ExtensionArtifacts/ext_emconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'author' => 'Helmut Hummel',
'author_email' => 'info@helhum.io',
'author_company' => 'helhum.io',
'version' => '3.2.3',
'version' => '3.3.0',
'constraints' =>
array(
'depends' =>
Expand Down
2 changes: 1 addition & 1 deletion ext_emconf.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
'author' => 'Helmut Hummel',
'author_email' => 'info@helhum.io',
'author_company' => 'helhum.io',
'version' => '3.2.3',
'version' => '3.3.0',
'constraints' =>
array(
'depends' =>
Expand Down