Skip to content

Global symbol search #31

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

Merged
merged 34 commits into from
Sep 30, 2016
Merged

Conversation

sunverwerth
Copy link
Contributor

@sunverwerth sunverwerth commented Sep 18, 2016

Closes #31

workspacesymbol

Parsing the whole php-language-server project takes about 10 seconds on my machine.
The php-cli process takes up about 180MB of ram with all symbol information parsed.

Changes made:

  • Added support for client:window/* commands
  • Added support for server:workspace/* commands
  • Refactored symbol database into own set of classes. (Was in Server/TextDocument before)
  • Disabled memory limit
  • ProtocolStreamReader was blocking the event loop, made non-blocking
  • Find all .php files on startup and start parsing one at a time with Loop/nextTick

To do:

  • Fix old tests
  • Add new tests
  • Reduce memory usage. Most of it is the AST, maybe we can throw it away after parsing,
  • Figure out solution for blocking STDIN. The current one is a hack. This is a windows limitation, see https://bugs.php.net/bug.php?id=34972
  • Extract URI generation into utils.php
  • Investigate escaping Parser Exception
  • Open issue at PhpParser regarding Lexer exceptions
  • Open issue at php-intellisense regarding OS switch
  • Upgrade Sabre/event dependency (4.0.0)
  • Can use NameResolver for generating FQN containerNames

@sunverwerth sunverwerth mentioned this pull request Sep 18, 2016
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.

I like this a lot :)
The indexing will help implementation of #9 aswell

There are test failures coming from incorrect parameters (type hint fails), cannot get coverage report until tests pass.
Also needs at least a simple test of course for the workspace symbol request.

Regarding memory usage: 180MB is not that much compared to Chome RAM usage these days 😄
If it really imposes a problem we could serialize() the PHPDocument objects, limit the in-memory index (configurable) and if the index is full, write to disk with a FIFO or LRU strategy.

Regarding blocking calls: I think the blocking issue comes from using nextTick instead of setTimeout see, my comments. The windows limitation seems to only be that it cannot read single chars in a non-blocking way, only full lines. But that shouldn't apply to us as a request always comes in in one piece and spans multiple lines. As long as we leverage the event loop correctly this should work.
If the indexing is really too blocking, we could spawn a separate process (it doesn't need to communicate, just exit when it finished).

Could you comment how large the project was that took 10 seconds (amount of files, SLOC)?

It would be nice if we could compare this to other implementations, namely crane, PHPStorm, PHPTools for VS.

@@ -69,11 +82,18 @@ public function __construct(ProtocolReader $reader, ProtocolWriter $writer)
*/
public function initialize(string $rootPath, int $processId, ClientCapabilities $capabilities): InitializeResult
Copy link
Owner

@felixfbecker felixfbecker Sep 18, 2016

Choose a reason for hiding this comment

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

I wasn't aware $rootPath can be null, need to remove the type hint then and adapt the @param. Or even better, move it to the end as a parameter and give it default value of null. There should also be a test for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. I didn't know the dispatcher figures out the correct parameter position, cool!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

