matchers = \array_filter( $matchers, fn ($matcher) => !\in_array(\get_class($matcher), self::SUPERSEDED_MATCHERS, true) ); } /** * {@inheritdoc} */ public function appliesToKind(int $kinds): bool { // Legacy matchers don't understand command argument contexts. return ($kinds & ~CompletionKind::COMMAND_ARGUMENT) !== 0; } /** * {@inheritdoc} */ public function getCompletions(AnalysisResult $analysis): array { $tokens = $this->convertToTokens($analysis); $info = $this->buildReadlineInfo($analysis); $matches = []; foreach ($this->matchers as $matcher) { if ($matcher->hasMatched($tokens)) { $matches = \array_merge($matches, $matcher->getMatches($tokens, $info)); } } return $matches; } /** * Convert AnalysisResult to token array for legacy matchers. * * Legacy matchers expect tokens from token_get_all(). We reconstruct * a reasonable approximation from the analysis result. * * @return array Token array compatible with legacy matchers */ private function convertToTokens(AnalysisResult $analysis): array { if (!empty($analysis->tokens)) { return $this->filterTokens($analysis->tokens); } // Reconstruct a token array from the analysis result $tokens = []; if ($analysis->leftSide !== null) { $leftTokens = \token_get_all('leftSide); $tokens = \array_merge($tokens, \array_slice($leftTokens, 1)); } if (($analysis->kinds & CompletionKind::OBJECT_MEMBER) !== 0) { $tokens[] = [\T_OBJECT_OPERATOR, '->', 0]; } elseif (($analysis->kinds & CompletionKind::STATIC_MEMBER) !== 0) { $tokens[] = [\T_DOUBLE_COLON, '::', 0]; } if ($analysis->prefix !== '') { $tokens[] = [\T_STRING, $analysis->prefix, 0]; } return $this->filterTokens($tokens); } /** * Filter tokens to remove whitespace (legacy matcher expectation). * * @return array Filtered token array */ private function filterTokens(array $tokens): array { $filtered = \array_filter($tokens, fn ($token) => !AbstractMatcher::tokenIs($token, AbstractMatcher::T_WHITESPACE)); return \array_values($filtered); } /** * Build readline_info array for legacy matchers. * * @return array readline_info-like array */ private function buildReadlineInfo(AnalysisResult $analysis): array { if ($analysis->readlineInfo !== []) { return $analysis->readlineInfo; } $lineBuffer = ''; if ($analysis->leftSide !== null) { $lineBuffer .= $analysis->leftSide; if (($analysis->kinds & CompletionKind::OBJECT_MEMBER) !== 0) { $lineBuffer .= '->'; } elseif (($analysis->kinds & CompletionKind::STATIC_MEMBER) !== 0) { $lineBuffer .= '::'; } } $lineBuffer .= $analysis->prefix; return [ 'line_buffer' => $lineBuffer, 'point' => \strlen($lineBuffer), 'end' => \strlen($lineBuffer), ]; } }