Chris@13: Chris@13: */ Chris@13: class AutoCompleter Chris@13: { Chris@13: /** @var Matcher\AbstractMatcher[] */ Chris@13: protected $matchers; Chris@13: Chris@13: /** Chris@13: * Register a tab completion Matcher. Chris@13: * Chris@13: * @param AbstractMatcher $matcher Chris@13: */ Chris@13: public function addMatcher(AbstractMatcher $matcher) Chris@13: { Chris@13: $this->matchers[] = $matcher; Chris@13: } Chris@13: Chris@13: /** Chris@13: * Activate readline tab completion. Chris@13: */ Chris@13: public function activate() Chris@13: { Chris@13: readline_completion_function([&$this, 'callback']); Chris@13: } Chris@13: Chris@13: /** Chris@13: * Handle readline completion. Chris@13: * Chris@13: * @param string $input Readline current word Chris@13: * @param int $index Current word index Chris@13: * @param array $info readline_info() data Chris@13: * Chris@13: * @return array Chris@13: */ Chris@13: public function processCallback($input, $index, $info = []) Chris@13: { Chris@13: // Some (Windows?) systems provide incomplete `readline_info`, so let's Chris@13: // try to work around it. Chris@13: $line = $info['line_buffer']; Chris@13: if (isset($info['end'])) { Chris@13: $line = substr($line, 0, $info['end']); Chris@13: } Chris@13: if ($line === '' && $input !== '') { Chris@13: $line = $input; Chris@13: } Chris@13: Chris@13: $tokens = token_get_all('matchers as $matcher) { Chris@13: if ($matcher->hasMatched($tokens)) { Chris@13: $matches = array_merge($matcher->getMatches($tokens), $matches); Chris@13: } Chris@13: } Chris@13: Chris@13: $matches = array_unique($matches); Chris@13: Chris@13: return !empty($matches) ? $matches : ['']; Chris@13: } Chris@13: Chris@13: /** Chris@13: * The readline_completion_function callback handler. Chris@13: * Chris@13: * @see processCallback Chris@13: * Chris@13: * @param string $input Chris@13: * @param int $index Chris@13: * Chris@13: * @return array Chris@13: */ Chris@13: public function callback($input, $index) Chris@13: { Chris@13: return $this->processCallback($input, $index, readline_info()); Chris@13: } Chris@13: Chris@13: /** Chris@13: * Remove readline callback handler on destruct. Chris@13: */ Chris@13: public function __destruct() Chris@13: { Chris@13: // PHP didn't implement the whole readline API when they first switched Chris@13: // to libedit. And they still haven't. Chris@13: // Chris@13: // So this is a thing to make PsySH work on 5.3.x: Chris@13: if (function_exists('readline_callback_handler_remove')) { Chris@13: readline_callback_handler_remove(); Chris@13: } Chris@13: } Chris@13: }