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 34ad618f..3529e19f 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 @@ public function __construct( 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 @@ public function __construct( $this->documentLoader = $documentLoader; $this->composerLock = $composerLock; $this->composerJson = $composerJson; + $this->options = $options; } /** @@ -104,8 +112,8 @@ public function __construct( 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 3c999f22..12ee1736 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -167,11 +167,12 @@ 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 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 @@ 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); + $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 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = $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 00000000..3a4842eb --- /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 00000000..b3d9bbc7 --- /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 3529e19f..4cd85d36 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 4cd85d36..c341dd29 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -112,7 +112,7 @@ public function __construct( 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 3a4842eb..d7e78847 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); + } + } + + /** + * Validate 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); + + $this->fileTypes = !empty($fileTypes) ? $fileTypes : $this->fileTypes; + } + + /** + * Get list of registered file types + * + * @return array + */ + public function getFileTypes(): array + { + return $this->fileTypes; } - private function normalizeFileTypes(array $fileTypes): array + /** + * 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) { - return array_map(function (string $fileType) { - if (substr($fileType, 0, 1) !== '.') { - $fileType = '.' . $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 c341dd29..4cd85d36 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -112,7 +112,7 @@ public function __construct( 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 12ee1736..7e5074b6 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -167,10 +167,10 @@ 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 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 @@ 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); - $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 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = $this->documentLoader, $this->composerLock, $this->composerJson, - $options + $initializationOptions ); $indexer->index()->otherwise('\\LanguageServer\\crash'); } diff --git a/src/Options.php b/src/Options.php index d7e78847..99f1aab9 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 00000000..d47afcf9 --- /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 7e5074b6..d3b1c5c8 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -191,6 +191,7 @@ 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; // 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 0c0740c3..9b169021 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -5,6 +5,7 @@ use PHPUnit\Framework\TestCase; use LanguageServer\LanguageServer; +use LanguageServer\Options; use LanguageServer\Protocol\{ Message, ClientCapabilities, @@ -123,12 +124,12 @@ public function testIndexingMultipleFileTypes() $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 d47afcf9..f2251d8b 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 f2251d8b..45e35f6d 100644 --- a/tests/OptionsTest.php +++ b/tests/OptionsTest.php @@ -6,7 +6,7 @@ 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 5377c3a4..39c1a1da 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -42,6 +42,9 @@ protected function registerIndex(ReadableIndex $index) $index->on('definition-added', function () { $this->emit('definition-added'); }); + $index->on('wipe', function() { + $this->emit('wipe'); + }); } /** @@ -147,4 +150,14 @@ public function getReferenceUris(string $fqn): array } 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 5c24813e..e912087b 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -211,4 +211,14 @@ public function serialize() '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 b94618cb..3a10b78c 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -170,4 +170,9 @@ public function xdependencies(): array } 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 99f1aab9..9f61edb8 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 @@ public function setFileTypes(array $fileTypes) $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 3a10b78c..fb3807d1 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -3,7 +3,7 @@ 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 @@ public function __construct(ProjectIndex $index, DependenciesIndex $dependencies $this->composerLock = $composerLock; $this->documentLoader = $documentLoader; $this->composerJson = $composerJson; + $this->indexer = $indexer; + $this->options = $options; } /** @@ -171,8 +185,43 @@ public function xdependencies(): array return $dependencyReferences; } - public function didChangeConfiguration($settings = null) + /** + * @param Options|null $settings + */ + public function didChangeConfiguration(Options $settings = null) + { + 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 { - $this->index->wipe(); + // 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 d3b1c5c8..62b84007 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -261,7 +261,9 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = $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 | 32 ++++++--- .../Workspace/DidChangeConfigurationTest.php | 72 +++++++++++++++++++ 2 files changed, 95 insertions(+), 9 deletions(-) create mode 100644 tests/Server/Workspace/DidChangeConfigurationTest.php diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 679191f9..1a45c642 100644 --- a/tests/Server/ServerTestCase.php +++ b/tests/Server/ServerTestCase.php @@ -5,10 +5,12 @@ 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 @@ public function setUp() { $sourceIndex = new Index; $dependenciesIndex = new DependenciesIndex; - $projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); - $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); + $this->projectIndex = new ProjectIndex($sourceIndex, $dependenciesIndex); + $this->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); + $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 00000000..18970e93 --- /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 1f351342..7f769772 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -248,10 +248,14 @@ public function didChangeConfiguration(Options $settings = null) */ 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 7f769772..e74490e4 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -225,18 +225,17 @@ public function didChangeConfiguration(Options $settings = null) } $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 18970e93..8479764e 100644 --- a/tests/Server/Workspace/DidChangeConfigurationTest.php +++ b/tests/Server/Workspace/DidChangeConfigurationTest.php @@ -51,7 +51,7 @@ public function testReindexingAfterWipe() 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 39c1a1da..33b86e55 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -42,9 +42,6 @@ protected function registerIndex(ReadableIndex $index) $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 e912087b..9879ced6 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -218,7 +218,5 @@ public function wipe() $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 e74490e4..7527e2e3 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -216,16 +216,38 @@ public function xdependencies(): array } /** - * @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 7527e2e3..98184ba6 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -224,12 +224,13 @@ public function xdependencies(): array * 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 @@ public function didChangeConfiguration($settings = null) } 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 @@ public function didChangeConfiguration($settings = null) $this->index->wipe(); $this->indexer->index()->otherwise('\\LanguageServer\\crash'); } + + return true; } /** diff --git a/tests/Server/ServerTestCase.php b/tests/Server/ServerTestCase.php index 7bee0518..5d9e309f 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 @@ public function setUp() { $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 8479764e..e0ad2eae 100644 --- a/tests/Server/Workspace/DidChangeConfigurationTest.php +++ b/tests/Server/Workspace/DidChangeConfigurationTest.php @@ -3,51 +3,135 @@ 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(); + + $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); - $this->output->on('message', function (Message $msg) use ($promise) { + $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 @@ public function testReindexingAfterWipe() '.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 98184ba6..ad724de1 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -274,7 +274,7 @@ private function getChangedOptions(Options $settings): array { $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 e0ad2eae..6028c6c8 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 @@ public function test_no_option_passed() /** * 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 @@ public function test_fails_with_invalid_options_type_or_format() /** * 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 @@ public function test_no_changed_options() /** * 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 @@ public function test_fileTypes_option_triggers_a_reindex() /** * 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 9f61edb8..99f1aab9 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 @@ public function setFileTypes(array $fileTypes) $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 ad724de1..b983bf6d 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -229,6 +229,9 @@ public function xdependencies(): array */ public function didChangeConfiguration($settings = null): bool { + // List of options that affect the indexer + $indexerOptions = ['fileTypes']; + if ($settings === null) { return false; } @@ -253,7 +256,7 @@ public function didChangeConfiguration($settings = null): bool $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 b983bf6d..954e5e9a 100644 --- a/src/Server/Workspace.php +++ b/src/Server/Workspace.php @@ -216,7 +216,7 @@ public function xdependencies(): array } /** - * 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 33b86e55..5377c3a4 100644 --- a/src/Index/AbstractAggregateIndex.php +++ b/src/Index/AbstractAggregateIndex.php @@ -147,14 +147,4 @@ public function getReferenceUris(string $fqn): array } 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 a88c7ba5..9cb975e5 100644 --- a/src/Index/Index.php +++ b/src/Index/Index.php @@ -222,12 +222,4 @@ public function serialize() '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 fd59aa39..e4110543 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -54,30 +54,30 @@ class Indexer private $documentLoader; /** - * @var \stdClasss + * @var Options */ - private $composerLock; + private $options; /** * @var \stdClasss */ - private $composerJson; + private $composerLock; /** - * @var Options + * @var \stdClasss */ - private $options; + 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 - * @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 @@ public function __construct( 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 @@ public function __construct( $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 193b0a11..b29cb1da 100644 --- a/src/LanguageServer.php +++ b/src/LanguageServer.php @@ -232,6 +232,7 @@ public function initialize(ClientCapabilities $capabilities, string $rootPath = $dependenciesIndex, $sourceIndex, $this->documentLoader, + $initializationOptions, $this->composerLock, $this->composerJson, $initializationOptions diff --git a/src/Options.php b/src/Options.php index 99f1aab9..48ba540b 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 3354b68e..5896eaca 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 @@ public function testIndexingWithDirectFileAccess() }); $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 @@ public function testIndexingWithFilesAndContentRequests() $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 @@ public function testIndexingMultipleFileTypes() $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 6028c6c8..00000000 --- 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 5896eaca..9fe890d4 100644 --- a/tests/LanguageServerTest.php +++ b/tests/LanguageServerTest.php @@ -61,14 +61,14 @@ public function testIndexingWithDirectFileAccess() 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 e4110543..a9e33358 100644 --- a/src/Indexer.php +++ b/src/Indexer.php @@ -221,7 +221,7 @@ private function indexFiles(array $files): Promise 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 48ba540b..81a0fa55 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 @@ private function filterFileTypes(string $fileType) 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 57a7e9c9..7bcb067e 100644 --- a/src/PhpDocumentLoader.php +++ b/src/PhpDocumentLoader.php @@ -100,13 +100,12 @@ public function getOrLoad(string $uri): Promise * 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 45e35f6d..04b2869a 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); + } }