Skip to content

Commit 550f24e

Browse files
committed
Use incremental text edit updates
Instead of the IDE passing the entire document each time it will now (by default?) pass only the text edits. This will reduce I/O (nobody cares) but it will also open the possibltiy of incrementally updating the AST.
1 parent e3c5877 commit 550f24e

5 files changed

Lines changed: 109 additions & 2 deletions

File tree

‎lib/Extension/LanguageServer/EventDispatcher/LazyAggregateProvider.php‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public function getListenersForEvent(object $event): iterable
2727
{
2828
if (null === $this->aggregateProvider) {
2929
$this->aggregateProvider = new ListenerProviderAggregate();
30+
3031
foreach ($this->serviceIds as $serviceId) {
3132
/** @var object|null $listenerProvider */
3233
$listenerProvider = $this->container->get($serviceId);

‎lib/Extension/LanguageServer/LanguageServerExtension.php‎

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
use Phpactor\Extension\LanguageServer\Dispatcher\PhpactorDispatcherFactory;
2121
use Phpactor\Extension\LanguageServer\EventDispatcher\LazyAggregateProvider;
2222
use Phpactor\Extension\LanguageServer\Handler\DebugHandler;
23+
use Phpactor\Extension\LanguageServer\Listener\IncrementalUpdateListener;
2324
use Phpactor\Extension\LanguageServer\Listener\InvalidConfigListener;
2425
use Phpactor\Extension\LanguageServer\Listener\ProjectConfigTrustListener;
2526
use Phpactor\Extension\LanguageServer\Listener\SelfDestructListener;
@@ -33,6 +34,7 @@
3334
use Phpactor\Extension\LanguageServer\Command\StartCommand;
3435
use Phpactor\FilePathResolver\PathResolver;
3536
use Phpactor\LanguageServerProtocol\ClientCapabilities;
37+
use Phpactor\LanguageServerProtocol\TextDocumentSyncKind;
3638
use Phpactor\LanguageServer\Core\CodeAction\AggregateCodeActionProvider;
3739
use Phpactor\LanguageServer\Core\CodeAction\CodeActionProvider;
3840
use Phpactor\LanguageServer\Core\Command\CommandDispatcher;
@@ -296,6 +298,14 @@ private function registerSession(ContainerBuilder $container): void
296298
self::TAG_LISTENER_PROVIDER => [],
297299
]);
298300

301+
$container->register(IncrementalUpdateListener::class, function (Container $container) {
302+
return new IncrementalUpdateListener(
303+
$container->expect(self::SERVICE_SESSION_WORKSPACE, Workspace::class),
304+
);
305+
}, [
306+
self::TAG_LISTENER_PROVIDER => [],
307+
]);
308+
299309
$container->register('language_server.session.handler.session', function (Container $container) {
300310
$providers = [];
301311
foreach ($container->getServiceIdsForTag(self::TAG_STATUS_PROVIDER) as $serviceId => $_) {
@@ -336,6 +346,7 @@ private function registerEventDispatcher(ContainerBuilder $container): void
336346
);
337347

338348
return new EventDispatcher($aggregate);
349+
339350
});
340351
}
341352

@@ -470,7 +481,10 @@ private function registerHandlers(ContainerBuilder $container): void
470481
});
471482

472483
$container->register(TextDocumentHandler::class, function (Container $container) {
473-
return new TextDocumentHandler($container->get(EventDispatcherInterface::class));
484+
return new TextDocumentHandler(
485+
$container->get(EventDispatcherInterface::class),
486+
TextDocumentSyncKind::INCREMENTAL,
487+
);
474488
}, [ self::TAG_METHOD_HANDLER => []]);
475489

