1
0
Fork 0
pull/60/merge
Felix Becker 2016-10-10 13:16:21 +00:00 committed by GitHub
commit bc08bcc97f
5 changed files with 88 additions and 26 deletions

View File

@ -153,7 +153,7 @@ class LanguageServer extends \AdvancedJsonRpc\Dispatcher
$shortName = substr($file, strlen($rootPath) + 1); $shortName = substr($file, strlen($rootPath) + 1);
$this->client->window->logMessage(MessageType::INFO, "Parsing file $fileNum/$numTotalFiles: $shortName."); $this->client->window->logMessage(MessageType::INFO, "Parsing file $fileNum/$numTotalFiles: $shortName.");
$this->project->getDocument($uri)->updateContent(file_get_contents($file)); $this->project->getDocument($uri)->parse(file_get_contents($file));
Loop\setTimeout($processFile, 0); Loop\setTimeout($processFile, 0);
} else { } else {

View File

@ -8,6 +8,7 @@ use LanguageServer\NodeVisitor\{NodeAtPositionFinder, ReferencesAdder, Definitio
use PhpParser\{Error, Comment, Node, ParserFactory, NodeTraverser, Lexer, Parser}; use PhpParser\{Error, Comment, Node, ParserFactory, NodeTraverser, Lexer, Parser};
use PhpParser\PrettyPrinter\Standard as PrettyPrinter; use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
use PhpParser\NodeVisitor\NameResolver; use PhpParser\NodeVisitor\NameResolver;
use Exception;
class PhpDocument class PhpDocument
{ {
@ -53,14 +54,14 @@ class PhpDocument
* *
* @var Node[] * @var Node[]
*/ */
private $stmts = []; private $statements;
/** /**
* Map from fully qualified name (FQN) to Node * Map from fully qualified name (FQN) to Node
* *
* @var Node[] * @var Node[]
*/ */
private $definitions = []; private $definitions;
/** /**
* Map from fully qualified name (FQN) to array of nodes that reference the symbol * Map from fully qualified name (FQN) to array of nodes that reference the symbol
@ -150,21 +151,32 @@ class PhpDocument
public function updateContent(string $content) public function updateContent(string $content)
{ {
$this->content = $content; $this->content = $content;
$this->parse(); $this->parse($content);
} }
/** /**
* Re-parses a source file, updates symbols, reports parsing errors * Unloads the content from memory
* that may have occured as diagnostics and returns parsed nodes.
* *
* @return void * @return void
*/ */
public function parse() public function removeContent()
{
unset($this->content);
}
/**
* Re-parses a source file, updates symbols and reports parsing errors
* that may have occured as diagnostics.
*
* @param string $content
* @return void
*/
public function parse(string $content)
{ {
$stmts = null; $stmts = null;
$errors = []; $errors = [];
try { try {
$stmts = $this->parser->parse($this->content); $stmts = $this->parser->parse($content);
} catch (\PhpParser\Error $e) { } catch (\PhpParser\Error $e) {
// Lexer can throw errors. e.g for unterminated comments // Lexer can throw errors. e.g for unterminated comments
// unfortunately we don't get a location back // unfortunately we don't get a location back
@ -177,8 +189,8 @@ class PhpDocument
foreach ($errors as $error) { foreach ($errors as $error) {
$diagnostic = new Diagnostic(); $diagnostic = new Diagnostic();
$diagnostic->range = new Range( $diagnostic->range = new Range(
new Position($error->getStartLine() - 1, $error->hasColumnInfo() ? $error->getStartColumn($this->content) - 1 : 0), new Position($error->getStartLine() - 1, $error->hasColumnInfo() ? $error->getStartColumn($content) - 1 : 0),
new Position($error->getEndLine() - 1, $error->hasColumnInfo() ? $error->getEndColumn($this->content) : 0) new Position($error->getEndLine() - 1, $error->hasColumnInfo() ? $error->getEndColumn($content) : 0)
); );
$diagnostic->severity = DiagnosticSeverity::ERROR; $diagnostic->severity = DiagnosticSeverity::ERROR;
$diagnostic->source = 'php'; $diagnostic->source = 'php';
@ -199,7 +211,7 @@ class PhpDocument
$traverser->addVisitor(new ReferencesAdder($this)); $traverser->addVisitor(new ReferencesAdder($this));
// Add column attributes to nodes // Add column attributes to nodes
$traverser->addVisitor(new ColumnCalculator($this->content)); $traverser->addVisitor(new ColumnCalculator($content));
// Collect all definitions // Collect all definitions
$definitionCollector = new DefinitionCollector; $definitionCollector = new DefinitionCollector;
@ -213,7 +225,7 @@ class PhpDocument
$this->project->addDefinitionDocument($fqn, $this); $this->project->addDefinitionDocument($fqn, $this);
} }
$this->stmts = $stmts; $this->statements = $stmts;
} }
} }
@ -234,12 +246,29 @@ class PhpDocument
* Returns this document's text content. * Returns this document's text content.
* *
* @return string * @return string
* @throws Exception If the content was not loaded
*/ */
public function getContent() public function getContent()
{ {
if (!isset($this->content)) {
throw new Exception('Content is not loaded');
}
return $this->content; return $this->content;
} }
/**
* Returns this document's AST.
*
* @return Node[]
*/
public function getStatements()
{
if (!isset($this->statements)) {
$this->parse($this->getContent());
}
return $this->statements;
}
/** /**
* Returns the URI of the document * Returns the URI of the document
* *
@ -258,13 +287,10 @@ class PhpDocument
*/ */
public function getNodeAtPosition(Position $position) public function getNodeAtPosition(Position $position)
{ {
if ($this->stmts === null) {
return null;
}
$traverser = new NodeTraverser; $traverser = new NodeTraverser;
$finder = new NodeAtPositionFinder($position); $finder = new NodeAtPositionFinder($position);
$traverser->addVisitor($finder); $traverser->addVisitor($finder);
$traverser->traverse($this->stmts); $traverser->traverse($this->getStatements());
return $finder->node; return $finder->node;
} }

View File

@ -77,6 +77,18 @@ class TextDocument
$this->project->getDocument($textDocument->uri)->updateContent($contentChanges[0]->text); $this->project->getDocument($textDocument->uri)->updateContent($contentChanges[0]->text);
} }
/**
* The document close notification is sent from the client to the server when the document got closed in the client.
* The document's truth now exists where the document's uri points to (e.g. if the document's uri is a file uri the
* truth now exists on disk).
*
* @param \LanguageServer\Protocol\TextDocumentItem $textDocument The document that was closed
* @return void
*/
public function didClose(TextDocumentIdentifier $textDocument)
{
$this->project->getDocument($textDocument->uri)->removeContent();
}
/** /**
* The document formatting request is sent from the server to the client to format a whole document. * The document formatting request is sent from the server to the client to format a whole document.

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types = 1);
namespace LanguageServer\Tests\Server\TextDocument;
use PHPUnit\Framework\TestCase;
use LanguageServer\Tests\MockProtocolStream;
use LanguageServer\{Server, Client, LanguageClient, Project};
use LanguageServer\Protocol\{TextDocumentItem, TextDocumentIdentifier};
use Exception;
class DidCloseTest extends TestCase
{
public function test()
{
$client = new LanguageClient(new MockProtocolStream());
$project = new Project($client);
$textDocument = new Server\TextDocument($project, $client);
$phpDocument = $project->getDocument('whatever');
$phpDocument->updateContent('hello world');
$textDocumentItem = new TextDocumentItem();
$textDocumentItem->uri = 'whatever';
$textDocumentItem->languageId = 'php';
$textDocumentItem->version = 1;
$textDocumentItem->text = 'hello world';
$textDocument->didOpen($textDocumentItem);
$textDocument->didClose(new TextDocumentIdentifier($textDocumentItem->uri));
$this->expectException(Exception::class);
$phpDocument->getContent();
}
}

View File

@ -57,14 +57,4 @@ class FormattingTest extends TestCase
'newText' => $expected 'newText' => $expected
]], json_decode(json_encode($result), true)); ]], json_decode(json_encode($result), true));
} }
public function testFormattingInvalidUri()
{
$client = new LanguageClient(new MockProtocolStream());
$project = new Project($client);
$textDocument = new Server\TextDocument($project, $client);
$result = $textDocument->formatting(new TextDocumentIdentifier('whatever'), new FormattingOptions());
$this->assertSame([], $result);
}
} }