diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f74aaf..929b23d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ All notable changes to `phpunit-watcher` will be documented in this file +## 1.6.0 - 2018-07-28 + +- add autocomplete when filtering on file names + ## 1.5.1 - 2018-05-15 - fix a bug around screen switching diff --git a/src/Screens/Completers/Completer.php b/src/Screens/Completers/Completer.php deleted file mode 100644 index 83d11ca..0000000 --- a/src/Screens/Completers/Completer.php +++ /dev/null @@ -1,190 +0,0 @@ -readline = $readline; - } - - /** - * Start completion - * - * @param string $word - * @param int $startOffset - * @param int $endOffset - * @return array|null - */ - public function __invoke($word, $startOffset, $endOffset) - { - $this->word = $word; - $this->wordStartOffset = $startOffset; - $this->wordEndOffset = $endOffset; - - return $this->handleSearchResults( - $this->search() - ); - } - - /** - * Search for things related to the completed word and return an array of suggestions - * - * Use an array with one element to autocomplete the word - * - * @return array - */ - abstract protected function search(); - - /** - * Display suggestions or complete the word depending on number of results - * - * When returning an array reactphp-stdio will display suggestions with the default behavior. - * - * @param array $searchResults - * @return array|null - */ - protected function handleSearchResults($searchResults) - { - if (empty($searchResults)) { - return null; - } - - if (count($searchResults) > 1) { - return $this->filterSuggestions($searchResults); - } - - $this->completeWord($searchResults[0]); - } - - /** - * Filter suggestions to display - * - * @param array $suggestions - * @return array|null - */ - protected function filterSuggestions($suggestions) - { - if (is_callable($this->filterSuggestionsCallback)) { - $suggestions = ($this->filterSuggestionsCallback)($suggestions); - } - return $suggestions; - } - - /** - * Register callback called when more than one suggestion is available - * - * You can use this callback to filter suggestions or to abort the - * default display behavior by returning null - * - * @param callable $callback - */ - public function onSuggestions($callback) - { - $this->filterSuggestionsCallback = $callback; - } - - /** - * Refresh the input with the completed word and move cursor to end of the word - * - * @param string $newWord - */ - protected function completeWord($newWord) - { - $endQuotedWord = $this->appendQuoteIfNeeded($newWord); - - $this->readline->setInput( - $this->getInputBeforeWord() . $endQuotedWord . $this->getInputAfterWord() - ); - - $this->readline->moveCursorTo($this->wordStartOffset + mb_strlen($endQuotedWord)); - } - - /** - * Return input string before the word - * - * @return string - */ - protected function getInputBeforeWord() - { - return mb_substr($this->getInput(), 0, $this->wordStartOffset); - } - - /** - * Return input string after the word - * - * @return string - */ - protected function getInputAfterWord() - { - return mb_substr($this->getInput(), $this->wordEndOffset); - } - - /** - * Return the character before the word - * - * @return null|string - */ - protected function getCharBeforeWord() - { - return $this->wordStartOffset > 0 - ? mb_substr($this->getInput(), $this->wordStartOffset - 1, 1) - : null; - } - - /** - * Append a quote if word is prefixed with a quote - * - * @param string $word - * @return string - */ - protected function appendQuoteIfNeeded($word) - { - $char = $this->getCharBeforeWord(); - - return ($char === '"' || $char === '\'') - ? $word . $char - : $word; - } - - /** - * Return the input string - * - * @return string - */ - protected function getInput() - { - return $this->readline->getInput(); - } -} diff --git a/src/Screens/Completers/FilenameCompleter.php b/src/Screens/Completers/FilenameCompleter.php deleted file mode 100644 index 012c67b..0000000 --- a/src/Screens/Completers/FilenameCompleter.php +++ /dev/null @@ -1,34 +0,0 @@ -word . '*', GLOB_MARK) ?: []; - } - - /** - * Append a quote if path is prefixed with a quote, except for directories - * - * @param string $path - * @return string - */ - protected function appendQuoteIfNeeded($path) - { - if (is_dir($path)) { - return $path; - } - - $char = $this->getCharBeforeWord(); - - return ($char === '"' || $char === '\'') - ? $path . $char - : $path; - } -} diff --git a/src/Screens/FilterFileName.php b/src/Screens/FilterFileName.php index 09c932a..aee338d 100644 --- a/src/Screens/FilterFileName.php +++ b/src/Screens/FilterFileName.php @@ -2,8 +2,6 @@ namespace Spatie\PhpUnitWatcher\Screens; -use Spatie\PhpUnitWatcher\Screens\Completers\FilenameCompleter; - class FilterFileName extends Screen { public function draw() @@ -13,7 +11,7 @@ public function draw() ->write('Type a pattern and press Enter to only run tests in the giving path or file.') ->write('Press Enter with an empty pattern to execute all tests in all files.') ->emptyLine() - ->comment('Start typing to filter by file name.') + ->comment('Start typing to filter by a test name.') ->prompt('pattern > '); return $this; @@ -21,15 +19,8 @@ public function draw() public function registerListeners() { - $this->registerDataListener(); - - $this->registerAutocompleter(); - - return $this; - } + $readline = $this->terminal->getReadline(); - protected function registerDataListener() - { $this->terminal->on('data', function ($line) { if ($line == '') { $this->terminal->goBack(); @@ -47,36 +38,51 @@ protected function registerDataListener() $this->terminal->displayScreen(new Phpunit($options)); }); + + $this->registerAutocompleter($readline); + + return $this; } - protected function registerAutocompleter() + protected function registerAutocompleter($readline) { - $readline = $this->terminal->getReadline(); + $readline->setAutocomplete(function ($word, $startOffset, $endOffset) use ($readline) { + $input = $readline->getInput(); - $filenameAutocompleter = new FilenameCompleter($readline); + $paths = glob("$word*", GLOB_MARK); - $filenameAutocompleter->onSuggestions(function ($suggestions) { - $this->refreshScreenWithSuggestions($suggestions, 10); - }); + if (empty($paths)) { + return; + } - $readline->setAutocomplete($filenameAutocompleter); - } + if (count($paths) > 1) { + $this->terminal->getStdio()->write(implode(' ', $paths) . "\n"); + return; + } - protected function refreshScreenWithSuggestions($suggestions, $limit) - { - $firstSuggestions = array_slice($suggestions, 0 , $limit); + $path = $paths[0]; - $this->terminal->refreshScreen(); + $lineStart = mb_substr($input, 0, $startOffset); + $lineEnd = mb_substr($input, $endOffset); - $stdio = $this->terminal->getStdio(); + $path = $this->sanitzeOffset($startOffset, $path, $input); - $stdio->write("\n" . implode("\n", $firstSuggestions) . "\n"); + $newInput = $lineStart . $path . $lineEnd; - $count = count($suggestions) - $limit; - if ($count > 0) { - $stdio->write("(+$count others)\n"); - } + $readline->setInput($newInput); - $stdio->write("\n"); + $readline->moveCursorTo($startOffset + mb_strlen($path)); + }); + } + + protected function sanitzeOffset($startOffset, $path, $input): string + { + if ($startOffset > 0 && mb_strlen($path) > 1 && mb_substr($path, -1) != DIRECTORY_SEPARATOR) { + $previousChar = mb_substr($input, $startOffset - 1, 1); + if ($previousChar === '"' || $previousChar === '\'') { + $path .= $previousChar; + } + } + return $path; } }