476490
$container->register(StatsHandler::class, function (Container $container) {
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServer\Listener;
4+
5+
use Generator;
6+
use Phpactor\Extension\LanguageServerBridge\Converter\RangeConverter;
7+
use Phpactor\LanguageServerProtocol\TextDocumentContentChangeIncrementalEvent;
8+
use Phpactor\LanguageServerProtocol\VersionedTextDocumentIdentifier;
9+
use Phpactor\LanguageServer\Core\Workspace\Workspace;
10+
use Phpactor\LanguageServer\Event\TextDocumentIncrementallyUpdated;
11+
use Phpactor\TextDocument\TextEdit;
12+
use Phpactor\TextDocument\TextEdits;
13+
use Psr\EventDispatcher\ListenerProviderInterface;
14+
15+
class IncrementalUpdateListener implements ListenerProviderInterface
16+
{
17+
public function __construct(private Workspace $workspace)
18+
{
19+
}
20+
21+
/**
22+
* @return Generator<callable>
23+
*/
24+
public function getListenersForEvent(object $event): Generator
25+
{
26+
if ($event instanceof TextDocumentIncrementallyUpdated) {
27+
yield function (TextDocumentIncrementallyUpdated $updated): void {
28+
$this->applyEdits($updated->identifier(), $updated->events());
29+
};
30+
return;
31+
}
32+
}
33+
34+
/**
35+
* @param TextDocumentContentChangeIncrementalEvent[] $array
36+
*/
37+
public function applyEdits(VersionedTextDocumentIdentifier $versionedTextDocumentIdentifier, array $array): void
38+
{
39+
$document = $this->workspace->get($versionedTextDocumentIdentifier->uri);
40+
$edits = array_map(function (TextDocumentContentChangeIncrementalEvent $event) use ($document) {
41+
$range = RangeConverter::toPhpactorRange($event->range, $document->text);
42+
return TextEdit::create(
43+
$range->start(),
44+
$range->length(),
45+
$event->text
46+
);
47+
}, $array);
48+
49+
;
50+
$this->workspace->update(
51+
$versionedTextDocumentIdentifier,
52+
TextEdits::fromTextEdits($edits)->apply($document->text)
53+
);
54+
}
55+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Phpactor\Extension\LanguageServer\Tests\Unit\Listener;
4+
5+
use PHPUnit\Framework\TestCase;
6+
use Phpactor\Extension\LanguageServer\Listener\IncrementalUpdateListener;
7+
use Phpactor\LanguageServerProtocol\TextDocumentContentChangeIncrementalEvent;
8+
use Phpactor\LanguageServerProtocol\VersionedTextDocumentIdentifier;
9+
use Phpactor\LanguageServer\LanguageServerTesterBuilder;
10+
use Phpactor\LanguageServer\Test\ProtocolFactory;
11+
12+
class IncrementalUpdateListenerTest extends TestCase
13+
{
14+
public function testApplyEdits(): void
15+
{
16+
$builder = LanguageServerTesterBuilder::create();
17+
$workspace = $builder->workspace();
18+
$builder->build()->textDocument()->open('file:///foo', 'hello');
19+
20+
(new IncrementalUpdateListener($workspace))->applyEdits(
21+
new VersionedTextDocumentIdentifier(version: 1, uri: 'file:///foo'),
22+
[
23+
new TextDocumentContentChangeIncrementalEvent(
24+
ProtocolFactory::range(0, 0, 0, 1),
25+
text: 'b',
26+
),
27+
new TextDocumentContentChangeIncrementalEvent(
28+
ProtocolFactory::range(0, 5, 0, 5),
29+
text: 'b',
30+
),
31+
],
32+
);
33+
34+
self::assertEquals('bellob', $workspace->get('file:///foo')->text);
35+
}
36+
}

‎lib/Extension/LanguageServerWorseReflection/Listener/InvalidateDocumentCacheListener.php‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Phpactor\Extension\LanguageServerWorseReflection\Listener;
44

55
use Phpactor\LanguageServer\Event\TextDocumentClosed;
6+
use Phpactor\LanguageServer\Event\TextDocumentIncrementallyUpdated;
67
use Phpactor\LanguageServer\Event\TextDocumentSaved;
78
use Phpactor\LanguageServer\Event\TextDocumentUpdated;
89
use Phpactor\TextDocument\TextDocumentUri;
@@ -20,7 +21,7 @@ public function __construct(private CacheForDocument $cache)
2021
*/
2122
public function getListenersForEvent($event): iterable
2223
{
23-
if ($event instanceof TextDocumentUpdated) {
24+
if ($event instanceof TextDocumentUpdated || $event instanceof TextDocumentIncrementallyUpdated) {
2425
yield function () use ($event): void {
2526
$this->cache->purge(TextDocumentUri::fromString($event->identifier()->uri));
2627
};

0 commit comments

Comments
 (0)