From cdb5b566136dd65e51d5061a272cdd908207cd4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 01:18:16 +0100 Subject: [PATCH 01/27] Add support to index multiple file extensions Will take the options sent by the client. Option: php.intellisense.fileTypes = [".php"] --- src/Indexer.php | 30 +++++++++++++++++++----------- src/LanguageServer.php | 9 ++++++--- src/Options.php | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 14 deletions(-) create mode 100644 src/Options.php diff --git a/src/Indexer.php b/src/Indexer.php index 34ad618..3529e19 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -65,14 +65,20 @@ class Indexer private $composerJson; /** - * @param FilesFinder $filesFinder - * @param string $rootPath - * @param LanguageClient $client - * @param Cache $cache - * @param DependenciesIndex $dependenciesIndex - * @param Index $sourceIndex - * @param PhpDocumentLoader $documentLoader - * @param \stdClass|null $composerLock + * @var Options + */ + private $options; + + /** + * @param FilesFinder $filesFinder + * @param string $rootPath + * @param LanguageClient $client + * @param Cache $cache + * @param DependenciesIndex $dependenciesIndex + * @param Index $sourceIndex + * @param PhpDocumentLoader $documentLoader + * @param \stdClass|null $composerLock + * @param IndexerOptions|null $options */ public function __construct( FilesFinder $filesFinder, @@ -83,7 +89,8 @@ class Indexer Index $sourceIndex, PhpDocumentLoader $documentLoader, \stdClass $composerLock = null, - \stdClass $composerJson = null + \stdClass $composerJson = null, + Options $options = null ) { $this->filesFinder = $filesFinder; $this->rootPath = $rootPath; @@ -94,6 +101,7 @@ class Indexer $this->documentLoader = $documentLoader; $this->composerLock = $composerLock; $this->composerJson = $composerJson; + $this->options = $options; } /** @@ -104,8 +112,8 @@ class Indexer public function index(): Promise { return coroutine(function () { - - $pattern = Path::makeAbsolute('**/*.php', $this->rootPath); + $fileTypes = implode(',', $this->options->fileTypes); + $pattern = Path::makeAbsolute('**/*{' . $fileTypes . '}', $this->rootPath); $uris = yield $this->filesFinder->find($pattern); $count = count($uris); diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 3c999f2..12ee173 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -167,11 +167,12 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher * @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 mixed $initializationOptions The options send from client to initialize the server * @return Promise */ - public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null): Promise + public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, $initializationOptions = null): Promise { - return coroutine(function () use ($capabilities, $rootPath, $processId) { + return coroutine(function () use ($capabilities, $rootPath, $processId, $initializationOptions) { if ($capabilities->xfilesProvider) { $this->filesFinder = new ClientFilesFinder($this->client); @@ -190,6 +191,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex, $this->composerJson); $stubsIndex = StubsIndex::read(); $this->globalIndex = new GlobalIndex($stubsIndex, $this->projectIndex); + $options = new Options($initializationOptions); // The DefinitionResolver should look in stubs, the project source and dependencies $this->definitionResolver = new DefinitionResolver($this->globalIndex); @@ -235,7 +237,8 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $sourceIndex, $this->documentLoader, $this->composerLock, - $this->composerJson + $this->composerJson, + $options ); $indexer->index()->otherwise('\\LanguageServer\\crash'); } diff --git a/src/Options.php b/src/Options.php new file mode 100644 index 0000000..3a4842e --- /dev/null +++ b/src/Options.php @@ -0,0 +1,37 @@ +fileTypes = $options->fileTypes ?? $this->normalizeFileTypes($this->fileTypes); + } + + private function normalizeFileTypes(array $fileTypes): array + { + return array_map(function (string $fileType) { + if (substr($fileType, 0, 1) !== '.') { + $fileType = '.' . $fileType; + } + + return $fileType; + }, $fileTypes); + } +} From 5f096c4bf782dbb4d72f399c3eafb1abd9fbb5b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 01:18:52 +0100 Subject: [PATCH 02/27] Add test for indexing multiple file types --- fixtures/different_extension.inc | 1 + tests/LanguageServerTest.php | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 fixtures/different_extension.inc diff --git a/fixtures/different_extension.inc b/fixtures/different_extension.inc new file mode 100644 index 0000000..b3d9bbc --- /dev/null +++ b/fixtures/different_extension.inc @@ -0,0 +1 @@ +assertTrue($filesCalled); $this->assertTrue($contentCalled); } + + public function testIndexingMultipleFileTypes() + { + $promise = new Promise; + $input = new MockProtocolStream; + $output = new MockProtocolStream; + $options = (object)[ + 'fileTypes' => [ + '.php', + '.inc' + ] + ]; + + $output->on('message', function (Message $msg) use ($promise, &$foundFiles) { + if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { + if ($msg->body->params->type === MessageType::ERROR) { + $promise->reject(new Exception($msg->body->params->message)); + } else if (strpos($msg->body->params->message, 'All 27 PHP files parsed') !== false) { + $promise->fulfill(); + } + } + }); + $server = new LanguageServer($input, $output); + $capabilities = new ClientCapabilities; + $server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid(), $options); + $promise->wait(); + } } From 7dc44776f7c93015054bd8e1f115c902c9acf179 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 09:37:50 +0100 Subject: [PATCH 03/27] Fix wrong phpDoc type --- src/Indexer.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Indexer.php b/src/Indexer.php index 3529e19..4cd85d3 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -78,7 +78,7 @@ class Indexer * @param Index $sourceIndex * @param PhpDocumentLoader $documentLoader * @param \stdClass|null $composerLock - * @param IndexerOptions|null $options + * @param Options|null $options */ public function __construct( FilesFinder $filesFinder, From f7175bc195b1895fca908d99033c4d68f076d87c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 10:38:55 +0100 Subject: [PATCH 04/27] Filter invalid file types and use default list as fallback --- src/Indexer.php | 2 +- src/Options.php | 60 ++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/src/Indexer.php b/src/Indexer.php index 4cd85d3..c341dd2 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -112,7 +112,7 @@ class Indexer public function index(): Promise { return coroutine(function () { - $fileTypes = implode(',', $this->options->fileTypes); + $fileTypes = implode(',', $this->options->getFileTypes()); $pattern = Path::makeAbsolute('**/*{' . $fileTypes . '}', $this->rootPath); $uris = yield $this->filesFinder->find($pattern); diff --git a/src/Options.php b/src/Options.php index 3a4842e..d7e7884 100644 --- a/src/Options.php +++ b/src/Options.php @@ -9,29 +9,67 @@ class Options * * @var array */ - public $fileTypes = [".php"]; + private $fileTypes = [".php"]; /** - * @param \stdClass|null $options + * @param \Traversable|\stdClass|array|null $options */ - public function __construct(\stdClass $options = null) + public function __construct($options = null) { // Do nothing when the $options parameter is not an object - if (!is_object($options)) { + if (!is_object($options) && !is_array($options) && (!$options instanceof \Traversable)) { return; } - $this->fileTypes = $options->fileTypes ?? $this->normalizeFileTypes($this->fileTypes); + foreach ($options as $option => $value) { + $method = 'set' . ucfirst($option); + + call_user_func([$this, $method], $value); + } } - private function normalizeFileTypes(array $fileTypes): array + /** + * Validate and set options for file types + * + * @param array $fileTypes List of file types + */ + public function setFileTypes(array $fileTypes) { - return array_map(function (string $fileType) { - if (substr($fileType, 0, 1) !== '.') { - $fileType = '.' . $fileType; - } + $fileTypes = filter_var_array($fileTypes, FILTER_SANITIZE_STRING); + $fileTypes = filter_var($fileTypes, FILTER_CALLBACK, ['options' => [$this, 'filterFileTypes']]); + $fileTypes = array_filter($fileTypes); + $this->fileTypes = !empty($fileTypes) ? $fileTypes : $this->fileTypes; + } + + /** + * Get list of registered file types + * + * @return array + */ + public function getFileTypes(): array + { + return $this->fileTypes; + } + + /** + * Filter valid file type + * + * @param string $fileType The file type to filter + * @return string|bool If valid it returns the file type, otherwise false + */ + private function filterFileTypes(string $fileType) + { + $fileType = trim($fileType); + + if (empty($fileType)) { return $fileType; - }, $fileTypes); + } + + if (substr($fileType, 0, 1) !== '.') { + return false; + } + + return $fileType; } } From 94336941bd585fb90df749f8b27183236603bff9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 19:30:34 +0100 Subject: [PATCH 05/27] Let JsonMapper intialize the options To sanitize the file type option, we provide a setter method for the property that will be called by the JsonMapper. --- src/Indexer.php | 2 +- src/LanguageServer.php | 7 +++---- src/Options.php | 38 ++++++-------------------------------- 3 files changed, 10 insertions(+), 37 deletions(-) diff --git a/src/Indexer.php b/src/Indexer.php index c341dd2..4cd85d3 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -112,7 +112,7 @@ class Indexer public function index(): Promise { return coroutine(function () { - $fileTypes = implode(',', $this->options->getFileTypes()); + $fileTypes = implode(',', $this->options->fileTypes); $pattern = Path::makeAbsolute('**/*{' . $fileTypes . '}', $this->rootPath); $uris = yield $this->filesFinder->find($pattern); diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 12ee173..7e5074b 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -167,10 +167,10 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher * @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 mixed $initializationOptions The options send from client to initialize the server + * @param Options $initializationOptions The options send from client to initialize the server * @return Promise */ - public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, $initializationOptions = null): Promise + public function initialize(ClientCapabilities $capabilities, string $rootPath = null, int $processId = null, Options $initializationOptions = null): Promise { return coroutine(function () use ($capabilities, $rootPath, $processId, $initializationOptions) { @@ -191,7 +191,6 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex, $this->composerJson); $stubsIndex = StubsIndex::read(); $this->globalIndex = new GlobalIndex($stubsIndex, $this->projectIndex); - $options = new Options($initializationOptions); // The DefinitionResolver should look in stubs, the project source and dependencies $this->definitionResolver = new DefinitionResolver($this->globalIndex); @@ -238,7 +237,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $this->documentLoader, $this->composerLock, $this->composerJson, - $options + $initializationOptions ); $indexer->index()->otherwise('\\LanguageServer\\crash'); } diff --git a/src/Options.php b/src/Options.php index d7e7884..99f1aab 100644 --- a/src/Options.php +++ b/src/Options.php @@ -7,51 +7,25 @@ class Options /** * Filetypes the indexer should process * - * @var array + * @var string[] */ - private $fileTypes = [".php"]; + public $fileTypes = ['.php']; /** - * @param \Traversable|\stdClass|array|null $options - */ - public function __construct($options = null) - { - // Do nothing when the $options parameter is not an object - if (!is_object($options) && !is_array($options) && (!$options instanceof \Traversable)) { - return; - } - - foreach ($options as $option => $value) { - $method = 'set' . ucfirst($option); - - call_user_func([$this, $method], $value); - } - } - - /** - * Validate and set options for file types + * Validate/Filter input and set options for file types * * @param array $fileTypes List of file types */ public function setFileTypes(array $fileTypes) { $fileTypes = filter_var_array($fileTypes, FILTER_SANITIZE_STRING); - $fileTypes = filter_var($fileTypes, FILTER_CALLBACK, ['options' => [$this, 'filterFileTypes']]); - $fileTypes = array_filter($fileTypes); + $fileTypes = filter_var($fileTypes, FILTER_CALLBACK, ['options' => [$this, 'filterFileTypes']]); // validate file type format + $fileTypes = array_filter($fileTypes, 'strlen'); // filter empty items + $fileTypes = array_values($fileTypes); //rebase indexes $this->fileTypes = !empty($fileTypes) ? $fileTypes : $this->fileTypes; } - /** - * Get list of registered file types - * - * @return array - */ - public function getFileTypes(): array - { - return $this->fileTypes; - } - /** * Filter valid file type * From 39cfbda77b59aaa67833632b11ffc011e2d10f7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 19:30:56 +0100 Subject: [PATCH 06/27] Add test for fileTypes option --- tests/OptionsTest.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/OptionsTest.php diff --git a/tests/OptionsTest.php b/tests/OptionsTest.php new file mode 100644 index 0000000..d47afcf --- /dev/null +++ b/tests/OptionsTest.php @@ -0,0 +1,26 @@ +setFileTypes([ + '.php', + false, + 12345, + '.valid' + ]); + + $this->assertSame($expected, $options->fileTypes); + } +} From 3c33e7f46602c71c81d6bbbc557653db76a1363b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 20:05:21 +0100 Subject: [PATCH 07/27] Initialize options with default values when not provided by client --- src/LanguageServer.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 7e5074b..d3b1c5c 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -191,6 +191,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex, $this->composerJson); $stubsIndex = StubsIndex::read(); $this->globalIndex = new GlobalIndex($stubsIndex, $this->projectIndex); + $initializationOptions = $initializationOptions ?? new Options; // The DefinitionResolver should look in stubs, the project source and dependencies $this->definitionResolver = new DefinitionResolver($this->globalIndex); From d2e5048ec8cf8d3b10178d4caa4ce68f6cc3df65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 20:06:24 +0100 Subject: [PATCH 08/27] Update testIndexingMultipleFileTypes --- tests/LanguageServerTest.php | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 0c0740c..9b16902 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -5,6 +5,7 @@ namespace LanguageServer\Tests; use PHPUnit\Framework\TestCase; use LanguageServer\LanguageServer; +use LanguageServer\Options; use LanguageServer\Protocol\{ Message, ClientCapabilities, @@ -123,12 +124,12 @@ class LanguageServerTest extends TestCase $promise = new Promise; $input = new MockProtocolStream; $output = new MockProtocolStream; - $options = (object)[ - 'fileTypes' => [ - '.php', - '.inc' - ] - ]; + $options = new Options; + + $options->setFileTypes([ + '.php', + '.inc' + ]); $output->on('message', function (Message $msg) use ($promise, &$foundFiles) { if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { From b9d0d1bfa7fbca0f1ac5be5a5cf7b054d24dabc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 18 Feb 2017 20:27:35 +0100 Subject: [PATCH 09/27] Add missing namespace in OptionsTest --- tests/OptionsTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/OptionsTest.php b/tests/OptionsTest.php index d47afcf..f2251d8 100644 --- a/tests/OptionsTest.php +++ b/tests/OptionsTest.php @@ -1,6 +1,8 @@ Date: Sat, 18 Feb 2017 20:33:03 +0100 Subject: [PATCH 10/27] Fix wrong classname for options test --- tests/OptionsTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/OptionsTest.php b/tests/OptionsTest.php index f2251d8..45e35f6 100644 --- a/tests/OptionsTest.php +++ b/tests/OptionsTest.php @@ -6,7 +6,7 @@ namespace LanguageServer\Tests; use PHPUnit\Framework\TestCase; use LanguageServer\Options; -class LanguageServerTest extends TestCase +class OptionsTest extends TestCase { public function testFileTypesOption() { From 1e319c7215c85e3e8e0f0546b5b68fbdbc7a4528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Fri, 24 Feb 2017 23:37:02 +0100 Subject: [PATCH 11/27] Wipe index when on configuration change --- src/Index/AbstractAggregateIndex.php | 13 +++++++++++++ src/Index/Index.php | 10 ++++++++++ src/Server/Workspace.php | 5 +++++ 3 files changed, 28 insertions(+) diff --git a/src/Index/AbstractAggregateIndex.php b/src/Index/AbstractAggregateIndex.php index 5377c3a..39c1a1d 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -42,6 +42,9 @@ abstract class AbstractAggregateIndex implements ReadableIndex $index->on('definition-added', function () { $this->emit('definition-added'); }); + $index->on('wipe', function() { + $this->emit('wipe'); + }); } /** @@ -147,4 +150,14 @@ abstract class AbstractAggregateIndex implements ReadableIndex } return $refs; } + + /** + * Wipe all indexes for a reindex + */ + public function wipe() + { + foreach ($this->getIndexes() as $index) { + $index->wipe(); + } + } } diff --git a/src/Index/Index.php b/src/Index/Index.php index 5c24813..e912087 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -211,4 +211,14 @@ class Index implements ReadableIndex, \Serializable 'staticComplete' => $this->staticComplete ]); } + + public function wipe() + { + $this->definitions = []; + $this->references = []; + $this->complete = false; + $this->staticComplete = false; + + $this->emit('wipe'); + } } diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index b94618c..3a10b78 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -170,4 +170,9 @@ class Workspace } return $dependencyReferences; } + + public function didChangeConfiguration($settings = null) + { + $this->index->wipe(); + } } From 58c82e6dc914a786603f845a33a89ce3afa1bab7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Thu, 2 Mar 2017 23:11:14 +0100 Subject: [PATCH 12/27] Add list of valid indexer options --- src/Options.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Options.php b/src/Options.php index 99f1aab..9f61edb 100644 --- a/src/Options.php +++ b/src/Options.php @@ -11,6 +11,11 @@ class Options */ public $fileTypes = ['.php']; + /** + * List of options that affect the indexer + */ + private $indexerOptions = ['fileTypes']; + /** * Validate/Filter input and set options for file types * @@ -26,6 +31,16 @@ class Options $this->fileTypes = !empty($fileTypes) ? $fileTypes : $this->fileTypes; } + /** + * Get list with options that affect the indexer + * + * @return array + */ + public function getIndexerOptions(): array + { + return $this->indexerOptions; + } + /** * Filter valid file type * From 44a942e714e8cd0cdf1ffc70c4c9fc3c1386de0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Thu, 2 Mar 2017 23:11:24 +0100 Subject: [PATCH 13/27] Implement didChangeConfiguration event --- src/Server/Workspace.php | 57 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 4 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 3a10b78..fb3807d 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -3,7 +3,7 @@ declare(strict_types = 1); namespace LanguageServer\Server; -use LanguageServer\{LanguageClient, Project, PhpDocumentLoader}; +use LanguageServer\{LanguageClient, Project, PhpDocumentLoader, Options, Indexer}; use LanguageServer\Index\{ProjectIndex, DependenciesIndex, Index}; use LanguageServer\Protocol\{SymbolInformation, SymbolDescriptor, ReferenceInformation, DependencyReference, Location}; use Sabre\Event\Promise; @@ -32,6 +32,16 @@ class Workspace */ private $sourceIndex; + /** + * @var Options + */ + private $options; + + /** + * @var Indexer + */ + private $indexer; + /** * @var \stdClass */ @@ -48,8 +58,10 @@ class Workspace * @param DependenciesIndex $sourceIndex Index that is used on a workspace/xreferences request * @param \stdClass $composerLock The parsed composer.lock of the project, if any * @param PhpDocumentLoader $documentLoader PhpDocumentLoader instance to load documents + * @param Indexer $indexer + * @param Options $options */ - public function __construct(ProjectIndex $index, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null) + public function __construct(ProjectIndex $index, DependenciesIndex $dependenciesIndex, Index $sourceIndex, \stdClass $composerLock = null, PhpDocumentLoader $documentLoader, \stdClass $composerJson = null, Indexer $indexer = null, Options $options = null) { $this->sourceIndex = $sourceIndex; $this->index = $index; @@ -57,6 +69,8 @@ class Workspace $this->composerLock = $composerLock; $this->documentLoader = $documentLoader; $this->composerJson = $composerJson; + $this->indexer = $indexer; + $this->options = $options; } /** @@ -171,8 +185,43 @@ class Workspace return $dependencyReferences; } - public function didChangeConfiguration($settings = null) + /** + * @param Options|null $settings + */ + public function didChangeConfiguration(Options $settings = null) { - $this->index->wipe(); + if ($settings === null) { + return; + } + + $changedOptions = $this->getChangedOptions($settings); + $this->options = $settings; + + if (!empty(array_intersect($changedOptions, $this->options->getIndexerOptions()))) { + // check list of options that changed since last time against the list of valid indexer options + + // start wiping from the main index + $this->index->wipe(); + + // check for existing indexer and start indexing + if ($this->indexer) { + $this->indexer->index()->otherwise('\\LanguageServer\\crash'); + } + } + } + + /** + * Get a list with all options that changed since last time + * + * @param Options $settings + * @return array List with changed options + */ + private function getChangedOptions(Options $settings): array + { + // squash nested array for comparing changed options + $old = array_map('json_encode', get_object_vars($this->options)); + $new = array_map('json_encode', get_object_vars($settings)); + + return array_keys(array_diff($old, $new)); } } From 940eb9787d98204887210d0b18016235f841b4e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Thu, 2 Mar 2017 23:11:38 +0100 Subject: [PATCH 14/27] Pass options and indexer to workspace --- src/LanguageServer.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/LanguageServer.php b/src/LanguageServer.php index d3b1c5c..62b8400 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -261,7 +261,9 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $sourceIndex, $this->composerLock, $this->documentLoader, - $this->composerJson + $this->composerJson, + $indexer, + $initializationOptions ); } From 5b1b6bfabed6c1681cf7a5091238186add41ac3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Thu, 2 Mar 2017 23:12:19 +0100 Subject: [PATCH 15/27] Add tests --- tests/Server/ServerTestCase.php | 30 +++++--- .../Workspace/DidChangeConfigurationTest.php | 72 +++++++++++++++++++ 2 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 tests/Server/Workspace/DidChangeConfigurationTest.php diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 679191f..1a45c64 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -5,10 +5,12 @@ namespace LanguageServer\Tests\Server; use PHPUnit\Framework\TestCase; use LanguageServer\Tests\MockProtocolStream; -use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver}; +use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver, Options, Indexer}; use LanguageServer\Index\{ProjectIndex, StubsIndex, GlobalIndex, DependenciesIndex, Index}; use LanguageServer\ContentRetriever\FileSystemContentRetriever; use LanguageServer\Protocol\{Position, Location, Range, ClientCapabilities}; +use LanguageServer\FilesFinder\FileSystemFilesFinder; +use LanguageServer\Cache\FileSystemCache; use function LanguageServer\pathToUri; use Sabre\Event\Promise; @@ -29,6 +31,10 @@ abstract class ServerTestCase extends TestCase */ protected $documentLoader; + protected $projectIndex; + protected $input; + protected $output; + /** * Map from FQN to Location of definition * @@ -47,14 +53,22 @@ abstract class ServerTestCase extends TestCase { $sourceIndex = new Index; $dependenciesIndex = new DependenciesIndex; - $projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); - $projectIndex->setComplete(); + $this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); + $this->projectIndex->setComplete(); - $definitionResolver = new DefinitionResolver($projectIndex); - $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); - $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $projectIndex); - $this->workspace = new Server\Workspace($projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader); + $rootPath = realpath(__DIR__ . '/../../fixtures/'); + $options = new Options; + $filesFinder = new FileSystemFilesFinder; + $cache = new FileSystemCache; + + $this->input = new MockProtocolStream; + $this->output = new MockProtocolStream; + $definitionResolver = new DefinitionResolver($this->projectIndex); + $client = new LanguageClient($this->input, $this->output); + $this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $this->projectIndex, $definitionResolver); + $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $this->projectIndex); + $indexer = new Indexer($filesFinder, $rootPath, $client, $cache, $dependenciesIndex, $sourceIndex, $this->documentLoader, null, null, $options); + $this->workspace = new Server\Workspace($this->projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader, null, $indexer, $options); $globalSymbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_symbols.php')); $globalReferencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_references.php')); diff --git a/tests/Server/Workspace/DidChangeConfigurationTest.php b/tests/Server/Workspace/DidChangeConfigurationTest.php new file mode 100644 index 0000000..18970e9 --- /dev/null +++ b/tests/Server/Workspace/DidChangeConfigurationTest.php @@ -0,0 +1,72 @@ +projectIndex->on('wipe', function() use ($promise) { + $promise->fulfill(); + }); + + $options = new Options; + $options->fileTypes = [ + '.inc' + ]; + + $this->workspace->didChangeConfiguration($options); + $promise->wait(); + } + + public function testReindexingAfterWipe() + { + $promise = new Promise; + + $this->output->on('message', function (Message $msg) use ($promise) { + if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { + if ($msg->body->params->type === MessageType::ERROR) { + $promise->reject(new Exception($msg->body->params->message)); + } elseif (strpos($msg->body->params->message, 'All 0 PHP files parsed') !== false) { + $promise->fulfill(); + } + } + }); + + $options = new Options; + $options->fileTypes = [ + '.inc' + ]; + + $this->workspace->didChangeConfiguration($options); + $promise->wait(); + } + + public function testGetChangedOptions() + { + } +} From 1e73d08033df2bd27d830cb4b848b7ee32c07936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 09:03:24 +0100 Subject: [PATCH 16/27] Improve gettting changed options --- src/Server/Workspace.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 1f35134..7f76977 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -248,10 +248,14 @@ class Workspace */ private function getChangedOptions(Options $settings): array { - // squash nested array for comparing changed options - $old = array_map('json_encode', get_object_vars($this->options)); - $new = array_map('json_encode', get_object_vars($settings)); + $old = get_object_vars($this->options); + $new = get_object_vars($settings); + $changed = array_udiff($old, $new, function($a, $b) { + // custom callback since array_diff uses strings for comparison - return array_keys(array_diff($old, $new)); + return $a <=> $b; + }); + + return array_keys($changed); } } From 1f90b4e3937f6bae37a74eade30ed73ce43faa7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 09:25:09 +0100 Subject: [PATCH 17/27] Update options one by one to update all instance --- src/Server/Workspace.php | 15 +++++++-------- .../Workspace/DidChangeConfigurationTest.php | 2 +- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 7f76977..e74490e 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -225,18 +225,17 @@ class Workspace } $changedOptions = $this->getChangedOptions($settings); - $this->options = $settings; - if (!empty(array_intersect($changedOptions, $this->options->getIndexerOptions()))) { + foreach (get_object_vars($settings) as $prop => $val) { + $this->options->$prop = $val; + } + + if ($this->indexer && !empty(array_intersect($changedOptions, $this->options->getIndexerOptions()))) { // check list of options that changed since last time against the list of valid indexer options - // start wiping from the main index + // wipe main index and start reindexing $this->index->wipe(); - - // check for existing indexer and start indexing - if ($this->indexer) { - $this->indexer->index()->otherwise('\\LanguageServer\\crash'); - } + $this->indexer->index()->otherwise('\\LanguageServer\\crash'); } } diff --git a/tests/Server/Workspace/DidChangeConfigurationTest.php b/tests/Server/Workspace/DidChangeConfigurationTest.php index 18970e9..8479764 100644 --- a/tests/Server/Workspace/DidChangeConfigurationTest.php +++ b/tests/Server/Workspace/DidChangeConfigurationTest.php @@ -51,7 +51,7 @@ class DidChangeConfigurationTest extends ServerTestCase if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { if ($msg->body->params->type === MessageType::ERROR) { $promise->reject(new Exception($msg->body->params->message)); - } elseif (strpos($msg->body->params->message, 'All 0 PHP files parsed') !== false) { + } elseif (strpos($msg->body->params->message, 'All 1 PHP files parsed') !== false) { $promise->fulfill(); } } From ca225ff6a6baf6228d22f6e9c6e94aa83f6a17f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 12:30:18 +0100 Subject: [PATCH 18/27] Remove emitting wipe events --- src/Index/AbstractAggregateIndex.php | 3 --- src/Index/Index.php | 2 -- 2 files changed, 5 deletions(-) diff --git a/src/Index/AbstractAggregateIndex.php b/src/Index/AbstractAggregateIndex.php index 39c1a1d..33b86e5 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -42,9 +42,6 @@ abstract class AbstractAggregateIndex implements ReadableIndex $index->on('definition-added', function () { $this->emit('definition-added'); }); - $index->on('wipe', function() { - $this->emit('wipe'); - }); } /** diff --git a/src/Index/Index.php b/src/Index/Index.php index e912087..9879ced 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -218,7 +218,5 @@ class Index implements ReadableIndex, \Serializable $this->references = []; $this->complete = false; $this->staticComplete = false; - - $this->emit('wipe'); } } From c4568bfc34b34732af5651640c0659b00565c9d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 12:32:10 +0100 Subject: [PATCH 19/27] Accept different types/formats from clients Currently only the default Options type and the vscode format are accepted. --- src/Server/Workspace.php | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index e74490e..7527e2e 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -216,16 +216,38 @@ class Workspace } /** - * @param Options|null $settings + * Fires when client changes settings in the client + * + * The default paramter type is Options but it also accepts different types + * which will be transformed on demand. + * + * Currently only the vscode format is supported + * + * @param mixed|null $settings + * @return void */ - public function didChangeConfiguration(Options $settings = null) + public function didChangeConfiguration($settings = null) { if ($settings === null) { return; } + // VSC sends the settings with the config section as main key + if ($settings instanceof \stdClass && $settings->phpIntelliSense) { + $mapper = new \JsonMapper(); + $settings = $mapper->map($settings->phpIntelliSense, new Options); + } + + if (!($settings instanceof Options)) { + return; + } + $changedOptions = $this->getChangedOptions($settings); + if (empty($changedOptions)) { + return; + } + foreach (get_object_vars($settings) as $prop => $val) { $this->options->$prop = $val; } From 5308e7a6bcba16b3a96d4b5245bdf53853a73dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 12:55:46 +0100 Subject: [PATCH 20/27] Add new tests and update old ones --- src/Server/Workspace.php | 13 +- tests/Server/ServerTestCase.php | 21 +-- .../Workspace/DidChangeConfigurationTest.php | 143 ++++++++++++++---- 3 files changed, 127 insertions(+), 50 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 7527e2e..98184ba 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -224,12 +224,13 @@ class Workspace * Currently only the vscode format is supported * * @param mixed|null $settings - * @return void + * @return bool + * @throws \Exception Settings format not valid */ - public function didChangeConfiguration($settings = null) + public function didChangeConfiguration($settings = null): bool { if ($settings === null) { - return; + return false; } // VSC sends the settings with the config section as main key @@ -239,13 +240,13 @@ class Workspace } if (!($settings instanceof Options)) { - return; + throw new \Exception('Settings format not valid.'); } $changedOptions = $this->getChangedOptions($settings); if (empty($changedOptions)) { - return; + return false; } foreach (get_object_vars($settings) as $prop => $val) { @@ -259,6 +260,8 @@ class Workspace $this->index->wipe(); $this->indexer->index()->otherwise('\\LanguageServer\\crash'); } + + return true; } /** diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 7bee051..5d9e309 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -31,10 +31,6 @@ abstract class ServerTestCase extends TestCase */ protected $documentLoader; - protected $projectIndex; - protected $input; - protected $output; - /** * Map from FQN to Location of definition * @@ -53,23 +49,20 @@ abstract class ServerTestCase extends TestCase { $sourceIndex = new Index; $dependenciesIndex = new DependenciesIndex; - $this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); - $this->projectIndex->setComplete(); + $projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); + $projectIndex->setComplete(); $rootPath = realpath(__DIR__ . '/../../fixtures/'); $options = new Options; $filesFinder = new FileSystemFilesFinder; $cache = new FileSystemCache; - $this->input = new MockProtocolStream; - $this->output = new MockProtocolStream; - - $definitionResolver = new DefinitionResolver($this->projectIndex); - $client = new LanguageClient($this->input, $this->output); - $this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $this->projectIndex, $definitionResolver); - $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $this->projectIndex); + $definitionResolver = new DefinitionResolver($projectIndex); + $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); + $this->documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); + $this->textDocument = new Server\TextDocument($this->documentLoader, $definitionResolver, $client, $projectIndex); $indexer = new Indexer($filesFinder, $rootPath, $client, $cache, $dependenciesIndex, $sourceIndex, $this->documentLoader, null, null, $options); - $this->workspace = new Server\Workspace($client, $this->projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader, null, $indexer, $options); + $this->workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $this->documentLoader, null, $indexer, $options); $globalSymbolsUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_symbols.php')); $globalReferencesUri = pathToUri(realpath(__DIR__ . '/../../fixtures/global_references.php')); diff --git a/tests/Server/Workspace/DidChangeConfigurationTest.php b/tests/Server/Workspace/DidChangeConfigurationTest.php index 8479764..e0ad2ea 100644 --- a/tests/Server/Workspace/DidChangeConfigurationTest.php +++ b/tests/Server/Workspace/DidChangeConfigurationTest.php @@ -3,51 +3,135 @@ declare(strict_types = 1); namespace LanguageServer\Tests\Server\Workspace; -use LanguageServer\Tests\MockProtocolStream; use LanguageServer\Tests\Server\ServerTestCase; -use LanguageServer\{Server, Client, LanguageClient, Project, PhpDocument, Options}; -use LanguageServer\Protocol\{ - Message, - MessageType, - TextDocumentItem, - TextDocumentIdentifier, - SymbolInformation, - SymbolKind, - DiagnosticSeverity, - FormattingOptions, - Location, - Range, - Position -}; -use AdvancedJsonRpc\{Request as RequestBody, Response as ResponseBody}; -use function LanguageServer\pathToUri; +use LanguageServer\Tests\MockProtocolStream; +use LanguageServer\{Server, LanguageClient, PhpDocumentLoader, DefinitionResolver, Options, Indexer}; +use LanguageServer\Index\{ProjectIndex, StubsIndex, GlobalIndex, DependenciesIndex, Index}; +use LanguageServer\ContentRetriever\FileSystemContentRetriever; +use LanguageServer\Protocol\{Position, Location, Range, ClientCapabilities, Message, MessageType}; +use LanguageServer\FilesFinder\FileSystemFilesFinder; +use LanguageServer\Cache\FileSystemCache; +use LanguageServer\Server\Workspace; use Sabre\Event\Promise; use Exception; class DidChangeConfigurationTest extends ServerTestCase { - public function testWipingIndex() + /** + * didChangeConfiguration does not need to do anything when no options/settings are passed + */ + public function test_no_option_passed() { - $promise = new Promise; + $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); + $projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex()); + $definitionResolver = new DefinitionResolver($projectIndex); + $loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver); + $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $loader, null); + + $result = $workspace->didChangeConfiguration(); + $this->assertFalse($result); + } + + /** + * When the passed options/settings do not differ from the previous, it has nothing to do + */ + public function test_fails_with_invalid_options_type_or_format() + { + $options = new Options; + $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); + $projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex()); + $definitionResolver = new DefinitionResolver($projectIndex); + $loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver); + $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $loader, null, null, null, null, $options); + + $this->expectException(\Exception::class); + $this->workspace->didChangeConfiguration(['invalid' => 'options format']); + } + + /** + * When the passed options/settings do not differ from the previous, it has nothing to do + */ + public function test_no_changed_options() + { + $options = new Options; + $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); + $projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex()); + $definitionResolver = new DefinitionResolver($projectIndex); + $loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver); + $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $loader, null, null, null, null, $options); + + $result = $this->workspace->didChangeConfiguration($options); + $this->assertFalse($result); + } + + /** + * Verify that the required methods for a reindex are called + */ + public function test_fileTypes_option_triggers_a_reindex() + { + $sourceIndex = new Index; + $dependenciesIndex = new DependenciesIndex; + $projectIndex = $this->getMockBuilder('LanguageServer\Index\ProjectIndex') + ->setConstructorArgs([$sourceIndex, $dependenciesIndex]) + ->setMethods(['wipe']) + ->getMock(); + $projectIndex->setComplete(); + + $rootPath = realpath(__DIR__ . '/../../../fixtures/'); + $filesFinder = new FileSystemFilesFinder; + $cache = new FileSystemCache; + + $definitionResolver = new DefinitionResolver($projectIndex); + $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); + $documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); + $textDocument = new Server\TextDocument($documentLoader, $definitionResolver, $client, $projectIndex); + $indexer = $this->getMockBuilder('LanguageServer\Indexer') + ->setConstructorArgs([$filesFinder, $rootPath, $client, $cache, $dependenciesIndex, $sourceIndex, $documentLoader, null, null, new Options]) + ->setMethods(['index']) + ->getMock(); + $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $documentLoader, null, $indexer, new Options); - $this->projectIndex->on('wipe', function() use ($promise) { - $promise->fulfill(); - }); $options = new Options; $options->fileTypes = [ '.inc' ]; - $this->workspace->didChangeConfiguration($options); - $promise->wait(); + $projectIndex->expects($this->once())->method('wipe'); + $indexer->expects($this->once())->method('index'); + + // invoke event + $result = $workspace->didChangeConfiguration($options); + $this->assertTrue($result); } - public function testReindexingAfterWipe() + /** + * Be sure that the indexer gets the new options/settings and uses them + */ + public function test_indexer_uses_new_options() { $promise = new Promise; + $sourceIndex = new Index; + $dependenciesIndex = new DependenciesIndex; + $projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); + $projectIndex->setComplete(); - $this->output->on('message', function (Message $msg) use ($promise) { + $rootPath = realpath(__DIR__ . '/../../../fixtures/'); + $filesFinder = new FileSystemFilesFinder; + $cache = new FileSystemCache; + $initialOptions = new Options; + + $input = new MockProtocolStream; + $output = new MockProtocolStream; + + $definitionResolver = new DefinitionResolver($projectIndex); + $client = new LanguageClient($input, $output); + $documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); + $textDocument = new Server\TextDocument($documentLoader, $definitionResolver, $client, $projectIndex); + $indexer = new Indexer($filesFinder, $rootPath, $client, $cache, $dependenciesIndex, $sourceIndex, $documentLoader, null, null, $initialOptions); + $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $documentLoader, null, $indexer, $initialOptions); + + $output->on('message', function (Message $msg) use ($promise) { if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { if ($msg->body->params->type === MessageType::ERROR) { $promise->reject(new Exception($msg->body->params->message)); @@ -62,11 +146,8 @@ class DidChangeConfigurationTest extends ServerTestCase '.inc' ]; - $this->workspace->didChangeConfiguration($options); + $result = $workspace->didChangeConfiguration($options); + $this->assertTrue($result); $promise->wait(); } - - public function testGetChangedOptions() - { - } } From a06057b7a33d9f42ad956265b50562cbeaacb7de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 12:59:22 +0100 Subject: [PATCH 21/27] Fix phpcs warnings/errors --- src/Server/Workspace.php | 2 +- tests/Server/Workspace/DidChangeConfigurationTest.php | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index 98184ba..ad724de 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -274,7 +274,7 @@ class Workspace { $old = get_object_vars($this->options); $new = get_object_vars($settings); - $changed = array_udiff($old, $new, function($a, $b) { + $changed = array_udiff($old, $new, function ($a, $b) { // custom callback since array_diff uses strings for comparison return $a <=> $b; diff --git a/tests/Server/Workspace/DidChangeConfigurationTest.php b/tests/Server/Workspace/DidChangeConfigurationTest.php index e0ad2ea..6028c6c 100644 --- a/tests/Server/Workspace/DidChangeConfigurationTest.php +++ b/tests/Server/Workspace/DidChangeConfigurationTest.php @@ -20,7 +20,7 @@ class DidChangeConfigurationTest extends ServerTestCase /** * didChangeConfiguration does not need to do anything when no options/settings are passed */ - public function test_no_option_passed() + public function testNoOptionPassed() { $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); $projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex()); @@ -35,7 +35,7 @@ class DidChangeConfigurationTest extends ServerTestCase /** * When the passed options/settings do not differ from the previous, it has nothing to do */ - public function test_fails_with_invalid_options_type_or_format() + public function testFailsWithInvalidOptionsTypeOrFormat() { $options = new Options; $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); @@ -51,7 +51,7 @@ class DidChangeConfigurationTest extends ServerTestCase /** * When the passed options/settings do not differ from the previous, it has nothing to do */ - public function test_no_changed_options() + public function testNoChangedOptions() { $options = new Options; $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); @@ -67,7 +67,7 @@ class DidChangeConfigurationTest extends ServerTestCase /** * Verify that the required methods for a reindex are called */ - public function test_fileTypes_option_triggers_a_reindex() + public function testFileTypesOptionTriggersAReindex() { $sourceIndex = new Index; $dependenciesIndex = new DependenciesIndex; @@ -108,7 +108,7 @@ class DidChangeConfigurationTest extends ServerTestCase /** * Be sure that the indexer gets the new options/settings and uses them */ - public function test_indexer_uses_new_options() + public function testIndexerUsesNewOptions() { $promise = new Promise; $sourceIndex = new Index; From 23a40f069b7ab0aafc73bd67b7059f541d51e091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 13:02:16 +0100 Subject: [PATCH 22/27] Let didChangeConfiguration decide what options are interesting for the indexer --- src/Options.php | 15 --------------- src/Server/Workspace.php | 5 ++++- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/Options.php b/src/Options.php index 9f61edb..99f1aab 100644 --- a/src/Options.php +++ b/src/Options.php @@ -11,11 +11,6 @@ class Options */ public $fileTypes = ['.php']; - /** - * List of options that affect the indexer - */ - private $indexerOptions = ['fileTypes']; - /** * Validate/Filter input and set options for file types * @@ -31,16 +26,6 @@ class Options $this->fileTypes = !empty($fileTypes) ? $fileTypes : $this->fileTypes; } - /** - * Get list with options that affect the indexer - * - * @return array - */ - public function getIndexerOptions(): array - { - return $this->indexerOptions; - } - /** * Filter valid file type * diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index ad724de..b983bf6 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -229,6 +229,9 @@ class Workspace */ public function didChangeConfiguration($settings = null): bool { + // List of options that affect the indexer + $indexerOptions = ['fileTypes']; + if ($settings === null) { return false; } @@ -253,7 +256,7 @@ class Workspace $this->options->$prop = $val; } - if ($this->indexer && !empty(array_intersect($changedOptions, $this->options->getIndexerOptions()))) { + if ($this->indexer && !empty(array_intersect($changedOptions, $indexerOptions))) { // check list of options that changed since last time against the list of valid indexer options // wipe main index and start reindexing From f4f106766f8fcf4bab1ce6be6a06888e232528ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Sat, 4 Mar 2017 17:26:23 +0100 Subject: [PATCH 23/27] Change didChangeConfiguration doc to protocol wording --- src/Server/Workspace.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Server/Workspace.php b/src/Server/Workspace.php index b983bf6..954e5e9 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -216,7 +216,7 @@ class Workspace } /** - * Fires when client changes settings in the client + * A notification sent from the client to the server to signal the change of configuration settings. * * The default paramter type is Options but it also accepts different types * which will be transformed on demand. From 09fbec247c22f2e5355652c47f55c6d2d54fd2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Wed, 29 Aug 2018 21:34:50 +0200 Subject: [PATCH 24/27] Refactor pull request * merge latest upstream * remove currently not required code blocks * fix tests --- src/Index/AbstractAggregateIndex.php | 10 -- src/Index/Index.php | 8 - src/Indexer.php | 34 ++-- src/LanguageServer.php | 1 + src/Options.php | 11 +- tests/LanguageServerTest.php | 16 +- .../Workspace/DidChangeConfigurationTest.php | 153 ------------------ 7 files changed, 31 insertions(+), 202 deletions(-) delete mode 100644 tests/Server/Workspace/DidChangeConfigurationTest.php diff --git a/src/Index/AbstractAggregateIndex.php b/src/Index/AbstractAggregateIndex.php index 33b86e5..5377c3a 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -147,14 +147,4 @@ abstract class AbstractAggregateIndex implements ReadableIndex } return $refs; } - - /** - * Wipe all indexes for a reindex - */ - public function wipe() - { - foreach ($this->getIndexes() as $index) { - $index->wipe(); - } - } } diff --git a/src/Index/Index.php b/src/Index/Index.php index a88c7ba..9cb975e 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -222,12 +222,4 @@ class Index implements ReadableIndex, \Serializable 'staticComplete' => $this->staticComplete ]); } - - public function wipe() - { - $this->definitions = []; - $this->references = []; - $this->complete = false; - $this->staticComplete = false; - } } diff --git a/src/Indexer.php b/src/Indexer.php index fd59aa3..e411054 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -53,6 +53,11 @@ class Indexer */ private $documentLoader; + /** + * @var Options + */ + private $options; + /** * @var \stdClasss */ @@ -64,20 +69,15 @@ class Indexer private $composerJson; /** - * @var Options - */ - private $options; - - /** - * @param FilesFinder $filesFinder - * @param string $rootPath - * @param LanguageClient $client - * @param Cache $cache - * @param DependenciesIndex $dependenciesIndex - * @param Index $sourceIndex - * @param PhpDocumentLoader $documentLoader - * @param \stdClass|null $composerLock - * @param Options|null $options + * @param FilesFinder $filesFinder + * @param string $rootPath + * @param LanguageClient $client + * @param Cache $cache + * @param DependenciesIndex $dependenciesIndex + * @param Index $sourceIndex + * @param Options $options + * @param PhpDocumentLoader $documentLoader + * @param \stdClass|null $composerLock */ public function __construct( FilesFinder $filesFinder, @@ -87,9 +87,9 @@ class Indexer DependenciesIndex $dependenciesIndex, Index $sourceIndex, PhpDocumentLoader $documentLoader, + Options $options, \stdClass $composerLock = null, - \stdClass $composerJson = null, - Options $options = null + \stdClass $composerJson = null ) { $this->filesFinder = $filesFinder; $this->rootPath = $rootPath; @@ -98,9 +98,9 @@ class Indexer $this->dependenciesIndex = $dependenciesIndex; $this->sourceIndex = $sourceIndex; $this->documentLoader = $documentLoader; + $this->options = $options; $this->composerLock = $composerLock; $this->composerJson = $composerJson; - $this->options = $options; } /** diff --git a/src/LanguageServer.php b/src/LanguageServer.php index 193b0a1..b29cb1d 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -232,6 +232,7 @@ class LanguageServer extends AdvancedJsonRpc\Dispatcher $dependenciesIndex, $sourceIndex, $this->documentLoader, + $initializationOptions, $this->composerLock, $this->composerJson, $initializationOptions diff --git a/src/Options.php b/src/Options.php index 99f1aab..48ba540 100644 --- a/src/Options.php +++ b/src/Options.php @@ -1,11 +1,12 @@ [$this, 'filterFileTypes']]); // validate file type format - $fileTypes = array_filter($fileTypes, 'strlen'); // filter empty items - $fileTypes = array_values($fileTypes); //rebase indexes + $fileTypes = filter_var($fileTypes, FILTER_CALLBACK, ['options' => [$this, 'filterFileTypes']]); + $fileTypes = array_filter($fileTypes, 'strlen'); + $fileTypes = array_values($fileTypes); $this->fileTypes = !empty($fileTypes) ? $fileTypes : $this->fileTypes; } diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 3354b68..5896eac 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -30,7 +30,7 @@ class LanguageServerTest extends TestCase public function testInitialize() { $server = new LanguageServer(new MockProtocolStream, new MockProtocolStream); - $result = $server->initialize(new ClientCapabilities, __DIR__, getmypid())->wait(); + $result = $server->initialize(new ClientCapabilities, __DIR__, getmypid(), new Options)->wait(); $serverCapabilities = new ServerCapabilities(); $serverCapabilities->textDocumentSync = TextDocumentSyncKind::FULL; @@ -67,7 +67,7 @@ class LanguageServerTest extends TestCase }); $server = new LanguageServer($input, $output); $capabilities = new ClientCapabilities; - $server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid()); + $server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid(), new Options); $promise->wait(); } @@ -115,7 +115,7 @@ class LanguageServerTest extends TestCase $capabilities = new ClientCapabilities; $capabilities->xfilesProvider = true; $capabilities->xcontentProvider = true; - $server->initialize($capabilities, $rootPath, getmypid()); + $server->initialize($capabilities, $rootPath, getmypid(), new Options); $promise->wait(); $this->assertTrue($filesCalled); $this->assertTrue($contentCalled); @@ -127,24 +127,22 @@ class LanguageServerTest extends TestCase $input = new MockProtocolStream; $output = new MockProtocolStream; $options = new Options; - $options->setFileTypes([ '.php', '.inc' ]); - - $output->on('message', function (Message $msg) use ($promise, &$foundFiles) { + $output->on('message', function (Message $msg) use ($promise, &$allFilesParsed) { if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { if ($msg->body->params->type === MessageType::ERROR) { $promise->reject(new Exception($msg->body->params->message)); - } else if (strpos($msg->body->params->message, 'All 27 PHP files parsed') !== false) { - $promise->fulfill(); + } elseif (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { + $promise->fulfill(true); } } }); $server = new LanguageServer($input, $output); $capabilities = new ClientCapabilities; $server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid(), $options); - $promise->wait(); + $this->assertTrue($promise->wait()); } } diff --git a/tests/Server/Workspace/DidChangeConfigurationTest.php b/tests/Server/Workspace/DidChangeConfigurationTest.php deleted file mode 100644 index 6028c6c..0000000 --- a/tests/Server/Workspace/DidChangeConfigurationTest.php +++ /dev/null @@ -1,153 +0,0 @@ -didChangeConfiguration(); - $this->assertFalse($result); - } - - /** - * When the passed options/settings do not differ from the previous, it has nothing to do - */ - public function testFailsWithInvalidOptionsTypeOrFormat() - { - $options = new Options; - $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); - $projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex()); - $definitionResolver = new DefinitionResolver($projectIndex); - $loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver); - $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $loader, null, null, null, null, $options); - - $this->expectException(\Exception::class); - $this->workspace->didChangeConfiguration(['invalid' => 'options format']); - } - - /** - * When the passed options/settings do not differ from the previous, it has nothing to do - */ - public function testNoChangedOptions() - { - $options = new Options; - $client = new LanguageClient(new MockProtocolStream(), $writer = new MockProtocolStream()); - $projectIndex = new ProjectIndex($sourceIndex = new Index(), $dependenciesIndex = new DependenciesIndex()); - $definitionResolver = new DefinitionResolver($projectIndex); - $loader = new PhpDocumentLoader(new FileSystemContentRetriever(), $projectIndex, $definitionResolver); - $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $loader, null, null, null, null, $options); - - $result = $this->workspace->didChangeConfiguration($options); - $this->assertFalse($result); - } - - /** - * Verify that the required methods for a reindex are called - */ - public function testFileTypesOptionTriggersAReindex() - { - $sourceIndex = new Index; - $dependenciesIndex = new DependenciesIndex; - $projectIndex = $this->getMockBuilder('LanguageServer\Index\ProjectIndex') - ->setConstructorArgs([$sourceIndex, $dependenciesIndex]) - ->setMethods(['wipe']) - ->getMock(); - $projectIndex->setComplete(); - - $rootPath = realpath(__DIR__ . '/../../../fixtures/'); - $filesFinder = new FileSystemFilesFinder; - $cache = new FileSystemCache; - - $definitionResolver = new DefinitionResolver($projectIndex); - $client = new LanguageClient(new MockProtocolStream, new MockProtocolStream); - $documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); - $textDocument = new Server\TextDocument($documentLoader, $definitionResolver, $client, $projectIndex); - $indexer = $this->getMockBuilder('LanguageServer\Indexer') - ->setConstructorArgs([$filesFinder, $rootPath, $client, $cache, $dependenciesIndex, $sourceIndex, $documentLoader, null, null, new Options]) - ->setMethods(['index']) - ->getMock(); - $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $documentLoader, null, $indexer, new Options); - - - $options = new Options; - $options->fileTypes = [ - '.inc' - ]; - - $projectIndex->expects($this->once())->method('wipe'); - $indexer->expects($this->once())->method('index'); - - // invoke event - $result = $workspace->didChangeConfiguration($options); - $this->assertTrue($result); - } - - /** - * Be sure that the indexer gets the new options/settings and uses them - */ - public function testIndexerUsesNewOptions() - { - $promise = new Promise; - $sourceIndex = new Index; - $dependenciesIndex = new DependenciesIndex; - $projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); - $projectIndex->setComplete(); - - $rootPath = realpath(__DIR__ . '/../../../fixtures/'); - $filesFinder = new FileSystemFilesFinder; - $cache = new FileSystemCache; - $initialOptions = new Options; - - $input = new MockProtocolStream; - $output = new MockProtocolStream; - - $definitionResolver = new DefinitionResolver($projectIndex); - $client = new LanguageClient($input, $output); - $documentLoader = new PhpDocumentLoader(new FileSystemContentRetriever, $projectIndex, $definitionResolver); - $textDocument = new Server\TextDocument($documentLoader, $definitionResolver, $client, $projectIndex); - $indexer = new Indexer($filesFinder, $rootPath, $client, $cache, $dependenciesIndex, $sourceIndex, $documentLoader, null, null, $initialOptions); - $workspace = new Server\Workspace($client, $projectIndex, $dependenciesIndex, $sourceIndex, null, $documentLoader, null, $indexer, $initialOptions); - - $output->on('message', function (Message $msg) use ($promise) { - if ($msg->body->method === 'window/logMessage' && $promise->state === Promise::PENDING) { - if ($msg->body->params->type === MessageType::ERROR) { - $promise->reject(new Exception($msg->body->params->message)); - } elseif (strpos($msg->body->params->message, 'All 1 PHP files parsed') !== false) { - $promise->fulfill(); - } - } - }); - - $options = new Options; - $options->fileTypes = [ - '.inc' - ]; - - $result = $workspace->didChangeConfiguration($options); - $this->assertTrue($result); - $promise->wait(); - } -} From a5417cdf72256ae8a928dcabe2169a3bd01d7f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Wed, 29 Aug 2018 21:35:18 +0200 Subject: [PATCH 25/27] Fix risky test warning --- tests/LanguageServerTest.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/LanguageServerTest.php b/tests/LanguageServerTest.php index 5896eac..9fe890d 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -61,14 +61,14 @@ class LanguageServerTest extends TestCase if ($msg->body->params->type === MessageType::ERROR) { $promise->reject(new Exception($msg->body->params->message)); } else if (preg_match('/All \d+ PHP files parsed/', $msg->body->params->message)) { - $promise->fulfill(); + $promise->fulfill(true); } } }); $server = new LanguageServer($input, $output); $capabilities = new ClientCapabilities; $server->initialize($capabilities, realpath(__DIR__ . '/../fixtures'), getmypid(), new Options); - $promise->wait(); + $this->assertTrue($promise->wait()); } public function testIndexingWithFilesAndContentRequests() From 24e3b77b58ec5faedc042dbe0fcfc42c2aac86f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Wed, 29 Aug 2018 23:02:55 +0200 Subject: [PATCH 26/27] Add option to set file size limit --- src/Indexer.php | 2 +- src/Options.php | 41 ++++++++++++++++++++++++++++++++++++++- src/PhpDocumentLoader.php | 7 +++---- 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/Indexer.php b/src/Indexer.php index e411054..a9e3335 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -221,7 +221,7 @@ class Indexer yield timeout(); $this->client->window->logMessage(MessageType::LOG, "Parsing $uri"); try { - $document = yield $this->documentLoader->load($uri); + $document = yield $this->documentLoader->load($uri, $this->options->fileSizeLimit); if (!isVendored($document, $this->composerJson)) { $this->client->textDocument->publishDiagnostics($uri, $document->getDiagnostics()); } diff --git a/src/Options.php b/src/Options.php index 48ba540..81a0fa5 100644 --- a/src/Options.php +++ b/src/Options.php @@ -12,6 +12,13 @@ class Options */ public $fileTypes = ['.php']; + /** + * Maximum file size to index + * + * @var int + */ + public $fileSizeLimit = 150000; + /** * Validate/Filter input and set options for file types * @@ -20,13 +27,29 @@ class Options public function setFileTypes(array $fileTypes) { $fileTypes = filter_var_array($fileTypes, FILTER_SANITIZE_STRING); - $fileTypes = filter_var($fileTypes, FILTER_CALLBACK, ['options' => [$this, 'filterFileTypes']]); + $fileTypes = filter_var($fileTypes, FILTER_CALLBACK, ['options' => [$this, 'filterFileTypes']]); $fileTypes = array_filter($fileTypes, 'strlen'); $fileTypes = array_values($fileTypes); $this->fileTypes = !empty($fileTypes) ? $fileTypes : $this->fileTypes; } + /** + * Validate/Filter input and set option for file size limit + * + * @param string $fileSizeLimit Size in human readable format or -1 for unlimited + */ + public function setFileSizeLimit(string $fileSizeLimit) + { + $fileSizeLimit = filter_var($fileSizeLimit, FILTER_SANITIZE_STRING); + + if ($fileSizeLimit === '-1') { + $this->fileSizeLimit = PHP_INT_MAX; + } else { + $this->fileSizeLimit = $this->convertFileSize($fileSizeLimit); + } + } + /** * Filter valid file type * @@ -47,4 +70,20 @@ class Options return $fileType; } + + /** + * Convert human readable file size to byte + * + * @param string $fileSize + * @return int + */ + private function convertFileSize(string $fileSize) + { + preg_match('/(\d+)(\w)/', $fileSize, $match); + $sizes = 'KMG'; + $size = (int) $match[1]; + $factor = strpos($sizes, strtoupper($match[2])) + 1; + + return $size * pow(1000, $factor); + } } diff --git a/src/PhpDocumentLoader.php b/src/PhpDocumentLoader.php index 57a7e9c..7bcb067 100644 --- a/src/PhpDocumentLoader.php +++ b/src/PhpDocumentLoader.php @@ -100,13 +100,12 @@ class PhpDocumentLoader * The document is NOT added to the list of open documents, but definitions are registered. * * @param string $uri + * @param int $limit * @return Promise */ - public function load(string $uri): Promise + public function load(string $uri, int $limit): Promise { - return coroutine(function () use ($uri) { - - $limit = 150000; + return coroutine(function () use ($uri, $limit) { $content = yield $this->contentRetriever->retrieve($uri); $size = strlen($content); if ($size > $limit) { From b7bcf00ab3d8f842e34d6e496ab02a444bd130b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=BCrgen=20Steitz?= Date: Wed, 29 Aug 2018 23:03:25 +0200 Subject: [PATCH 27/27] Add basic test --- tests/OptionsTest.php | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/OptionsTest.php b/tests/OptionsTest.php index 45e35f6..04b2869 100644 --- a/tests/OptionsTest.php +++ b/tests/OptionsTest.php @@ -1,10 +1,10 @@ assertSame($expected, $options->fileTypes); } + + public function testConvertFileSize() + { + $options = new Options(); + + $options->setFileSizeLimit('150K'); + $this->assertEquals(150000, $options->fileSizeLimit); + + $options->setFileSizeLimit('15M'); + $this->assertEquals(15000000, $options->fileSizeLimit); + + $options->setFileSizeLimit('15G'); + $this->assertEquals(15000000000, $options->fileSizeLimit); + + $options->setFileSizeLimit('-1'); + $this->assertEquals(PHP_INT_MAX, $options->fileSizeLimit); + } }