From 66fc210b25f8b78a6448e013de837833cb1f753b Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Mon, 24 Feb 2025 02:41:23 +0000 Subject: [PATCH 01/19] Use futures more efficiently --- server/lib/PLS/Parser/Index.pm | 9 +-- server/lib/PLS/Server.pm | 77 ++++++++++++------- .../lib/PLS/Server/Request/CancelRequest.pm | 8 +- server/lib/PLS/Server/Request/Initialized.pm | 12 +-- .../Server/Request/TextDocument/DidChange.pm | 7 +- .../Server/Request/TextDocument/DidOpen.pm | 2 +- .../TextDocument/PublishDiagnostics.pm | 8 +- .../Workspace/DidChangeWatchedFiles.pm | 6 +- .../Workspace/DidChangeWorkspaceFolders.pm | 7 +- .../lib/PLS/Server/Response/DocumentSymbol.pm | 51 ++---------- 10 files changed, 88 insertions(+), 99 deletions(-) diff --git a/server/lib/PLS/Parser/Index.pm b/server/lib/PLS/Parser/Index.pm index 05bc87a1..a57aba8f 100644 --- a/server/lib/PLS/Parser/Index.pm +++ b/server/lib/PLS/Parser/Index.pm @@ -178,7 +178,7 @@ sub index_files return Future->done(@futures); } - )->retain(); + ); } ## end sub index_files sub get_all_perl_files_async @@ -221,16 +221,15 @@ sub index_workspace my ($self, $path) = @_; push @{$self->workspace_folders}, $path; + @{$self->workspace_folders} = List::Util::uniq @{$self->workspace_folders}; - $self->get_all_perl_files_async($path)->then( + return $self->get_all_perl_files_async($path)->then( sub { my ($workspace_uris) = @_; return $self->index_files(@{$workspace_uris}); } - )->then(sub { Future->wait_all(@_) })->retain(); - - return; + )->then(sub { Future->wait_all(@_) }); } ## end sub index_workspace sub cleanup_file diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 3f9e45de..4018fa80 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -88,7 +88,7 @@ sub run $self->handle_client_message($content); return 1; - }; + }; ## end sub } ); @@ -149,14 +149,10 @@ sub send_server_request } elsif ($request->isa('Future')) { - $request->on_done( - sub { - my ($request) = @_; - - $self->handle_server_request($request); - } - )->retain(); - } ## end elsif ($request->isa('Future'...)) + $request = $request->get(); + $self->handle_server_request($request); + } + return; } ## end sub send_server_request @@ -167,7 +163,7 @@ sub send_message return if (not blessed($message) or not $message->isa('PLS::Server::Message')); my $json = $message->serialize(); my $length = length ${$json}; - $self->{stream}->write("Content-Length: $length\r\n\r\n$$json")->retain(); + $self->{stream}->write("Content-Length: $length\r\n\r\n$$json")->get(); return; } ## end sub send_message @@ -178,28 +174,38 @@ sub handle_client_request my $response = $request->service($self); - if (blessed($response)) + if (not blessed($response)) + { + return; + } + + if ($response->isa('PLS::Server::Response')) { - if ($response->isa('PLS::Server::Response')) + $self->send_message($response); + } + elsif ($response->isa('Future')) + { + my $id = $request->{id}; + + if (length $id) { - $self->send_message($response); + $self->{running_futures}{$id} = $response; } - elsif ($response->isa('Future')) + + $response->await(); + + if ($response->is_cancelled) { - $self->{running_futures}{$request->{id}} = $response if (length $request->{id}); - - $response->on_done( - sub { - my ($response) = @_; - $self->send_message($response); - } - )->on_cancel( - sub { - $self->send_message(PLS::Server::Response::Cancelled->new(id => $request->{id})); - } - ); - } ## end elsif ($response->isa('Future'...)) - } ## end if (blessed($response)...) + $response = PLS::Server::Response::Cancelled->new(id => $id); + } + else + { + $response = $response->result; + } + + delete $self->{running_futures}{$id}; + $self->send_message($response); + } ## end elsif ($response->isa('Future'...)) return; } ## end sub handle_client_request @@ -232,7 +238,6 @@ sub handle_server_request $self->{pending_requests}{$request->{id}} = $request; } - delete $self->{running_futures}{$request->{id}} if (length $request->{id}); $self->send_message($request); return; } ## end sub handle_server_request @@ -245,6 +250,20 @@ sub handle_server_response return; } ## end sub handle_server_response +sub cancel_request +{ + my ($self, $id) = @_; + + my $future = delete $self->{running_futures}{$id}; + + if (blessed($future) and $future->isa('Future')) + { + $future->cancel(); + } + + return; +} ## end sub cancel_request + sub stop { my ($self, $exit_code) = @_; diff --git a/server/lib/PLS/Server/Request/CancelRequest.pm b/server/lib/PLS/Server/Request/CancelRequest.pm index cbd90765..6947cb2b 100644 --- a/server/lib/PLS/Server/Request/CancelRequest.pm +++ b/server/lib/PLS/Server/Request/CancelRequest.pm @@ -23,13 +23,7 @@ sub service my ($self, $server) = @_; my $id = $self->{params}{id}; - return unless (exists $server->{running_futures}{$id}); - my $request_to_cancel = $server->{running_futures}{$id}; - - return unless (blessed($request_to_cancel) and $request_to_cancel->isa('Future')); - $request_to_cancel->cancel(); - - delete $server->{running_futures}{$id}; + $server->cancel_request($id); return; } ## end sub service diff --git a/server/lib/PLS/Server/Request/Initialized.pm b/server/lib/PLS/Server/Request/Initialized.pm index 8097ff6e..8fb8bc93 100644 --- a/server/lib/PLS/Server/Request/Initialized.pm +++ b/server/lib/PLS/Server/Request/Initialized.pm @@ -86,7 +86,7 @@ sub index_files_without_progress { my (undef, $index) = @_; - $index->index_files()->then(sub { Future->wait_all(@_) })->retain(); + $index->index_files()->then(sub { Future->wait_all(@_) })->get(); return; } ## end sub index_files_without_progress @@ -115,9 +115,11 @@ sub index_files_with_progress my $done = 0; my $total = scalar @futures; + my @new_futures; + foreach my $future (@futures) { - $future->then( + push @new_futures, $future->then( sub { my ($file) = @_; @@ -133,10 +135,10 @@ sub index_files_with_progress ) ); } - )->retain(); + ); } ## end foreach my $future (@futures...) - return Future->wait_all(@futures)->then( + return Future->wait_all(@new_futures)->then( sub { $server->send_server_request( PLS::Server::Request::Progress->new( @@ -149,7 +151,7 @@ sub index_files_with_progress } ); } - )->retain(); + )->get(); return; } ## end sub index_files_with_progress diff --git a/server/lib/PLS/Server/Request/TextDocument/DidChange.pm b/server/lib/PLS/Server/Request/TextDocument/DidChange.pm index 31192192..4e160c83 100644 --- a/server/lib/PLS/Server/Request/TextDocument/DidChange.pm +++ b/server/lib/PLS/Server/Request/TextDocument/DidChange.pm @@ -51,11 +51,12 @@ sub service $timers{$uri} = IO::Async::Timer::Countdown->new( delay => 2, on_expire => sub { - my $index = PLS::Parser::Index->new(); - $index->index_files($uri)->then(sub { Future->wait_all(@_) })->retain(); + delete $timers{$uri}; $server->send_server_request(PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $uri)); - delete $timers{$uri}; + + my $index = PLS::Parser::Index->new(); + $index->index_files($uri)->then(sub { Future->wait_all(@_) })->get(); }, remove_on_expire => 1 ); diff --git a/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm b/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm index fa8d9afb..e221ae88 100644 --- a/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm +++ b/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm @@ -32,7 +32,7 @@ sub service # Warm up the cache for imported package symbols my $text = PLS::Parser::Document->text_from_uri($text_document->{uri}); my $imports = PLS::Parser::Document->get_imports($text); - PLS::Parser::PackageSymbols::get_imported_package_symbols($PLS::Server::State::CONFIG, @{$imports})->retain(); + PLS::Parser::PackageSymbols::get_imported_package_symbols($PLS::Server::State::CONFIG, @{$imports})->get(); return; } ## end sub service diff --git a/server/lib/PLS/Server/Request/TextDocument/PublishDiagnostics.pm b/server/lib/PLS/Server/Request/TextDocument/PublishDiagnostics.pm index 592e0c32..236129d1 100644 --- a/server/lib/PLS/Server/Request/TextDocument/PublishDiagnostics.pm +++ b/server/lib/PLS/Server/Request/TextDocument/PublishDiagnostics.pm @@ -118,7 +118,7 @@ sub get_compilation_errors $temp = eval { File::Temp->new(CLEANUP => 0, TEMPLATE => '.pls-tmp-XXXXXXXXXX', DIR => $dir) }; $temp = eval { File::Temp->new(CLEANUP => 0) } if (ref $temp ne 'File::Temp'); $path = $temp->filename; - $future->on_done(sub { unlink $temp }); + $future->on_ready(sub { unlink $temp }); my $source_text = Encode::encode('UTF-8', ${$source}); @@ -183,7 +183,7 @@ sub get_compilation_errors # Load code using module name, but redirect Perl to the temp file # when loading the file we are compiling. - $code = <<~ "EOF"; + $code = <<~"EOF"; BEGIN { unshift \@INC, sub { @@ -270,7 +270,7 @@ sub get_compilation_errors } ## end while (${$buffref} =~ s/^(.*)\n//...) return 0; - } + } ## end sub }, stdout => { on_read => sub { @@ -280,7 +280,7 @@ sub get_compilation_errors # This can happen if there is a BEGIN block that prints to STDOUT. ${$buffref} = ''; return 0; - } + } ## end sub }, on_finish => sub { $future->done(@diagnostics); diff --git a/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm b/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm index c002ea32..f8e9aa87 100644 --- a/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm +++ b/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm @@ -54,7 +54,11 @@ sub service } ## end foreach my $change (@{$self...}) @changed_files = uniq @changed_files; - $index->index_files(@changed_files)->then(sub { Future->wait_all(@_) })->retain() if (scalar @changed_files); + + if (scalar @changed_files) + { + $index->index_files(@changed_files)->then(sub { Future->wait_all(@_) })->get(); + } return; } ## end sub service diff --git a/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm b/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm index 23cc5694..2aa86899 100644 --- a/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm +++ b/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm @@ -5,6 +5,7 @@ use warnings; use parent 'PLS::Server::Request'; +use Future; use URI; use PLS::Parser::Index; @@ -35,12 +36,16 @@ sub service $index->deindex_workspace($path); } + my @futures; + foreach my $folder (@{$added}) { my $path = URI->new($folder->{uri})->file; - $index->index_workspace($path); + push @futures, $index->index_workspace($path); } + Future->wait_all(@futures)->get(); + return; } ## end sub service diff --git a/server/lib/PLS/Server/Response/DocumentSymbol.pm b/server/lib/PLS/Server/Response/DocumentSymbol.pm index 4556e52b..87e85dc0 100644 --- a/server/lib/PLS/Server/Response/DocumentSymbol.pm +++ b/server/lib/PLS/Server/Response/DocumentSymbol.pm @@ -5,11 +5,6 @@ use warnings; use parent 'PLS::Server::Response'; -use feature 'state'; - -use IO::Async::Loop; -use IO::Async::Timer::Countdown; - use PLS::Parser::Document; use PLS::Parser::DocumentSymbols; @@ -30,36 +25,10 @@ sub new my $self = bless {id => $request->{id}, result => undef}, $class; - my $uri = $request->{params}{textDocument}{uri}; - - # Delay document symbols by a couple of seconds to allow cancelling before processing starts. - my $future = Future->new(); - my $timer = IO::Async::Timer::Countdown->new( - delay => 2, - on_expire => sub { $self->on_expire($uri, $future) }, - remove_on_expire => 1 - ); - - IO::Async::Loop->new->add($timer->start()); - - # When the future is canceled, make sure to stop the timer so that it never actually starts generating document symbols. - $future->on_cancel( - sub { - $timer->stop(); - $timer->remove_from_parent(); - } - ); - - return $future; -} ## end sub new - -sub on_expire -{ - my ($self, $uri, $future) = @_; - + my $uri = $request->{params}{textDocument}{uri}; my $version = PLS::Parser::Document::uri_version($uri); - PLS::Parser::DocumentSymbols->get_all_document_symbols_async($uri)->on_done( + return PLS::Parser::DocumentSymbols->get_all_document_symbols_async($uri)->then( sub { my ($symbols) = @_; @@ -67,20 +36,16 @@ sub on_expire if (not length $current_version or length $version and $current_version > $version) { - $future->done($self); - return; + return $self; } $self->{result} = $symbols; - $future->done($self); - } - )->on_fail( + return $self; + }, sub { - $future->done($self); + return $self; } - )->retain(); - - return; -} ## end sub on_expire + ); +} ## end sub new 1; From 238e4dd6700ae9deecee54162aff5dc98adf31d0 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Mon, 24 Feb 2025 02:48:15 +0000 Subject: [PATCH 02/19] Remove bare use of open --- server/.vscodeignore | 1 + server/lib/PLS/Server/Cache.pm | 44 +++++++++++++++++++++------------- 2 files changed, 29 insertions(+), 16 deletions(-) create mode 100644 server/.vscodeignore diff --git a/server/.vscodeignore b/server/.vscodeignore new file mode 100644 index 00000000..aa9ef3ae --- /dev/null +++ b/server/.vscodeignore @@ -0,0 +1 @@ +blib \ No newline at end of file diff --git a/server/lib/PLS/Server/Cache.pm b/server/lib/PLS/Server/Cache.pm index e941de4d..27619d94 100644 --- a/server/lib/PLS/Server/Cache.pm +++ b/server/lib/PLS/Server/Cache.pm @@ -77,22 +77,34 @@ sub get_builtin_variables return $builtin_variables if (scalar @{$builtin_variables}); - if (open my $fh, '-|', $perldoc, '-Tu', 'perlvar') - { - while (my $line = <$fh>) - { - if ($line =~ /=item\s*(C<)?([\$\@\%]\S+)\s*/) - { - # If variable started with pod sequence "C<" remove ">" from the end - my $variable = $2; - $variable = substr $variable, 0, -1 if (length $1); - - # Remove variables indicated by pod sequences - next if ($variable =~ /^\$) - } ## end if (open my $fh, '-|',...) + my $process = IO::Async::Process->new( + command => [$perldoc, qw(-Tu perlvar)], + stdout => { + on_read => sub { + my (undef, $buffref) = @_; + + while (${$buffref} =~ s/^(.*)\n//) + { + my $line = $1; + + if ($line =~ /=item\s*(C<)?([\$\@\%]\S+)\s*/) + { + # If variable started with pod sequence "C<" remove ">" from the end + my $variable = $2; + $variable = substr $variable, 0, -1 if (length $1); + + # Remove variables indicated by pod sequences + next if ($variable =~ /^\$ sub { } + ); + + IO::Async::Loop->new->add($process); + $process->finish_future->get(); return $builtin_variables; } ## end sub get_builtin_variables From 1654a61234d61b7aed4879cf613d49e6f8c0f5e0 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Mon, 24 Feb 2025 06:16:21 +0000 Subject: [PATCH 03/19] Fix handling of cancellation so that it just uses Future->done --- .vscode/settings.json | 6 +- server/MANIFEST | 2 + server/lib/PLS/Parser/Index.pm | 2 +- server/lib/PLS/Server.pm | 79 +++++++++---------- server/lib/PLS/Server/Method/ServerMethod.pm | 21 +++-- server/lib/PLS/Server/Request/Sleep.pm | 20 +++++ .../lib/PLS/Server/Response/DocumentSymbol.pm | 49 +++++++++--- server/lib/PLS/Server/Response/Sleep.pm | 36 +++++++++ server/t/01server.t | 51 ++++++------ server/t/Communicate.pm | 65 +++++++++++---- server/t/packets/formatting.json | 6 -- server/t/packets/sleep.json | 6 ++ 12 files changed, 240 insertions(+), 103 deletions(-) create mode 100644 server/lib/PLS/Server/Request/Sleep.pm create mode 100644 server/lib/PLS/Server/Response/Sleep.pm delete mode 100644 server/t/packets/formatting.json create mode 100644 server/t/packets/sleep.json diff --git a/.vscode/settings.json b/.vscode/settings.json index dfa206ae..5de7d59b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -12,11 +12,13 @@ }, "files.exclude": { "client/out": true, // set this to true to hide the "out" folder with the compiled JS files - "client/dist": true // set this to true to hide the "dist" folder with the compiled JS files + "client/dist": true, // set this to true to hide the "dist" folder with the compiled JS files + "server/blib": true }, "search.exclude": { "client/out": true, // set this to false to include "out" folder in search results - "client/dist": true // set this to false to include "dist" folder in search results + "client/dist": true, // set this to false to include "dist" folder in search results + "server/blib": true }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts "typescript.tsc.autoDetect": "off" diff --git a/server/MANIFEST b/server/MANIFEST index f87359a3..d5474f28 100644 --- a/server/MANIFEST +++ b/server/MANIFEST @@ -45,7 +45,9 @@ lib/PLS/Server/Response/Resolve.pm lib/PLS/Server/Response/Location.pm lib/PLS/Server/Response/WorkspaceSymbols.pm lib/PLS/Server/Response/Shutdown.pm +lib/PLS/Server/Response/Sleep.pm lib/PLS/Server/Request/Progress.pm +lib/PLS/Server/Request/Sleep.pm lib/PLS/Server/Request/Window/WorkDoneProgress/Create.pm lib/PLS/Server/Response/InvalidRequest.pm lib/PLS/Server/Message.pm diff --git a/server/lib/PLS/Parser/Index.pm b/server/lib/PLS/Parser/Index.pm index a57aba8f..478c7052 100644 --- a/server/lib/PLS/Parser/Index.pm +++ b/server/lib/PLS/Parser/Index.pm @@ -171,7 +171,7 @@ sub index_files } ## end foreach my $ref (keys %{$subs...}) - return Future->done($file); + return $file; } ); } ## end foreach my $uri (@{$uris}) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 4018fa80..ffd4fad5 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -56,39 +56,34 @@ sub run $self->{stream} = IO::Async::Stream->new_for_stdio( autoflush => 0, on_read => sub { - my $size = 0; + my ($stream, $buffref, $eof) = @_; - return sub { - my ($stream, $buffref, $eof) = @_; + exit if $eof; - exit if $eof; + my @futures; - if (not $size) - { - if (${$buffref} =~ s/^(.*?)\r\n\r\n//s) - { - my $headers = $1; - - my %headers = map { split /: / } grep { length } split /\r\n/, $headers; - $size = $headers{'Content-Length'}; - die 'no Content-Length header provided' unless $size; - } ## end if (${$buffref} =~ s/^(.*?)\r\n\r\n//s...) - else - { - return 0; - } - } ## end if (not $size) + while (${$buffref} =~ s/^(.*?)\r\n\r\n//s) + { + my $headers = $1; - return 0 if (length(${$buffref}) < $size); + my %headers = map { split /: / } grep { length } split /\r\n/, $headers; + my $size = $headers{'Content-Length'}; + die 'no Content-Length header provided' unless $size; - my $json = substr ${$buffref}, 0, $size, ''; - $size = 0; + if (length ${$buffref} < $size) + { + ${$buffref} = "$headers\r\n\r\n${$buffref}"; + return 0; + } + my $json = substr ${$buffref}, 0, $size, ''; my $content = decode_json $json; - $self->handle_client_message($content); - return 1; - }; ## end sub + push @futures, $self->handle_client_message($content); + } ## end while (${$buffref} =~ s/^(.*?)\r\n\r\n//s...) + + Future->wait_all(@futures)->get(); + return 0; } ); @@ -127,11 +122,11 @@ sub handle_client_message if ($message->isa('PLS::Server::Request')) { - $self->handle_client_request($message); + return $self->handle_client_request($message); } if ($message->isa('PLS::Server::Response')) { - $self->handle_client_response($message); + return $self->handle_client_response($message); } return; @@ -182,6 +177,7 @@ sub handle_client_request if ($response->isa('PLS::Server::Response')) { $self->send_message($response); + return; } elsif ($response->isa('Future')) { @@ -192,19 +188,19 @@ sub handle_client_request $self->{running_futures}{$id} = $response; } - $response->await(); + return $response->then( + sub { + my ($response) = @_; - if ($response->is_cancelled) - { - $response = PLS::Server::Response::Cancelled->new(id => $id); - } - else - { - $response = $response->result; - } + if (not exists $self->{running_futures}{$id}) + { + $response = PLS::Server::Response::Cancelled->new(id => $id); + } - delete $self->{running_futures}{$id}; - $self->send_message($response); + $self->send_message($response); + return Future->done(); + } + ); } ## end elsif ($response->isa('Future'...)) return; @@ -258,8 +254,11 @@ sub cancel_request if (blessed($future) and $future->isa('Future')) { - $future->cancel(); - } + if (not $future->is_ready) + { + $future->done(); + } + } ## end if (blessed($future) and...) return; } ## end sub cancel_request diff --git a/server/lib/PLS/Server/Method/ServerMethod.pm b/server/lib/PLS/Server/Method/ServerMethod.pm index a1b4b1d4..dbd96d5b 100644 --- a/server/lib/PLS/Server/Method/ServerMethod.pm +++ b/server/lib/PLS/Server/Method/ServerMethod.pm @@ -9,6 +9,7 @@ use PLS::Server::Request::Initialized; use PLS::Server::Request::CancelRequest; use PLS::Server::Request::Shutdown; use PLS::Server::Request::Exit; +use PLS::Server::Request::Sleep; use PLS::Server::Response::ServerNotInitialized; use PLS::Server::Response::InvalidRequest; @@ -86,6 +87,11 @@ sub get_request return PLS::Server::Request::CancelRequest->new($request); } + if ($method eq 'sleep') + { + return PLS::Server::Request::Sleep->new($request); + } + if ($method eq 'shutdown') { return PLS::Server::Request::Shutdown->new($request); @@ -98,11 +104,16 @@ sub is_server_method { my ($method) = @_; - return 1 if ($method eq 'initialize'); - return 1 if ($method eq 'initialized'); - return 1 if ($method eq 'shutdown'); - return 1 if ($method eq 'exit'); - return 1 if ($method eq '$'); + my %valid = ( + initialize => 1, + initialized => 1, + shutdown => 1, + exit => 1, + sleep => 1, + '$' => 1 + ); + + return 1 if $valid{$method}; return 0; } ## end sub is_server_method diff --git a/server/lib/PLS/Server/Request/Sleep.pm b/server/lib/PLS/Server/Request/Sleep.pm new file mode 100644 index 00000000..c77ef115 --- /dev/null +++ b/server/lib/PLS/Server/Request/Sleep.pm @@ -0,0 +1,20 @@ +package PLS::Server::Request::Sleep; + +use strict; +use warnings; + +use parent 'PLS::Server::Request'; + +use IO::Async::Loop; +use IO::Async::Timer::Countdown; + +use PLS::Server::Response::Sleep; + +sub service +{ + my ($self) = @_; + + return PLS::Server::Response::Sleep->new($self); +} + +1; diff --git a/server/lib/PLS/Server/Response/DocumentSymbol.pm b/server/lib/PLS/Server/Response/DocumentSymbol.pm index 87e85dc0..9f73357f 100644 --- a/server/lib/PLS/Server/Response/DocumentSymbol.pm +++ b/server/lib/PLS/Server/Response/DocumentSymbol.pm @@ -5,6 +5,9 @@ use warnings; use parent 'PLS::Server::Response'; +use IO::Async::Loop; +use IO::Async::Timer::Countdown; + use PLS::Parser::Document; use PLS::Parser::DocumentSymbols; @@ -25,27 +28,55 @@ sub new my $self = bless {id => $request->{id}, result => undef}, $class; - my $uri = $request->{params}{textDocument}{uri}; + my $uri = $request->{params}{textDocument}{uri}; + + # Document symbols are requested and canceled very often. We should wait to make sure + # that we aren't requesting them too quickly. + my $loop = IO::Async::Loop->new(); + my $future = $loop->new_future(); + my $timer = IO::Async::Timer::Countdown->new(delay => 2, + on_expire => sub { $self->on_expire($uri, $future) }); + $timer->start(); + $loop->add($timer); + + return $future; +} ## end sub new + +sub on_expire +{ + my ($self, $uri, $future) = @_; + + if ($future->is_ready) + { + return; + } + my $version = PLS::Parser::Document::uri_version($uri); - return PLS::Parser::DocumentSymbols->get_all_document_symbols_async($uri)->then( + PLS::Parser::DocumentSymbols->get_all_document_symbols_async($uri)->then( sub { my ($symbols) = @_; + if ($future->is_ready) + { + return Future->done(); + } + my $current_version = PLS::Parser::Document::uri_version($uri); if (not length $current_version or length $version and $current_version > $version) { - return $self; + $future->done($self); + return Future->done(); } $self->{result} = $symbols; - return $self; - }, - sub { - return $self; + $future->done($self); + return Future->done(); } - ); -} ## end sub new + )->get(); + + return; +} ## end sub on_expire 1; diff --git a/server/lib/PLS/Server/Response/Sleep.pm b/server/lib/PLS/Server/Response/Sleep.pm new file mode 100644 index 00000000..cf14ac4e --- /dev/null +++ b/server/lib/PLS/Server/Response/Sleep.pm @@ -0,0 +1,36 @@ +package PLS::Server::Response::Sleep; + +use strict; +use warnings; + +use parent 'PLS::Server::Response'; + +use IO::Async::Loop; +use IO::Async::Timer::Countdown; + +sub new +{ + my ($class, $request) = @_; + + my $self = bless { + id => $request->{id}, + result => undef + }, $class; + + my $loop = IO::Async::Loop->new(); + my $future = $loop->new_future(); + $future->set_label('sleep'); + my $timer = IO::Async::Timer::Countdown->new( + delay => 10, + on_expire => sub { + $future->done($self); + }, + remove_on_expire => 1 + ); + $timer->start(); + $loop->add($timer); + + return $future; +} ## end sub new + +1; diff --git a/server/t/01server.t b/server/t/01server.t index e94b6bea..c954f8db 100755 --- a/server/t/01server.t +++ b/server/t/01server.t @@ -136,8 +136,8 @@ subtest 'server not initialized' => sub { ok(valid_response($response), 'valid response'); cmp_ok($response->{jsonrpc}, '==', 2.0, 'response is jsonrpc 2.0'); cmp_ok($response->{id}, '==', 0, 'response is id 0'); - is_deeply($response->{error}, {code => -32002, message => 'server not yet initialized'}, 'server not yet initialized'); -}; + is_deeply($response->{error}, {code => -32_002, message => 'server not yet initialized'}, 'server not yet initialized'); +}; ## end 'server not initialized' => sub subtest 'initialize server' => sub { plan tests => 15; @@ -165,7 +165,7 @@ subtest 'initialize server' => sub { is_deeply($capabilities->{completionProvider}, {triggerCharacters => ['>', ':', '$', '@', '%', ' ', '-'], resolveProvider => JSON::PP::true}, 'server is completion provider'); is_deeply($capabilities->{executeCommandProvider}, {commands => ['pls.sortImports']}, 'server can execute commands'); ok($capabilities->{workspaceSymbolProvider}, 'server is workspace symbol provider'); -}; +}; ## end 'initialize server' => sub subtest 'initial requests' => sub { plan tests => 30; @@ -179,10 +179,12 @@ subtest 'initial requests' => sub { push @messages, $comm->recv_message(); } - my $work_done_create = List::Util::first { $_->{method} eq 'window/workDoneProgress/create' } @messages; - my $work_done_begin = List::Util::first { $_->{method} eq '$/progress' and $_->{params}{value}{kind} eq 'begin' } @messages; - my $work_done_report = List::Util::first { $_->{method} eq '$/progress' and $_->{params}{value}{kind} eq 'report' } @messages; - my $work_done_end = List::Util::first { $_->{method} eq '$/progress' and $_->{params}{value}{kind} eq 'end' } @messages; + my $work_done_create = List::Util::first { $_->{method} eq 'window/workDoneProgress/create' } @messages; + ## no critic (RequireInterpolationOfMetachars) + my $work_done_begin = List::Util::first { $_->{method} eq '$/progress' and $_->{params}{value}{kind} eq 'begin' } @messages; + my $work_done_report = List::Util::first { $_->{method} eq '$/progress' and $_->{params}{value}{kind} eq 'report' } @messages; + my $work_done_end = List::Util::first { $_->{method} eq '$/progress' and $_->{params}{value}{kind} eq 'end' } @messages; + ## use critic my $register_capability = List::Util::first { $_->{method} eq 'client/registerCapability' } @messages; my $configuration = List::Util::first { $_->{method} eq 'workspace/configuration' } @messages; @@ -201,20 +203,20 @@ subtest 'initial requests' => sub { cmp_ok(@{$register_capability->{params}{registrations}[1]{registerOptions}{watchers}}, '==', 1, 'correct number of watchers'); is($register_capability->{params}{registrations}[1]{registerOptions}{watchers}[0]{globPattern}, '**/*', 'correct glob pattern'); - is($work_done_begin->{method}, '$/progress', 'work done begin sent'); + is($work_done_begin->{method}, '$/progress', 'work done begin sent'); ## no critic (RequireInterpolationOfMetachars) is($work_done_begin->{params}{token}, $token, 'correct token'); is($work_done_begin->{params}{value}{kind}, 'begin', 'begin sent first'); is($work_done_begin->{params}{value}{title}, 'Indexing', 'correct title'); cmp_ok($work_done_begin->{params}{value}{percentage}, '==', 0, 'correct percentage'); ok(!$work_done_begin->{params}{value}{cancellable}, 'work is not cancellable'); - is($work_done_report->{method}, '$/progress', 'work done report sent'); + is($work_done_report->{method}, '$/progress', 'work done report sent'); ## no critic (RequireInterpolationOfMetachars) is($work_done_report->{params}{token}, $token, 'correct token'); is($work_done_report->{params}{value}{kind}, 'report', 'report sent second'); is($work_done_report->{params}{value}{message}, 'Indexed Communicate.pm (1/1)', 'correct message'); cmp_ok($work_done_report->{params}{value}{percentage}, '==', 100, 'correct percentage'); - is($work_done_end->{method}, '$/progress', 'work done report sent'); + is($work_done_end->{method}, '$/progress', 'work done report sent'); ## no critic (RequireInterpolationOfMetachars) is($work_done_end->{params}{token}, $token, 'correct token'); is($work_done_end->{params}{value}{kind}, 'end', 'end sent last'); is($work_done_end->{params}{value}{message}, 'Finished indexing all files', 'correct message'); @@ -247,7 +249,7 @@ subtest 'initial requests' => sub { ); $comm->stop_server(); -}; +}; ## end 'initial requests' => sub subtest 'cancel request' => sub { plan tests => 4; @@ -255,26 +257,25 @@ subtest 'cancel request' => sub { my $comm = t::Communicate->new(); initialize_server($comm); complete_initialization($comm); - my $uri = open_file('Communicate.pm', $comm); - my $format = slurp('formatting.json', 2); + my $uri = open_file('Communicate.pm', $comm); + my $sleep = slurp('sleep.json', 2); my $cancel = slurp('cancel.json'); - $format->{params}{textDocument}{uri} = $uri; - $cancel->{params}{id} = $format->{id}; + $cancel->{params}{id} = $sleep->{id}; - # request formatting and then cancel immediately. + # request sleep and then cancel immediately. # should receive a response that the request was canceled. - $comm->send_message($format); + $comm->send_message($sleep); $comm->send_message($cancel); my $response = $comm->recv_message(); ok(valid_response($response), 'valid response'); - cmp_ok($response->{id}, '==', $format->{id}, 'correct id'); - cmp_ok($response->{error}{code}, '==', -32800, 'correct code'); # request cancelled = -32800 + cmp_ok($response->{id}, '==', $sleep->{id}, 'correct id'); + cmp_ok($response->{error}{code}, '==', -32_800, 'correct code'); # request cancelled = -32800 is($response->{error}{message}, 'Request cancelled.', 'correct error message'); $comm->stop_server(); -}; +}; ## end 'cancel request' => sub subtest 'bad message' => sub { plan tests => 1; @@ -284,7 +285,7 @@ subtest 'bad message' => sub { chomp(my $error = $comm->recv_err()); like($error, qr/no content-length header/i, 'no content length header error thrown'); waitpid $comm->{pid}, 0; -}; +}; ## end 'bad message' => sub subtest 'shutdown and exit' => sub { plan tests => 5; @@ -296,9 +297,9 @@ subtest 'shutdown and exit' => sub { my $shutdown_response = $comm->send_message_and_recv_response(slurp('shutdown.json', 2)); cmp_ok($shutdown_response->{id}, '==', 2, 'got shutdown response'); - my $invalid_request = $comm->send_message_and_recv_response(slurp('formatting.json', 3)); - cmp_ok($invalid_request->{id}, '==', 3, 'got invalid request response'); - cmp_ok($invalid_request->{error}{code}, '==', -32600, 'got correct error code'); + my $invalid_request = $comm->send_message_and_recv_response(slurp('sleep.json', 3)); + cmp_ok($invalid_request->{id}, '==', 3, 'got invalid request response'); + cmp_ok($invalid_request->{error}{code}, '==', -32_600, 'got correct error code'); $comm->send_message(slurp('exit.json')); waitpid $comm->{pid}, 0; @@ -311,4 +312,4 @@ subtest 'shutdown and exit' => sub { $comm->send_message(slurp('exit.json')); waitpid $comm->{pid}, 0; cmp_ok($? >> 8, '==', 1, 'got 1 exit code when exit request sent without shutdown'); -}; +}; ## end 'shutdown and exit' => sub diff --git a/server/t/Communicate.pm b/server/t/Communicate.pm index 0a9dcc9f..7ab6bba6 100644 --- a/server/t/Communicate.pm +++ b/server/t/Communicate.pm @@ -1,4 +1,4 @@ -package t::Communicate; +package t::Communicate; ## no critic (Capitalization) use strict; use warnings; @@ -29,10 +29,11 @@ sub new close $server_write_fh; my $self = { - pid => $pid, - read_fh => $client_read_fh, - write_fh => $client_write_fh, - err_fh => $client_err_fh + pid => $pid, + read_fh => $client_read_fh, + read_buff => '', + write_fh => $client_write_fh, + err_fh => $client_err_fh }; return bless $self, $class; @@ -49,7 +50,7 @@ sub new open STDERR, '>&', $server_err_fh; my $server = PLS::Server->new(); exit $server->run(); - } ## end else [ if ($pid) ] + } ## end else[ if ($pid)] return; } ## end sub new @@ -69,17 +70,51 @@ sub recv_message { my ($self) = @_; - local $/ = "\r\n"; - my $response = readline $self->{read_fh}; - return unless (length $response); - readline $self->{read_fh}; # blank line - my ($content_length) = $response =~ /Content-Length: (\d+)/; - my $json; - read $self->{read_fh}, $json, $content_length; + my $content = $self->read_content(); - return eval { JSON::PP->new->utf8->decode($json) }; + if ($content) + { + return $content; + } + + while (sysread $self->{read_fh}, $self->{read_buff}, 8192, length($self->{read_buff})) + { + $content = $self->read_content(); + + if ($content) + { + return $content; + } + } ## end while (sysread $self->{read_fh...}) + + return; } ## end sub recv_message +sub read_content +{ + my ($self) = @_; + + if (not length $self->{read_buff}) + { + return; + } + + if ($self->{read_buff} =~ s/^(.*?)\r\n\r\n//s) + { + my ($content_length) = $1 =~ /Content-Length: (\d+)/; + + if (length $self->{read_buff} < $content_length) + { + sysread $self->{read_fh}, $self->{read_buff}, ($content_length - length($self->{read_buff})), length($self->{read_buff}); + } + + my $json = substr $self->{read_buff}, 0, $content_length, ''; + return eval { decode_json($json) }; + } ## end if ($self->{read_buff}...) + + return; +} ## end sub read_content + sub send_message_and_recv_response { my ($self, $message) = @_; @@ -92,7 +127,7 @@ sub send_raw_message { my ($self, $message) = @_; - print {$self->{write_fh}} $message; + syswrite $self->{write_fh}, $message; return; } ## end sub send_raw_message diff --git a/server/t/packets/formatting.json b/server/t/packets/formatting.json deleted file mode 100644 index d2c10f91..00000000 --- a/server/t/packets/formatting.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": null, - "method": "textDocument/formatting", - "params": { "textDocument": { "uri": null } } -} diff --git a/server/t/packets/sleep.json b/server/t/packets/sleep.json new file mode 100644 index 00000000..f7aa31eb --- /dev/null +++ b/server/t/packets/sleep.json @@ -0,0 +1,6 @@ +{ + "jsonrpc": "2.0", + "id": null, + "method": "sleep", + "params": { "delay": 20 } +} From 612c8b7e8db2e4076457a4bda4ea66ce41e6a3c4 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 02:39:27 +0000 Subject: [PATCH 04/19] Fix cancellation --- server/lib/PLS/Server.pm | 27 ++++++++++--------- .../lib/PLS/Server/Response/DocumentSymbol.pm | 7 +++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index ffd4fad5..a3162b1a 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -188,19 +188,25 @@ sub handle_client_request $self->{running_futures}{$id} = $response; } - return $response->then( + my $future = $response->then( sub { my ($response) = @_; - if (not exists $self->{running_futures}{$id}) - { - $response = PLS::Server::Response::Cancelled->new(id => $id); - } - + warn "sending response for id $id\n"; $self->send_message($response); return Future->done(); } - ); + )->on_cancel( + sub { + $self->send_message(PLS::Server::Response::Cancelled->new(id => $id)); + } + ); + + # Kind of silly, but the sequence future doesn't get cancelled automatically - + # we need to set that up ourselves. + $response->on_cancel($future); + + return $future; } ## end elsif ($response->isa('Future'...)) return; @@ -254,11 +260,8 @@ sub cancel_request if (blessed($future) and $future->isa('Future')) { - if (not $future->is_ready) - { - $future->done(); - } - } ## end if (blessed($future) and...) + $future->cancel(); + } return; } ## end sub cancel_request diff --git a/server/lib/PLS/Server/Response/DocumentSymbol.pm b/server/lib/PLS/Server/Response/DocumentSymbol.pm index 9f73357f..c1d121dd 100644 --- a/server/lib/PLS/Server/Response/DocumentSymbol.pm +++ b/server/lib/PLS/Server/Response/DocumentSymbol.pm @@ -39,6 +39,13 @@ sub new $timer->start(); $loop->add($timer); + $future->on_cancel( + sub { + $timer->stop(); + $timer->remove_from_parent(); + } + ); + return $future; } ## end sub new From 225935da8d2e970fbc9379241e31a7a92cfcae15 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 02:42:52 +0000 Subject: [PATCH 05/19] Cleanup --- server/lib/PLS/Server.pm | 2 +- server/lib/PLS/Server/Response/DocumentSymbol.pm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index a3162b1a..1c0c1c2d 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -194,7 +194,7 @@ sub handle_client_request warn "sending response for id $id\n"; $self->send_message($response); - return Future->done(); + return; } )->on_cancel( sub { diff --git a/server/lib/PLS/Server/Response/DocumentSymbol.pm b/server/lib/PLS/Server/Response/DocumentSymbol.pm index c1d121dd..1f8f1957 100644 --- a/server/lib/PLS/Server/Response/DocumentSymbol.pm +++ b/server/lib/PLS/Server/Response/DocumentSymbol.pm @@ -66,7 +66,7 @@ sub on_expire if ($future->is_ready) { - return Future->done(); + return; } my $current_version = PLS::Parser::Document::uri_version($uri); @@ -74,12 +74,12 @@ sub on_expire if (not length $current_version or length $version and $current_version > $version) { $future->done($self); - return Future->done(); + return; } $self->{result} = $symbols; $future->done($self); - return Future->done(); + return; } )->get(); From 23ad4c80f04c30f797f0386f831e01e00a192839 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 02:43:37 +0000 Subject: [PATCH 06/19] Cleanup --- server/lib/PLS/Server.pm | 1 - 1 file changed, 1 deletion(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 1c0c1c2d..25f3dfa7 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -192,7 +192,6 @@ sub handle_client_request sub { my ($response) = @_; - warn "sending response for id $id\n"; $self->send_message($response); return; } From 13fa3bfa6fb3e724ee704d1894de623c7212c2ef Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 03:27:04 +0000 Subject: [PATCH 07/19] Make more things return futures --- server/lib/PLS/Parser/PackageSymbols.pm | 4 +- server/lib/PLS/Server.pm | 11 +++-- server/lib/PLS/Server/Request/Initialized.pm | 24 +++------- .../Server/Request/TextDocument/DidChange.pm | 9 ++-- .../Server/Request/TextDocument/DidClose.pm | 5 +- .../Server/Request/TextDocument/DidOpen.pm | 10 ++-- .../Server/Request/TextDocument/DidSave.pm | 9 ++-- .../Server/Request/Workspace/Configuration.pm | 10 +++- .../Workspace/DidChangeWatchedFiles.pm | 2 +- .../Workspace/DidChangeWorkspaceFolders.pm | 4 +- server/lib/PLS/Server/Response/Completion.pm | 47 +++++++++---------- 11 files changed, 72 insertions(+), 63 deletions(-) diff --git a/server/lib/PLS/Parser/PackageSymbols.pm b/server/lib/PLS/Parser/PackageSymbols.pm index 1884d203..06fbb285 100644 --- a/server/lib/PLS/Parser/PackageSymbols.pm +++ b/server/lib/PLS/Parser/PackageSymbols.pm @@ -83,9 +83,9 @@ sub _send_data_and_recv_result sub { my ($json) = @_; - return Future->done(eval { decode_json $json } // {}); + return eval { decode_json $json } // {}; }, - sub { Future->done({}) } + sub { return {} } ); } ## end sub _send_data_and_recv_result diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 25f3dfa7..6a229703 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -144,9 +144,14 @@ sub send_server_request } elsif ($request->isa('Future')) { - $request = $request->get(); - $self->handle_server_request($request); - } + $request->on_done( + sub { + my ($request) = @_; + + $self->handle_server_request($request); + } + ); + } ## end elsif ($request->isa('Future'...)) return; } ## end sub send_server_request diff --git a/server/lib/PLS/Server/Request/Initialized.pm b/server/lib/PLS/Server/Request/Initialized.pm index 8fb8bc93..a692b7f2 100644 --- a/server/lib/PLS/Server/Request/Initialized.pm +++ b/server/lib/PLS/Server/Request/Initialized.pm @@ -61,9 +61,7 @@ sub service $server->send_server_request(PLS::Server::Request::Client::RegisterCapability->new(\@capabilities)); # Now is a good time to start indexing files. - $self->index_files($index, $server); - - return; + return $self->index_files($index, $server); } ## end sub service sub index_files @@ -72,24 +70,18 @@ sub index_files if ($PLS::Server::State::CLIENT_CAPABILITIES->{window}{workDoneProgress}) { - $self->index_files_with_progress($index, $server); - } - else - { - $self->index_files_without_progress($index); + return $self->index_files_with_progress($index, $server); } - return; + return $self->index_files_without_progress($index); } ## end sub index_files sub index_files_without_progress { my (undef, $index) = @_; - $index->index_files()->then(sub { Future->wait_all(@_) })->get(); - - return; -} ## end sub index_files_without_progress + return $index->index_files()->then(sub { Future->wait_all(@_) }); +} sub index_files_with_progress { @@ -108,7 +100,7 @@ sub index_files_with_progress ) ); - $index->index_files()->then( + return $index->index_files()->then( sub { my @futures = @_; @@ -151,9 +143,7 @@ sub index_files_with_progress } ); } - )->get(); - - return; + ); } ## end sub index_files_with_progress 1; diff --git a/server/lib/PLS/Server/Request/TextDocument/DidChange.pm b/server/lib/PLS/Server/Request/TextDocument/DidChange.pm index 4e160c83..71b51c45 100644 --- a/server/lib/PLS/Server/Request/TextDocument/DidChange.pm +++ b/server/lib/PLS/Server/Request/TextDocument/DidChange.pm @@ -53,10 +53,13 @@ sub service on_expire => sub { delete $timers{$uri}; - $server->send_server_request(PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $uri)); + my $publish_future = PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $uri); + $server->send_server_request($publish_future); - my $index = PLS::Parser::Index->new(); - $index->index_files($uri)->then(sub { Future->wait_all(@_) })->get(); + my $index = PLS::Parser::Index->new(); + my $index_future = $index->index_files($uri)->then(sub { Future->wait_all(@_) }); + + Future->wait_all($publish_future, $index_future)->await(); }, remove_on_expire => 1 ); diff --git a/server/lib/PLS/Server/Request/TextDocument/DidClose.pm b/server/lib/PLS/Server/Request/TextDocument/DidClose.pm index f1cedf49..19a31d4e 100644 --- a/server/lib/PLS/Server/Request/TextDocument/DidClose.pm +++ b/server/lib/PLS/Server/Request/TextDocument/DidClose.pm @@ -23,12 +23,13 @@ sub service { my ($self, $server) = @_; - $server->send_server_request(PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $self->{params}{textDocument}{uri}, close => 1)); + my $publish_future = PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $self->{params}{textDocument}{uri}, close => 1); + $server->send_server_request($publish_future); my $text_document = $self->{params}{textDocument}; PLS::Parser::Document->close_file(%{$text_document}); - return; + return $publish_future->then(sub { }); } ## end sub service 1; diff --git a/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm b/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm index e221ae88..c863d8ec 100644 --- a/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm +++ b/server/lib/PLS/Server/Request/TextDocument/DidOpen.pm @@ -5,6 +5,8 @@ use warnings; use parent 'PLS::Server::Request'; +use Future; + use PLS::Parser::Document; use PLS::Parser::PackageSymbols; use PLS::Server::Request::TextDocument::PublishDiagnostics; @@ -27,14 +29,16 @@ sub service my $text_document = $self->{params}{textDocument}; PLS::Parser::Document->open_file(%{$text_document}); - $server->send_server_request(PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $text_document->{uri})); + my $publish_future = PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $text_document->{uri}); + $server->send_server_request($publish_future); # Warm up the cache for imported package symbols my $text = PLS::Parser::Document->text_from_uri($text_document->{uri}); my $imports = PLS::Parser::Document->get_imports($text); - PLS::Parser::PackageSymbols::get_imported_package_symbols($PLS::Server::State::CONFIG, @{$imports})->get(); - return; + my $symbols_future = PLS::Parser::PackageSymbols::get_imported_package_symbols($PLS::Server::State::CONFIG, @{$imports}); + + return Future->wait_all($publish_future, $symbols_future)->then(sub { }); } ## end sub service 1; diff --git a/server/lib/PLS/Server/Request/TextDocument/DidSave.pm b/server/lib/PLS/Server/Request/TextDocument/DidSave.pm index b9dffbd5..a0fc3ab4 100644 --- a/server/lib/PLS/Server/Request/TextDocument/DidSave.pm +++ b/server/lib/PLS/Server/Request/TextDocument/DidSave.pm @@ -5,6 +5,8 @@ use warnings; use parent 'PLS::Server::Request'; +use Future; + use PLS::Parser::Document; use PLS::Server::Request::TextDocument::PublishDiagnostics; @@ -23,10 +25,11 @@ sub service { my ($self, $server) = @_; - my $uri = $self->{params}{textDocument}{uri}; - $server->send_server_request(PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $uri)); + my $uri = $self->{params}{textDocument}{uri}; + my $publish_future = PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $uri); + $server->send_server_request($publish_future); - return; + return $publish_future->then(sub { }); } ## end sub service 1; diff --git a/server/lib/PLS/Server/Request/Workspace/Configuration.pm b/server/lib/PLS/Server/Request/Workspace/Configuration.pm index c7cc2196..9e0e3b8d 100644 --- a/server/lib/PLS/Server/Request/Workspace/Configuration.pm +++ b/server/lib/PLS/Server/Request/Workspace/Configuration.pm @@ -5,6 +5,7 @@ use warnings; use parent 'PLS::Server::Request'; +use Future; use List::Util; use Scalar::Util; @@ -91,18 +92,23 @@ sub handle_response } $PLS::Server::State::CONFIG = $config; + my @futures; # @INC may have changed - republish diagnostics foreach my $uri (@{PLS::Parser::Document->open_files()}) { - $server->send_server_request(PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $uri)); - } + my $future = PLS::Server::Request::TextDocument::PublishDiagnostics->new(uri => $uri); + $server->send_server_request($future); + push @futures, $future; + } ## end foreach my $uri (@{PLS::Parser::Document...}) PLS::Parser::PackageSymbols::start_package_symbols_process($config); PLS::Parser::PackageSymbols::start_imported_package_symbols_process($config); PLS::Server::Cache::warm_up(); + Future->wait_all(@futures)->await(); + return; } ## end sub handle_response diff --git a/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm b/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm index f8e9aa87..9325a0c1 100644 --- a/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm +++ b/server/lib/PLS/Server/Request/Workspace/DidChangeWatchedFiles.pm @@ -57,7 +57,7 @@ sub service if (scalar @changed_files) { - $index->index_files(@changed_files)->then(sub { Future->wait_all(@_) })->get(); + return $index->index_files(@changed_files)->then(sub { Future->wait_all(@_) }); } return; diff --git a/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm b/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm index 2aa86899..ac32cec0 100644 --- a/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm +++ b/server/lib/PLS/Server/Request/Workspace/DidChangeWorkspaceFolders.pm @@ -44,9 +44,7 @@ sub service push @futures, $index->index_workspace($path); } - Future->wait_all(@futures)->get(); - - return; + return Future->wait_all(@futures); } ## end sub service 1; diff --git a/server/lib/PLS/Server/Response/Completion.pm b/server/lib/PLS/Server/Response/Completion.pm index 88091daf..7b3c3945 100644 --- a/server/lib/PLS/Server/Response/Completion.pm +++ b/server/lib/PLS/Server/Response/Completion.pm @@ -81,34 +81,32 @@ sub new # Imported functions can't be called with an arrow push @futures, get_imported_package_functions($document, $full_text) unless $arrow; } ## end if ($filter) - } ## end else[ if ($filter =~ /^[\$\@\%]/...)] - push @results, @{Future->wait_all(@futures)->then( - sub { - [map { @{$_->result} } grep { $_->is_ready } @_] - } - )->get() - }; - - my %unique_by_detail; - - foreach my $result (@results) - { - my $new_text = $result->{label}; - $new_text = $result->{insertText} if (length $result->{insertText}); - delete $result->{insertText}; - next if (exists $result->{detail} and length $result->{detail} and $unique_by_detail{$result->{detail}}++); - - push @{$self->{result}}, {%{$result}, textEdit => {newText => $new_text, range => $range}}; - } ## end foreach my $result (@results...) - if (not $arrow and not $package and $filter !~ /^\%\@/) { push @{$self->{result}}, get_snippets(); } - return $self; + return Future->wait_all(@futures)->then( + sub { + push @results, map { @{$_->result} } @_; + + my %unique_by_detail; + + foreach my $result (@results) + { + my $new_text = $result->{label}; + $new_text = $result->{insertText} if (length $result->{insertText}); + delete $result->{insertText}; + next if (exists $result->{detail} and length $result->{detail} and $unique_by_detail{$result->{detail}}++); + + push @{$self->{result}}, {%{$result}, textEdit => {newText => $new_text, range => $range}}; + } ## end foreach my $result (@results...) + + return $self; + } + ); } ## end sub new sub get_keywords @@ -150,7 +148,7 @@ sub get_package_functions sub { my ($functions) = @_; - return Future->done([]) if (ref $functions ne 'HASH'); + return [] if (ref $functions ne 'HASH'); my $separator = $arrow ? '->' : '::'; my @functions; @@ -191,7 +189,7 @@ sub get_package_functions } ## end foreach my $name (@{$functions...}) } ## end foreach my $package_name (keys...) - return Future->done(\@functions); + return \@functions; } ); } ## end sub get_package_functions @@ -224,7 +222,8 @@ sub get_imported_package_functions push @results, $result; } ## end foreach my $subroutine (@{$imported_functions...}) } ## end foreach my $package_name (keys...) - return Future->done(\@results); + + return \@results; } ); } ## end sub get_imported_package_functions From 9d3af543f0362c827d22eafadea5879258abbe9b Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 03:48:20 +0000 Subject: [PATCH 08/19] Make more things return futures --- server/lib/PLS/Server.pm | 2 +- server/lib/PLS/Server/Cache.pm | 9 +-- .../Server/Request/Workspace/Configuration.pm | 8 +-- server/lib/PLS/Server/Response/Completion.pm | 64 ++++++++++--------- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 6a229703..099fb902 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -224,7 +224,7 @@ sub handle_client_response if (blessed($request) and $request->isa('PLS::Server::Request')) { - $request->handle_response($response, $self); + return $request->handle_response($response, $self); } return; diff --git a/server/lib/PLS/Server/Cache.pm b/server/lib/PLS/Server/Cache.pm index 27619d94..da3da885 100644 --- a/server/lib/PLS/Server/Cache.pm +++ b/server/lib/PLS/Server/Cache.pm @@ -12,11 +12,10 @@ use PLS::Parser::Pod; sub warm_up { - get_builtin_variables(); get_core_modules(); get_ext_modules(); - return; + return get_builtin_variables(); } ## end sub warm_up sub get_ext_modules @@ -75,7 +74,7 @@ sub get_builtin_variables my $perldoc = PLS::Parser::Pod->get_perldoc_location(); state $builtin_variables = []; - return $builtin_variables if (scalar @{$builtin_variables}); + return Future->done($builtin_variables) if (scalar @{$builtin_variables}); my $process = IO::Async::Process->new( command => [$perldoc, qw(-Tu perlvar)], @@ -104,9 +103,7 @@ sub get_builtin_variables ); IO::Async::Loop->new->add($process); - $process->finish_future->get(); - - return $builtin_variables; + return $process->finish_future->then(sub { $builtin_variables }); } ## end sub get_builtin_variables 1; diff --git a/server/lib/PLS/Server/Request/Workspace/Configuration.pm b/server/lib/PLS/Server/Request/Workspace/Configuration.pm index 9e0e3b8d..773c11ed 100644 --- a/server/lib/PLS/Server/Request/Workspace/Configuration.pm +++ b/server/lib/PLS/Server/Request/Workspace/Configuration.pm @@ -102,14 +102,12 @@ sub handle_response push @futures, $future; } ## end foreach my $uri (@{PLS::Parser::Document...}) + push @futures, PLS::Server::Cache::warm_up(); + PLS::Parser::PackageSymbols::start_package_symbols_process($config); PLS::Parser::PackageSymbols::start_imported_package_symbols_process($config); - PLS::Server::Cache::warm_up(); - - Future->wait_all(@futures)->await(); - - return; + return Future->wait_all(@futures)->then(sub { }); } ## end sub handle_response sub convert_config diff --git a/server/lib/PLS/Server/Response/Completion.pm b/server/lib/PLS/Server/Response/Completion.pm index 7b3c3945..6609ed55 100644 --- a/server/lib/PLS/Server/Response/Completion.pm +++ b/server/lib/PLS/Server/Response/Completion.pm @@ -54,7 +54,7 @@ sub new if ($filter =~ /^[\$\@\%]/) { - push @results, @{get_variables($document, $filter, $full_text)}; + push @futures, get_variables($document, $filter, $full_text); } else { @@ -317,35 +317,41 @@ sub get_variables { my ($document, $full_text) = @_; - my @variables; - my %seen_variables; + return PLS::Server::Cache::get_builtin_variables()->then( + sub { + my ($builtin_variables) = @_; - foreach my $variable (@{PLS::Server::Cache::get_builtin_variables()}, @{$document->get_variables_fast($full_text)}) - { - next if $seen_variables{$variable}++; - next if ($variable =~ /\n/); - push @variables, - { - label => $variable, - kind => 6 - }; - - # add other variable forms to the list for arrays and hashes - if ($variable =~ /^[\@\%]/) - { - my $name = $variable =~ s/^[\@\%]/\$/r; - my $append = $variable =~ /^\@/ ? '[' : '{'; - push @variables, - { - label => $variable, - insertText => $name . $append, - filterText => $name, - kind => 6 - }; - } ## end if ($variable =~ /^[\@\%]/...) - } ## end foreach my $variable (@{PLS::Server::Cache::get_builtin_variables...}) - - return \@variables; + my @variables; + my %seen_variables; + + foreach my $variable (@{$builtin_variables}, @{$document->get_variables_fast($full_text)}) + { + next if $seen_variables{$variable}++; + next if ($variable =~ /\n/); + push @variables, + { + label => $variable, + kind => 6 + }; + + # add other variable forms to the list for arrays and hashes + if ($variable =~ /^[\@\%]/) + { + my $name = $variable =~ s/^[\@\%]/\$/r; + my $append = $variable =~ /^\@/ ? '[' : '{'; + push @variables, + { + label => $variable, + insertText => $name . $append, + filterText => $name, + kind => 6 + }; + } ## end if ($variable =~ /^[\@\%]/...) + } ## end foreach my $variable (@{$builtin_variables...}) + + return \@variables; + } + ); } ## end sub get_variables sub get_snippets From 3e630c6101b15df4fadeb4b3da092ce68307a3d5 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 03:50:53 +0000 Subject: [PATCH 09/19] Reduce nesting --- server/lib/PLS/Server/Request/Initialized.pm | 27 ++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/server/lib/PLS/Server/Request/Initialized.pm b/server/lib/PLS/Server/Request/Initialized.pm index a692b7f2..9538e440 100644 --- a/server/lib/PLS/Server/Request/Initialized.pm +++ b/server/lib/PLS/Server/Request/Initialized.pm @@ -130,20 +130,21 @@ sub index_files_with_progress ); } ## end foreach my $future (@futures...) - return Future->wait_all(@new_futures)->then( - sub { - $server->send_server_request( - PLS::Server::Request::Progress->new( - token => $work_done_progress_create->{params}{token}, - kind => 'end', - message => 'Finished indexing all files' - ) - ); - - } - ); + return Future->wait_all(@new_futures); } - ); + )->then( + sub { + $server->send_server_request( + PLS::Server::Request::Progress->new( + token => $work_done_progress_create->{params}{token}, + kind => 'end', + message => 'Finished indexing all files' + ) + ); + + } + ); + } ## end sub index_files_with_progress 1; From d32219a0227141054eb75c736bbe097451b601f5 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 03:52:58 +0000 Subject: [PATCH 10/19] Use await when result is thrown away --- server/lib/PLS/Server/Response/DocumentSymbol.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/lib/PLS/Server/Response/DocumentSymbol.pm b/server/lib/PLS/Server/Response/DocumentSymbol.pm index 1f8f1957..c8ec1882 100644 --- a/server/lib/PLS/Server/Response/DocumentSymbol.pm +++ b/server/lib/PLS/Server/Response/DocumentSymbol.pm @@ -81,7 +81,7 @@ sub on_expire $future->done($self); return; } - )->get(); + )->await(); return; } ## end sub on_expire From 89c12815603d7d6604301cec6b446f15ff86a6ff Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 03:57:05 +0000 Subject: [PATCH 11/19] Return send_message future back to caller --- server/lib/PLS/Server.pm | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 099fb902..a78ce624 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -109,8 +109,7 @@ sub handle_client_message if (blessed($message) and $message->isa('PLS::Server::Response')) { - $self->send_message($message); - return; + return $self->send_message($message); } } ## end if (length $message->{...}) else @@ -163,9 +162,7 @@ sub send_message return if (not blessed($message) or not $message->isa('PLS::Server::Message')); my $json = $message->serialize(); my $length = length ${$json}; - $self->{stream}->write("Content-Length: $length\r\n\r\n$$json")->get(); - - return; + return $self->{stream}->write("Content-Length: $length\r\n\r\n$$json"); } ## end sub send_message sub handle_client_request @@ -181,8 +178,7 @@ sub handle_client_request if ($response->isa('PLS::Server::Response')) { - $self->send_message($response); - return; + return $self->send_message($response); } elsif ($response->isa('Future')) { @@ -197,12 +193,11 @@ sub handle_client_request sub { my ($response) = @_; - $self->send_message($response); - return; + return $self->send_message($response); } )->on_cancel( sub { - $self->send_message(PLS::Server::Response::Cancelled->new(id => $id)); + $self->send_message(PLS::Server::Response::Cancelled->new(id => $id))->await(); } ); @@ -244,18 +239,10 @@ sub handle_server_request $self->{pending_requests}{$request->{id}} = $request; } - $self->send_message($request); + $self->send_message($request)->await(); return; } ## end sub handle_server_request -sub handle_server_response -{ - my ($self, $response) = @_; - - $self->send_message($response); - return; -} ## end sub handle_server_response - sub cancel_request { my ($self, $id) = @_; From 2b1b6e34705c9957d88577173636b2264b4c1daa Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 04:10:51 +0000 Subject: [PATCH 12/19] Fix awaiting undef --- server/lib/PLS/Server.pm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index a78ce624..605e605d 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -146,7 +146,6 @@ sub send_server_request $request->on_done( sub { my ($request) = @_; - $self->handle_server_request($request); } ); @@ -159,7 +158,7 @@ sub send_message { my ($self, $message) = @_; - return if (not blessed($message) or not $message->isa('PLS::Server::Message')); + return Future->done() if (not blessed($message) or not $message->isa('PLS::Server::Message')); my $json = $message->serialize(); my $length = length ${$json}; return $self->{stream}->write("Content-Length: $length\r\n\r\n$$json"); @@ -192,7 +191,6 @@ sub handle_client_request my $future = $response->then( sub { my ($response) = @_; - return $self->send_message($response); } )->on_cancel( From 811fed2d8ea6930f87bc4dfe565f72511caa2504 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 04:12:18 +0000 Subject: [PATCH 13/19] Fix MANIFEST --- server/MANIFEST | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/MANIFEST b/server/MANIFEST index d5474f28..5cd06eb9 100644 --- a/server/MANIFEST +++ b/server/MANIFEST @@ -87,8 +87,8 @@ t/packets/cancel.json t/packets/configuration.json t/packets/didopen.json t/packets/exit.json -t/packets/formatting.json t/packets/initialize.json t/packets/initialized.json t/packets/shutdown.json +t/packets/sleep.json t/Communicate.pm From 47af3cd8195679e41ebb8e262c13e3ba60323622 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 04:22:04 +0000 Subject: [PATCH 14/19] Hide warnings --- server/lib/PLS/Server.pm | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 605e605d..4c41c5ec 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -17,6 +17,20 @@ use PLS::Server::Request::Factory; use PLS::Server::Response; use PLS::Server::Response::Cancelled; +# Install $SIG{__WARN__} handler to hide warnings coming from IO::Async and Future. +$SIG{__WARN__} = sub { ## no critic (RequireLocalizedPunctuationVars) + my ($warning) = @_; + + if ( $warning =~ m{Deep recursion on subroutine "Future.+(?:done|on_ready)"} + or $warning =~ m{Use of uninitialized value \$events in bitwise and (&).*IO/Async/Loop/Poll.pm}) + { + return; + } + + warn $warning; + return; +}; ## end sub + =head1 NAME PLS::Server From cef90735f9c99936afa7dc684ecfc575ba6ac8f0 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 04:24:13 +0000 Subject: [PATCH 15/19] Clean up imports --- server/lib/PLS/Server.pm | 3 --- 1 file changed, 3 deletions(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 4c41c5ec..342cf7bd 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -4,12 +4,9 @@ use strict; use warnings; use Future; -use Future::Queue; -use Future::Utils; use IO::Async::Loop; use IO::Async::Signal; use IO::Async::Stream; -use IO::Handle; use Scalar::Util qw(blessed); use PLS::JSON; From 6730db9492771877b9a4e9d3b5ebdd9bed07eb40 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 04:33:44 +0000 Subject: [PATCH 16/19] Add perltidy check to CI --- .github/workflows/server.yml | 19 +++++++++++-------- server/lib/PLS/Parser/Document.pm | 2 +- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.github/workflows/server.yml b/.github/workflows/server.yml index c76a65f3..f4c22c0a 100644 --- a/.github/workflows/server.yml +++ b/.github/workflows/server.yml @@ -3,21 +3,21 @@ name: Server CI on: push: branches: [master] - tags: '**' + tags: "**" paths: - - 'server/**' - - '.github/workflows/server.yml' + - "server/**" + - ".github/workflows/server.yml" pull_request: branches: [master] paths: - - 'server/**' - - '.github/workflows/server.yml' + - "server/**" + - ".github/workflows/server.yml" workflow_dispatch: defaults: run: - working-directory: 'server' + working-directory: "server" jobs: build_test_deploy: @@ -25,8 +25,8 @@ jobs: strategy: matrix: - os: ['ubuntu-latest', 'macos-latest'] - perl: ['5.40', '5.38', '5.36', '5.34', '5.32', '5.30', '5.28', '5.26'] + os: ["ubuntu-latest", "macos-latest"] + perl: ["5.40", "5.38", "5.36", "5.34", "5.32", "5.30", "5.28", "5.26"] name: Perl ${{ matrix.perl }} on ${{ matrix.os }} steps: @@ -48,6 +48,9 @@ jobs: - name: Run tests run: make test + - name: Check perltidy + run: (find lib -type f; find bin -type f) | xargs -n1 perltidy -ast -se -pro=../.vscode/.perltidyrc -st >/dev/null + - name: Build distribution run: make dist diff --git a/server/lib/PLS/Parser/Document.pm b/server/lib/PLS/Parser/Document.pm index 04806467..d1276e05 100644 --- a/server/lib/PLS/Parser/Document.pm +++ b/server/lib/PLS/Parser/Document.pm @@ -1414,7 +1414,7 @@ sub find_word_under_cursor or $_->type eq 'PPI::Token::QuoteLike::Regexp' or $_->type eq 'PPI::Token::QuoteLike::Command' or $_->element->isa('PPI::Token::Regexp'); - }; + }; ## end $predicate = sub my $element = first { $predicate->() or $_->type eq 'PPI::Token::Operator' } @in_range; my $closest_operator = first { $_->type eq 'PPI::Token::Operator' } grep { $_->lsp_column_number < $character } @elements; From 4d6c46b669a9bca5e8fe7b1fba2721ad8f898553 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 04:41:17 +0000 Subject: [PATCH 17/19] Add documentation to the fake sleep request/response --- server/lib/PLS/Server/Request/Sleep.pm | 11 +++++++++++ server/lib/PLS/Server/Response/Sleep.pm | 23 +++++++++++++++++++++-- server/t/packets/sleep.json | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/server/lib/PLS/Server/Request/Sleep.pm b/server/lib/PLS/Server/Request/Sleep.pm index c77ef115..0969c471 100644 --- a/server/lib/PLS/Server/Request/Sleep.pm +++ b/server/lib/PLS/Server/Request/Sleep.pm @@ -10,6 +10,17 @@ use IO::Async::Timer::Countdown; use PLS::Server::Response::Sleep; +=head1 NAME + +PLS::Server::Request::Sleep + +=head1 DESCRIPTION + +This is not a real language server request - it is only used for testing, +to start a request that takes an arbitrary amount of time to complete. + +=cut + sub service { my ($self) = @_; diff --git a/server/lib/PLS/Server/Response/Sleep.pm b/server/lib/PLS/Server/Response/Sleep.pm index cf14ac4e..c65086cb 100644 --- a/server/lib/PLS/Server/Response/Sleep.pm +++ b/server/lib/PLS/Server/Response/Sleep.pm @@ -8,6 +8,18 @@ use parent 'PLS::Server::Response'; use IO::Async::Loop; use IO::Async::Timer::Countdown; +=head1 NAME + +PLS::Server::Response::Sleep + +=head1 DESCRIPTION + +This is not a real language server response - it is only used for testing. + +It will wait the requested delay in seconds before returning an empty response. + +=cut + sub new { my ($class, $request) = @_; @@ -19,9 +31,9 @@ sub new my $loop = IO::Async::Loop->new(); my $future = $loop->new_future(); - $future->set_label('sleep'); + my $timer = IO::Async::Timer::Countdown->new( - delay => 10, + delay => $request->{params}{delay}, on_expire => sub { $future->done($self); }, @@ -30,6 +42,13 @@ sub new $timer->start(); $loop->add($timer); + $future->on_cancel( + sub { + $timer->stop(); + $timer->remove_from_parent(); + } + ); + return $future; } ## end sub new diff --git a/server/t/packets/sleep.json b/server/t/packets/sleep.json index f7aa31eb..f4064511 100644 --- a/server/t/packets/sleep.json +++ b/server/t/packets/sleep.json @@ -2,5 +2,5 @@ "jsonrpc": "2.0", "id": null, "method": "sleep", - "params": { "delay": 20 } + "params": { "delay": 10 } } From 06417a90f60f5ba1c9138917b035ad61507df4b2 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 05:02:47 +0000 Subject: [PATCH 18/19] Rename running_futures to pending_responses --- server/lib/PLS/Server.pm | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/server/lib/PLS/Server.pm b/server/lib/PLS/Server.pm index 342cf7bd..1d819e4f 100644 --- a/server/lib/PLS/Server.pm +++ b/server/lib/PLS/Server.pm @@ -53,10 +53,10 @@ sub new return bless { - loop => IO::Async::Loop->new(), - stream => undef, - running_futures => {}, - pending_requests => {} + loop => IO::Async::Loop->new(), + stream => undef, + pending_requests => {}, + pending_responses => {} }, $class; } ## end sub new @@ -196,7 +196,7 @@ sub handle_client_request if (length $id) { - $self->{running_futures}{$id} = $response; + $self->{pending_responses}{$id} = $response; } my $future = $response->then( @@ -256,7 +256,7 @@ sub cancel_request { my ($self, $id) = @_; - my $future = delete $self->{running_futures}{$id}; + my $future = delete $self->{pending_responses}{$id}; if (blessed($future) and $future->isa('Future')) { From bf90bd39c531beb3cb57dbae397c278080fa6152 Mon Sep 17 00:00:00 2001 From: Marc Reisner Date: Wed, 26 Feb 2025 05:08:47 +0000 Subject: [PATCH 19/19] Fix old use of $ROOT_PATH --- server/lib/PLS/Parser/PackageSymbols.pm | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/server/lib/PLS/Parser/PackageSymbols.pm b/server/lib/PLS/Parser/PackageSymbols.pm index 06fbb285..bd18945c 100644 --- a/server/lib/PLS/Parser/PackageSymbols.pm +++ b/server/lib/PLS/Parser/PackageSymbols.pm @@ -9,6 +9,7 @@ use IO::Async::Loop; use IO::Async::Process; use PLS::JSON; +use PLS::Util; =head1 NAME @@ -113,14 +114,7 @@ sub _get_setup { my ($config) = @_; - require PLS::Parser::Index; - - # Just use the first workspace folder as ROOT_PATH - we don't know - # which folder the code will ultimately be in, and it doesn't really matter - # for anyone except me. - my ($workspace_folder) = @{PLS::Parser::Index->new->workspace_folders}; - my $cwd = $config->{cwd} // ''; - $cwd =~ s/\$ROOT_PATH/$workspace_folder/; + my ($cwd) = PLS::Util::resolve_workspace_relative_path($config->{cwd}, undef, 1); my @setup; push @setup, (chdir => $cwd) if (length $cwd and -d $cwd);