1
0
Fork 0

Cache index on disk (#82)

pull/104/head
Felix Becker 2016-10-20 03:48:30 +02:00 committed by GitHub
parent 8e36e59e9a
commit 1e7260a2ea
4 changed files with 114 additions and 10 deletions

1
.gitignore vendored
View File

@ -2,4 +2,5 @@
.vscode
.idea
vendor/
.phpls/
composer.lock

View File

@ -10,10 +10,12 @@ use LanguageServer\Protocol\{
TextDocumentSyncKind,
Message,
MessageType,
InitializeResult
InitializeResult,
SymbolInformation
};
use AdvancedJsonRpc;
use Sabre\Event\Loop;
use JsonMapper;
use Exception;
use Throwable;
@ -42,6 +44,12 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
private $protocolWriter;
private $client;
/**
* The root project path that was passed to initialize()
*
* @var string
*/
private $rootPath;
private $project;
public function __construct(ProtocolReader $reader, ProtocolWriter $writer)
@ -91,9 +99,12 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
*/
public function initialize(int $processId, ClientCapabilities $capabilities, string $rootPath = null): InitializeResult
{
$this->rootPath = $rootPath;
// start building project index
if ($rootPath) {
$this->indexProject($rootPath);
if ($rootPath !== null) {
$this->restoreCache();
$this->indexProject();
}
$serverCapabilities = new ServerCapabilities();
@ -124,7 +135,9 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
*/
public function shutdown()
{
if ($this->rootPath !== null) {
$this->saveCache();
}
}
/**
@ -140,23 +153,23 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
/**
* Parses workspace files, one at a time.
*
* @param string $rootPath The rootPath of the workspace.
* @return void
*/
private function indexProject(string $rootPath)
private function indexProject()
{
$fileList = findFilesRecursive($rootPath, '/^.+\.php$/i');
$fileList = findFilesRecursive($this->rootPath, '/^.+\.php$/i');
$numTotalFiles = count($fileList);
$startTime = microtime(true);
$fileNum = 0;
$processFile = function() use (&$fileList, &$fileNum, &$processFile, $rootPath, $numTotalFiles, $startTime) {
$processFile = function() use (&$fileList, &$fileNum, &$processFile, $numTotalFiles, $startTime) {
if ($fileNum < $numTotalFiles) {
$file = $fileList[$fileNum];
$uri = pathToUri($file);
$fileNum++;
$shortName = substr($file, strlen($rootPath) + 1);
$shortName = substr($file, strlen($this->rootPath) + 1);
$this->client->window->logMessage(MessageType::INFO, "Parsing file $fileNum/$numTotalFiles: $shortName.");
if (filesize($file) > 500000) {
$this->client->window->logMessage(MessageType::INFO, "Not parsing $shortName because it exceeds size limit of 0.5MB");
@ -169,14 +182,71 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher
}
}
if ($fileNum % 1000 === 0) {
$this->saveCache();
}
Loop\setTimeout($processFile, 0);
} else {
$duration = (int)(microtime(true) - $startTime);
$mem = (int)(memory_get_usage(true) / (1024 * 1024));
$this->client->window->logMessage(MessageType::INFO, "All PHP files parsed in $duration seconds. $mem MiB allocated.");
$this->saveCache();
}
};
Loop\setTimeout($processFile, 0);
}
/**
* Restores the definition and reference index from the .phpls cache directory, if available
*
* @return void
*/
public function restoreCache()
{
$cacheDir = $this->rootPath . '/.phpls';
if (is_dir($cacheDir)) {
if (file_exists($cacheDir . '/symbols.json')) {
$json = json_decode(file_get_contents($cacheDir . '/symbols.json'));
$mapper = new JsonMapper;
$symbols = $mapper->mapArray($json, [], SymbolInformation::class);
$count = count($symbols);
$this->project->setSymbols($symbols);
$this->client->window->logMessage(MessageType::INFO, "Restoring $count symbols");
}
if (file_exists($cacheDir . '/references.json')) {
$references = json_decode(file_get_contents($cacheDir . '/references.json'), true);
$count = array_sum(array_map('count', $references));
$this->project->setReferenceUris($references);
$this->client->window->logMessage(MessageType::INFO, "Restoring $count references");
}
} else {
$this->client->window->logMessage(MessageType::INFO, 'No cache found');
}
}
/**
* Saves the definition and reference index to the .phpls cache directory
*
* @return void
*/
public function saveCache()
{
// Cache definitions, references
$cacheDir = $this->rootPath . '/.phpls';
if (!is_dir($cacheDir)) {
mkdir($cacheDir);
}
$symbols = $this->project->getSymbols();
$count = count($symbols);
$this->client->window->logMessage(MessageType::INFO, "Saving $count symbols to cache");
file_put_contents($cacheDir . "/symbols.json", json_encode($symbols, JSON_UNESCAPED_SLASHES));
$references = $this->project->getReferenceUris();
$count = array_sum(array_map('count', $references));
$this->client->window->logMessage(MessageType::INFO, "Saving $count references to cache");
file_put_contents($cacheDir . "/references.json", json_encode($references, JSON_UNESCAPED_SLASHES));
}
}

View File

@ -160,6 +160,17 @@ class Project
$this->symbols[$fqn] = $symbol;
}
/**
* Sets the SymbolInformation index
*
* @param SymbolInformation[] $symbols
* @return void
*/
public function setSymbols(array $symbols)
{
$this->symbols = $symbols;
}
/**
* Unsets the SymbolInformation for a specific symbol
* and removes all references pointing to that symbol
@ -221,6 +232,28 @@ class Project
return array_map([$this, 'getDocument'], $this->references[$fqn]);
}
/**
* Returns an associative array [string => string[]] that maps fully qualified symbol names
* to URIs of the document where the symbol is referenced
*
* @return string[][]
*/
public function getReferenceUris()
{
return $this->references;
}
/**
* Sets the reference index
*
* @param string[][] $references an associative array [string => string[]] from FQN to URIs
* @return void
*/
public function setReferenceUris(array $references)
{
$this->references = $references;
}
/**
* Returns the document where a symbol is defined
*

View File

@ -21,7 +21,7 @@ class SymbolInformation
/**
* The kind of this symbol.
*
* @var number
* @var int
*/
public $kind;