Update Composer, update everything
This commit is contained in:
parent
ea3e94409f
commit
dda5c284b6
19527 changed files with 1135420 additions and 351004 deletions
5
vendor/stecman/symfony-console-completion/.gitignore
vendored
Normal file
5
vendor/stecman/symfony-console-completion/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
vendor
|
||||
.idea
|
||||
/build/
|
||||
phpunit.xml
|
||||
/composer.lock
|
28
vendor/stecman/symfony-console-completion/.travis.yml
vendored
Normal file
28
vendor/stecman/symfony-console-completion/.travis.yml
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
language: php
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.4
|
||||
- php: 5.5
|
||||
env: COMPOSER_OPTS="--prefer-lowest"
|
||||
- php: 5.6
|
||||
- php: 7.0
|
||||
- php: 7.1
|
||||
- php: 7.2
|
||||
- php: nightly
|
||||
- php: hhvm
|
||||
env: COMPOSER_OPTS="--prefer-lowest"
|
||||
|
||||
allow_failures:
|
||||
- php: nightly
|
||||
|
||||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -y bash zsh
|
||||
|
||||
install:
|
||||
- composer self-update
|
||||
- composer update $COMPOSER_OPTS
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit
|
21
vendor/stecman/symfony-console-completion/LICENCE
vendored
Normal file
21
vendor/stecman/symfony-console-completion/LICENCE
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Stephen Holdaway
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
253
vendor/stecman/symfony-console-completion/README.md
vendored
Normal file
253
vendor/stecman/symfony-console-completion/README.md
vendored
Normal file
|
@ -0,0 +1,253 @@
|
|||
# BASH/ZSH auto-complete for Symfony Console applications
|
||||
|
||||
[](https://travis-ci.org/stecman/symfony-console-completion)
|
||||
[](https://scrutinizer-ci.com/g/stecman/symfony-console-completion/?branch=master)
|
||||
|
||||
[](https://packagist.org/packages/stecman/symfony-console-completion)
|
||||
[](https://packagist.org/packages/stecman/symfony-console-completion)
|
||||
[](https://packagist.org/packages/stecman/symfony-console-completion)
|
||||
[](https://packagist.org/packages/stecman/symfony-console-completion)
|
||||
|
||||
This package provides automatic (tab) completion in BASH and ZSH for Symfony Console Component based applications. With zero configuration, this package allows completion of available command names and the options they provide. User code can define custom completion behaviour for argument and option values.
|
||||
|
||||
Example of zero-config use with Composer:
|
||||
|
||||

|
||||
|
||||
## Zero-config use
|
||||
|
||||
If you don't need any custom completion behaviour, you can simply add the completion command to your application:
|
||||
|
||||
1. Install `stecman/symfony-console-completion` using [composer](https://getcomposer.org/) by running:
|
||||
```
|
||||
$ composer require stecman/symfony-console-completion
|
||||
```
|
||||
|
||||
2. For standalone Symfony Console applications, add an instance of `CompletionCommand` to your application's `Application::getDefaultCommands()` method:
|
||||
|
||||
```php
|
||||
protected function getDefaultCommands()
|
||||
{
|
||||
//...
|
||||
$commands[] = new \Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand();
|
||||
//...
|
||||
}
|
||||
```
|
||||
|
||||
For Symfony Framework applications, register the `CompletionCommand` as a service in `app/config/services.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
#...
|
||||
console.completion_command:
|
||||
class: Stecman\Component\Symfony\Console\BashCompletion\CompletionCommand
|
||||
tags:
|
||||
- { name: console.command }
|
||||
#...
|
||||
```
|
||||
|
||||
3. Register completion for your application by running one of the following in a terminal, replacing `[program]` with the command you use to run your application (eg. 'composer'):
|
||||
|
||||
```bash
|
||||
# BASH ~4.x, ZSH
|
||||
source <([program] _completion --generate-hook)
|
||||
|
||||
# BASH ~3.x, ZSH
|
||||
[program] _completion --generate-hook | source /dev/stdin
|
||||
|
||||
# BASH (any version)
|
||||
eval $([program] _completion --generate-hook)
|
||||
```
|
||||
|
||||
By default this registers completion for the absolute path to you application, which will work if the program on accessible on your PATH. You can specify a program name to complete for instead using the `--program` option, which is required if you're using an alias to run the program.
|
||||
|
||||
4. If you want the completion to apply automatically for all new shell sessions, add the command from step 3 to your shell's profile (eg. `~/.bash_profile` or `~/.zshrc`)
|
||||
|
||||
Note: The type of shell (ZSH/BASH) is automatically detected using the `SHELL` environment variable at run time. In some circumstances, you may need to explicitly specify the shell type with the `--shell-type` option.
|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
The `--generate-hook` option of `CompletionCommand` generates a small shell script that registers a function with your shell's completion system to act as a bridge between the shell and the completion command in your application. When you request completion for your program (by pressing tab with your program name as the first word on the command line), the bridge function is run; passing the current command line contents and cursor position to `[program] _completion`, and feeding the resulting output back to the shell.
|
||||
|
||||
|
||||
## Defining value completions
|
||||
|
||||
By default, no completion results will be returned for option and argument values. There are two ways of defining custom completion values for values: extend `CompletionCommand`, or implement `CompletionAwareInterface`.
|
||||
|
||||
### Implementing `CompletionAwareInterface`
|
||||
|
||||
`CompletionAwareInterface` allows a command to be responsible for completing its own option and argument values. When completion is run with a command name specified (eg. `myapp mycommand ...`) and the named command implements this interface, the appropriate interface method is called automatically:
|
||||
|
||||
```php
|
||||
class MyCommand extends Command implements CompletionAwareInterface
|
||||
{
|
||||
...
|
||||
|
||||
public function completeOptionValues($optionName, CompletionContext $context)
|
||||
{
|
||||
if ($optionName == 'some-option') {
|
||||
return ['myvalue', 'other-value', 'word'];
|
||||
}
|
||||
}
|
||||
|
||||
public function completeArgumentValues($argumentName, CompletionContext $context)
|
||||
{
|
||||
if ($argumentName == 'package') {
|
||||
return $this->getPackageNamesFromDatabase($context->getCurrentWord());
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This method of generating completions doesn't support use of `CompletionInterface` implementations at the moment, which make it easy to share completion behaviour between commands. To use this functionality, you'll need write your value completions by extending `CompletionCommand`.
|
||||
|
||||
|
||||
### Extending `CompletionCommand`
|
||||
|
||||
Argument and option value completions can also be defined by extending `CompletionCommand` and overriding the `configureCompletion` method:
|
||||
|
||||
```php
|
||||
class MyCompletionCommand extends CompletionCommand
|
||||
{
|
||||
protected function configureCompletion(CompletionHandler $handler)
|
||||
{
|
||||
$handler->addHandlers([
|
||||
// Instances of Completion go here.
|
||||
// See below for examples.
|
||||
]);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### The `Completion` class
|
||||
|
||||
The following snippets demonstrate how the `Completion` class works with `CompletionHandler`, and some possible configurations. The examples are for an application with the signature:
|
||||
|
||||
`myapp (walk|run) [-w|--weather=""] direction`
|
||||
|
||||
|
||||
##### Command-specific argument completion with an array
|
||||
|
||||
```php
|
||||
$handler->addHandler(
|
||||
new Completion(
|
||||
'walk', // match command name
|
||||
'direction', // match argument/option name
|
||||
Completion::TYPE_ARGUMENT, // match definition type (option/argument)
|
||||
[ // array or callback for results
|
||||
'north',
|
||||
'east',
|
||||
'south',
|
||||
'west'
|
||||
]
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
This will complete the `direction` argument for this:
|
||||
|
||||
```bash
|
||||
$ myapp walk [tab]
|
||||
```
|
||||
|
||||
but not this:
|
||||
|
||||
```bash
|
||||
$ myapp run [tab]
|
||||
```
|
||||
|
||||
##### Non-command-specific (global) argument completion with a function
|
||||
|
||||
```php
|
||||
$handler->addHandler(
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'direction',
|
||||
Completion::TYPE_ARGUMENT,
|
||||
function() {
|
||||
return range(1, 10);
|
||||
}
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
This will complete the `direction` argument for both commands:
|
||||
|
||||
```bash
|
||||
$ myapp walk [tab]
|
||||
$ myapp run [tab]
|
||||
```
|
||||
|
||||
##### Option completion
|
||||
|
||||
Option handlers work the same way as argument handlers, except you use `Completion::TYPE_OPTION` for the type.
|
||||
|
||||
```php
|
||||
$handler->addHandler(
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'weather',
|
||||
Completion::TYPE_OPTION,
|
||||
[
|
||||
'raining',
|
||||
'sunny',
|
||||
'everything is on fire!'
|
||||
]
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
##### Completing the for both arguments and options
|
||||
|
||||
To have a completion run for both options and arguments matching the specified name, you can use the type `Completion::ALL_TYPES`. Combining this with `Completion::ALL_COMMANDS` and consistent option/argument naming throughout your application, it's easy to share completion behaviour between commands, options and arguments:
|
||||
|
||||
```php
|
||||
$handler->addHandler(
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'pacakge',
|
||||
Completion::ALL_TYPES,
|
||||
function() {
|
||||
// ...
|
||||
}
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
## Example completions
|
||||
|
||||
### Completing references from a Git repository
|
||||
|
||||
```php
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'ref',
|
||||
Completion::TYPE_OPTION,
|
||||
function () {
|
||||
$raw = shell_exec('git show-ref --abbr');
|
||||
if (preg_match_all('/refs\/(?:heads|tags)?\/?(.*)/', $raw, $matches)) {
|
||||
return $matches[1];
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Completing filesystem paths
|
||||
|
||||
This library provides the completion implementation `ShellPathCompletion` which defers path completion to the shell's built-in path completion behaviour rather than implementing it in PHP, so that users get the path completion behaviour they expect from their shell.
|
||||
|
||||
```php
|
||||
new Completion\ShellPathCompletion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'path',
|
||||
Completion::TYPE_OPTION
|
||||
)
|
||||
|
||||
```
|
||||
|
||||
## Behaviour notes
|
||||
|
||||
* Option shortcuts are not offered as completion options, however requesting completion (ie. pressing tab) on a valid option shortcut will complete.
|
||||
* Completion is not implemented for the `--option="value"` style of passing a value to an option, however `--option value` and `--option "value"` work and are functionally identical.
|
||||
* Value completion is always run for options marked as `InputOption::VALUE_OPTIONAL` since there is currently no way to determine the desired behaviour from the command line contents (ie. skip the optional value or complete for it)
|
28
vendor/stecman/symfony-console-completion/composer.json
vendored
Normal file
28
vendor/stecman/symfony-console-completion/composer.json
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"name": "stecman/symfony-console-completion",
|
||||
"description": "Automatic BASH completion for Symfony Console Component based applications.",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Stephen Holdaway",
|
||||
"email": "stephen@stecman.co.nz"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.2",
|
||||
"symfony/console": "~2.3 || ~3.0 || ~4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.8.36 || ~5.7 || ~6.4"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Stecman\\Component\\Symfony\\Console\\BashCompletion\\": "src/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "0.6.x-dev"
|
||||
}
|
||||
}
|
||||
}
|
24
vendor/stecman/symfony-console-completion/phpunit.xml.dist
vendored
Normal file
24
vendor/stecman/symfony-console-completion/phpunit.xml.dist
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
bootstrap="tests/bootstrap.php"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="Symfony console completion">
|
||||
<directory>./tests</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist processUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src</directory>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
180
vendor/stecman/symfony-console-completion/src/Completion.php
vendored
Normal file
180
vendor/stecman/symfony-console-completion/src/Completion.php
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion;
|
||||
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionInterface;
|
||||
|
||||
class Completion implements CompletionInterface
|
||||
{
|
||||
/**
|
||||
* The type of input (option/argument) the completion should be run for
|
||||
*
|
||||
* @see CompletionInterface::ALL_TYPES
|
||||
* @var string
|
||||
*/
|
||||
protected $type;
|
||||
|
||||
/**
|
||||
* The command name the completion should be run for
|
||||
*
|
||||
* @see CompletionInterface::ALL_COMMANDS
|
||||
* @var string|null
|
||||
*/
|
||||
protected $commandName;
|
||||
|
||||
/**
|
||||
* The option/argument name the completion should be run for
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $targetName;
|
||||
|
||||
/**
|
||||
* Array of values to return, or a callback to generate completion results with
|
||||
* The callback can be in any form accepted by call_user_func.
|
||||
*
|
||||
* @var callable|array
|
||||
*/
|
||||
protected $completion;
|
||||
|
||||
/**
|
||||
* Create a Completion with the command name set to CompletionInterface::ALL_COMMANDS
|
||||
*
|
||||
* @deprecated - This will be removed in 1.0.0 as it is redundant and isn't any more concise than what it implements.
|
||||
*
|
||||
* @param string $targetName
|
||||
* @param string $type
|
||||
* @param array|callable $completion
|
||||
* @return Completion
|
||||
*/
|
||||
public static function makeGlobalHandler($targetName, $type, $completion)
|
||||
{
|
||||
return new Completion(CompletionInterface::ALL_COMMANDS, $targetName, $type, $completion);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $commandName
|
||||
* @param string $targetName
|
||||
* @param string $type
|
||||
* @param array|callable $completion
|
||||
*/
|
||||
public function __construct($commandName, $targetName, $type, $completion)
|
||||
{
|
||||
$this->commandName = $commandName;
|
||||
$this->targetName = $targetName;
|
||||
$this->type = $type;
|
||||
$this->completion = $completion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the stored completion, or the results returned from the completion callback
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
if ($this->isCallable()) {
|
||||
return call_user_func($this->completion);
|
||||
}
|
||||
|
||||
return $this->completion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get type of input (option/argument) the completion should be run for
|
||||
*
|
||||
* @see CompletionInterface::ALL_TYPES
|
||||
* @return string|null
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set type of input (option/argument) the completion should be run for
|
||||
*
|
||||
* @see CompletionInterface::ALL_TYPES
|
||||
* @param string|null $type
|
||||
*/
|
||||
public function setType($type)
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the command name the completion should be run for
|
||||
*
|
||||
* @see CompletionInterface::ALL_COMMANDS
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCommandName()
|
||||
{
|
||||
return $this->commandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the command name the completion should be run for
|
||||
*
|
||||
* @see CompletionInterface::ALL_COMMANDS
|
||||
* @param string|null $commandName
|
||||
*/
|
||||
public function setCommandName($commandName)
|
||||
{
|
||||
$this->commandName = $commandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the option/argument name the completion should be run for
|
||||
*
|
||||
* @see setType()
|
||||
* @return string
|
||||
*/
|
||||
public function getTargetName()
|
||||
{
|
||||
return $this->targetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the option/argument name the completion should be run for
|
||||
*
|
||||
* @see getType()
|
||||
* @param string $targetName
|
||||
*/
|
||||
public function setTargetName($targetName)
|
||||
{
|
||||
$this->targetName = $targetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the array or callback configured for for the Completion
|
||||
*
|
||||
* @return array|callable
|
||||
*/
|
||||
public function getCompletion()
|
||||
{
|
||||
return $this->completion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the array or callback to return/run when Completion is run
|
||||
*
|
||||
* @see run()
|
||||
* @param array|callable $completion
|
||||
*/
|
||||
public function setCompletion($completion)
|
||||
{
|
||||
$this->completion = $completion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the configured completion value is a callback function
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isCallable()
|
||||
{
|
||||
return is_callable($this->completion);
|
||||
}
|
||||
}
|
27
vendor/stecman/symfony-console-completion/src/Completion/CompletionAwareInterface.php
vendored
Normal file
27
vendor/stecman/symfony-console-completion/src/Completion/CompletionAwareInterface.php
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Completion;
|
||||
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
|
||||
|
||||
interface CompletionAwareInterface
|
||||
{
|
||||
|
||||
/**
|
||||
* Return possible values for the named option
|
||||
*
|
||||
* @param string $optionName
|
||||
* @param CompletionContext $context
|
||||
* @return array
|
||||
*/
|
||||
public function completeOptionValues($optionName, CompletionContext $context);
|
||||
|
||||
/**
|
||||
* Return possible values for the named argument
|
||||
*
|
||||
* @param string $argumentName
|
||||
* @param CompletionContext $context
|
||||
* @return array
|
||||
*/
|
||||
public function completeArgumentValues($argumentName, CompletionContext $context);
|
||||
}
|
48
vendor/stecman/symfony-console-completion/src/Completion/CompletionInterface.php
vendored
Normal file
48
vendor/stecman/symfony-console-completion/src/Completion/CompletionInterface.php
vendored
Normal file
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Completion;
|
||||
|
||||
interface CompletionInterface
|
||||
{
|
||||
// Sugar for indicating that a Completion should run for all command names and for all types
|
||||
// Intended to avoid meaningless null parameters in the constructors of implementing classes
|
||||
const ALL_COMMANDS = null;
|
||||
const ALL_TYPES = null;
|
||||
|
||||
const TYPE_OPTION = 'option';
|
||||
const TYPE_ARGUMENT = 'argument';
|
||||
|
||||
/**
|
||||
* Return the type of input (option/argument) completion should be run for
|
||||
*
|
||||
* @see \Symfony\Component\Console\Command\Command::addArgument
|
||||
* @see \Symfony\Component\Console\Command\Command::addOption
|
||||
* @return string - one of the CompletionInterface::TYPE_* constants
|
||||
*/
|
||||
public function getType();
|
||||
|
||||
/**
|
||||
* Return the name of the command completion should be run for
|
||||
* If the return value is CompletionInterface::ALL_COMMANDS, the completion will be run for any command name
|
||||
*
|
||||
* @see \Symfony\Component\Console\Command\Command::setName
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCommandName();
|
||||
|
||||
/**
|
||||
* Return the option/argument name the completion should be run for
|
||||
* CompletionInterface::getType determines whether the target name refers to an option or an argument
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTargetName();
|
||||
|
||||
/**
|
||||
* Execute the completion
|
||||
*
|
||||
* @return string[] - an array of possible completion values
|
||||
*/
|
||||
public function run();
|
||||
}
|
65
vendor/stecman/symfony-console-completion/src/Completion/ShellPathCompletion.php
vendored
Normal file
65
vendor/stecman/symfony-console-completion/src/Completion/ShellPathCompletion.php
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Completion;
|
||||
|
||||
/**
|
||||
* Shell Path Completion
|
||||
*
|
||||
* Defers completion to the calling shell's built-in path completion functionality.
|
||||
*/
|
||||
class ShellPathCompletion implements CompletionInterface
|
||||
{
|
||||
/**
|
||||
* Exit code set up to trigger path completion in the completion hooks
|
||||
* @see Stecman\Component\Symfony\Console\BashCompletion\HookFactory
|
||||
*/
|
||||
const PATH_COMPLETION_EXIT_CODE = 200;
|
||||
|
||||
protected $type;
|
||||
|
||||
protected $commandName;
|
||||
|
||||
protected $targetName;
|
||||
|
||||
public function __construct($commandName, $targetName, $type)
|
||||
{
|
||||
$this->commandName = $commandName;
|
||||
$this->targetName = $targetName;
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getType()
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getCommandName()
|
||||
{
|
||||
return $this->commandName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
public function getTargetName()
|
||||
{
|
||||
return $this->targetName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exit with a status code configured to defer completion to the shell
|
||||
*
|
||||
* @see \Stecman\Component\Symfony\Console\BashCompletion\HookFactory::$hooks
|
||||
*/
|
||||
public function run()
|
||||
{
|
||||
exit(self::PATH_COMPLETION_EXIT_CODE);
|
||||
}
|
||||
}
|
149
vendor/stecman/symfony-console-completion/src/CompletionCommand.php
vendored
Normal file
149
vendor/stecman/symfony-console-completion/src/CompletionCommand.php
vendored
Normal file
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion;
|
||||
|
||||
use Symfony\Component\Console\Command\Command as SymfonyCommand;
|
||||
use Symfony\Component\Console\Input\InputDefinition;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class CompletionCommand extends SymfonyCommand
|
||||
{
|
||||
|
||||
/**
|
||||
* @var CompletionHandler
|
||||
*/
|
||||
protected $handler;
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('_completion')
|
||||
->setDefinition($this->createDefinition())
|
||||
->setDescription('BASH completion hook.')
|
||||
->setHelp(<<<END
|
||||
To enable BASH completion, run:
|
||||
|
||||
<comment>eval `[program] _completion -g`</comment>.
|
||||
|
||||
Or for an alias:
|
||||
|
||||
<comment>eval `[program] _completion -g -p [alias]`</comment>.
|
||||
|
||||
END
|
||||
);
|
||||
|
||||
// Hide this command from listing if supported
|
||||
// Command::setHidden() was not available before Symfony 3.2.0
|
||||
if (method_exists($this, 'setHidden')) {
|
||||
$this->setHidden(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getNativeDefinition()
|
||||
{
|
||||
return $this->createDefinition();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->handler = new CompletionHandler($this->getApplication());
|
||||
$handler = $this->handler;
|
||||
|
||||
if ($input->getOption('generate-hook')) {
|
||||
global $argv;
|
||||
$program = $argv[0];
|
||||
|
||||
$factory = new HookFactory();
|
||||
$alias = $input->getOption('program');
|
||||
$multiple = (bool)$input->getOption('multiple');
|
||||
|
||||
if (!$alias) {
|
||||
$alias = basename($program);
|
||||
}
|
||||
|
||||
$hook = $factory->generateHook(
|
||||
$input->getOption('shell-type') ?: $this->getShellType(),
|
||||
$program,
|
||||
$alias,
|
||||
$multiple
|
||||
);
|
||||
|
||||
$output->write($hook, true);
|
||||
} else {
|
||||
$handler->setContext(new EnvironmentCompletionContext());
|
||||
$output->write($this->runCompletion(), true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the completion handler and return a filtered list of results
|
||||
*
|
||||
* @deprecated - This will be removed in 1.0.0 in favour of CompletionCommand::configureCompletion
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function runCompletion()
|
||||
{
|
||||
$this->configureCompletion($this->handler);
|
||||
return $this->handler->runCompletion();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the CompletionHandler instance before it is run
|
||||
*
|
||||
* @param CompletionHandler $handler
|
||||
*/
|
||||
protected function configureCompletion(CompletionHandler $handler)
|
||||
{
|
||||
// Override this method to configure custom value completions
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the shell type for use with HookFactory
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getShellType()
|
||||
{
|
||||
if (!getenv('SHELL')) {
|
||||
throw new \RuntimeException('Could not read SHELL environment variable. Please specify your shell type using the --shell-type option.');
|
||||
}
|
||||
|
||||
return basename(getenv('SHELL'));
|
||||
}
|
||||
|
||||
protected function createDefinition()
|
||||
{
|
||||
return new InputDefinition(array(
|
||||
new InputOption(
|
||||
'generate-hook',
|
||||
'g',
|
||||
InputOption::VALUE_NONE,
|
||||
'Generate BASH code that sets up completion for this application.'
|
||||
),
|
||||
new InputOption(
|
||||
'program',
|
||||
'p',
|
||||
InputOption::VALUE_REQUIRED,
|
||||
"Program name that should trigger completion\n<comment>(defaults to the absolute application path)</comment>."
|
||||
),
|
||||
new InputOption(
|
||||
'multiple',
|
||||
'm',
|
||||
InputOption::VALUE_NONE,
|
||||
"Generated hook can be used for multiple applications."
|
||||
),
|
||||
new InputOption(
|
||||
'shell-type',
|
||||
null,
|
||||
InputOption::VALUE_OPTIONAL,
|
||||
'Set the shell type (zsh or bash). Otherwise this is determined automatically.'
|
||||
),
|
||||
));
|
||||
}
|
||||
}
|
256
vendor/stecman/symfony-console-completion/src/CompletionContext.php
vendored
Normal file
256
vendor/stecman/symfony-console-completion/src/CompletionContext.php
vendored
Normal file
|
@ -0,0 +1,256 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion;
|
||||
|
||||
/**
|
||||
* Command line context for completion
|
||||
*
|
||||
* Represents the current state of the command line that is being completed
|
||||
*/
|
||||
class CompletionContext
|
||||
{
|
||||
/**
|
||||
* The current contents of the command line as a single string
|
||||
*
|
||||
* Bash equivalent: COMP_LINE
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $commandLine;
|
||||
|
||||
/**
|
||||
* The index of the user's cursor relative to the start of the command line.
|
||||
*
|
||||
* If the current cursor position is at the end of the current command,
|
||||
* the value of this variable is equal to the length of $this->commandLine
|
||||
*
|
||||
* Bash equivalent: COMP_POINT
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $charIndex = 0;
|
||||
|
||||
/**
|
||||
* An array containing the individual words in the current command line.
|
||||
*
|
||||
* This is not set until $this->splitCommand() is called, when it is populated by
|
||||
* $commandLine exploded by $wordBreaks
|
||||
*
|
||||
* Bash equivalent: COMP_WORDS
|
||||
*
|
||||
* @var array|null
|
||||
*/
|
||||
protected $words = null;
|
||||
|
||||
/**
|
||||
* The index in $this->words containing the word at the current cursor position.
|
||||
*
|
||||
* This is not set until $this->splitCommand() is called.
|
||||
*
|
||||
* Bash equivalent: COMP_CWORD
|
||||
*
|
||||
* @var int|null
|
||||
*/
|
||||
protected $wordIndex = null;
|
||||
|
||||
/**
|
||||
* Characters that $this->commandLine should be split on to get a list of individual words
|
||||
*
|
||||
* Bash equivalent: COMP_WORDBREAKS
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $wordBreaks = "'\"()= \t\n";
|
||||
|
||||
/**
|
||||
* Set the whole contents of the command line as a string
|
||||
*
|
||||
* @param string $commandLine
|
||||
*/
|
||||
public function setCommandLine($commandLine)
|
||||
{
|
||||
$this->commandLine = $commandLine;
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current command line verbatim as a string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCommandLine()
|
||||
{
|
||||
return $this->commandLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the word from the command line that the cursor is currently in
|
||||
*
|
||||
* Most of the time this will be a partial word. If the cursor has a space before it,
|
||||
* this will return an empty string, indicating a new word.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCurrentWord()
|
||||
{
|
||||
if (isset($this->words[$this->wordIndex])) {
|
||||
return $this->words[$this->wordIndex];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a word by index from the command line
|
||||
*
|
||||
* @see $words, $wordBreaks
|
||||
* @param int $index
|
||||
* @return string
|
||||
*/
|
||||
public function getWordAtIndex($index)
|
||||
{
|
||||
if (isset($this->words[$index])) {
|
||||
return $this->words[$index];
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the contents of the command line, exploded into words based on the configured word break characters
|
||||
*
|
||||
* @see $wordBreaks, setWordBreaks
|
||||
* @return array
|
||||
*/
|
||||
public function getWords()
|
||||
{
|
||||
if ($this->words === null) {
|
||||
$this->splitCommand();
|
||||
}
|
||||
|
||||
return $this->words;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the word the cursor is currently in
|
||||
*
|
||||
* @see getWords, getCurrentWord
|
||||
* @return int
|
||||
*/
|
||||
public function getWordIndex()
|
||||
{
|
||||
if ($this->wordIndex === null) {
|
||||
$this->splitCommand();
|
||||
}
|
||||
|
||||
return $this->wordIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the character index of the user's cursor on the command line
|
||||
*
|
||||
* This is in the context of the full command line string, so includes word break characters.
|
||||
* Note that some shells can only provide an approximation for character index. Under ZSH for
|
||||
* example, this will always be the character at the start of the current word.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCharIndex()
|
||||
{
|
||||
return $this->charIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the cursor position as a character index relative to the start of the command line
|
||||
*
|
||||
* @param int $index
|
||||
*/
|
||||
public function setCharIndex($index)
|
||||
{
|
||||
$this->charIndex = $index;
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set characters to use as split points when breaking the command line into words
|
||||
*
|
||||
* This defaults to a sane value based on BASH's word break characters and shouldn't
|
||||
* need to be changed unless your completions contain the default word break characters.
|
||||
*
|
||||
* @see wordBreaks
|
||||
* @param string $charList - a single string containing all of the characters to break words on
|
||||
*/
|
||||
public function setWordBreaks($charList)
|
||||
{
|
||||
$this->wordBreaks = $charList;
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Split the command line into words using the configured word break characters
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function splitCommand()
|
||||
{
|
||||
$this->words = array();
|
||||
$this->wordIndex = null;
|
||||
$cursor = 0;
|
||||
|
||||
$breaks = preg_quote($this->wordBreaks);
|
||||
|
||||
if (!preg_match_all("/([^$breaks]*)([$breaks]*)/", $this->commandLine, $matches)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Groups:
|
||||
// 1: Word
|
||||
// 2: Break characters
|
||||
foreach ($matches[0] as $index => $wholeMatch) {
|
||||
// Determine which word the cursor is in
|
||||
$cursor += strlen($wholeMatch);
|
||||
$word = $matches[1][$index];
|
||||
$breaks = $matches[2][$index];
|
||||
|
||||
if ($this->wordIndex === null && $cursor >= $this->charIndex) {
|
||||
$this->wordIndex = $index;
|
||||
|
||||
// Find the user's cursor position relative to the end of this word
|
||||
// The end of the word is the internal cursor minus any break characters that were captured
|
||||
$cursorWordOffset = $this->charIndex - ($cursor - strlen($breaks));
|
||||
|
||||
if ($cursorWordOffset < 0) {
|
||||
// Cursor is inside the word - truncate the word at the cursor
|
||||
// (This emulates normal BASH completion behaviour I've observed, though I'm not entirely sure if it's useful)
|
||||
$word = substr($word, 0, strlen($word) + $cursorWordOffset);
|
||||
|
||||
} elseif ($cursorWordOffset > 0) {
|
||||
// Cursor is in the break-space after a word
|
||||
// Push an empty word at the cursor to allow completion of new terms at the cursor, ignoring words ahead
|
||||
$this->wordIndex++;
|
||||
$this->words[] = $word;
|
||||
$this->words[] = '';
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ($word !== '') {
|
||||
$this->words[] = $word;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->wordIndex > count($this->words) - 1) {
|
||||
$this->wordIndex = count($this->words) - 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the computed words so that $this->splitWords is forced to run again
|
||||
*/
|
||||
protected function reset()
|
||||
{
|
||||
$this->words = null;
|
||||
$this->wordIndex = null;
|
||||
}
|
||||
}
|
472
vendor/stecman/symfony-console-completion/src/CompletionHandler.php
vendored
Normal file
472
vendor/stecman/symfony-console-completion/src/CompletionHandler.php
vendored
Normal file
|
@ -0,0 +1,472 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion;
|
||||
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionInterface;
|
||||
use Symfony\Component\Console\Application;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class CompletionHandler
|
||||
{
|
||||
/**
|
||||
* Application to complete for
|
||||
* @var \Symfony\Component\Console\Application
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
/**
|
||||
* @var Command
|
||||
*/
|
||||
protected $command;
|
||||
|
||||
/**
|
||||
* @var CompletionContext
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* Array of completion helpers.
|
||||
* @var CompletionInterface[]
|
||||
*/
|
||||
protected $helpers = array();
|
||||
|
||||
public function __construct(Application $application, CompletionContext $context = null)
|
||||
{
|
||||
$this->application = $application;
|
||||
$this->context = $context;
|
||||
|
||||
// Set up completions for commands that are built-into Application
|
||||
$this->addHandler(
|
||||
new Completion(
|
||||
'help',
|
||||
'command_name',
|
||||
Completion::TYPE_ARGUMENT,
|
||||
$this->getCommandNames()
|
||||
)
|
||||
);
|
||||
|
||||
$this->addHandler(
|
||||
new Completion(
|
||||
'list',
|
||||
'namespace',
|
||||
Completion::TYPE_ARGUMENT,
|
||||
$application->getNamespaces()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function setContext(CompletionContext $context)
|
||||
{
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CompletionContext
|
||||
*/
|
||||
public function getContext()
|
||||
{
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CompletionInterface[] $array
|
||||
*/
|
||||
public function addHandlers(array $array)
|
||||
{
|
||||
$this->helpers = array_merge($this->helpers, $array);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param CompletionInterface $helper
|
||||
*/
|
||||
public function addHandler(CompletionInterface $helper)
|
||||
{
|
||||
$this->helpers[] = $helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the actual completion, returning an array of strings to provide to the parent shell's completion system
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
* @return string[]
|
||||
*/
|
||||
public function runCompletion()
|
||||
{
|
||||
if (!$this->context) {
|
||||
throw new \RuntimeException('A CompletionContext must be set before requesting completion.');
|
||||
}
|
||||
|
||||
$cmdName = $this->getInput()->getFirstArgument();
|
||||
|
||||
try {
|
||||
$this->command = $this->application->find($cmdName);
|
||||
} catch (\InvalidArgumentException $e) {
|
||||
// Exception thrown, when multiple or none commands are found.
|
||||
}
|
||||
|
||||
$process = array(
|
||||
'completeForOptionValues',
|
||||
'completeForOptionShortcuts',
|
||||
'completeForOptionShortcutValues',
|
||||
'completeForOptions',
|
||||
'completeForCommandName',
|
||||
'completeForCommandArguments'
|
||||
);
|
||||
|
||||
foreach ($process as $methodName) {
|
||||
$result = $this->{$methodName}();
|
||||
|
||||
if (false !== $result) {
|
||||
// Return the result of the first completion mode that matches
|
||||
return $this->filterResults((array) $result);
|
||||
}
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an InputInterface representation of the completion context
|
||||
*
|
||||
* @return ArrayInput
|
||||
*/
|
||||
public function getInput()
|
||||
{
|
||||
// Filter the command line content to suit ArrayInput
|
||||
$words = $this->context->getWords();
|
||||
array_shift($words);
|
||||
$words = array_filter($words);
|
||||
|
||||
return new ArrayInput($words);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to complete the current word as a long-form option (--my-option)
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function completeForOptions()
|
||||
{
|
||||
$word = $this->context->getCurrentWord();
|
||||
|
||||
if (substr($word, 0, 2) === '--') {
|
||||
$options = array();
|
||||
|
||||
foreach ($this->getAllOptions() as $opt) {
|
||||
$options[] = '--'.$opt->getName();
|
||||
}
|
||||
|
||||
return $options;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to complete the current word as an option shortcut.
|
||||
*
|
||||
* If the shortcut exists it will be completed, but a list of possible shortcuts is never returned for completion.
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function completeForOptionShortcuts()
|
||||
{
|
||||
$word = $this->context->getCurrentWord();
|
||||
|
||||
if (strpos($word, '-') === 0 && strlen($word) == 2) {
|
||||
$definition = $this->command ? $this->command->getNativeDefinition() : $this->application->getDefinition();
|
||||
|
||||
if ($definition->hasShortcut(substr($word, 1))) {
|
||||
return array($word);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to complete the current word as the value of an option shortcut
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function completeForOptionShortcutValues()
|
||||
{
|
||||
$wordIndex = $this->context->getWordIndex();
|
||||
|
||||
if ($this->command && $wordIndex > 1) {
|
||||
$left = $this->context->getWordAtIndex($wordIndex - 1);
|
||||
|
||||
// Complete short options
|
||||
if ($left[0] == '-' && strlen($left) == 2) {
|
||||
$shortcut = substr($left, 1);
|
||||
$def = $this->command->getNativeDefinition();
|
||||
|
||||
if (!$def->hasShortcut($shortcut)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$opt = $def->getOptionForShortcut($shortcut);
|
||||
if ($opt->isValueRequired() || $opt->isValueOptional()) {
|
||||
return $this->completeOption($opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attemp to complete the current word as the value of a long-form option
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function completeForOptionValues()
|
||||
{
|
||||
$wordIndex = $this->context->getWordIndex();
|
||||
|
||||
if ($this->command && $wordIndex > 1) {
|
||||
$left = $this->context->getWordAtIndex($wordIndex - 1);
|
||||
|
||||
if (strpos($left, '--') === 0) {
|
||||
$name = substr($left, 2);
|
||||
$def = $this->command->getNativeDefinition();
|
||||
|
||||
if (!$def->hasOption($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$opt = $def->getOption($name);
|
||||
if ($opt->isValueRequired() || $opt->isValueOptional()) {
|
||||
return $this->completeOption($opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to complete the current word as a command name
|
||||
*
|
||||
* @return array|false
|
||||
*/
|
||||
protected function completeForCommandName()
|
||||
{
|
||||
if (!$this->command || (count($this->context->getWords()) == 2 && $this->context->getWordIndex() == 1)) {
|
||||
return $this->getCommandNames();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to complete the current word as a command argument value
|
||||
*
|
||||
* @see Symfony\Component\Console\Input\InputArgument
|
||||
* @return array|false
|
||||
*/
|
||||
protected function completeForCommandArguments()
|
||||
{
|
||||
if (!$this->command || strpos($this->context->getCurrentWord(), '-') === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$definition = $this->command->getNativeDefinition();
|
||||
$argWords = $this->mapArgumentsToWords($definition->getArguments());
|
||||
$wordIndex = $this->context->getWordIndex();
|
||||
|
||||
if (isset($argWords[$wordIndex])) {
|
||||
$name = $argWords[$wordIndex];
|
||||
} elseif (!empty($argWords) && $definition->getArgument(end($argWords))->isArray()) {
|
||||
$name = end($argWords);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($helper = $this->getCompletionHelper($name, Completion::TYPE_ARGUMENT)) {
|
||||
return $helper->run();
|
||||
}
|
||||
|
||||
if ($this->command instanceof CompletionAwareInterface) {
|
||||
return $this->command->completeArgumentValues($name, $this->context);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a CompletionInterface that matches the current command, target name, and target type
|
||||
*
|
||||
* @param string $name
|
||||
* @param string $type
|
||||
* @return CompletionInterface|null
|
||||
*/
|
||||
protected function getCompletionHelper($name, $type)
|
||||
{
|
||||
foreach ($this->helpers as $helper) {
|
||||
if ($helper->getType() != $type && $helper->getType() != CompletionInterface::ALL_TYPES) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($helper->getCommandName() == CompletionInterface::ALL_COMMANDS || $helper->getCommandName() == $this->command->getName()) {
|
||||
if ($helper->getTargetName() == $name) {
|
||||
return $helper;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete the value for the given option if a value completion is availble
|
||||
*
|
||||
* @param InputOption $option
|
||||
* @return array|false
|
||||
*/
|
||||
protected function completeOption(InputOption $option)
|
||||
{
|
||||
if ($helper = $this->getCompletionHelper($option->getName(), Completion::TYPE_OPTION)) {
|
||||
return $helper->run();
|
||||
}
|
||||
|
||||
if ($this->command instanceof CompletionAwareInterface) {
|
||||
return $this->command->completeOptionValues($option->getName(), $this->context);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Step through the command line to determine which word positions represent which argument values
|
||||
*
|
||||
* The word indexes of argument values are found by eliminating words that are known to not be arguments (options,
|
||||
* option values, and command names). Any word that doesn't match for elimination is assumed to be an argument value,
|
||||
*
|
||||
* @param InputArgument[] $argumentDefinitions
|
||||
* @return array as [argument name => word index on command line]
|
||||
*/
|
||||
protected function mapArgumentsToWords($argumentDefinitions)
|
||||
{
|
||||
$argumentPositions = array();
|
||||
$argumentNumber = 0;
|
||||
$previousWord = null;
|
||||
$argumentNames = array_keys($argumentDefinitions);
|
||||
|
||||
// Build a list of option values to filter out
|
||||
$optionsWithArgs = $this->getOptionWordsWithValues();
|
||||
|
||||
foreach ($this->context->getWords() as $wordIndex => $word) {
|
||||
// Skip program name, command name, options, and option values
|
||||
if ($wordIndex < 2
|
||||
|| ($word && '-' === $word[0])
|
||||
|| in_array($previousWord, $optionsWithArgs)) {
|
||||
$previousWord = $word;
|
||||
continue;
|
||||
} else {
|
||||
$previousWord = $word;
|
||||
}
|
||||
|
||||
// If argument n exists, pair that argument's name with the current word
|
||||
if (isset($argumentNames[$argumentNumber])) {
|
||||
$argumentPositions[$wordIndex] = $argumentNames[$argumentNumber];
|
||||
}
|
||||
|
||||
$argumentNumber++;
|
||||
}
|
||||
|
||||
return $argumentPositions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a list of option words/flags that will have a value after them
|
||||
* Options are returned in the format they appear as on the command line.
|
||||
*
|
||||
* @return string[] - eg. ['--myoption', '-m', ... ]
|
||||
*/
|
||||
protected function getOptionWordsWithValues()
|
||||
{
|
||||
$strings = array();
|
||||
|
||||
foreach ($this->getAllOptions() as $option) {
|
||||
if ($option->isValueRequired()) {
|
||||
$strings[] = '--' . $option->getName();
|
||||
|
||||
if ($option->getShortcut()) {
|
||||
$strings[] = '-' . $option->getShortcut();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $strings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter out results that don't match the current word on the command line
|
||||
*
|
||||
* @param string[] $array
|
||||
* @return string[]
|
||||
*/
|
||||
protected function filterResults(array $array)
|
||||
{
|
||||
$curWord = $this->context->getCurrentWord();
|
||||
|
||||
return array_filter($array, function($val) use ($curWord) {
|
||||
return fnmatch($curWord.'*', $val);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the combined options of the application and entered command
|
||||
*
|
||||
* @return InputOption[]
|
||||
*/
|
||||
protected function getAllOptions()
|
||||
{
|
||||
if (!$this->command) {
|
||||
return $this->application->getDefinition()->getOptions();
|
||||
}
|
||||
|
||||
return array_merge(
|
||||
$this->command->getNativeDefinition()->getOptions(),
|
||||
$this->application->getDefinition()->getOptions()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get command names available for completion
|
||||
*
|
||||
* Filters out hidden commands where supported.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getCommandNames()
|
||||
{
|
||||
// Command::Hidden isn't supported before Symfony Console 3.2.0
|
||||
// We don't complete hidden command names as these are intended to be private
|
||||
if (method_exists('\Symfony\Component\Console\Command\Command', 'isHidden')) {
|
||||
$commands = array();
|
||||
|
||||
foreach ($this->application->all() as $name => $command) {
|
||||
if (!$command->isHidden()) {
|
||||
$commands[] = $name;
|
||||
}
|
||||
}
|
||||
|
||||
return $commands;
|
||||
|
||||
} else {
|
||||
|
||||
// Fallback for compatibility with Symfony Console < 3.2.0
|
||||
// This was the behaviour prior to pull #75
|
||||
$commands = $this->application->all();
|
||||
unset($commands['_completion']);
|
||||
|
||||
return array_keys($commands);
|
||||
}
|
||||
}
|
||||
}
|
46
vendor/stecman/symfony-console-completion/src/EnvironmentCompletionContext.php
vendored
Normal file
46
vendor/stecman/symfony-console-completion/src/EnvironmentCompletionContext.php
vendored
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion;
|
||||
|
||||
class EnvironmentCompletionContext extends CompletionContext
|
||||
{
|
||||
/**
|
||||
* Set up completion context from the environment variables set by the parent shell
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->commandLine = getenv('CMDLINE_CONTENTS');
|
||||
$this->charIndex = intval(getenv('CMDLINE_CURSOR_INDEX'));
|
||||
|
||||
if ($this->commandLine === false) {
|
||||
$message = 'Failed to configure from environment; Environment var CMDLINE_CONTENTS not set.';
|
||||
|
||||
if (getenv('COMP_LINE')) {
|
||||
$message .= "\n\nYou appear to be attempting completion using an out-dated hook. If you've just updated,"
|
||||
. " you probably need to reinitialise the completion shell hook by reloading your shell"
|
||||
. " profile or starting a new shell session. If you are using a hard-coded (rather than generated)"
|
||||
. " hook, you will need to update that function with the new environment variable names."
|
||||
. "\n\nSee here for details: https://github.com/stecman/symfony-console-completion/issues/31";
|
||||
}
|
||||
|
||||
throw new \RuntimeException($message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the word break characters set by the parent shell.
|
||||
*
|
||||
* @throws \RuntimeException
|
||||
*/
|
||||
public function useWordBreaksFromEnvironment()
|
||||
{
|
||||
$breaks = getenv('CMDLINE_WORDBREAKS');
|
||||
|
||||
if (!$breaks) {
|
||||
throw new \RuntimeException('Failed to read word breaks from environment; Environment var CMDLINE_WORDBREAKS not set');
|
||||
}
|
||||
|
||||
$this->wordBreaks = $breaks;
|
||||
}
|
||||
}
|
207
vendor/stecman/symfony-console-completion/src/HookFactory.php
vendored
Normal file
207
vendor/stecman/symfony-console-completion/src/HookFactory.php
vendored
Normal file
|
@ -0,0 +1,207 @@
|
|||
<?php
|
||||
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion;
|
||||
|
||||
final class HookFactory
|
||||
{
|
||||
/**
|
||||
* Hook scripts
|
||||
*
|
||||
* These are shell-specific scripts that pass required information from that shell's
|
||||
* completion system to the interface of the completion command in this module.
|
||||
*
|
||||
* The following placeholders are replaced with their value at runtime:
|
||||
*
|
||||
* %%function_name%% - name of the generated shell function run for completion
|
||||
* %%program_name%% - command name completion will be enabled for
|
||||
* %%program_path%% - path to program the completion is for/generated by
|
||||
* %%completion_command%% - command to be run to compute completions
|
||||
*
|
||||
* NOTE: Comments are stripped out by HookFactory::stripComments as eval reads
|
||||
* input as a single line, causing it to break if comments are included.
|
||||
* While comments work using `... | source /dev/stdin`, existing installations
|
||||
* are likely using eval as it's been part of the instructions for a while.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $hooks = array(
|
||||
// BASH Hook
|
||||
'bash' => <<<'END'
|
||||
# BASH completion for %%program_path%%
|
||||
function %%function_name%% {
|
||||
|
||||
# Copy BASH's completion variables to the ones the completion command expects
|
||||
# These line up exactly as the library was originally designed for BASH
|
||||
local CMDLINE_CONTENTS="$COMP_LINE"
|
||||
local CMDLINE_CURSOR_INDEX="$COMP_POINT"
|
||||
local CMDLINE_WORDBREAKS="$COMP_WORDBREAKS";
|
||||
|
||||
export CMDLINE_CONTENTS CMDLINE_CURSOR_INDEX CMDLINE_WORDBREAKS
|
||||
|
||||
local RESULT STATUS;
|
||||
|
||||
RESULT="$(%%completion_command%% </dev/null)";
|
||||
STATUS=$?;
|
||||
|
||||
local cur mail_check_backup;
|
||||
|
||||
mail_check_backup=$MAILCHECK;
|
||||
MAILCHECK=-1;
|
||||
|
||||
_get_comp_words_by_ref -n : cur;
|
||||
|
||||
# Check if shell provided path completion is requested
|
||||
# @see Completion\ShellPathCompletion
|
||||
if [ $STATUS -eq 200 ]; then
|
||||
_filedir;
|
||||
return 0;
|
||||
|
||||
# Bail out if PHP didn't exit cleanly
|
||||
elif [ $STATUS -ne 0 ]; then
|
||||
echo -e "$RESULT";
|
||||
return $?;
|
||||
fi;
|
||||
|
||||
COMPREPLY=(`compgen -W "$RESULT" -- $cur`);
|
||||
|
||||
__ltrim_colon_completions "$cur";
|
||||
|
||||
MAILCHECK=mail_check_backup;
|
||||
};
|
||||
|
||||
if [ "$(type -t _get_comp_words_by_ref)" == "function" ]; then
|
||||
complete -F %%function_name%% "%%program_name%%";
|
||||
else
|
||||
>&2 echo "Completion was not registered for %%program_name%%:";
|
||||
>&2 echo "The 'bash-completion' package is required but doesn't appear to be installed.";
|
||||
fi
|
||||
END
|
||||
|
||||
// ZSH Hook
|
||||
, 'zsh' => <<<'END'
|
||||
# ZSH completion for %%program_path%%
|
||||
function %%function_name%% {
|
||||
local -x CMDLINE_CONTENTS="$words"
|
||||
local -x CMDLINE_CURSOR_INDEX
|
||||
(( CMDLINE_CURSOR_INDEX = ${#${(j. .)words[1,CURRENT]}} ))
|
||||
|
||||
local RESULT STATUS
|
||||
RESULT=("${(@f)$( %%completion_command%% )}")
|
||||
STATUS=$?;
|
||||
|
||||
# Check if shell provided path completion is requested
|
||||
# @see Completion\ShellPathCompletion
|
||||
if [ $STATUS -eq 200 ]; then
|
||||
_path_files;
|
||||
return 0;
|
||||
|
||||
# Bail out if PHP didn't exit cleanly
|
||||
elif [ $STATUS -ne 0 ]; then
|
||||
echo -e "$RESULT";
|
||||
return $?;
|
||||
fi;
|
||||
|
||||
compadd -- $RESULT
|
||||
};
|
||||
|
||||
compdef %%function_name%% "%%program_name%%";
|
||||
END
|
||||
);
|
||||
|
||||
/**
|
||||
* Return the names of shells that have hooks
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public static function getShellTypes()
|
||||
{
|
||||
return array_keys(self::$hooks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a completion hook for the specified shell type
|
||||
*
|
||||
* @param string $type - a key from self::$hooks
|
||||
* @param string $programPath
|
||||
* @param string $programName
|
||||
* @param bool $multiple
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function generateHook($type, $programPath, $programName = null, $multiple = false)
|
||||
{
|
||||
if (!isset(self::$hooks[$type])) {
|
||||
throw new \RuntimeException(sprintf(
|
||||
"Cannot generate hook for unknown shell type '%s'. Available hooks are: %s",
|
||||
$type,
|
||||
implode(', ', self::getShellTypes())
|
||||
));
|
||||
}
|
||||
|
||||
// Use the program path if an alias/name is not given
|
||||
$programName = $programName ?: $programPath;
|
||||
|
||||
if ($multiple) {
|
||||
$completionCommand = '$1 _completion';
|
||||
} else {
|
||||
$completionCommand = $programPath . ' _completion';
|
||||
}
|
||||
|
||||
return str_replace(
|
||||
array(
|
||||
'%%function_name%%',
|
||||
'%%program_name%%',
|
||||
'%%program_path%%',
|
||||
'%%completion_command%%',
|
||||
),
|
||||
array(
|
||||
$this->generateFunctionName($programPath, $programName),
|
||||
$programName,
|
||||
$programPath,
|
||||
$completionCommand
|
||||
),
|
||||
$this->stripComments(self::$hooks[$type])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a function name that is unlikely to conflict with other generated function names in the same shell
|
||||
*/
|
||||
protected function generateFunctionName($programPath, $programName)
|
||||
{
|
||||
return sprintf(
|
||||
'_%s_%s_complete',
|
||||
$this->sanitiseForFunctionName(basename($programName)),
|
||||
substr(md5($programPath), 0, 16)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a string safe for use as a shell function name
|
||||
*
|
||||
* @param string $name
|
||||
* @return string
|
||||
*/
|
||||
protected function sanitiseForFunctionName($name)
|
||||
{
|
||||
$name = str_replace('-', '_', $name);
|
||||
return preg_replace('/[^A-Za-z0-9_]+/', '', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip '#' style comments from a string
|
||||
*
|
||||
* BASH's eval doesn't work with comments as it removes line breaks, so comments have to be stripped out
|
||||
* for this method of sourcing the hook to work. Eval seems to be the most reliable method of getting a
|
||||
* hook into a shell, so while it would be nice to render comments, this stripping is required for now.
|
||||
*
|
||||
* @param string $script
|
||||
* @return string
|
||||
*/
|
||||
protected function stripComments($script)
|
||||
{
|
||||
return preg_replace('/(^\s*\#.*$)/m', '', $script);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Tests\Common;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionHandler;
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
/**
|
||||
* Base test case for running CompletionHandlers
|
||||
*/
|
||||
abstract class CompletionHandlerTestCase extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var Application
|
||||
*/
|
||||
protected $application;
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
require_once __DIR__ . '/../Fixtures/CompletionAwareCommand.php';
|
||||
require_once __DIR__ . '/../Fixtures/HiddenCommand.php';
|
||||
require_once __DIR__ . '/../Fixtures/TestBasicCommand.php';
|
||||
require_once __DIR__ . '/../Fixtures/TestSymfonyStyleCommand.php';
|
||||
}
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->application = new Application('Base application');
|
||||
$this->application->addCommands(array(
|
||||
new \CompletionAwareCommand(),
|
||||
new \TestBasicCommand(),
|
||||
new \TestSymfonyStyleCommand()
|
||||
));
|
||||
|
||||
if (method_exists('\HiddenCommand', 'setHidden')) {
|
||||
$this->application->add(new \HiddenCommand());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a handler set up with the given commandline and cursor position
|
||||
*
|
||||
* @param $commandLine
|
||||
* @param int $cursorIndex
|
||||
* @return CompletionHandler
|
||||
*/
|
||||
protected function createHandler($commandLine, $cursorIndex = null)
|
||||
{
|
||||
$context = new CompletionContext();
|
||||
$context->setCommandLine($commandLine);
|
||||
$context->setCharIndex($cursorIndex === null ? strlen($commandLine) : $cursorIndex);
|
||||
|
||||
return new CompletionHandler($this->application, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of terms from the output of CompletionHandler
|
||||
* The array index needs to be reset so that PHPUnit's array equality assertions match correctly.
|
||||
*
|
||||
* @param string $handlerOutput
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getTerms($handlerOutput)
|
||||
{
|
||||
return array_values($handlerOutput);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\EnvironmentCompletionContext;
|
||||
|
||||
class CompletionContextTest extends TestCase
|
||||
{
|
||||
|
||||
public function testWordBreakSplit()
|
||||
{
|
||||
$context = new CompletionContext();
|
||||
$context->setCommandLine('console config:application --direction="west" --with-bruce --repeat 3');
|
||||
|
||||
// Cursor at the end of the first word
|
||||
$context->setCharIndex(7);
|
||||
$words = $context->getWords();
|
||||
|
||||
$this->assertEquals(array(
|
||||
'console',
|
||||
'config:application',
|
||||
'--direction',
|
||||
'west',
|
||||
'--with-bruce',
|
||||
'--repeat',
|
||||
'3'
|
||||
), $words);
|
||||
}
|
||||
|
||||
public function testCursorPosition()
|
||||
{
|
||||
$context = new CompletionContext();
|
||||
$context->setCommandLine('make horse --legs 4 --colour black ');
|
||||
|
||||
// Cursor at the start of the line
|
||||
$context->setCharIndex(0);
|
||||
$this->assertEquals(0, $context->getWordIndex());
|
||||
|
||||
// Cursor at the end of the line
|
||||
$context->setCharIndex(34);
|
||||
$this->assertEquals(5, $context->getWordIndex());
|
||||
$this->assertEquals('black', $context->getCurrentWord());
|
||||
|
||||
// Cursor after space at the end of the string
|
||||
$context->setCharIndex(35);
|
||||
$this->assertEquals(6, $context->getWordIndex());
|
||||
$this->assertEquals('', $context->getCurrentWord());
|
||||
|
||||
// Cursor in the middle of 'horse'
|
||||
$context->setCharIndex(8);
|
||||
$this->assertEquals(1, $context->getWordIndex());
|
||||
$this->assertEquals('hor', $context->getCurrentWord());
|
||||
|
||||
// Cursor at the end of '--legs'
|
||||
$context->setCharIndex(17);
|
||||
$this->assertEquals(2, $context->getWordIndex());
|
||||
$this->assertEquals('--legs', $context->getCurrentWord());
|
||||
}
|
||||
|
||||
public function testWordBreakingWithSmallInputs()
|
||||
{
|
||||
$context = new CompletionContext();
|
||||
|
||||
// Cursor at the end of a word and not in the following space has no effect
|
||||
$context->setCommandLine('cmd a');
|
||||
$context->setCharIndex(5);
|
||||
$this->assertEquals(array('cmd', 'a'), $context->getWords());
|
||||
$this->assertEquals(1, $context->getWordIndex());
|
||||
$this->assertEquals('a', $context->getCurrentWord());
|
||||
|
||||
// As above, but in the middle of the command line string
|
||||
$context->setCommandLine('cmd a');
|
||||
$context->setCharIndex(3);
|
||||
$this->assertEquals(array('cmd', 'a'), $context->getWords());
|
||||
$this->assertEquals(0, $context->getWordIndex());
|
||||
$this->assertEquals('cmd', $context->getCurrentWord());
|
||||
|
||||
// Cursor at the end of the command line with a space appends an empty word
|
||||
$context->setCommandLine('cmd a ');
|
||||
$context->setCharIndex(8);
|
||||
$this->assertEquals(array('cmd', 'a', ''), $context->getWords());
|
||||
$this->assertEquals(2, $context->getWordIndex());
|
||||
$this->assertEquals('', $context->getCurrentWord());
|
||||
|
||||
// Cursor in break space before a word appends an empty word in that position
|
||||
$context->setCommandLine('cmd a');
|
||||
$context->setCharIndex(4);
|
||||
$this->assertEquals(array('cmd', '', 'a',), $context->getWords());
|
||||
$this->assertEquals(1, $context->getWordIndex());
|
||||
$this->assertEquals('', $context->getCurrentWord());
|
||||
}
|
||||
|
||||
public function testConfigureFromEnvironment()
|
||||
{
|
||||
putenv("CMDLINE_CONTENTS=beam up li");
|
||||
putenv('CMDLINE_CURSOR_INDEX=10');
|
||||
|
||||
$context = new EnvironmentCompletionContext();
|
||||
|
||||
$this->assertEquals(
|
||||
array(
|
||||
'beam',
|
||||
'up',
|
||||
'li'
|
||||
),
|
||||
$context->getWords()
|
||||
);
|
||||
|
||||
$this->assertEquals('li', $context->getCurrentWord());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,166 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Tests;
|
||||
|
||||
require_once __DIR__ . '/Common/CompletionHandlerTestCase.php';
|
||||
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Tests\Common\CompletionHandlerTestCase;
|
||||
|
||||
class CompletionHandlerTest extends CompletionHandlerTestCase
|
||||
{
|
||||
public function testCompleteAppName()
|
||||
{
|
||||
$handler = $this->createHandler('app');
|
||||
|
||||
// It's not valid to complete the application name, so this should return nothing
|
||||
$this->assertEmpty($handler->runCompletion());
|
||||
}
|
||||
|
||||
public function testCompleteCommandNames()
|
||||
{
|
||||
$handler = $this->createHandler('app ');
|
||||
$this->assertEquals(
|
||||
array('help', 'list', 'completion-aware', 'wave', 'walk:north'),
|
||||
$this->getTerms($handler->runCompletion())
|
||||
);
|
||||
}
|
||||
|
||||
public function testCompleteCommandNameNonMatch()
|
||||
{
|
||||
$handler = $this->createHandler('app br');
|
||||
$this->assertEmpty($handler->runCompletion());
|
||||
}
|
||||
|
||||
public function testCompleteCommandNamePartialTwoMatches()
|
||||
{
|
||||
$handler = $this->createHandler('app wa');
|
||||
$this->assertEquals(array('wave', 'walk:north'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testCompleteCommandNamePartialOneMatch()
|
||||
{
|
||||
$handler = $this->createHandler('app wav');
|
||||
$this->assertEquals(array('wave'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testCompleteCommandNameFull()
|
||||
{
|
||||
$handler = $this->createHandler('app wave');
|
||||
|
||||
// Completing on a matching word should return that word so that completion can continue
|
||||
$this->assertEquals(array('wave'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testCompleteSingleDash()
|
||||
{
|
||||
$handler = $this->createHandler('app wave -');
|
||||
|
||||
// Short options are not given as suggestions
|
||||
$this->assertEmpty($handler->runCompletion());
|
||||
}
|
||||
|
||||
public function testCompleteOptionShortcut()
|
||||
{
|
||||
$handler = $this->createHandler('app wave -j');
|
||||
|
||||
// If a valid option shortcut is completed on, the shortcut is returned so that completion can continue
|
||||
$this->assertEquals(array('-j'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testCompleteDoubleDash()
|
||||
{
|
||||
$handler = $this->createHandler('app wave --');
|
||||
$this->assertArraySubset(array('--vigorous', '--jazz-hands'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testCompleteOptionFull()
|
||||
{
|
||||
$handler = $this->createHandler('app wave --jazz');
|
||||
$this->assertArraySubset(array('--jazz-hands'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testCompleteOptionOrder()
|
||||
{
|
||||
// Completion of options should be able to happen anywhere after the command name
|
||||
$handler = $this->createHandler('app wave bruce --vi');
|
||||
$this->assertEquals(array('--vigorous'), $this->getTerms($handler->runCompletion()));
|
||||
|
||||
// Completing an option mid-commandline should work as normal
|
||||
$handler = $this->createHandler('app wave --vi --jazz-hands bruce', 13);
|
||||
$this->assertEquals(array('--vigorous'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testCompleteColonCommand()
|
||||
{
|
||||
// Normal bash behaviour is to count the colon character as a word break
|
||||
// Since a colon is used to namespace Symfony Framework console commands the
|
||||
// character in a command name should not be taken as a word break
|
||||
//
|
||||
// @see https://github.com/stecman/symfony-console-completion/pull/1
|
||||
$handler = $this->createHandler('app walk');
|
||||
$this->assertEquals(array('walk:north'), $this->getTerms($handler->runCompletion()));
|
||||
|
||||
$handler = $this->createHandler('app walk:north');
|
||||
$this->assertEquals(array('walk:north'), $this->getTerms($handler->runCompletion()));
|
||||
|
||||
$handler = $this->createHandler('app walk:north --deploy');
|
||||
$this->assertEquals(array('--deploy:jazz-hands'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider completionAwareCommandDataProvider
|
||||
*/
|
||||
public function testCompletionAwareCommand($commandLine, array $suggestions)
|
||||
{
|
||||
$handler = $this->createHandler($commandLine);
|
||||
$this->assertSame($suggestions, $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function completionAwareCommandDataProvider()
|
||||
{
|
||||
return array(
|
||||
'not complete aware command' => array('app wave --vigorous ', array()),
|
||||
'argument suggestions' => array('app completion-aware any-arg ', array('one-arg', 'two-arg')),
|
||||
'argument no suggestions' => array('app completion-aware ', array()),
|
||||
'argument suggestions + context' => array('app completion-aware any-arg one', array('one-arg', 'one-arg-context')),
|
||||
'array argument suggestions' => array('app completion-aware any-arg one-arg array-arg1 ', array('one-arg', 'two-arg')),
|
||||
'array argument suggestions + context' => array('app completion-aware any-arg one-arg array-arg1 one', array('one-arg', 'one-arg-context')),
|
||||
'option suggestions' => array('app completion-aware --option-with-suggestions ', array('one-opt', 'two-opt')),
|
||||
'option no suggestions' => array('app completion-aware --option-without-suggestions ', array()),
|
||||
'option suggestions + context' => array(
|
||||
'app completion-aware --option-with-suggestions one', array('one-opt', 'one-opt-context')
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function testShortCommandMatched()
|
||||
{
|
||||
$handler = $this->createHandler('app w:n --deploy');
|
||||
$this->assertEquals(array('--deploy:jazz-hands'), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testShortCommandNotMatched()
|
||||
{
|
||||
$handler = $this->createHandler('app w --deploy');
|
||||
$this->assertEquals(array(), $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
|
||||
public function testHelpCommandCompletion()
|
||||
{
|
||||
$handler = $this->createHandler('app help ');
|
||||
$this->assertEquals(
|
||||
array('help', 'list', 'completion-aware', 'wave', 'walk:north'),
|
||||
$this->getTerms($handler->runCompletion())
|
||||
);
|
||||
}
|
||||
|
||||
public function testListCommandCompletion()
|
||||
{
|
||||
$handler = $this->createHandler('app list ');
|
||||
$this->assertEquals(
|
||||
array('walk'),
|
||||
$this->getTerms($handler->runCompletion())
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Tests;
|
||||
|
||||
require_once __DIR__ . '/Common/CompletionHandlerTestCase.php';
|
||||
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Tests\Common\CompletionHandlerTestCase;
|
||||
|
||||
class CompletionTest extends CompletionHandlerTestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider getCompletionTestInput
|
||||
*/
|
||||
public function testCompletionResults($completions, $commandlineResultMap)
|
||||
{
|
||||
if (!is_array($completions)) {
|
||||
$completions = array($completions);
|
||||
}
|
||||
|
||||
foreach ($commandlineResultMap as $commandLine => $result) {
|
||||
$handler = $this->createHandler($commandLine);
|
||||
$handler->addHandlers($completions);
|
||||
$this->assertEquals($result, $this->getTerms($handler->runCompletion()));
|
||||
}
|
||||
}
|
||||
|
||||
public function getCompletionTestInput()
|
||||
{
|
||||
$options = array('smooth', 'latin', 'moody');
|
||||
|
||||
return array(
|
||||
'command match' => array(
|
||||
new Completion(
|
||||
'wave',
|
||||
'target',
|
||||
Completion::ALL_TYPES,
|
||||
$options
|
||||
),
|
||||
array(
|
||||
'app walk:north --target ' => array(),
|
||||
'app wave ' => $options
|
||||
)
|
||||
),
|
||||
|
||||
'type restriction option' => array(
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'target',
|
||||
Completion::TYPE_OPTION,
|
||||
$options
|
||||
),
|
||||
array(
|
||||
'app walk:north --target ' => $options,
|
||||
'app wave ' => array()
|
||||
)
|
||||
),
|
||||
|
||||
'type restriction argument' => array(
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'target',
|
||||
Completion::TYPE_ARGUMENT,
|
||||
$options
|
||||
),
|
||||
array(
|
||||
'app walk:north --target ' => array(),
|
||||
'app wave ' => $options
|
||||
)
|
||||
),
|
||||
|
||||
'makeGlobalHandler static' => array(
|
||||
Completion::makeGlobalHandler(
|
||||
'target',
|
||||
Completion::ALL_TYPES,
|
||||
$options
|
||||
),
|
||||
array(
|
||||
'app walk:north --target ' => $options,
|
||||
'app wave ' => $options
|
||||
)
|
||||
),
|
||||
|
||||
'with anonymous function' => array(
|
||||
new Completion(
|
||||
'wave',
|
||||
'style',
|
||||
Completion::TYPE_OPTION,
|
||||
function() {
|
||||
return range(1, 5);
|
||||
}
|
||||
),
|
||||
array(
|
||||
'app walk:north --target ' => array(),
|
||||
'app wave ' => array(),
|
||||
'app wave --style ' => array(1, 2,3, 4, 5)
|
||||
)
|
||||
),
|
||||
|
||||
'with callable array' => array(
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'target',
|
||||
Completion::ALL_TYPES,
|
||||
array($this, 'instanceMethodForCallableCheck')
|
||||
),
|
||||
array(
|
||||
'app walk:north --target ' => array('hello', 'world'),
|
||||
'app wave ' => array('hello', 'world')
|
||||
)
|
||||
),
|
||||
|
||||
'multiple handlers' => array(
|
||||
array(
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'target',
|
||||
Completion::TYPE_OPTION,
|
||||
array('all:option:target')
|
||||
),
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'target',
|
||||
Completion::ALL_TYPES,
|
||||
array('all:all:target')
|
||||
),
|
||||
new Completion(
|
||||
Completion::ALL_COMMANDS,
|
||||
'style',
|
||||
Completion::TYPE_OPTION,
|
||||
array('all:option:style')
|
||||
),
|
||||
),
|
||||
array(
|
||||
'app walk:north ' => array(),
|
||||
'app walk:north -t ' => array('all:option:target'),
|
||||
'app wave ' => array('all:all:target'),
|
||||
'app wave bruce -s ' => array('all:option:style'),
|
||||
'app walk:north --style ' => array('all:option:style'),
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used in the test "with callable array"
|
||||
* @return array
|
||||
*/
|
||||
public function instanceMethodForCallableCheck()
|
||||
{
|
||||
return array('hello', 'world');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\Completion\CompletionAwareInterface;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\CompletionContext;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
|
||||
class CompletionAwareCommand extends Command implements CompletionAwareInterface
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('completion-aware')
|
||||
->addOption('option-with-suggestions', null, InputOption::VALUE_REQUIRED)
|
||||
->addOption('option-without-suggestions', null, InputOption::VALUE_REQUIRED)
|
||||
->addArgument('argument-without-suggestions')
|
||||
->addArgument('argument-with-suggestions')
|
||||
->addArgument('array-argument-with-suggestions', InputArgument::IS_ARRAY)
|
||||
;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns possible option values.
|
||||
*
|
||||
* @param string $optionName Option name.
|
||||
* @param CompletionContext $context Completion context.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function completeOptionValues($optionName, CompletionContext $context)
|
||||
{
|
||||
if ($optionName === 'option-with-suggestions') {
|
||||
$suggestions = array('one-opt', 'two-opt');
|
||||
|
||||
if ('one' === $context->getCurrentWord()) {
|
||||
$suggestions[] = 'one-opt-context';
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns possible argument values.
|
||||
*
|
||||
* @param string $argumentName Argument name.
|
||||
* @param CompletionContext $context Completion context.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function completeArgumentValues($argumentName, CompletionContext $context)
|
||||
{
|
||||
if (in_array($argumentName, array('argument-with-suggestions', 'array-argument-with-suggestions'))) {
|
||||
$suggestions = array('one-arg', 'two-arg');
|
||||
|
||||
if ('one' === $context->getCurrentWord()) {
|
||||
$suggestions[] = 'one-arg-context';
|
||||
}
|
||||
|
||||
return $suggestions;
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
|
||||
class HiddenCommand extends \Symfony\Component\Console\Command\Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('internals')
|
||||
->setHidden(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
|
||||
class TestBasicCommand extends \Symfony\Component\Console\Command\Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('wave')
|
||||
->addOption(
|
||||
'vigorous'
|
||||
)
|
||||
->addOption(
|
||||
'jazz-hands',
|
||||
'j'
|
||||
)
|
||||
->addOption(
|
||||
'style',
|
||||
's',
|
||||
\Symfony\Component\Console\Input\InputOption::VALUE_REQUIRED
|
||||
)
|
||||
->addArgument(
|
||||
'target',
|
||||
\Symfony\Component\Console\Input\InputArgument::REQUIRED
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
class TestSymfonyStyleCommand extends \Symfony\Component\Console\Command\Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('walk:north')
|
||||
->addOption(
|
||||
'power',
|
||||
'p'
|
||||
)
|
||||
->addOption(
|
||||
'deploy:jazz-hands',
|
||||
'j'
|
||||
)
|
||||
->addOption(
|
||||
'style',
|
||||
's',
|
||||
\Symfony\Component\Console\Input\InputOption::VALUE_REQUIRED
|
||||
)
|
||||
->addOption(
|
||||
'target',
|
||||
't',
|
||||
\Symfony\Component\Console\Input\InputOption::VALUE_REQUIRED
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace Stecman\Component\Symfony\Console\BashCompletion\Tests;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Stecman\Component\Symfony\Console\BashCompletion\HookFactory;
|
||||
|
||||
class HookFactoryTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @var HookFactory
|
||||
*/
|
||||
protected $factory;
|
||||
|
||||
protected function setUp()
|
||||
{
|
||||
$this->factory = new HookFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider generateHookDataProvider
|
||||
*/
|
||||
public function testBashSyntax($programPath, $programName, $multiple)
|
||||
{
|
||||
if ($this->hasProgram('bash')) {
|
||||
$script = $this->factory->generateHook('bash', $programPath, $programName, $multiple);
|
||||
$this->assertSyntaxIsValid($script, 'bash -n', 'BASH hook');
|
||||
} else {
|
||||
$this->markTestSkipped("Couldn't detect BASH program to run hook syntax check");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider generateHookDataProvider
|
||||
*/
|
||||
public function testZshSyntax($programPath, $programName, $multiple)
|
||||
{
|
||||
if ($this->hasProgram('zsh')) {
|
||||
$script = $this->factory->generateHook('zsh', $programPath, $programName, $multiple);
|
||||
$this->assertSyntaxIsValid($script, 'zsh -n', 'ZSH hook');
|
||||
} else {
|
||||
$this->markTestSkipped("Couldn't detect ZSH program to run hook syntax check");
|
||||
}
|
||||
}
|
||||
|
||||
public function generateHookDataProvider()
|
||||
{
|
||||
return array(
|
||||
array('/path/to/myprogram', null, false),
|
||||
array('/path/to/myprogram', null, true),
|
||||
array('/path/to/myprogram', 'myprogram', false),
|
||||
array('/path/to/myprogram', 'myprogram', true),
|
||||
array('/path/to/my-program', 'my-program', false)
|
||||
);
|
||||
}
|
||||
|
||||
protected function hasProgram($programName)
|
||||
{
|
||||
exec(sprintf(
|
||||
'command -v %s',
|
||||
escapeshellarg($programName)
|
||||
), $output, $return);
|
||||
|
||||
return $return === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $code - code to pipe to the syntax checking command
|
||||
* @param string $syntaxCheckCommand - equivalent to `bash -n`.
|
||||
* @param string $context - what the syntax check is for
|
||||
*/
|
||||
protected function assertSyntaxIsValid($code, $syntaxCheckCommand, $context)
|
||||
{
|
||||
$process = proc_open(
|
||||
escapeshellcmd($syntaxCheckCommand),
|
||||
array(
|
||||
0 => array('pipe', 'r'),
|
||||
1 => array('pipe', 'w'),
|
||||
2 => array('pipe', 'w')
|
||||
),
|
||||
$pipes
|
||||
);
|
||||
|
||||
if (is_resource($process)) {
|
||||
// Push code into STDIN for the syntax checking process
|
||||
fwrite($pipes[0], $code);
|
||||
fclose($pipes[0]);
|
||||
|
||||
$output = stream_get_contents($pipes[1]) . stream_get_contents($pipes[2]);
|
||||
fclose($pipes[1]);
|
||||
fclose($pipes[2]);
|
||||
|
||||
$status = proc_close($process);
|
||||
|
||||
$this->assertSame(0, $status, "Syntax check for $context failed:\n$output");
|
||||
} else {
|
||||
throw new \RuntimeException("Failed to start process with command '$syntaxCheckCommand'");
|
||||
}
|
||||
}
|
||||
}
|
10
vendor/stecman/symfony-console-completion/tests/bootstrap.php
vendored
Normal file
10
vendor/stecman/symfony-console-completion/tests/bootstrap.php
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
$filename = __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
if (!file_exists($filename)) {
|
||||
echo 'You must first install the vendors using composer.' . PHP_EOL;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
require_once $filename;
|
Reference in a new issue