Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Completion #165

Merged
merged 27 commits into from
Nov 30, 2016
Merged

Completion #165

merged 27 commits into from
Nov 30, 2016

Conversation

felixfbecker
Copy link
Owner

@felixfbecker felixfbecker commented Nov 19, 2016

Closes #9 💪
Big ups to @nikic for making this possible with the improvements in error recovery in v3!

animation

Works for properties and methods with all of the recursive type goodness of #54

Todo:

  • variables
  • keywords
  • classes
  • namespaces
  • constants
  • differentiate between static and instance properties/methods

Testing

Two things are preventing a nice testing experience:

@felixfbecker felixfbecker force-pushed the completion branch 3 times, most recently from 4780b34 to 0be6551 Compare November 19, 2016 05:29
@codecov-io
Copy link

codecov-io commented Nov 19, 2016

Current coverage is 95.02% (diff: 96.00%)

Merging #165 into master will increase coverage by 0.31%

@@             master       #165   diff @@
==========================================
  Files            31         34     +3   
  Lines           416        583   +167   
  Methods          67         75     +8   
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits            394        554   +160   
- Misses           22         29     +7   
  Partials          0          0          
Diff Coverage File Path
••••••••• 94% new src/CompletionProvider.php
•••••••••• 100% src/NodeVisitor/DefinitionCollector.php
•••••••••• 100% src/Protocol/CompletionList.php
•••••••••• 100% src/Protocol/SymbolInformation.php
•••••••••• 100% src/Protocol/Position.php
•••••••••• 100% src/Protocol/CompletionItem.php
•••••••••• 100% src/utils.php

Powered by Codecov. Last update 5077b1a...cc8365d

@mniewrzal
Copy link
Contributor

Hi, I'm trying to play with this PR but I cannot make it working. I'm using code from gif. Is there anything specific I need to do to test it?

@felixfbecker
Copy link
Owner Author

Are trying with VS Code? You need to manually patch your VS Code installation to fix the bugs in VS Code
Besides, this is WIP, maybe latest commit is broken. Are you getting any stack trace?

@mniewrzal
Copy link
Contributor

Yes, I'm using vscode 1.7.2, but without patching. Only stack trace is for $a->':

shell.ts:440 Cannot read property 'range' of null: TypeError: Cannot read property 'range' of null
    at asTextEdit (/home/wywrzal/git/vscode-php-intellisense/node_modules/vscode-languageclient/lib/protocolConverter.js:100:46)
    at /home/wywrzal/git/vscode-php-intellisense/node_modules/vscode-languageclient/lib/protocolConverter.js:95:67
    at set (/home/wywrzal/git/vscode-php-intellisense/node_modules/vscode-languageclient/lib/protocolConverter.js:83:13)
    at asCompletionItem (/home/wywrzal/git/vscode-php-intellisense/node_modules/vscode-languageclient/lib/protocolConverter.js:95:9)
    at Array.map (native)
    at asCompletionResult (/home/wywrzal/git/vscode-php-intellisense/node_modules/vscode-languageclient/lib/protocolConverter.js:76:26)
    at process._tickCallback (internal/process/next_tick.js:103:7)

If you feel its to early to test it let me know, I can wait.

@felixfbecker
Copy link
Owner Author

That is the described bug in VS Code. I have proposed a fix here: https://github.com/Microsoft/vscode-languageserver-node/pull/128/files

You need to go into ~/.vscode/extensions/felixfbecker.vscode-php-intellisense-x.x.x/node_modules/vscode-languageclient/out/protocolConverter.js and patch it there, then it works fine.

@mniewrzal
Copy link
Contributor

Ok, I will give it a try.

@mniewrzal
Copy link
Contributor

With patch completion works as described :) Thanks!

@mniewrzal
Copy link
Contributor

Ok, I will take a look at this.

@felixfbecker
Copy link
Owner Author

Never mind, fixed it. I referenced the wrong branch in composer.json. Sorry for the ping.

@mniewrzal
Copy link
Contributor

I found one issue, such code:

<?php

class Test
{
    public function getAAA()
    {
    }
    public function getBBB()
    {
    }
}
$a = new Test();
$a->|getAAA();

If you will invoke completion in place of | you will see only getAAA but also getBBB should be proposed and should replace getAAA if applied.

@mniewrzal
Copy link
Contributor

mniewrzal commented Nov 30, 2016

Another issue:

<?php
$a->
$a->|getAAA();

Invoke completion in place of | and you should see:

[Error - 10:52:03 AM] Request textDocument/completion failed.
  Message: strlen() expects parameter 1 to be string, null given
  Code: -32603 
TypeError: strlen() expects parameter 1 to be string, null given in /home/wywrzal/git/vscode-php-intellisense/vendor/felixfbecker/language-server/src/CompletionProvider.php:164
Stack trace:
#0 /home/wywrzal/git/vscode-php-intellisense/vendor/felixfbecker/language-server/src/CompletionProvider.php(164): strlen(NULL)
#1 /home/wywrzal/git/vscode-php-intellisense/vendor/felixfbecker/language-server/src/Server/TextDocument.php(241): LanguageServer\CompletionProvider->provideCompletion(Object(LanguageServer\PhpDocument), Object(LanguageServer\Protocol\Position))
#2 [internal function]: LanguageServer\Server\TextDocument->LanguageServer\Server\{closure}()
#3 /home/wywrzal/git/vscode-php-intellisense/vendor/sabre/event/lib/coroutine.php(70): Generator->send(Object(LanguageServer\PhpDocument))
#4 /home/wywrzal/git/vscode-php-intellisense/vendor/sabre/event/lib/Promise.php(242): Sabre\Event\{closure}(Object(LanguageServer\PhpDocument))
#5 /home/wywrzal/git/vscode-php-intellisense/vendor/sabre/event/lib/Loop/Loop.php(26

