Cache index on disk (#82)
parent
8e36e59e9a
commit
1e7260a2ea
|
@ -2,4 +2,5 @@
|
|||
.vscode
|
||||
.idea
|
||||
vendor/
|
||||
.phpls/
|
||||
composer.lock
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -21,7 +21,7 @@ class SymbolInformation
|
|||
/**
|
||||
* The kind of this symbol.
|
||||
*
|
||||
* @var number
|
||||
* @var int
|
||||
*/
|
||||
public $kind;
|
||||
|
||||
|
|
Loading…
Reference in New Issue