Skip to content

Commit

Permalink
Start indexing after initialization
Browse files Browse the repository at this point in the history
The indexer is moved to the method initialized, so we can request
configurations from the client to init the indexer itself.
  • Loading branch information
JSteitz committed Aug 31, 2018
1 parent a5417cd commit e317e8c
Show file tree
Hide file tree
Showing 5 changed files with 167 additions and 70 deletions.
21 changes: 21 additions & 0 deletions src/Client/Workspace.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace LanguageServer\Client;

use LanguageServer\ClientHandler;
use LanguageServer\Protocol\ConfigurationItem;
use LanguageServer\Protocol\TextDocumentIdentifier;
use Sabre\Event\Promise;
use JsonMapper;
Expand Down Expand Up @@ -44,4 +45,24 @@ public function xfiles(string $base = null): Promise
return $this->mapper->mapArray($textDocuments, [], TextDocumentIdentifier::class);
});
}

/**
* The workspace/configuration request is sent from the server to the
* client to fetch configuration settings from the client.
*
* The request can fetch n configuration settings in one roundtrip.
* The order of the returned configuration settings correspond to the order
* of the passed ConfigurationItems (e.g. the first item in the response is
* the result for the first configuration item in the params).
*
* @param ConfigurationItem[] $items
* @return Promise
*/
public function configuration(array $items): Promise
{
return $this->handler->request(
'workspace/configuration',
['items' => $items]
);
}
}
2 changes: 1 addition & 1 deletion src/Index/ProjectIndex.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct(Index $sourceIndex, DependenciesIndex $dependenciesI
/**
* @return ReadableIndex[]
*/
protected function getIndexes(): array
public function getIndexes(): array
{
return [$this->sourceIndex, $this->dependenciesIndex];
}
Expand Down
152 changes: 97 additions & 55 deletions src/LanguageServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
namespace LanguageServer;

use LanguageServer\Protocol\{
ConfigurationItem,
ServerCapabilities,
ClientCapabilities,
TextDocumentSyncKind,
Expand All @@ -15,7 +16,7 @@
use LanguageServer\FilesFinder\{FilesFinder, ClientFilesFinder, FileSystemFilesFinder};
use LanguageServer\ContentRetriever\{ContentRetriever, ClientContentRetriever, FileSystemContentRetriever};
use LanguageServer\Index\{DependenciesIndex, GlobalIndex, Index, ProjectIndex, StubsIndex};
use LanguageServer\Cache\{FileSystemCache, ClientCache};
use LanguageServer\Cache\{Cache, FileSystemCache, ClientCache};
use AdvancedJsonRpc;
use Sabre\Event\Promise;
use function Sabre\Event\coroutine;
Expand Down Expand Up @@ -106,6 +107,16 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
*/
protected $definitionResolver;

/**
* @var string|null
*/
protected $rootPath;

/**
* @var Cache
*/
protected $cache;

/**
* @param ProtocolReader $reader
* @param ProtocolWriter $writer
Expand Down Expand Up @@ -162,14 +173,18 @@ public function __construct(ProtocolReader $reader, ProtocolWriter $writer)
*
* @param ClientCapabilities $capabilities The capabilities provided by the client (editor)
* @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open.
* @param int|null $processId The process Id of the parent process that started the server. Is null if the process has not been started by another process. If the parent process is not alive then the server should exit (see exit notification) its process.
* @param Options $initializationOptions The options send from client to initialize the server
* @param int|null $processId The process Id of the parent process that started the server.
* Is null if the process has not been started by another process.
* If the parent process is not alive then the server should exit
* (see exit notification) its process.
* @return Promise <InitializeResult>
*/
public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, Options $initializationOptions = null): Promise
{
return coroutine(function () use ($capabilities, $rootPath, $processId, $initializationOptions) {

public function initialize(
ClientCapabilities $capabilities,
string $rootPath = null,
int $processId = null
): Promise {
return coroutine(function () use ($capabilities, $rootPath, $processId) {
if ($capabilities->xfilesProvider) {
$this->filesFinder = new ClientFilesFinder($this->client);
} else {
Expand All @@ -187,60 +202,115 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath =
$this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex, $this->composerJson);
$stubsIndex = StubsIndex::read();
$this->globalIndex = new GlobalIndex($stubsIndex, $this->projectIndex);
$initializationOptions = $initializationOptions ?? new Options;
$this->rootPath = $rootPath;

// The DefinitionResolver should look in stubs, the project source and dependencies
$this->definitionResolver = new DefinitionResolver($this->globalIndex);

$this->documentLoader = new PhpDocumentLoader(
$this->contentRetriever,
$this->projectIndex,
$this->definitionResolver
);

if ($rootPath !== null) {
yield $this->beforeIndex($rootPath);
if ($this->rootPath !== null) {
yield $this->beforeIndex($this->rootPath);

// Find composer.json
if ($this->composerJson === null) {
$composerJsonFiles = yield $this->filesFinder->find(Path::makeAbsolute('**/composer.json', $rootPath));
$composerJsonFiles = yield $this->filesFinder->find(
Path::makeAbsolute('**/composer.json', $this->rootPath)
);
sortUrisLevelOrder($composerJsonFiles);

if (!empty($composerJsonFiles)) {
$this->composerJson = json_decode(yield $this->contentRetriever->retrieve($composerJsonFiles[0]));
$this->composerJson = json_decode(
yield $this->contentRetriever->retrieve($composerJsonFiles[0])
);
}
}

// Find composer.lock
if ($this->composerLock === null) {
$composerLockFiles = yield $this->filesFinder->find(Path::makeAbsolute('**/composer.lock', $rootPath));
$composerLockFiles = yield $this->filesFinder->find(
Path::makeAbsolute('**/composer.lock', $this->rootPath)
);
sortUrisLevelOrder($composerLockFiles);

if (!empty($composerLockFiles)) {
$this->composerLock = json_decode(yield $this->contentRetriever->retrieve($composerLockFiles[0]));
$this->composerLock = json_decode(
yield $this->contentRetriever->retrieve($composerLockFiles[0])
);
}
}

$cache = $capabilities->xcacheProvider ? new ClientCache($this->client) : new FileSystemCache;
$this->cache = $capabilities->xcacheProvider ? new ClientCache($this->client) : new FileSystemCache;
}

$serverCapabilities = new ServerCapabilities();
// Ask the client to return always full documents (because we need to rebuild the AST from scratch)
$serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL;
// Support "Find all symbols"
$serverCapabilities->documentSymbolProvider = true;
// Support "Find all symbols in workspace"
$serverCapabilities->workspaceSymbolProvider = true;
// Support "Go to definition"
$serverCapabilities->definitionProvider = true;
// Support "Find all references"
$serverCapabilities->referencesProvider = true;
// Support "Hover"
$serverCapabilities->hoverProvider = true;
// Support "Completion"
$serverCapabilities->completionProvider = new CompletionOptions;
$serverCapabilities->completionProvider->resolveProvider = false;
$serverCapabilities->completionProvider->triggerCharacters = ['$', '>'];

$serverCapabilities->signatureHelpProvider = new SignatureHelpOptions();
$serverCapabilities->signatureHelpProvider->triggerCharacters = ['(', ','];

// Support global references
$serverCapabilities->xworkspaceReferencesProvider = true;
$serverCapabilities->xdefinitionProvider = true;
$serverCapabilities->xdependenciesProvider = true;

return new InitializeResult($serverCapabilities);
});
}

/**
* The initialized notification is sent from the client to the server after
* the client received the result of the initialize request but before the
* client is sending any other request or notification to the server.
*
* @return Promise
*/
public function initialized(): Promise
{
return coroutine(function () {
list($sourceIndex, $dependenciesIndex) = $this->projectIndex->getIndexes();
$mapper = new \JsonMapper();
$configurationitem = new ConfigurationItem();
$configurationitem->section = 'php';
$configuration = yield $this->client->workspace->configuration([$configurationitem]);
$options = $mapper->map($configuration[0], new Options());

if ($this->rootPath) {
// Index in background
$indexer = new Indexer(
$this->filesFinder,
$rootPath,
$this->rootPath,
$this->client,
$cache,
$this->cache,
$dependenciesIndex,
$sourceIndex,
$this->documentLoader,
$initializationOptions,
$options,
$this->composerLock,
$this->composerJson,
$initializationOptions
$this->composerJson
);

$indexer->index()->otherwise('\\LanguageServer\\crash');
}


if ($this->textDocument === null) {
$this->textDocument = new Server\TextDocument(
$this->documentLoader,
Expand All @@ -251,54 +321,26 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath =
$this->composerLock
);
}

if ($this->workspace === null) {
$this->workspace = new Server\Workspace(
$this->client,
$this->projectIndex,
$dependenciesIndex,
$sourceIndex,
$options,
$this->composerLock,
$this->documentLoader,
$this->composerJson,
$indexer,
$initializationOptions
$this->composerJson
);
}

$serverCapabilities = new ServerCapabilities();
// Ask the client to return always full documents (because we need to rebuild the AST from scratch)
$serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL;
// Support "Find all symbols"
$serverCapabilities->documentSymbolProvider = true;
// Support "Find all symbols in workspace"
$serverCapabilities->workspaceSymbolProvider = true;
// Support "Go to definition"
$serverCapabilities->definitionProvider = true;
// Support "Find all references"
$serverCapabilities->referencesProvider = true;
// Support "Hover"
$serverCapabilities->hoverProvider = true;
// Support "Completion"
$serverCapabilities->completionProvider = new CompletionOptions;
$serverCapabilities->completionProvider->resolveProvider = false;
$serverCapabilities->completionProvider->triggerCharacters = ['$', '>'];

$serverCapabilities->signatureHelpProvider = new SignatureHelpOptions();
$serverCapabilities->signatureHelpProvider->triggerCharacters = ['(', ','];

// Support global references
$serverCapabilities->xworkspaceReferencesProvider = true;
$serverCapabilities->xdefinitionProvider = true;
$serverCapabilities->xdependenciesProvider = true;

return new InitializeResult($serverCapabilities);
});
}

/**
* The shutdown request is sent from the client to the server. It asks the server to shut down, but to not exit
* (otherwise the response might not be delivered correctly to the client). There is a separate exit notification that
* asks the server to exit.
* (otherwise the response might not be delivered correctly to the client). There is a separate exit notification
* that asks the server to exit.
*
* @return void
*/
Expand Down
20 changes: 20 additions & 0 deletions src/Protocol/ConfigurationItem.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace LanguageServer\Protocol;

class ConfigurationItem
{
/**
* The scope to get the configuration section for.
*
* @var string|null
*/
public $scopeUri;

/**
* The configuration section asked for.
*
* @var string|null
*/
public $section;
}

0 comments on commit e317e8c

Please sign in to comment.