@felixfbecker
Copy link
Owner Author

@mniewrzal can you get the full stack trace? You need to change this line to (string)$e

@kaloyan-raev I set isIncomplete to true.

@mniewrzal
Copy link
Contributor

@felixfbecker I updated issue description.

@felixfbecker
Copy link
Owner Author

Fixed, thanks!

@mniewrzal
Copy link
Contributor

Another thing that is missing (and its important imho) is completion for types. Now its hard to get completion in many places. Simple example:

<?php
interface Example {
}
class Test implements Ex| {
}

From code I see that completion is limited to closed list of nodes. I think it would be great that if node isn't from handled list (or there is problem with parser) then result list should be as big as possible (types and keywords). It will be filtered by vscode after first letters.

@felixfbecker
Copy link
Owner Author

extends / implements is a scenario that should be supported & tested. Should be quite easy with a check for Node\Name and parent for Node\Stmt\Class where currently only Node\Expr\New_ and Node\Expr\ConstFetch are accepted. We can do these improvements in follow-up PRs though.

@mniewrzal
Copy link
Contributor

I understand that its doable but until new cases will be supported maybe it would be good approach to return 'everything' that workspace contains. It will also help with cases were parser will be not error tolerant. Of course it can be done later :)

@mniewrzal
Copy link
Contributor

Another thing that should be considered is completion filtering in server code. This PR is using substring but vscode supports filtering on client side and its 'fuzzy' filtering (e.g. in CSS bc will give border-color) which is more useful in my opinion.

In general vscode built-in language servers like CSS doesn't do filtering on server side, results list is depends on code context not on prefix. This part was left for client (at least for now).

If you feel that this is too much for this PR just merge it and I will open issues for things I mentioned in previous comments :)

@mniewrzal
Copy link
Contributor

Ok, one more thing that isn't enhancement but a bug ;) Example:

<?php
class Test
{
    public function def(){}
    public function abc()
    {
        $this->|
    }
}

No completion after ->. I need add one letter (a or d) to have proposals.

@felixfbecker
Copy link
Owner Author

The thing is that fuzzy filtering has much worse performance. Remember that we might have to iterate over 100k definitions. Checking the key for a prefix is fast. Doing strpos is slower already. Fuzzy search would be even slower. We also don't do fuzzy search in symbol search.

Is your last bug related to $this? There are tests for property access without prefix.

@mniewrzal
Copy link
Contributor

The thing is that fuzzy filtering has much worse performance. Remember that we might have to iterate over 100k definitions.

I understand, what I wanted to say is that there are many approaches:

  • leave strpos as it is
  • remove strpos filtering completely (client will filter response)
  • apply strpos only to places where performance is endangered

Is your last bug related to $this? There are tests for property access without prefix.

Yes, looks like that. I checked this:

<?php
class Test
{
    public function adef(){
        $a = new Test();
        $a->
    }
}

and works ok.

@felixfbecker
Copy link
Owner Author

Yes, but sending 100k suggestions has a performance impact too. I think the current solution is the best compromise for now.

This might be #171 then

@kaloyan-raev
Copy link
Contributor

@felixfbecker I suggest that we follow the pattern already used for the VSCode language servers implemented by Microsoft, which is:

  • Do context-based filtering, i.e. avoid returning completion item kinds that are not suited for the current cursor position.
  • Don't do input-based filtering and leave this to the client. By "input-based filtering" I mean filtering based on the current word entered by the user.
  • Return complete result lists, i.e. with isIncomplete: false

The pros of the above approach is:

  • Client can decide on the input-based filtering algorithm. The fuzzy filtering is a great usability feature and we should give the opportunity to clients to implement it.
  • Any additional typing after the initial completion request is very fast and does not involve further client-server communication.

Cons:

  • The initial completion request may take some time due to the big completion result, e.g. tens of thousands of items. This is partially mitigated by the context-based filtering and especially by the faster updates on the additional typing.

If we decide on a stricter server-based filtering to reduce the size of the completion list then:

  • We give up the fuzzy filtering, which is a pitty
  • We give up on returning complete result lists, forcing the client to send completion requests on every keystroke on further typing after the initial completion request. This has its own performance implications.

@felixfbecker
Copy link
Owner Author

What does "context-based" filtering mean? When giving completions for a method for example, we have to filter by an FQN prefix to filter by the class - so why not include the part of the typed property name too?

@kaloyan-raev
Copy link
Contributor

kaloyan-raev commented Nov 30, 2016

Example for "context-based" filtering. If you have $this->meth|, the completion list should return all visible methods and members of the current class (and its hierarchy), without filtering it by the "meth" input (the input-based filtering). Perhaps, variables should be included too to make something like $this->$my_local_var possible.

But the completion list should not include:

  • non-visible methods and members, e.g. private methods of a superclass
  • keywords
  • global functions
  • Class/Interface/Traits/Namespaces names
  • etc.

@felixfbecker
Copy link
Owner Author

yeah, gotcha. Just saying that the class-filtering is implemented atm by doing a FQN prefix search. So I don't really see the disadvantage of also excluding anything that doesn't match what is already typed? Besides fuzzy searching of course. It saves a few items being sent to the client that don't match anyway.

@kaloyan-raev
Copy link
Contributor

I wouldn't worry about the size of the completion list at that place. I don't believe a class can have thousands of methods and members.

The largest list is produced when you invoke code completion on an empty line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants