Skip to content

Signature help #438

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

Closed
wants to merge 12 commits into from
Closed

Signature help #438

wants to merge 12 commits into from

Conversation

vakata
Copy link
Contributor

@vakata vakata commented Jul 14, 2017

Initial idea to start the discussion (still needs work)

@vakata
Copy link
Contributor Author

vakata commented Jul 14, 2017

Currently this PR is not ready for merging - I want to get some opinions on the approach and fix as necessary.

There is one major problem I noticed so far - the currently active parameter is not properly determined (as soon as something is written it shifts one parameter to the right) - I would like some help with that if someone can spare the time and have a look, as I am not very familiar with tolerant parser's objects.
UPDATE: I managed to improve the situation a bit, please review.

One thing I definitely noticed - this implementation is way cleaner than the one with PhpParser.

@codecov
Copy link

codecov bot commented Jul 14, 2017

Codecov Report

Merging #438 into master will increase coverage by 0.57%.
The diff coverage is 95.83%.

@@             Coverage Diff              @@
##             master     #438      +/-   ##
============================================
+ Coverage     79.38%   79.96%   +0.57%     
- Complexity      837      867      +30     
============================================
  Files            56       60       +4     
  Lines          1979     2051      +72     
============================================
+ Hits           1571     1640      +69     
- Misses          408      411       +3
Impacted Files Coverage Δ Complexity Δ
src/Definition.php 100% <ø> (ø) 6 <0> (ø) ⬇️
src/Server/TextDocument.php 75.91% <100%> (+0.91%) 56 <1> (+1) ⬆️
src/Protocol/SignatureInformation.php 100% <100%> (ø) 1 <1> (?)
src/Protocol/SignatureHelp.php 100% <100%> (ø) 1 <1> (?)
src/DefinitionResolver.php 84.31% <100%> (+0.26%) 307 <0> (+5) ⬆️
src/LanguageServer.php 77.87% <100%> (+0.39%) 27 <0> (ø) ⬇️
src/Protocol/ParameterInformation.php 100% <100%> (ø) 1 <1> (?)
src/SignatureHelpProvider.php 92.85% <92.85%> (ø) 21 <21> (?)
... and 2 more

Copy link
Owner

@felixfbecker felixfbecker left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very good and is what I had in mind for the implementation (no use of lexer/tokenizer).

@@ -234,6 +235,18 @@ public function createDefinitionFromNode(Node $node, string $fqn = null): Defini
$def->documentation = $this->getDocumentationFromNode($node);
}

$def->parameters = [];
if (property_exists($node, 'parameters') && $node->parameters) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you do this without reflection? i.e. instanceof

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will need @roblourens help for this - I have no idea what to check for. From what I gather it is a trait.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@roblourens could you add an interface for FunctionHeader or callables so this can be checked for with instanceof?

