123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- <?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;
- }
- }
|