diff --git a/src/Readline/GNUReadline.php b/src/Readline/GNUReadline.php index d4dba0cc..3b0c30fa 100644 --- a/src/Readline/GNUReadline.php +++ b/src/Readline/GNUReadline.php @@ -11,6 +11,8 @@ namespace Psy\Readline; +use Psy\TabCompletion\AutoCompleter; + /** * A Readline interface implementation for GNU Readline. * @@ -176,4 +178,18 @@ public function writeHistory(): bool return true; } + + public function activateAutoCompleter(AutoCompleter $autoCompleter): void + { + \readline_completion_function([$autoCompleter, 'callback']); + } + + public function deactivateAutoCompleter(): void + { + // PHP didn't implement the whole readline API when they first switched + // to libedit. And they still haven't. + if (\function_exists('readline_callback_handler_remove')) { + \readline_callback_handler_remove(); + } + } } diff --git a/src/Readline/HoaAutocompleterAdapter.php b/src/Readline/HoaAutocompleterAdapter.php new file mode 100644 index 00000000..532c167f --- /dev/null +++ b/src/Readline/HoaAutocompleterAdapter.php @@ -0,0 +1,27 @@ +autoCompleter = $autoCompleter; + } + + public function complete(string $prefix, int $index, array $info) + { + return $this->autoCompleter->complete($prefix, $index, $info); + } + + public function getWordDefinition(): string + { + return '.'; + } +} diff --git a/src/Readline/Readline.php b/src/Readline/Readline.php index 8a8f79bb..29e0e2e0 100644 --- a/src/Readline/Readline.php +++ b/src/Readline/Readline.php @@ -11,6 +11,8 @@ namespace Psy\Readline; +use Psy\TabCompletion\AutoCompleter; + /** * An interface abstracting the various readline_* functions. */ @@ -80,4 +82,14 @@ public function redisplay(); * @return bool Success */ public function writeHistory(): bool; + + /** + * Activete auto completer for tab completion. + */ + public function activateAutoCompleter(AutoCompleter $autoCompleter): void; + + /** + * Deactivete auto completer for tab completion. + */ + public function deactivateAutoCompleter(): void; } diff --git a/src/Readline/Transient.php b/src/Readline/Transient.php index 128ef867..e474089f 100644 --- a/src/Readline/Transient.php +++ b/src/Readline/Transient.php @@ -12,6 +12,7 @@ namespace Psy\Readline; use Psy\Exception\BreakException; +use Psy\TabCompletion\AutoCompleter; /** * An array-based Readline emulation implementation. @@ -152,4 +153,14 @@ private function getStdin() return $this->stdin; } + + public function activateAutoCompleter(AutoCompleter $autoCompleter): void + { + // noop + } + + public function deactivateAutoCompleter(): void + { + // noop + } } diff --git a/src/Readline/Userland.php b/src/Readline/Userland.php index 2faf47a5..9f798631 100644 --- a/src/Readline/Userland.php +++ b/src/Readline/Userland.php @@ -19,6 +19,7 @@ use Psy\Readline\Hoa\ConsoleTput as HoaConsoleTput; use Psy\Readline\Hoa\Readline as HoaReadline; use Psy\Readline\Hoa\Ustring as HoaUstring; +use Psy\TabCompletion\AutoCompleter; /** * Userland Readline implementation. @@ -158,4 +159,14 @@ public function writeHistory(): bool { return true; } + + public function activateAutoCompleter(AutoCompleter $autoCompleter): void + { + $this->hoaReadline->setAutocompleter(new HoaAutocompleterAdapter($autoCompleter)); + } + + public function deactivateAutoCompleter(): void + { + // noop + } } diff --git a/src/Shell.php b/src/Shell.php index 2fe643a3..b4300371 100644 --- a/src/Shell.php +++ b/src/Shell.php @@ -1501,7 +1501,7 @@ protected function initializeTabCompletion() $this->addMatchersToAutoCompleter($this->getDefaultMatchers()); $this->addMatchersToAutoCompleter($this->matchers); - $this->autoCompleter->activate(); + $this->readline->activateAutoCompleter($this->autoCompleter); } /** diff --git a/src/TabCompletion/AutoCompleter.php b/src/TabCompletion/AutoCompleter.php index 17d0ec56..dfa3c7c6 100644 --- a/src/TabCompletion/AutoCompleter.php +++ b/src/TabCompletion/AutoCompleter.php @@ -11,6 +11,7 @@ namespace Psy\TabCompletion; +use Psy\Readline\Readline; use Psy\TabCompletion\Matcher\AbstractMatcher; /** @@ -23,6 +24,9 @@ class AutoCompleter /** @var Matcher\AbstractMatcher[] */ protected $matchers; + /** @var ?Readline */ + protected $readline; + /** * Register a tab completion Matcher. * @@ -33,24 +37,15 @@ public function addMatcher(AbstractMatcher $matcher) $this->matchers[] = $matcher; } - /** - * Activate readline tab completion. - */ - public function activate() - { - \readline_completion_function([&$this, 'callback']); - } - /** * Handle readline completion. * * @param string $input Readline current word * @param int $index Current word index - * @param array $info readline_info() data - * - * @return array + * @param array{line_buffer: string, end: int} $info {@see readline_info()} data + * @return list */ - public function processCallback(string $input, int $index, array $info = []): array + public function complete(string $input, int $index, array $info = []): array { // Some (Windows?) systems provide incomplete `readline_info`, so let's // try to work around it. @@ -78,15 +73,13 @@ public function processCallback(string $input, int $index, array $info = []): ar } } - $matches = \array_unique($matches); - - return !empty($matches) ? $matches : ['']; + return \array_values(\array_unique($matches)) ?: ['']; } /** * The readline_completion_function callback handler. * - * @see processCallback + * @see AutoCompleter::complete() * * @param string $input * @param int $index @@ -95,7 +88,7 @@ public function processCallback(string $input, int $index, array $info = []): ar */ public function callback(string $input, int $index): array { - return $this->processCallback($input, $index, \readline_info()); + return $this->complete($input, $index, \readline_info()); } /** @@ -103,10 +96,9 @@ public function callback(string $input, int $index): array */ public function __destruct() { - // PHP didn't implement the whole readline API when they first switched - // to libedit. And they still haven't. - if (\function_exists('readline_callback_handler_remove')) { - \readline_callback_handler_remove(); + if (isset($this->readline)) { + $this->readline->deactivateCompletion(); + $this->readline = null; } } }