client = $client; $lexer = new Lexer(['usedAttributes' => ['comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos']]); $this->parser = (new ParserFactory)->create(ParserFactory::PREFER_PHP7, $lexer, ['throwOnError' => false]); } /** * The document symbol request is sent from the client to the server to list all symbols found in a given text * document. * * @param \LanguageServer\Protocol\TextDocumentIdentifier $textDocument * @return SymbolInformation[] */ public function documentSymbol(TextDocumentIdentifier $textDocument): array { $stmts = $this->asts[$textDocument->uri]; if (!$stmts) { return []; } $finder = new SymbolFinder($textDocument->uri); $traverser = new NodeTraverser; $traverser->addVisitor($finder); $traverser->traverse($stmts); return $finder->symbols; } /** * The document open notification is sent from the client to the server to signal newly opened text documents. The * document's truth is now managed by the client and the server must not try to read the document's truth using the * document's uri. * * @param \LanguageServer\Protocol\TextDocumentItem $textDocument The document that was opened. * @return void */ public function didOpen(TextDocumentItem $textDocument) { $this->updateAst($textDocument->uri, $textDocument->text); } /** * The document change notification is sent from the client to the server to signal changes to a text document. * * @param \LanguageServer\Protocol\VersionedTextDocumentIdentifier $textDocument * @param \LanguageServer\Protocol\TextDocumentContentChangeEvent[] $contentChanges * @return void */ public function didChange(VersionedTextDocumentIdentifier $textDocument, array $contentChanges) { $this->updateAst($textDocument->uri, $contentChanges[0]->text); } /** * Re-parses a source file, updates the AST and reports parsing errors that may occured as diagnostics * * @param string $uri The URI of the source file * @param string $content The new content of the source file * @return void */ private function updateAst(string $uri, string $content) { $stmts = $this->parser->parse($content); $diagnostics = []; foreach ($this->parser->getErrors() as $error) { $diagnostic = new Diagnostic(); $diagnostic->range = new Range( new Position($error->getStartLine() - 1, $error->hasColumnInfo() ? $error->getStartColumn($content) - 1 : 0), new Position($error->getEndLine() - 1, $error->hasColumnInfo() ? $error->getEndColumn($content) - 1 : 0) ); $diagnostic->severity = DiagnosticSeverity::ERROR; $diagnostic->source = 'php'; // Do not include "on line ..." in the error message $diagnostic->message = $error->getRawMessage(); $diagnostics[] = $diagnostic; } if (count($diagnostics) > 0) { $this->client->textDocument->publishDiagnostics($uri, $diagnostics); } // $stmts can be null in case of a fatal parsing error if ($stmts) { $traverser = new NodeTraverser; $traverser->addVisitor(new NameResolver); $traverser->addVisitor(new ColumnCalculator($content)); $traverser->traverse($stmts); $this->asts[$uri] = $stmts; } } }