Skip to content

Commit 0de7ba8

Browse files
mousetrapsfelixfbecker
authored andcommitted
Ensure diagnostics are cleared on file deletion (#319)
* Ensure diagnostics are cleared on file deletion Previously, error diagnostics would not be cleared when a file was deleted while it was closed. This would result in lingering errors in the problems view that could only be cleared by reloading the language server. This fix addresses the issue by adding support for workspace/didChangeWatchedFiles and automatically clearing diagnostics for deleted files. * add FileEvent constructor
1 parent 56bd465 commit 0de7ba8

File tree

5 files changed

+85
-3
lines changed

5 files changed

+85
-3
lines changed

src/LanguageServer.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,7 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath =
253253
}
254254
if ($this->workspace === null) {
255255
$this->workspace = new Server\Workspace(
256+
$this->client,
256257
$this->projectIndex,
257258
$dependenciesIndex,
258259
$sourceIndex,

src/Protocol/FileEvent.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,14 @@ class FileEvent
2020
* @var int
2121
*/
2222
public $type;
23+
24+
/**
25+
* @param string $uri
26+
* @param int $type
27+
*/
28+
public function __construct(string $uri, int $type)
29+
{
30+
$this->uri = $uri;
31+
$this->type = $type;
32+
}
2333
}

src/Server/Workspace.php

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,15 @@
55

66
use LanguageServer\{LanguageClient, Project, PhpDocumentLoader};
77
use LanguageServer\Index\{ProjectIndex, DependenciesIndex, Index};
8-
use LanguageServer\Protocol\{SymbolInformation, SymbolDescriptor, ReferenceInformation, DependencyReference, Location};
8+
use LanguageServer\Protocol\{
9+
FileChangeType,
10+
FileEvent,
11+
SymbolInformation,
12+
SymbolDescriptor,
13+
ReferenceInformation,
14+
DependencyReference,
15+
Location
16+
};
917
use Sabre\Event\Promise;
1018
use function Sabre\Event\coroutine;
1119
use function LanguageServer\{waitForEvent, getPackageName};
@@ -15,6 +23,11 @@
1523
*/
1624
class Workspace
1725
{
26+
/**
27+
* @var LanguageClient
28+
*/
29+
public $client;
30+
1831
/**
1932
* The symbol index for the workspace
2033
*
@@ -43,14 +56,16 @@ class Workspace
4356
public $documentLoader;
4457

4558
/**
59+
* @param LanguageClient $client LanguageClient instance used to signal updated results
4660
* @param ProjectIndex $index Index that is searched on a workspace/symbol request
4761
* @param DependenciesIndex $dependenciesIndex Index that is used on a workspace/xreferences request
4862
* @param DependenciesIndex $sourceIndex Index that is used on a workspace/xreferences request
4963
* @param \stdClass $composerLock The parsed composer.lock of the project, if any
5064
* @param PhpDocumentLoader $documentLoader PhpDocumentLoader instance to load documents
5165
*/
52-
public function __construct(ProjectIndex $index, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null)
66+
public function __construct(LanguageClient $client, ProjectIndex $index, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null)
5367
{
68+
$this->client = $client;
5469
$this->sourceIndex = $sourceIndex;
5570
$this->index = $index;
5671
$this->dependenciesIndex = $dependenciesIndex;
@@ -82,6 +97,21 @@ public function symbol(string $query): Promise
8297
});
8398
}
8499

100+
/**
101+
* The watched files notification is sent from the client to the server when the client detects changes to files watched by the language client.
102+
*
103+
* @param FileEvent[] $changes
104+
* @return void
105+
*/
106+
public function didChangeWatchedFiles(array $changes)
107+
{
108+
foreach ($changes as $change) {
109+
if ($change->type === FileChangeType::DELETED) {
110+
$this->client->textDocument->publishDiagnostics($change->uri, []);
111+
}
112+
}
113+
}
114+
85115
/**
86116
* The workspace references request is sent from the client to the server to locate project-wide references to a symbol given its description / metadata.
87117
*

tests/Server/ServerTestCase.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ public function setUp()
5454
$client = new LanguageClient(new MockProtocolStream, new MockProtocolStream);
5555
$this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver);
5656
$this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $projectIndex);
57-
$this->workspace = new Server\Workspace($projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader);
57+
$this->workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader);
5858

5959
$globalSymbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_symbols.php'));
6060
$globalReferencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_references.php'));
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
declare(strict_types = 1);
3+
4+
namespace LanguageServer\Tests\Server\Workspace;
5+
6+
use LanguageServer\ContentRetriever\FileSystemContentRetriever;
7+
use LanguageServer\{DefinitionResolver, LanguageClient, PhpDocumentLoader, Server};
8+
use LanguageServer\Index\{DependenciesIndex, Index, ProjectIndex};
9+
use LanguageServer\Protocol\{FileChangeType, FileEvent, Message};
10+
use LanguageServer\Tests\MockProtocolStream;
11+
use LanguageServer\Tests\Server\ServerTestCase;
12+
use LanguageServer\Server\Workspace;
13+
use Sabre\Event\Loop;
14+
15+
class DidChangeWatchedFilesTest extends ServerTestCase
16+
{
17+
public function testDeletingFileClearsAllDiagnostics()
18+
{
19+
$client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream());
20+
$projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex());
21+
$definitionResolver = new DefinitionResolver($projectIndex);
22+
$loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver);
23+
$workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $loader, null);
24+
25+
$fileEvent = new FileEvent('my uri', FileChangeType::DELETED);
26+
27+
$isDiagnosticsCleared = false;
28+
$writer->on('message', function (Message $message) use ($fileEvent, &$isDiagnosticsCleared) {
29+
if ($message->body->method === "textDocument/publishDiagnostics") {
30+
$this->assertEquals($message->body->params->uri, $fileEvent->uri);
31+
$this->assertEquals($message->body->params->diagnostics, []);
32+
$isDiagnosticsCleared = true;
33+
}
34+
});
35+
36+
$workspace->didChangeWatchedFiles([$fileEvent]);
37+
Loop\tick(true);
38+
39+
$this->assertTrue($isDiagnosticsCleared, "Deleting file should clear all diagnostics.");
40+
}
41+
}

0 commit comments

Comments
 (0)