diff --git a/src/Client/TextDocument.php b/src/Client/TextDocument.php index a7759c8..6cf09cd 100644 --- a/src/Client/TextDocument.php +++ b/src/Client/TextDocument.php @@ -4,7 +4,7 @@ declare(strict_types = 1); namespace LanguageServer\Client; use LanguageServer\ClientHandler; -use LanguageServer\Protocol\{Message, Content}; +use LanguageServer\Protocol\{Message, Content, TextDocumentIdentifier}; use Sabre\Event\Promise; use JsonMapper; diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 1da6744..0af06aa 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -191,7 +191,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher } catch (Exception $e) { $this->client->window->logMessage( MessageType::ERROR, - "Error parsing file $shortName: " . (string)$e + "Error parsing file {$textDocument->uri}: " . (string)$e ); } }); diff --git a/src/Protocol/Content.php b/src/Protocol/Content.php index 6f95a8a..c1b9381 100644 --- a/src/Protocol/Content.php +++ b/src/Protocol/Content.php @@ -10,4 +10,12 @@ class Content * @var string */ public $text; + + /** + * @param string $text The content of the text document + */ + public function __construct(string $text = null) + { + $this->text = $text; + } } diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 3f0c314..fcab0c3 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -5,9 +5,12 @@ namespace LanguageServer\Tests; use PHPUnit\Framework\TestCase; use LanguageServer\LanguageServer; -use LanguageServer\Protocol\{Message, ClientCapabilities, TextDocumentSyncKind, MessageType}; +use LanguageServer\Protocol\{Message, ClientCapabilities, TextDocumentSyncKind, MessageType, Content}; use AdvancedJsonRpc; +use Webmozart\Glob\Glob; +use Webmozart\PathUtil\Path; use Sabre\Event\Promise; +use Exception; use function LanguageServer\pathToUri; class LanguageServerTest extends TestCase @@ -17,15 +20,14 @@ class LanguageServerTest extends TestCase $reader = new MockProtocolStream(); $writer = new MockProtocolStream(); $server = new LanguageServer($reader, $writer); - $msg = null; - $writer->on('message', function (Message $message) use (&$msg) { - $msg = $message; - }); + $promise = new Promise; + $writer->once('message', [$promise, 'fulfill']); $reader->write(new Message(new AdvancedJsonRpc\Request(1, 'initialize', [ 'rootPath' => __DIR__, 'processId' => getmypid(), 'capabilities' => new ClientCapabilities() ]))); + $msg = $promise->wait(); $this->assertNotNull($msg, 'message event should be emitted'); $this->assertInstanceOf(AdvancedJsonRpc\SuccessResponse::class, $msg->body); $this->assertEquals((object)[ @@ -49,7 +51,7 @@ class LanguageServerTest extends TestCase ], $msg->body->result); } - public function testIndexing() + public function testIndexingWithDirectFileAccess() { $promise = new Promise; $input = new MockProtocolStream; @@ -68,4 +70,53 @@ class LanguageServerTest extends TestCase $server->initialize(getmypid(), $capabilities, realpath(__DIR__ . '/../fixtures')); $promise->wait(); } + + public function testIndexingWithGlobAndContentRequests() + { + $promise = new Promise; + $globCalled = false; + $contentCalled = false; + $rootPath = realpath(__DIR__ . '/../fixtures'); + $input = new MockProtocolStream; + $output = new MockProtocolStream; + $output->on('message', function (Message $msg) use ($promise, $input, $rootPath, &$globCalled, &$contentCalled) { + if ($msg->body->method === 'textDocument/xcontent') { + // Document content requested + $contentCalled = true; + $input->write(new Message(new AdvancedJsonRpc\SuccessResponse( + $msg->body->id, + new Content(file_get_contents($msg->body->params->textDocument->uri)) + ))); + } else if ($msg->body->method === 'workspace/xglob') { + // Glob requested + $globCalled = true; + $files = array_map( + '\\LanguageServer\\pathToUri', + array_merge(...array_map(function (string $pattern) use ($rootPath) { + return Glob::glob(Path::makeAbsolute($pattern, $rootPath)); + }, $msg->body->params->patterns)) + ); + $input->write(new Message(new AdvancedJsonRpc\SuccessResponse($msg->body->id, $files))); + } else if ($msg->body->method === 'window/logMessage') { + // Message logged + if ($msg->body->params->type === MessageType::ERROR) { + // Error happened during indexing, fail test + if ($promise->state === Promise::PENDING) { + $promise->reject(new Exception($msg->body->params->message)); + } + } else if (strpos($msg->body->params->message, 'All 10 PHP files parsed') !== false) { + // Indexing finished + $promise->fulfill(); + } + } + }); + $server = new LanguageServer($input, $output); + $capabilities = new ClientCapabilities; + $capabilities->xglobProvider = true; + $capabilities->xcontentProvider = true; + $server->initialize(getmypid(), $capabilities, $rootPath); + $promise->wait(); + $this->assertTrue($globCalled); + $this->assertTrue($contentCalled); + } } diff --git a/tests/MockProtocolStream.php b/tests/MockProtocolStream.php index 5550b3f..b2a489d 100644 --- a/tests/MockProtocolStream.php +++ b/tests/MockProtocolStream.php @@ -5,7 +5,7 @@ namespace LanguageServer\Tests; use LanguageServer\{ProtocolReader, ProtocolWriter}; use LanguageServer\Protocol\Message; -use Sabre\Event\{Emitter, Promise}; +use Sabre\Event\{Loop, Emitter, Promise}; /** * A fake duplex protocol stream @@ -20,7 +20,9 @@ class MockProtocolStream extends Emitter implements ProtocolReader, ProtocolWrit */ public function write(Message $msg): Promise { - $this->emit('message', [Message::parse((string)$msg)]); + Loop\nextTick(function () use ($msg) { + $this->emit('message', [Message::parse((string)$msg)]); + }); return Promise\resolve(null); } }