1
0
Fork 0

fix(completion): don't suggest <?php on > characer (#527)

closes #372
pull/532/head v5.0.1
Felix Becker 2017-11-15 22:38:01 -08:00 committed by GitHub
parent 06747bb734
commit b1a1875070
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 112 additions and 9 deletions

View File

@ -0,0 +1 @@
<

View File

@ -10,7 +10,9 @@ use LanguageServer\Protocol\{
Position, Position,
CompletionList, CompletionList,
CompletionItem, CompletionItem,
CompletionItemKind CompletionItemKind,
CompletionContext,
CompletionTriggerKind
}; };
use Microsoft\PhpParser; use Microsoft\PhpParser;
use Microsoft\PhpParser\Node; use Microsoft\PhpParser\Node;
@ -122,9 +124,10 @@ class CompletionProvider
* *
* @param PhpDocument $doc The opened document * @param PhpDocument $doc The opened document
* @param Position $pos The cursor position * @param Position $pos The cursor position
* @param CompletionContext $context The completion context
* @return CompletionList * @return CompletionList
*/ */
public function provideCompletion(PhpDocument $doc, Position $pos): CompletionList public function provideCompletion(PhpDocument $doc, Position $pos, CompletionContext $context = null): CompletionList
{ {
// This can be made much more performant if the tree follows specific invariants. // This can be made much more performant if the tree follows specific invariants.
$node = $doc->getNodeAtPosition($pos); $node = $doc->getNodeAtPosition($pos);
@ -152,7 +155,21 @@ class CompletionProvider
// Inspect the type of expression under the cursor // Inspect the type of expression under the cursor
if ($node === null || $node instanceof Node\Statement\InlineHtml || $pos == new Position(0, 0)) { $content = $doc->getContent();
$offset = $pos->toOffset($content);
if (
$node === null
|| (
$node instanceof Node\Statement\InlineHtml
&& (
$context === null
// Make sure to not suggest on the > trigger character in HTML
|| $context->triggerKind === CompletionTriggerKind::INVOKED
|| $context->triggerCharacter === '<'
)
)
|| $pos == new Position(0, 0)
) {
// HTML, beginning of file // HTML, beginning of file
// Inside HTML and at the beginning of the file, propose <?php // Inside HTML and at the beginning of the file, propose <?php

View File

@ -0,0 +1,30 @@
<?php
namespace LanguageServer\Protocol;
/**
* Contains additional information about the context in which a completion request is triggered.
*/
class CompletionContext
{
/**
* How the completion was triggered.
*
* @var int
*/
public $triggerKind;
/**
* The trigger character (a single character) that has trigger code complete.
* Is null if `triggerKind !== CompletionTriggerKind::TRIGGER_CHARACTER`
*
* @var string|null
*/
public $triggerCharacter;
public function __construct(int $triggerKind, string $triggerCharacter = null)
{
$this->triggerKind = $triggerKind;
$this->triggerCharacter = $triggerCharacter;
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace LanguageServer\Protocol;
class CompletionTriggerKind
{
/**
* Completion was triggered by invoking it manuall or using API.
*/
const INVOKED = 1;
/**
* Completion was triggered by a trigger character.
*/
const TRIGGER_CHARACTER = 2;
}

View File

@ -20,7 +20,8 @@ use LanguageServer\Protocol\{
SymbolLocationInformation, SymbolLocationInformation,
TextDocumentIdentifier, TextDocumentIdentifier,
TextDocumentItem, TextDocumentItem,
VersionedTextDocumentIdentifier VersionedTextDocumentIdentifier,
CompletionContext
}; };
use Microsoft\PhpParser; use Microsoft\PhpParser;
use Microsoft\PhpParser\Node; use Microsoft\PhpParser\Node;
@ -335,13 +336,14 @@ class TextDocument
* *
* @param TextDocumentIdentifier The text document * @param TextDocumentIdentifier The text document
* @param Position $position The position * @param Position $position The position
* @param CompletionContext|null $context The completion context
* @return Promise <CompletionItem[]|CompletionList> * @return Promise <CompletionItem[]|CompletionList>
*/ */
public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise public function completion(TextDocumentIdentifier $textDocument, Position $position, CompletionContext $context = null): Promise
{ {
return coroutine(function () use ($textDocument, $position) { return coroutine(function () use ($textDocument, $position, $context) {
$document = yield $this->documentLoader->getOrLoad($textDocument->uri); $document = yield $this->documentLoader->getOrLoad($textDocument->uri);
return $this->completionProvider->provideCompletion($document, $position); return $this->completionProvider->provideCompletion($document, $position, $context);
}); });
} }

View File

@ -17,7 +17,9 @@ use LanguageServer\Protocol\{
Position, Position,
CompletionList, CompletionList,
CompletionItem, CompletionItem,
CompletionItemKind CompletionItemKind,
CompletionContext,
CompletionTriggerKind
}; };
use function LanguageServer\pathToUri; use function LanguageServer\pathToUri;
@ -464,6 +466,41 @@ class CompletionTest extends TestCase
], true), $items); ], true), $items);
} }
public function testHtmlPrefixShouldNotTriggerCompletion()
{
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php');
$this->loader->open($completionUri, file_get_contents($completionUri));
$items = $this->textDocument->completion(
new TextDocumentIdentifier($completionUri),
new Position(0, 1),
new CompletionContext(CompletionTriggerKind::TRIGGER_CHARACTER, '>')
)->wait();
$this->assertEquals(new CompletionList([], true), $items);
}
public function testHtmlPrefixShouldTriggerCompletionIfManuallyInvoked()
{
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/html_no_completion.php');
$this->loader->open($completionUri, file_get_contents($completionUri));
$items = $this->textDocument->completion(
new TextDocumentIdentifier($completionUri),
new Position(0, 1),
new CompletionContext(CompletionTriggerKind::INVOKED)
)->wait();
$this->assertEquals(new CompletionList([
new CompletionItem(
'<?php',
CompletionItemKind::KEYWORD,
null,
null,
null,
null,
null,
new TextEdit(new Range(new Position(0, 1), new Position(0, 1)), '?php')
)
], true), $items);
}
public function testNamespace() public function testNamespace()
{ {
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/namespace.php'); $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/namespace.php');
@ -605,7 +642,7 @@ class CompletionTest extends TestCase
) )
], true), $items); ], true), $items);
} }
public function testThisWithPrefix() public function testThisWithPrefix()
{ {
$completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_with_prefix.php'); $completionUri = pathToUri(__DIR__ . '/../../../fixtures/completion/this_with_prefix.php');