Skip to content
This repository has been archived by the owner on Mar 6, 2022. It is now read-only.

Commit

Permalink
Optimize workspace update (#17)
Browse files Browse the repository at this point in the history
* Added benchmark for workspace update

* Add a minimum delay for workspace updates

* Fix CS

* Do not use custom script location
  • Loading branch information
dantleech authored Dec 29, 2020
1 parent e61f389 commit 89eae53
Show file tree
Hide file tree
Showing 9 changed files with 326 additions and 9 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
/tests/*/Workspace
/composer.lock
/.php_cs.cache
/.phpbench
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"friendsofphp/php-cs-fixer": "^2.17",
"jangregor/phpstan-prophecy": "^0.8.0",
"phpactor/test-utils": "^1.1",
"phpbench/phpbench": "^1.0.0-alpha3",
"phpbench/phpbench": "^1.0.0",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/phpstan": "~0.12.0",
"phpunit/phpunit": "^9.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

class LanguageServerWorseReflectionExtension implements Extension
{
const PARAM_UPDATE_INTERVAL = 'language_server_worse_reflection.workspace_index.update_interval';

/**
* {@inheritDoc}
*/
Expand All @@ -25,6 +27,12 @@ public function load(ContainerBuilder $container)

public function configure(Resolver $schema)
{
$schema->setDefaults([
self::PARAM_UPDATE_INTERVAL => 100,
]);
$schema->setDescriptions([
self::PARAM_UPDATE_INTERVAL => 'Minimum interval to update the workspace index as documents are updated (in milliseconds)'
]);
}

private function registerSourceLocator(ContainerBuilder $container): void
Expand All @@ -45,7 +53,8 @@ private function registerSourceLocator(ContainerBuilder $container): void

$container->register(WorkspaceIndex::class, function (Container $container) {
return new WorkspaceIndex(
ReflectorBuilder::create()->build()
ReflectorBuilder::create()->build(),
$container->getParameter(self::PARAM_UPDATE_INTERVAL)
);
});
}
Expand Down
60 changes: 56 additions & 4 deletions lib/LanguageServerWorseReflection/Workspace/WorkspaceIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
use Phpactor\WorseReflection\Core\Name;
use Phpactor\WorseReflection\Core\Reflector\SourceCodeReflector;
use RuntimeException;
use function Amp\asyncCall;
use function Amp\delay;

class WorkspaceIndex
{
Expand All @@ -29,9 +31,25 @@ class WorkspaceIndex
*/
private $documentToNameMap = [];

public function __construct(SourceCodeReflector $reflector)
/**
* @var TextDocument|null
*/
private $documentToUpdate;

/**
* @var bool
*/
private $waiting = false;

/**
* @var int
*/
private $updateInterval;

public function __construct(SourceCodeReflector $reflector, int $updateInterval = 1000)
{
$this->reflector = $reflector;
$this->updateInterval = $updateInterval;
}

public function documentForName(Name $name): ?TextDocument
Expand All @@ -49,24 +67,58 @@ public function index(TextDocument $textDocument): void
$this->updateDocument($textDocument);
}

/**
* Refresh the document.
*
* In order to prevent continuous reparsing the document will
* only be refreshed at the sepecified interval.
*/
private function updateDocument(TextDocument $textDocument): void
{
if ($this->waiting) {
$this->documentToUpdate = $textDocument;
return;
}

$this->documentToUpdate = null;

$newNames = [];
foreach ($this->reflector->reflectClassesIn($textDocument) as $reflectionClass) {
$newNames[] = $reflectionClass->name()->full();
}

foreach ($this->reflector->reflectFunctionsIn($textDocument) as $reflectionFunction) {
$newNames[] = $reflectionFunction->name()->full();
}

$this->updateNames($textDocument, $newNames, $this->documentToNameMap[(string)$textDocument->uri()] ?? []);
$this->updateNames(
$textDocument,
$newNames,
$this->documentToNameMap[(string)$textDocument->uri()] ?? []
);

if ($this->updateInterval === 0) {
return;
}

$this->waiting = true;

asyncCall(function () {
yield delay($this->updateInterval);
$this->waiting = false;

if (null === $this->documentToUpdate) {
return;
}

$this->updateDocument($this->documentToUpdate);
});
}

private function updateNames(TextDocument $textDocument, array $newNames, array $currentNames): void
{
$namesToRemove = array_diff($currentNames, $newNames);

foreach ($newNames as $name) {
$this->byName[$name] = $textDocument;
}
Expand Down
5 changes: 4 additions & 1 deletion phpbench.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
{
"bootstrap": "vendor/autoload.php",
"path": "tests/LanguageServerCodeTransform/Benchmark"
"path": [
"tests/LanguageServerCodeTransform/Benchmark",
"tests/LanguageServerWorseReflection/Benchmark"
]
}
2 changes: 1 addition & 1 deletion tests/LanguageServerCompletion/IntegrationTestCase.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ protected function createTester(): LanguageServerTester

LanguageServerBridgeExtension::class,
], [
FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ .'/../../'
FilePathResolverExtension::PARAM_APPLICATION_ROOT => __DIR__ .'/../../',
]);

$builder = $container->get(LanguageServerBuilder::class);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace Phpactor\Extension\LanguageServerWorseReflection\Tests\Benchmark;

use PhpBench\Benchmark\Metadata\Annotations\Executor;
use PhpBench\Benchmark\Metadata\Annotations\OutputTimeUnit;
use PhpBench\Benchmark\Metadata\Annotations\Revs;
use Phpactor\Extension\LanguageServerCompletion\Tests\IntegrationTestCase;
use Phpactor\LanguageServer\Test\LanguageServerTester;
use PhpBench\Benchmark\Metadata\Annotations\ParamProviders;
use Generator;

/**
* @BeforeMethods({"setUp"})
* @OutputTimeUnit("milliseconds")
* @Executor("local")
*/
class WorkspaceIndexBench extends IntegrationTestCase
{
/**
* @var LanguageServerTester
*/
private $tester;

public function setUp(): void
{
$this->tester = $this->createTester();
$this->tester->initialize();
$this->tester->textDocument()->open('file:///foobar', '');
}

/**
* @ParamProviders({"provideUpdate"})
* @Revs(10)
* @Iterations(10)
*/
public function benchUpdate(array $params): void
{
$this->tester->textDocument()->update('file:///foobar', $params['text']);
}

public function provideUpdate(): Generator
{
$source = mb_str_split((string)file_get_contents(
__DIR__ . '/source/source.php.example'
), 1);

$buffer = '';

foreach ($source as $index => $char) {
$buffer .= $char;
if (0 === $index % 1000) {
yield 'length: ' . strlen($buffer) => [
'text' => $buffer
];
}
}
}
}
Loading

0 comments on commit 89eae53

Please sign in to comment.