diff --git a/src/CompletionProvider.php b/src/CompletionProvider.php index e8dcdbc..4f28811 100644 --- a/src/CompletionProvider.php +++ b/src/CompletionProvider.php @@ -10,6 +10,7 @@ use LanguageServer\Protocol\{ Range, Position, SymbolKind, + CompletionList, CompletionItem, CompletionItemKind }; @@ -112,9 +113,9 @@ class CompletionProvider * * @param PhpDocument $doc The opened document * @param Position $pos The cursor position - * @return CompletionItem[] + * @return CompletionList */ - public function provideCompletion(PhpDocument $doc, Position $pos): array + public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList { $node = $doc->getNodeAtPosition($pos); @@ -122,8 +123,7 @@ class CompletionProvider $node = $node->getAttribute('parentNode'); } - /** @var CompletionItem[] */ - $items = []; + $list = new CompletionList; // A non-free node means we do NOT suggest global symbols if ( @@ -161,7 +161,7 @@ class CompletionProvider foreach ($this->project->getDefinitions() as $fqn => $def) { foreach ($prefixes as $prefix) { if (substr($fqn, 0, strlen($prefix)) === $prefix && !$def->isGlobal) { - $items[] = CompletionItem::fromDefinition($def); + $list->items[] = CompletionItem::fromDefinition($def); } } } @@ -204,7 +204,7 @@ class CompletionProvider // Search the aliases for the typed-in name foreach ($aliasedDefs as $alias => $def) { if (substr($alias, 0, $prefixLen) === $prefix) { - $items[] = CompletionItem::fromDefinition($def); + $list->items[] = CompletionItem::fromDefinition($def); } } } @@ -240,7 +240,7 @@ class CompletionProvider // Insert the FQN without trailing backlash $item->insertText = $fqn; } - $items[] = $item; + $list->items[] = $item; } } // Suggest keywords @@ -249,7 +249,7 @@ class CompletionProvider if (substr($keyword, 0, $prefixLen) === $prefix) { $item = new CompletionItem($keyword, CompletionItemKind::KEYWORD); $item->insertText = $keyword . ' '; - $items[] = $item; + $list->items[] = $item; } } } @@ -270,7 +270,7 @@ class CompletionProvider new Range($pos, $pos), stripStringOverlap($doc->getRange(new Range(new Position(0, 0), $pos)), $item->label) ); - $items[] = $item; + $list->items[] = $item; } } else if ($node instanceof Node\Stmt\InlineHTML || $pos == new Position(0, 0)) { $item = new CompletionItem('getRange(new Range(new Position(0, 0), $pos)), 'items[] = $item; } - return $items; + return $list; } /** diff --git a/src/Protocol/CompletionList.php b/src/Protocol/CompletionList.php index d348830..4d0bd64 100644 --- a/src/Protocol/CompletionList.php +++ b/src/Protocol/CompletionList.php @@ -22,4 +22,14 @@ class CompletionList * @var CompletionItem[] */ public $items; + + /** + * @param CompletionItem[] $items The completion items. + * @param bool $isIncomplete This list it not complete. Further typing should result in recomputing this list. + */ + public function __construct(array $items = [], bool $isIncomplete = false) + { + $this->items = $items; + $this->isIncomplete = $isIncomplete; + } } diff --git a/tests/Server/TextDocument/CompletionTest.php b/tests/Server/TextDocument/CompletionTest.php index c76c7fd..cbbbd7c 100644 --- a/tests/Server/TextDocument/CompletionTest.php +++ b/tests/Server/TextDocument/CompletionTest.php @@ -12,6 +12,7 @@ use LanguageServer\Protocol\{ Range, Position, ClientCapabilities, + CompletionList, CompletionItem, CompletionItemKind }; @@ -46,7 +47,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(3, 7) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'testProperty', CompletionItemKind::PROPERTY, @@ -59,7 +60,7 @@ class CompletionTest extends TestCase '\TestClass', // Return type of the method 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' ) - ], $items); + ]), $items); } public function testPropertyAndMethodWithoutPrefix() @@ -70,7 +71,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(3, 6) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'testProperty', CompletionItemKind::PROPERTY, @@ -83,7 +84,7 @@ class CompletionTest extends TestCase '\TestClass', // Return type of the method 'Non culpa nostrud mollit esse sunt laboris in irure ullamco cupidatat amet.' ) - ], $items); + ]), $items); } public function testVariable() @@ -94,7 +95,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(8, 5) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( '$var', CompletionItemKind::VARIABLE, @@ -115,7 +116,7 @@ class CompletionTest extends TestCase null, new TextEdit(new Range(new Position(8, 5), new Position(8, 5)), 'param') ) - ], $items); + ]), $items); } public function testVariableWithPrefix() @@ -126,7 +127,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(8, 6) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( '$param', CompletionItemKind::VARIABLE, @@ -137,7 +138,7 @@ class CompletionTest extends TestCase null, new TextEdit(new Range(new Position(8, 6), new Position(8, 6)), 'aram') ) - ], $items); + ]), $items); } public function testNewInNamespace() @@ -148,7 +149,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(6, 10) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ // Global TestClass definition (inserted as \TestClass) new CompletionItem( 'TestClass', @@ -169,7 +170,7 @@ class CompletionTest extends TestCase null, 'TestClass' ), - ], $items); + ]), $items); } public function testUsedClass() @@ -180,14 +181,14 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(6, 5) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'TestClass', CompletionItemKind::CLASS_, 'TestNamespace', 'Pariatur ut laborum tempor voluptate consequat ea deserunt.' ) - ], $items); + ]), $items); } public function testStaticPropertyWithPrefix() @@ -198,7 +199,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 14) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'staticTestProperty', CompletionItemKind::PROPERTY, @@ -208,7 +209,7 @@ class CompletionTest extends TestCase null, '$staticTestProperty' ) - ], $items); + ]), $items); } public function testStaticWithoutPrefix() @@ -219,7 +220,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 11) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'TEST_CLASS_CONST', CompletionItemKind::VARIABLE, @@ -241,7 +242,7 @@ class CompletionTest extends TestCase 'mixed', // Method return type 'Do magna consequat veniam minim proident eiusmod incididunt aute proident.' ) - ], $items); + ]), $items); } public function testStaticMethodWithPrefix() @@ -252,14 +253,14 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 13) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'staticTestMethod', CompletionItemKind::METHOD, 'mixed', // Method return type 'Do magna consequat veniam minim proident eiusmod incididunt aute proident.' ) - ], $items); + ]), $items); } public function testClassConstWithPrefix() @@ -270,14 +271,14 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 13) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'TEST_CLASS_CONST', CompletionItemKind::VARIABLE, 'int', 'Anim labore veniam consectetur laboris minim quis aute aute esse nulla ad.' ) - ], $items); + ]), $items); } public function testFullyQualifiedClass() @@ -288,7 +289,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(6, 6) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'TestClass', CompletionItemKind::CLASS_, @@ -298,7 +299,7 @@ class CompletionTest extends TestCase null, 'TestClass' ) - ], $items); + ]), $items); } public function testKeywords() @@ -309,10 +310,10 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(2, 1) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem('class', CompletionItemKind::KEYWORD, null, null, null, null, 'class '), new CompletionItem('clone', CompletionItemKind::KEYWORD, null, null, null, null, 'clone ') - ], $items); + ]), $items); } public function testHtmlWithoutPrefix() @@ -323,7 +324,7 @@ class CompletionTest extends TestCase new TextDocumentIdentifier($completionUri), new Position(0, 0) )->wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'wait(); - $this->assertEquals([ + $this->assertEquals(new CompletionList([ new CompletionItem( 'SomeNamespace', CompletionItemKind::MODULE, @@ -377,6 +378,6 @@ class CompletionTest extends TestCase null, 'SomeNamespace' ) - ], $items); + ]), $items); } }