$processFile = function() use (&$fileList, &$processFile, &$rootPath){
if ($file = array_pop($fileList)) {

$uri = 'file://'.(substr($file, -1) == '/' || substr($file, -1) == '\\' ? '' : '/').str_replace('\\', '/', $file);
Copy link
Owner

Choose a reason for hiding this comment

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

We should make a helper function for this, will need it more often. I am for adding a utils.php with plain functions that are autoloaded through composer.json files property

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good to me. Will also add a test for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

use PhpParser\PrettyPrinter\Standard as PrettyPrinter;
use PhpParser\NodeVisitor\NameResolver;

class PhpDocument
Copy link
Owner

Choose a reason for hiding this comment

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

👍 for this class, wanted to do that at one point aswell

$stmts = $this->parser->parse($content);
}
catch(Error $e) {
// Parser still throws errors. e.g for unterminated comments
Copy link
Owner

Choose a reason for hiding this comment

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

Is this really true? If yes, isn't that a bug in PHPParser?
Probably also shouldn't catch all Errors but only Exception or PHPParser's Exception type

Copy link
Contributor Author

@sunverwerth sunverwerth Sep 18, 2016

Choose a reason for hiding this comment

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

I found this weird as well. I had the extension crash multiple times while writing a block comment.
But you are right, we should probably catch this specific exception and add it as a parse error.

Copy link
Owner

Choose a reason for hiding this comment

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

We clearly set ['throwOnError' => true] so Imo it should never throw. If it does, could you open an issue at PHPParser, posting the exception? We are using the 3.0 alpha so bugs can happen.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is probably a misunderstanding. The comment indicates that this setting only prevents the parser from bailing out with an exception on the first parse error and instructs it to continue parsing anyway. The exception here seems to originate from the Lexer.


class Project
{
private $documents;
Copy link
Owner

Choose a reason for hiding this comment

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

Need some @var annotations here and document this is an associative array of URI => document

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

$class = get_class($node);
if (!isset(self::NODE_SYMBOL_KIND_MAP[$class])) {
return;
}

// if we enter a method or function, increase the function counter
if ($class === Node\Stmt\Function_::class || $class === Node\Stmt\ClassMethod::class) {
Copy link
Owner

Choose a reason for hiding this comment

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

Why not instanceof here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No particular reason, just reused the $class variable. Instanceof would probably make the purpose clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


// if we leave a method or function, decrease the function counter
$class = get_class($node);
if ($class === Node\Stmt\Function_::class || $class === Node\Stmt\ClassMethod::class) {
Copy link
Owner

Choose a reason for hiding this comment

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

Also here, why not 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.

No particular reason, just reused the $class variable. Instanceof would probably make the purpose clearer.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


$this->project->getDocument($uri)->updateAst(file_get_contents($file));

Loop\nextTick($processFile);
Copy link
Owner

@felixfbecker felixfbecker Sep 18, 2016

Choose a reason for hiding this comment

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

I think this should be setTimeout. nextTick will queue the callback before any I/O callbacks. setTimeout on the other hand will push it to the end of the queue, first executing all other callbacks (like the STDIN handling for example), then getting back to processing. See this note from the Node docs:

This is not a simple alias to setTimeout(fn, 0), it's much more efficient. It runs before any additional I/O events (including timers) fire in subsequent ticks of the event loop.
Note: the next tick queue is completely drained on each pass of the event loop before additional I/O is processed. As a result, recursively setting nextTick callbacks will block any I/O from happening, just like a while(true); loop.

The current way would mean that I/O is blocked (including STDIN), so the language server would become unresponsive until the indexing is finished.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. SetTimeout would also allow dynamic throttling later.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

public function findSymbols(string $query)
{
return array_filter($this->symbols, function($symbol) use(&$query) {
return stripos($symbol->name, $query) !== false;
Copy link
Owner

Choose a reason for hiding this comment

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

Fuzzy searching would be nice for a future update

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 tried to 😀 maybe you have a link?

Copy link
Owner

Choose a reason for hiding this comment

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

I would not implement it myself but look for a library from composer that we can integrate (or improve so it can be integrated here).

* @param string $rootPath The rootPath of the workspace. Is null if no folder is open.
* @return void
*/
private function indexProject(string $rootPath)
Copy link
Owner

Choose a reason for hiding this comment

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

I think you cannot pass null if you type hint it as string. It needs to be string $rootPath = null then.

Copy link
Contributor Author

@sunverwerth sunverwerth Sep 18, 2016

Choose a reason for hiding this comment

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

Shouldn't we check this at the calling site? Indexing a null path doesn't make sense, after all.
I would then adjust the param

Copy link
Owner

Choose a reason for hiding this comment

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

Yeah, you're right. I just went by the @param desc


$numFiles = count($fileList);
if (($numFiles % 100) == 0) {
$this->client->window->logMessage(3, $numFiles.' PHP files remaining.');
Copy link
Owner

Choose a reason for hiding this comment

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

Would be nice to log the file name aswell

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you mean? Log the name of the next file to be parsed?

Copy link
Owner

@felixfbecker felixfbecker Sep 18, 2016

Choose a reason for hiding this comment

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

Yeah something like

Parsing src\LanguageServer.php (1/100)
Parsing src\LanguageClient.php (2/100)
...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done, also added a memory diagnostic message

@sunverwerth
Copy link
Contributor Author

I like this a lot :)

Thanks :)

Could you comment how large the project was that took 10 seconds (amount of files, SLOC)?

It was this project :) php-language-server with all dependencies included. I don't know the LOC but it has around 1300 Files.

Regarding memory usage

I also think 180MB is fine, however there is room for improvement.

Regarding blocking calls

Maybe I am just overthinking this. I will test on a linux machine tomorrow and report back.

@mniewrzal
Copy link
Contributor

Hi,
This PR looks very nice :) I'm still trying to test is on Linux but for large projects (1k files) indexing is not finishing and for smaller project I cannot get results;) I will try to investigate this and I will let you know.

I want just add a comment about memory consumption. Right now you are talking about relatively small project (1k-2k files). If amount of memory will grow linear then for larger project (e.g. magento 20k+) this value will be extremely high. I think keeping AST for all files from workspace is unnecessary because most of the time such detailed information aren't needed. Small, light model (abstract layer like namespace/class/method) created from AST would be also more useful from code readability point of view.

@felixfbecker
Copy link
Owner

felixfbecker commented Sep 18, 2016

@mniewrzal Tests are failing currently, I don't expect it to work 😄

I want just add a comment about memory consumption. Right now you are talking about relatively small project (1k-2k files). If amount of memory will grow linear then for larger project (e.g. magento 20k+) this value will be extremely high. I think keeping AST for all files from workspace is unnecessary because most of the time such detailed information aren't needed. Small, light model (abstract layer like namespace/class/method) created from AST would be also more useful from code readability point of view.

That's why I asked how big the project is this was tested with. I think we could add to the PHPDocument class some properties that keep the nodes that are relevant to us. We could then set the $stmts to null and only reload it when we need it. This would also result in faster lookup than walking the whole AST every time.

Also see my comment about how we could limit the PHPDocument objects in memory and then either remove them or serialize and write them to a cache on disk, what do you think about this?

@codecov-io
Copy link

codecov-io commented Sep 18, 2016

Current coverage is 91.72% (diff: 92.15%)

Merging #31 into master will increase coverage by 1.77%

@@             master        #31   diff @@
==========================================
  Files            14         19     +5   
  Lines           189        290   +101   
  Methods          30         45    +15   
  Messages          0          0          
  Branches          0          0          
==========================================
+ Hits            170        266    +96   
- Misses           19         24     +5   
  Partials          0          0          
Diff Coverage File Path
••••• 58% new src/Client/Window.php
•••••• 66% src/ProtocolStreamWriter.php
••••••••• 94% new src/PhpDocument.php
•••••••••• 100% src/SymbolFinder.php
•••••••••• 100% src/ProtocolStreamReader.php
•••••••••• 100% src/LanguageServer.php
•••••••••• 100% new src/utils.php
•••••••••• 100% new src/Project.php
•••••••••• 100% new src/Server/Workspace.php
•••••••••• 100% src/Server/TextDocument.php

Review all 11 files changed

Powered by Codecov. Last update bc2d6b9...a9c8f9c

@sunverwerth
Copy link
Contributor Author

Hold your horses. 😄 I just pushed some fixes, so I can test tomorrow.

@mniewrzal I think most of the AST is of little interest to us, anyway. The only situation where the full AST is needed is code formatting, but the user expects this to be relatively slow, so we should be able to reparse in this case.

@felixfbecker I had to adjust the expected result for the testDocumentSymbol test. Both methods and properties should have their class as containerName, right?

I reverted the fgetc logic for now. It doesn't work on windows anymore (only parses a file after some message arrived) but if I understand the behaviour it should work now under linux. I will test this tomorrow...

@mniewrzal
Copy link
Contributor

That's why I asked how big the project is this was tested with. I think we could add to the PHPDocument class some properties that keep the nodes that are relevant to us. We could then set the $stmts to null and only reload it when we need it. This would also result in faster lookup than walking the whole AST every time.

AST node contains lot of redundant information. At the beginning collecting only some of nodes would be ok but when more functions will be added to LS and memory usage will grow then I believe nodes should be transform into something more optimal. Parsing single file is fast and in many places can be done on demand.

Also see my comment about how we could limit the PHPDocument objects in memory and then either remove them or serialize and write them to a cache on disk, what do you think about this?

I agree. Mechanism for storing collected data should be added later. Not only to reduce amount of used memory but also to avoid parsing after every initialization. Just not sure at the moment what will be the best approach ;-)

@felixfbecker
Copy link
Owner

felixfbecker commented Sep 18, 2016

What kind of structure would you propose? Just as a reminder, we also need to save stuff from the docblocks in addition to the node. We need

  • FQN as key
  • SymbolKind
  • comment / description
  • for functions, signature for signature help (parameters with type and description)

The protocol has structures to represent all these so it makes sense to convert the AST / PHPDocumentator objects to these structures directly and only save those.

It might actually make sense though to save the FQN => symbol map at the Project object because we want to look up an FQN globally without iterating over all PHPDocument objects.

* @param int $processId The process Id of the parent process that started the server.
* @param ClientCapabilities $capabilities The capabilities provided by the client (editor)
* @param string $rootPath The rootPath of the workspace. Is null if no folder is open.
Copy link
Owner

Choose a reason for hiding this comment

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

The type here must be string|null (this is significant for the dispatcher)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed

@mniewrzal
Copy link
Contributor

Yes, using protocol structures would be easiest solution. Of course if Symbolinformation/SignatureInfromation/... can store all necessary data. Also its possible that the same information will be duplicated across this structures. At this level I think it will be suitable. For later I'm thinking more about one structure, simpler and much smaller than AST, easy to produce protocol structures from it. For advanced code assist (type detection/type inference) language server need to collect many things like:

  • inheritance
  • access modifiers
  • phpDoc tags

But as I wrote this is future. Before that this PR will be great extension to language server capabilities :-)

* @return string
*/
function pathToUri(string $filepath): string {
return 'file://'.($filepath[0] == '/' || $filepath[0] == '\\' ? '' : '/').str_replace('\\', '/', $filepath);
Copy link
Owner

Choose a reason for hiding this comment

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

I think this implementation is not correct. A file URI must always start with file:/// (triple slash). In your case, you are checking if the first char is a backslash, but on windows it could be a drive letter. Also, special chars need to be URL-encoded. There is a very good implementation I've been using on NPM: https://github.com/sindresorhus/file-url/blob/master/index.js#L17-L24

Copy link
Owner

Choose a reason for hiding this comment

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

@felixfbecker
Copy link
Owner

I wonder how we can test the Windows-blocking issue?

@sunverwerth
Copy link
Contributor Author

sunverwerth commented Sep 21, 2016

I did a quick test with communicating over a socket. This worked flawlessly. We should put an OS switch into the client side script and spawn the Server with an argument, telling it to connect through a socket.

@felixfbecker
Copy link
Owner

But if this really is a Windows issue, not a PHP one, then how do all the other language servers solve this? TCP has a lot of overhead compared to STDIO.

@sunverwerth
Copy link
Contributor Author

sunverwerth commented Sep 21, 2016

I think it is kind of both, but I am really not sure. stdin on windows behaves differently than on *nix but php doesn't include a workaround to be able to treat it as a normal stream. I tested the code on linux and didn't have the problems as on Windows.
I think the other LS are using environments where these workarounds are implemented... but who knows.

Loopback sockets don't seem to be so bad performance-wise after all.

I'm really out of Ideas here.

Personally, I didn't notice any difference in performance.

@felixfbecker
Copy link
Owner

This SO answer states that you can read STDIN non-blocking in Windows on a line-by-line basis, which is fine for our usecase as we don't need to process individual chars, only full responses. Maybe we have to look at the implementation of addReadStream?

$server = new LanguageServer(new ProtocolStreamReader(STDIN), new ProtocolStreamWriter(STDOUT));
if (count($argv) >= 3 && $argv[1] == '--tcp') {
$address = $argv[2];
$socket = stream_socket_client('tcp://' . $address, $errno, $errstr);
Copy link
Owner

Choose a reason for hiding this comment

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

This means the IDE has to open a socket on its side, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. extension.js opens a listen socket at 127.0.0.1:random_port and immediately stops listening when the language server connects.

Copy link
Owner

Choose a reason for hiding this comment

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

I don't get the second part of your sentence, why does it have to stop listening? I though it would then solely communicate through the socket?

Copy link
Contributor Author

@sunverwerth sunverwerth Sep 22, 2016

Choose a reason for hiding this comment

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

Only the listen socket is closed, the established connection keeps going. It just won't allow any additional connections after the first one.

Copy link
Owner

Choose a reason for hiding this comment

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

Ah okay so the server should only accept a single connection

@sunverwerth
Copy link
Contributor Author

sunverwerth commented Sep 22, 2016

This SO answer states that you can read STDIN non-blocking in Windows on a line-by-line basis.

The problem ist that stream_select blocks on windows using STDIN when no data is available, even when timeout is set to 0.

@felixfbecker
Copy link
Owner

@madhed are you working on more tests? Would like to base more implementations on your refactoring :)

@sunverwerth
Copy link
Contributor Author

@felixfbecker I am :) just couldn't work on it the last few days. I hope to push a ready version later today.

@felixfbecker
Copy link
Owner

felixfbecker commented Sep 29, 2016

Tested this an looks good to me! There was one case though were I met this error here:

Notice: fwrite(): send of 8192 bytes failed with errno=10035 Non-blocking socket task could not be executed.
 in C:\Users\felix\git\OpenSource\php-language-server\src\ProtocolStreamWriter.php on line 28
Call Stack:
    0.2230     370016   1. {main}() C:\Users\felix\git\OpenSource\php-language-server\bin\php-language-server.php:0
    0.2869    4141816   2. Sabre\Event\Loop\run() C:\Users\felix\git\OpenSource\php-language-server\bin\php-language-server.php:34
    0.2869    4141816   3. Sabre\Event\Loop\Loop->run() C:\Users\felix\git\OpenSource\php-language-server\vendor\sabre\event\lib\Loop\functions.php:122
  129.3512   19208048   4. Sabre\Event\Loop\Loop->tick() C:\Users\felix\git\OpenSource\php-language-server\vendor\sabre\event\lib\Loop\Loop.php:194
  129.3512   19208048   5. Sabre\Event\Loop\Loop->runStreams() C:\Users\felix\git\OpenSource\php-language-server\vendor\sabre\event\lib\Loop\Loop.php:233
  134.4875   19208552   6. LanguageServer\ProtocolStreamReader->LanguageServer\{closure}() C:\Users\felix\git\OpenSource\php-language-server\vendor\sabre\event\lib\Loop\Loop.php:311
  134.4893   19210672   7. LanguageServer\LanguageServer->LanguageServer\{closure}() C:\Users\felix\git\OpenSource\php-language-server\src\ProtocolStreamReader.php:52
  134.6761   19477648   8. LanguageServer\ProtocolStreamWriter->write() C:\Users\felix\git\OpenSource\php-language-server\src\LanguageServer.php:69
  134.6949   33229832   9. fwrite() C:\Users\felix\git\OpenSource\php-language-server\src\ProtocolStreamWriter.php:28

Any idea how we can prevent this?

@sunverwerth
Copy link
Contributor Author

sunverwerth commented Sep 29, 2016

Tested this an looks good to me! There was one case though were I met this error here: [...]

Ugh... didn't even think about this.
Since the connection is now nonblocking for input and output we need to buffer the outgoing messages.

Edit: will write a fix.

@sunverwerth
Copy link
Contributor Author

Can you reproduce the StreamWriter bug? It should be fixed now.

$totalBytesWritten = 0;

while ($totalBytesWritten < $msgSize) {
$bytesWritten = fwrite($this->output, substr($data, $totalBytesWritten));
Copy link
Owner

Choose a reason for hiding this comment

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

We should suppress the notice triggered with @

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.
What about the exception? Should we throw another type? Another message?

throw new Error('Could not write message.');
}
$totalBytesWritten += $bytesWritten;
}
Copy link
Owner

Choose a reason for hiding this comment

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

Could you explain a bit more what you are doing here? As I understand we block until everything is written out, is this intended? Should we use a setTimeout or nextTick here?

Also, Error is reserved for PHP internal errors, should be Exception

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we write more data than the output buffer can hold we get partial writes, so we loop until all data is written.
This didn't happen before because in blocking mode this loop is handled internally.

We need to do this synchronously because otherwise we could end up with partial messages intermingled with each other.

Will change to Exception

@felixfbecker
Copy link
Owner

@madhed Do you feel this is ready to merge?

I also did some final benchmark. Indexing this project on a Surface Pro 3 (i5-4300U):

  • 1299 files, ~13000 physical lines of code
  • 41 seconds, 64 MiB memory
  • 0.032s / file, 0.0031s / PLOC
  • 0.0493 MiB / file, 0.0049 MiB / PLOC

@sunverwerth
Copy link
Contributor Author

@felixfbecker I am not aware of any remaining bugs, so if you want, go ahead.

I am also using this version productively on an internal PHP Project with 2613 files and had no crashes or anything.

Even though some features need improvement I think this will serve as a great starting point for future development.

@felixfbecker
Copy link
Owner

@Ahmad-Alhourani that question would be better suited for StackOverflow as a general question about LSP / JSON RPC.

@felixfbecker
Copy link
Owner

I don't know what you mean by "protocole doesn't call the method". I am sorry that you are having issues but it is really hard for me to help you, and this is not a support forum, it is an issue tracker. To be precise, this specific thread you are on is a 2 year old pull request. Please ask this in an appropriate forum like StackOverflow. More people can help you there and future readers can more easily find the answers.

Repository owner locked as off-topic and limited conversation to collaborators Sep 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants