1
0
Fork 0

WIP: Implement didChangeConfiguration with reindexing

* Handle the case where didChangeConfiguration is called before workspace/configuration request
is resolved.
* Implement basic cancellation signal request
* Use defaults options and only apply new on request
pull/308/head
Jürgen Steitz 2018-08-31 20:49:23 +02:00
parent a1c3845c9f
commit a1e56543c3
5 changed files with 142 additions and 77 deletions

View File

@ -85,7 +85,6 @@ class Indexer
* @param Cache $cache * @param Cache $cache
* @param DependenciesIndex $dependenciesIndex * @param DependenciesIndex $dependenciesIndex
* @param Index $sourceIndex * @param Index $sourceIndex
* @param Options $options
* @param PhpDocumentLoader $documentLoader * @param PhpDocumentLoader $documentLoader
* @param \stdClass|null $composerLock * @param \stdClass|null $composerLock
*/ */
@ -97,7 +96,6 @@ class Indexer
DependenciesIndex $dependenciesIndex, DependenciesIndex $dependenciesIndex,
Index $sourceIndex, Index $sourceIndex,
PhpDocumentLoader $documentLoader, PhpDocumentLoader $documentLoader,
Options $options,
\stdClass $composerLock = null, \stdClass $composerLock = null,
\stdClass $composerJson = null \stdClass $composerJson = null
) { ) {
@ -108,11 +106,24 @@ class Indexer
$this->dependenciesIndex = $dependenciesIndex; $this->dependenciesIndex = $dependenciesIndex;
$this->sourceIndex = $sourceIndex; $this->sourceIndex = $sourceIndex;
$this->documentLoader = $documentLoader; $this->documentLoader = $documentLoader;
$this->options = $options;
$this->composerLock = $composerLock; $this->composerLock = $composerLock;
$this->composerJson = $composerJson; $this->composerJson = $composerJson;
$this->hasCancellationSignal = false; $this->hasCancellationSignal = false;
$this->isIndexing = false; $this->isIndexing = false;
$this->options = new Options();
}
/**
* @param Options $options
*/
public function setOptions(Options $options)
{
$this->options = $options;
}
public function getOptions(): Options
{
return $this->options;
} }
/** /**
@ -156,6 +167,7 @@ class Indexer
$this->client->window->logMessage(MessageType::INFO, 'Indexing project for definitions and static references'); $this->client->window->logMessage(MessageType::INFO, 'Indexing project for definitions and static references');
yield $this->indexFiles($source); yield $this->indexFiles($source);
$this->sourceIndex->setStaticComplete(); $this->sourceIndex->setStaticComplete();
// Dynamic references // Dynamic references
$this->client->window->logMessage(MessageType::INFO, 'Indexing project for dynamic references'); $this->client->window->logMessage(MessageType::INFO, 'Indexing project for dynamic references');
yield $this->indexFiles($source); yield $this->indexFiles($source);
@ -243,6 +255,7 @@ class Indexer
} }
$this->hasCancellationSignal = false; $this->hasCancellationSignal = false;
$this->client->window->logMessage(MessageType::INFO, 'Indexing project canceled');
}); });
} }
@ -254,6 +267,7 @@ class Indexer
{ {
return coroutine(function () use ($files) { return coroutine(function () use ($files) {
foreach ($files as $i => $uri) { foreach ($files as $i => $uri) {
// abort current running indexing
if ($this->hasCancellationSignal) { if ($this->hasCancellationSignal) {
return; return;
} }

View File

@ -117,6 +117,16 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
*/ */
protected $cache; protected $cache;
/**
* @var ClientCapabilities
*/
protected $clientCapabilities;
/**
* @var Indexer
*/
protected $indexer;
/** /**
* @param ProtocolReader $reader * @param ProtocolReader $reader
* @param ProtocolWriter $writer * @param ProtocolWriter $writer
@ -203,6 +213,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
$stubsIndex = StubsIndex::read(); $stubsIndex = StubsIndex::read();
$this->globalIndex = new GlobalIndex($stubsIndex, $this->projectIndex); $this->globalIndex = new GlobalIndex($stubsIndex, $this->projectIndex);
$this->rootPath = $rootPath; $this->rootPath = $rootPath;
$this->clientCapabilities = $capabilities;
// The DefinitionResolver should look in stubs, the project source and dependencies // The DefinitionResolver should look in stubs, the project source and dependencies
$this->definitionResolver = new DefinitionResolver($this->globalIndex); $this->definitionResolver = new DefinitionResolver($this->globalIndex);
@ -244,6 +255,43 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
} }
$this->cache = $capabilities->xcacheProvider ? new ClientCache($this->client) : new FileSystemCache; $this->cache = $capabilities->xcacheProvider ? new ClientCache($this->client) : new FileSystemCache;
// Index in background
$this->indexer = new Indexer(
$this->filesFinder,
$this->rootPath,
$this->client,
$this->cache,
$dependenciesIndex,
$sourceIndex,
$this->documentLoader,
$this->composerLock,
$this->composerJson
);
}
if ($this->textDocument === null) {
$this->textDocument = new Server\TextDocument(
$this->documentLoader,
$this->definitionResolver,
$this->client,
$this->globalIndex,
$this->composerJson,
$this->composerLock
);
}
if ($this->workspace === null) {
$this->workspace = new Server\Workspace(
$this->client,
$this->projectIndex,
$dependenciesIndex,
$sourceIndex,
$this->indexer,
$this->composerLock,
$this->documentLoader,
$this->composerJson
);
} }
$serverCapabilities = new ServerCapabilities(); $serverCapabilities = new ServerCapabilities();
@ -286,55 +334,31 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
public function initialized(): Promise public function initialized(): Promise
{ {
return coroutine(function () { return coroutine(function () {
list($sourceIndex, $dependenciesIndex) = $this->projectIndex->getIndexes(); if (!$this->rootPath) {
$mapper = new \JsonMapper(); return;
}
// request configuration if it is supported
// support comes with protocol version 3.6.0
if ($this->clientCapabilities->workspace->configuration) {
$configurationitem = new ConfigurationItem(); $configurationitem = new ConfigurationItem();
$configurationitem->section = 'php'; $configurationitem->section = 'php';
$configuration = yield $this->client->workspace->configuration([$configurationitem]); $configuration = yield $this->client->workspace->configuration([$configurationitem]);
$options = $mapper->map($configuration[0], new Options()); $options = $this->mapper->map($configuration[0], new Options());
if ($this->rootPath) {
// Index in background
$indexer = new Indexer(
$this->filesFinder,
$this->rootPath,
$this->client,
$this->cache,
$dependenciesIndex,
$sourceIndex,
$this->documentLoader,
$options,
$this->composerLock,
$this->composerJson
);
$indexer->index()->otherwise('\\LanguageServer\\crash');
} }
if ($this->textDocument === null) { // depending on the implementation of the client
$this->textDocument = new Server\TextDocument( // the workspace/didChangeConfiguration can be invoked before
$this->documentLoader, // the response from the workspace/configuration request is resolved
$this->definitionResolver, if ($this->indexer->isIndexing()) {
$this->client, return;
$this->globalIndex,
$this->composerJson,
$this->composerLock
);
} }
if ($this->workspace === null) { if ($options) {
$this->workspace = new Server\Workspace( $this->indexer->setOptions($options);
$this->client,
$this->projectIndex,
$dependenciesIndex,
$sourceIndex,
$options,
$indexer,
$this->composerLock,
$this->documentLoader,
$this->composerJson
);
} }
$this->indexer->index()->otherwise('\\LanguageServer\\crash');
}); });
} }

View File

@ -24,4 +24,9 @@ class ClientCapabilities
* @var bool|null * @var bool|null
*/ */
public $xcacheProvider; public $xcacheProvider;
/**
* @var WorkspaceClientCapabilities
*/
public $workspace;
} }

View File

@ -0,0 +1,11 @@
<?php
namespace LanguageServer\Protocol;
class WorkspaceClientCapabilities
{
/**
* @var bool|null
*/
public $configuration;
}

View File

@ -46,11 +46,6 @@ class Workspace
*/ */
private $sourceIndex; private $sourceIndex;
/**
* @var Options
*/
private $options;
/** /**
* @var Indexer * @var Indexer
*/ */
@ -71,7 +66,6 @@ class Workspace
* @param ProjectIndex $projectIndex Index that is used to wait for full index completeness * @param ProjectIndex $projectIndex Index that is used to wait for full index completeness
* @param DependenciesIndex $dependenciesIndex Index that is used on a workspace/xreferences request * @param DependenciesIndex $dependenciesIndex Index that is used on a workspace/xreferences request
* @param DependenciesIndex $sourceIndex Index that is used on a workspace/xreferences request * @param DependenciesIndex $sourceIndex Index that is used on a workspace/xreferences request
* @param Options $options Initialization options that are used on a workspace/didChangeConfiguration
* @param Indexer $indexer * @param Indexer $indexer
* @param \stdClass $composerLock The parsed composer.lock of the project, if any * @param \stdClass $composerLock The parsed composer.lock of the project, if any
* @param PhpDocumentLoader $documentLoader PhpDocumentLoader instance to load documents * @param PhpDocumentLoader $documentLoader PhpDocumentLoader instance to load documents
@ -81,7 +75,6 @@ class Workspace
ProjectIndex $projectIndex, ProjectIndex $projectIndex,
DependenciesIndex $dependenciesIndex, DependenciesIndex $dependenciesIndex,
Index $sourceIndex, Index $sourceIndex,
Options $options,
Indexer $indexer, Indexer $indexer,
\stdClass $composerLock = null, \stdClass $composerLock = null,
PhpDocumentLoader $documentLoader, PhpDocumentLoader $documentLoader,
@ -94,7 +87,6 @@ class Workspace
$this->composerLock = $composerLock; $this->composerLock = $composerLock;
$this->documentLoader = $documentLoader; $this->documentLoader = $documentLoader;
$this->composerJson = $composerJson; $this->composerJson = $composerJson;
$this->options = $options;
$this->indexer = $indexer; $this->indexer = $indexer;
} }
@ -205,36 +197,32 @@ class Workspace
/** /**
* A notification sent from the client to the server to signal the change of configuration settings. * A notification sent from the client to the server to signal the change of configuration settings.
* *
* @param \stdClass $settings Settings as JSON object structure with php as primary key * @param mixed $settings Settings as JSON object structure with php as primary key
* @return Promise * @return Promise
*/ */
public function didChangeConfiguration(\stdClass $settings): Promise public function didChangeConfiguration($settings): Promise
{ {
xdebug_break();
return coroutine(function () use ($settings) { return coroutine(function () use ($settings) {
try { if (!property_exists($settings, 'php') || $settings->php === new \stdClass()) {
xdebug_break();
$mapper = new \JsonMapper();
$settings = $mapper->map($settings->php, new Options);
if ($this->options == $settings) {
return; return;
} }
// @TODO: get changed settings and apply them try {
// @TODO: check settings that affect the indexer $mapper = new \JsonMapper();
$options = $mapper->map($settings->php, new Options);
// handle options for indexer
$currentIndexerOptions = $this->indexer->getOptions();
$this->indexer->setOptions($options);
if ($this->hasIndexerOptionsChanged($currentIndexerOptions, $options)) {
if ($this->indexer->isIndexing()) { if ($this->indexer->isIndexing()) {
yield $this->indexer->cancel(); yield $this->indexer->cancel();
} }
$this->projectIndex->wipe(); $this->projectIndex->wipe();
$this->indexer->index(); $this->indexer->index()->otherwise('\\LanguageServer\\crash');
}
$this->client->window->showMessage(
MessageType::INFO,
'Reindexing with new settings.'
);
} catch (\JsonMapper_Exception $exception) { } catch (\JsonMapper_Exception $exception) {
$this->client->window->showMessage( $this->client->window->showMessage(
MessageType::ERROR, MessageType::ERROR,
@ -244,4 +232,27 @@ class Workspace
} }
}); });
} }
/**
* Compare current options with new
*
* When the new options differ from the current, then we need start
* to reindex the project folder.
*
* @param Options $current
* @param Options $new
* @return bool
*/
private function hasIndexerOptionsChanged(Options $current, Options $new): bool
{
$properties = ['fileTypes'];
foreach ($properties as $property) {
if ($current->{$property} !== $new->{$property}) {
return true;
}
}
return false;
}
} }