if (!($node instanceof ArgumentExpressionList) &&
!($node instanceof CallExpression)
) {
return new SignatureHelp;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A signature without label is not valid according to the docblock

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no label property in the SignatureHelp object - maybe I am missin something - please explain? This is supposed to be an empty result (no nested SignatureHelpInformation objects).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@felixfbecker please provide further information on how I can help improve this? I am still not sure what the issue is.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I confused it with the SignatureInformation class

return new SignatureHelp(
[
new SignatureInformation(
trim(str_replace(['public', 'protected', 'private', 'function', 'static'], '', $def->declarationLine)),
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't like the string manipulation here, you could have a parameter $function. I would rather extend Definition with another property or method (if possible, without saving new duplicate data to save RAM)

@@ -97,6 +98,12 @@ class Definition
* @var string
*/
public $documentation;
/**
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add an empty line above this

//var_dump($param); die();
$def->parameters[] = new ParameterInformation(
$this->getDeclarationLineFromNode($param),
//$param->getName(), // TODO: rebuild this
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was old code - I am cleaning it up.

@@ -411,4 +416,12 @@ public function xdefinition(TextDocumentIdentifier $textDocument, Position $posi
return [new SymbolLocationInformation($descriptor, $def->symbolInformation->location)];
});
}

public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a docblock for this method with the description from the protocol spec (makes it easier for newcomers to understand the purpose of the method)

@felixfbecker
Copy link
Owner

Maybe @roblourens can help with the parser specifics, he knows more about it than me. Maybe the parameters array gets a MissingToken so the count gets confused?

@vakata
Copy link
Contributor Author

vakata commented Jul 15, 2017

The count should be OK now, but I will need @roblourens help with the property_exists issue.

@vakata
Copy link
Contributor Author

vakata commented Jul 15, 2017

I am afraid the failing build is not my fault - please advise on how to proceed?
The error is in the parse-stubs script (while parsing ...phpstorm-stubs/SplType/SplType.php):

Class 'phpDocumentor\Reflection\Types\Mixed' not found

I do not get this error locally.

@felixfbecker
Copy link
Owner

weird

@roblourens
Copy link
Contributor

I'm not sure what you mean by "the property_exists issue". Your change from the last commit seems good to me - I think it's better to explicitly check the type with instanceof instead of assuming that any node with parameters is the type you want.

@felixfbecker
Copy link
Owner

@roblourens it would be nice if checking for a callable, function-like node only required a single instanceof check for an interface instead of for every possible concrete implementation. PHPParser had a FunctionLike interface for this. Given TolerantParser uses a trait for to introduce the parameters property, it would make sense to have an interface that the classes that use the trait implement

@vakata
Copy link
Contributor Author

vakata commented Jul 19, 2017

I've been using this branch for a few days now. Issues I've noticed so far:

  • rarely the parameters can not be counted and there is no active parameter highlighted (but I have not been able to reproduce this in a test so far)
  • the position is sometimes off array_reverse(|explode( gives signature help for explode
  • sometimes there is a minimal delay when moving the cursor fast

I will look into the second issue - I will create a test and try to fix it.

It would be great if someone else also tried this branch, just so that we can gather opinions (although subjective) about performance - I am using a Surface Pro 4 (i5, 8GB) and experience no issues, but it may be different for someone else.

@roblourens
Copy link
Contributor

roblourens commented Jul 19, 2017

I do see some relevant helpers in ParserHelpers.php, isFunctionLike does the same check. Maybe that should be in the parser.

@felixfbecker
Copy link
Owner

I think an interface would be more useful for static analysis if we ever implement type narrowing through instanceof like TS does (#168)

@felixfbecker
Copy link
Owner

@vakata do you feel this is stable enough to release?

{
$parts = explode('(', $declaration, 2);
$name = array_reverse(explode(' ', trim($parts[0])))[0];
return $name . '(' . $parts[1];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this method looks like it would ignore many edge cases, for example ( or spaces inside a default string value.
Why attempt to explode a declaration string when we have a parser available? I would add the wanted output to the Definition class

Copy link
Contributor Author

@vakata vakata Jul 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am absolutely certain that this code does not ignore any edge cases - the first ( in a declaration is always after the function name. The function name is always the last token after all modifiers.

I am not sure how to change the current implementation in a robust way - all I need is to remove the modifiers and leave the function name, but keep all parameters (along with their type and default values). Doing this using the parser would require quite a huge block of code (from what I gather) especially since there is no way to pretty print the result.

Basically I would be reconstructing the already available declaration using string concatenation by inspecting the underlying AST just to remove the public / private / abstract / static / function keywords that precede the function name.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, maybe I'm was just unable to follow this code.

  • you split the declaration into two parts, the part before the ( (function name and keywords) and the rest
  • you trim whitespace from the first part (function name and keywords)
  • you split the first part by (function name and keywords) by spaces
  • you reverse the whole array
  • you take the first (initially last) element (function name)
  • you concatenate the function name, ( and the rest

I am pretty sure end() is faster to get the last array element than reversing the whole array and taking the first element

Copy link
Contributor Author

@vakata vakata Jul 28, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

end requires a reference, which would mean creating a middle step and allocating a separate variable for the array. I am not very familiar with PHP internals, but I believe this means allocating storage in the heap, instead of the stack, so as far as optimization goes - I am not sure which will be faster and more memory efficient. Anyway - this really is a micro-optimization. I will create a few tests and change it if necessary.

EDIT: For what it is worth - I created a very crude test and at least for my configuration using a temporary array and end is on average about 3% slower.

@vakata
Copy link
Contributor Author

vakata commented Jul 28, 2017

@felixfbecker as I mentioned in my previous comment:

  1. There is a known issue where the position is not calculated correctly. One could argue that any signature help (even with a few edge cases not handled) is better than no signature help. Personally I think the issue should be resolved before merging, but it is ultimately your call. I will have some time to work on this issue next week.
  2. Performance-wise: I use this branch on a daily basis and have not experienced any issues, but I would be a lot more comfortable if someone else also tested and shared their experience.

@felixfbecker
Copy link
Owner

I don't work on any PHP project currently so I can't do any real usage testing. I don't think a lot of people will try it out by cloning this branch, you could build a .vsix though. Otherwise I would just release it and let people try it out.

@vakata
Copy link
Contributor Author

vakata commented Jul 28, 2017

If you feel the issue I described above is minor enough I am OK with publishing. I am not sure many people would download the .vsix anyway. If this PR is merged I will still try to fix the issue next week in a separate PR.

@jens1o
Copy link
Contributor

jens1o commented Aug 7, 2017

well, if somebody builds me such a file or lets me know how I do that, I'm happy to test it. :)

@felixfbecker
Copy link
Owner

@vakata any update?

@felixfbecker
Copy link
Owner

@vakata are you still working on this?

@vakata
Copy link
Contributor Author

vakata commented Oct 20, 2017

@felixfbecker no, I haven't had the time to do any further work. Actually the only thing that was left was the occasional slip up in position calculation, but I doubt I will have time any time soon to work on this.

@jens1o
Copy link
Contributor

jens1o commented Nov 15, 2017

Could I do something to help you? @vakata

@vakata
Copy link
Contributor Author

vakata commented Nov 20, 2017

@felixfbecker @jens1o sorry I was not around sooner. Currently the only issue I have with this PR is the occasional position miscalculation, but I doubt I will have time to work on it any time soon. I guess the most helpful thing would be for someone else beside me to test this change and share if all worked fine.

@phil-nelson phil-nelson mentioned this pull request Dec 4, 2017
@felixfbecker
Copy link
Owner

Closing in favor of #547

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