1
0
Fork 0

merge from upstream

pull/529/head
jens1o 2017-11-16 16:42:57 +01:00
commit 9dc35344f4
No known key found for this signature in database
GPG Key ID: C7437FC1B445CC49
8 changed files with 143 additions and 10 deletions

View File

@ -58,7 +58,7 @@ jobs:
stages: stages:
- test - test
- name: release - name: release
if: branch = master if: branch = master AND type = push AND fork = false
branches: branches:
except: except:

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;
@ -337,13 +338,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);
}); });
} }

30
tests/Index/IndexTest.php Normal file
View File

@ -0,0 +1,30 @@
<?php
namespace LanguageServer\Tests;
use PHPUnit\Framework\TestCase;
use LanguageServer\Index\Index;
use LanguageServer\Definition;
class IndexTest extends TestCase
{
public function testGetSetMethodDefinition()
{
$index = new Index;
$index->setDefinition('SomeNamespace\SomeClass', new Definition);
$methodDefinition = new Definition;
$methodFqn = 'SomeNamespace\SomeClass->someMethod()';
$index->setDefinition($methodFqn, $methodDefinition);
$index->setDefinition('SomeNamespace\SomeClass->someProperty', new Definition);
$this->assertSame($methodDefinition, $index->getDefinition($methodFqn));
}
public function testGetSetClassDefinition()
{
$index = new Index;
$definition = new Definition;
$fqn = 'SomeNamespace\SomeClass';
$index->setDefinition($fqn, $definition);
$this->assertSame($definition, $index->getDefinition($fqn));
}
}

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');