Skip to content

Commit b1a1875

Browse files
authored
fix(completion): don't suggest <?php on > characer (#527)
closes #372
1 parent 06747bb commit b1a1875

File tree

6 files changed

+112
-9
lines changed

6 files changed

+112
-9
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<

src/CompletionProvider.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
Position,
1111
CompletionList,
1212
CompletionItem,
13-
CompletionItemKind
13+
CompletionItemKind,
14+
CompletionContext,
15+
CompletionTriggerKind
1416
};
1517
use Microsoft\PhpParser;
1618
use Microsoft\PhpParser\Node;
@@ -122,9 +124,10 @@ public function __construct(DefinitionResolver $definitionResolver, ReadableInde
122124
*
123125
* @param PhpDocument $doc The opened document
124126
* @param Position $pos The cursor position
127+
* @param CompletionContext $context The completion context
125128
* @return CompletionList
126129
*/
127-
public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList
130+
public function provideCompletion(PhpDocument $doc, Position $pos, CompletionContext $context = null): CompletionList
128131
{
129132
// This can be made much more performant if the tree follows specific invariants.
130133
$node = $doc->getNodeAtPosition($pos);
@@ -152,7 +155,21 @@ public function provideCompletion(PhpDocument $doc, Position $pos): CompletionLi
152155

153156
// Inspect the type of expression under the cursor
154157

155-
if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) {
158+
$content = $doc->getContent();
159+
$offset = $pos->toOffset($content);
160+
if (
161+
$node === null
162+
|| (
163+
$node instanceof Node\Statement\InlineHtml
164+
&& (
165+
$context === null
166+
// Make sure to not suggest on the > trigger character in HTML
167+
|| $context->triggerKind === CompletionTriggerKind::INVOKED
168+
|| $context->triggerCharacter === '<'
169+
)
170+
)
171+
|| $pos == new Position(0, 0)
172+
) {
156173
// HTML, beginning of file
157174

158175
// Inside HTML and at the beginning of the file, propose <?php

src/Protocol/CompletionContext.php

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
namespace LanguageServer\Protocol;
4+
5+
/**
6+
* Contains additional information about the context in which a completion request is triggered.
7+
*/
8+
class CompletionContext
9+
{
10+
/**
11+
* How the completion was triggered.
12+
*
13+
* @var int
14+
*/
15+
public $triggerKind;
16+
17+
/**
18+
* The trigger character (a single character) that has trigger code complete.
19+
* Is null if `triggerKind !== CompletionTriggerKind::TRIGGER_CHARACTER`
20+
*
21+
* @var string|null
22+
*/
23+
public $triggerCharacter;
24+
25+
public function __construct(int $triggerKind, string $triggerCharacter = null)
26+
{
27+
$this->triggerKind = $triggerKind;
28+
$this->triggerCharacter = $triggerCharacter;
29+
}
30+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace LanguageServer\Protocol;
4+
5+
class CompletionTriggerKind
6+
{
7+
/**
8+
* Completion was triggered by invoking it manuall or using API.
9+
*/
10+
const INVOKED = 1;
11+
12+
/**
13+
* Completion was triggered by a trigger character.
14+
*/
15+
const TRIGGER_CHARACTER = 2;
16+
}

src/Server/TextDocument.php

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
SymbolLocationInformation,
2121
TextDocumentIdentifier,
2222
TextDocumentItem,
23-
VersionedTextDocumentIdentifier
23+
VersionedTextDocumentIdentifier,
24+
CompletionContext
2425
};
2526
use Microsoft\PhpParser;
2627
use Microsoft\PhpParser\Node;
@@ -335,13 +336,14 @@ public function hover(TextDocumentIdentifier $textDocument, Position $position):
335336
*
336337
* @param TextDocumentIdentifier The text document
337338
* @param Position $position The position
339+
* @param CompletionContext|null $context The completion context
338340
* @return Promise <CompletionItem[]|CompletionList>
339341
*/
340-
public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise
342+
public function completion(TextDocumentIdentifier $textDocument, Position $position, CompletionContext $context = null): Promise
341343
{
342-
return coroutine(function () use ($textDocument, $position) {
344+
return coroutine(function () use ($textDocument, $position, $context) {
343345
$document = yield $this->documentLoader->getOrLoad($textDocument->uri);
344-
return $this->completionProvider->provideCompletion($document, $position);
346+
return $this->completionProvider->provideCompletion($document, $position, $context);
345347
});
346348
}
347349

tests/Server/TextDocument/CompletionTest.php

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
Position,
1818
CompletionList,
1919
CompletionItem,
20-
CompletionItemKind
20+
CompletionItemKind,
21+
CompletionContext,
22+
CompletionTriggerKind
2123
};
2224
use function LanguageServer\pathToUri;
2325

@@ -464,6 +466,41 @@ public function testHtmlWithPrefix()
464466
], true), $items);
465467
}
466468

469+
public function testHtmlPrefixShouldNotTriggerCompletion()
470+
{
471+
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php');
472+
$this->loader->open($completionUri, file_get_contents($completionUri));
473+
$items = $this->textDocument->completion(
474+
new TextDocumentIdentifier($completionUri),
475+
new Position(0, 1),
476+
new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '>')
477+
)->wait();
478+
$this->assertEquals(new CompletionList([], true), $items);
479+
}
480+
481+
public function testHtmlPrefixShouldTriggerCompletionIfManuallyInvoked()
482+
{
483+
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php');
484+
$this->loader->open($completionUri, file_get_contents($completionUri));
485+
$items = $this->textDocument->completion(
486+
new TextDocumentIdentifier($completionUri),
487+
new Position(0, 1),
488+
new CompletionContext(CompletionTriggerKind::INVOKED)
489+
)->wait();
490+
$this->assertEquals(new CompletionList([
491+
new CompletionItem(
492+
'<?php',
493+
CompletionItemKind::KEYWORD,
494+
null,
495+
null,
496+
null,
497+
null,
498+
null,
499+
new TextEdit(new Range(new Position(0, 1), new Position(0, 1)), '?php')
500+
)
501+
], true), $items);
502+
}
503+
467504
public function testNamespace()
468505
{
469506
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/namespace.php');
@@ -605,7 +642,7 @@ public function testThisWithoutPrefix()
605642
)
606643
], true), $items);
607644
}
608-
645+
609646
public function testThisWithPrefix()
610647
{
611648
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_with_prefix.php');

0 commit comments

Comments
 (0)