-
Notifications
You must be signed in to change notification settings - Fork 183
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
support canceling pending completions request #2177
Conversation
First call hierarchy, then this! 🙂 Rafal can you suggest a server where this can be tested? |
@@ -1969,6 +1969,12 @@ def send_request_task(self, request: Request) -> Promise: | |||
self.send_request_async(request, resolver, lambda x: resolver(Error.from_lsp(x))) | |||
return promise | |||
|
|||
def send_request_task_2(self, request: Request) -> Tuple[Promise, int]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name is kinda silly but I needed a variant that returns request_id
and naming is hard.
The alternative could be to pass request_id
"by reference" through an optional argument but I've chosen this approach in the end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently the request_id is stored on the session,
but if would store the id on the Request
class the send_request_task_2
could be removed.
I did a quick experiment here -> d266190
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did think about it like that but implementing it without hacks seemed like too much work. I don't like the approach with a global. Globals are rarely a correct way to do things. Also it's not thread-safe since we are creating Request
instances both on the main and async thread.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You know what, this function is not that bad.
It is the same as the above function, not that complicated, and the only difference is the return type.
If you find a way to remove it, good, if not, it still ok.
I'm adding support for canceling in For now you could test in LSP-volar or LSP-typescript in some context where completion request is slow and returns a lot of completions. In that case if you quickly type let's say "x " (where the second character is a space) then you should trigger two completion requests and the first one should get canceled. For example this is a log of such action: For testing it would also be useful to log the |
Note that there is open core issue for the fact that ST doesn't re-trigger |
Sorry for the late reply, Here are just my investigation: Some things I have read about the specHere is what the spec says: // method: ‘$/cancelRequest’
interface CancelParams {
/**
* The request id to cancel.
*/
id: integer | string;
}
Right now, the current PR:
This is ok.
If the server does support canceling, it can return partial results. For me the b) option is better. I like the PR The current PR handles only completions, I have an idea but haven't tried it. Lets look at the Session(LSP/plugin/core/sessions.py): class Session(TransportCallbacks):
def __init__(...):
...
+ self.pending_requests = {
+ # key: method_name, value: request_id
+ }
def send_request_async(self, request: Request,...):
+ # add code to cancel tokens
+ pending_id = self.pending_requests.get(request.method
+ if pending_id:
+ send_cancel_request(pending_id)
self.request_id += 1
+ self.pending_requests[request.method] = self.request_id
request_id = self.request_id
if request.progress and isinstance(request.params, dict):
request.params["workDoneToken"] = _WORK_DONE_PROGRESS_PREFIX + str(request_id)
def deduce_payload(self, payload...):
if "method" in payload:
method = payload["method"]
req_id = payload["id"]
+ if req_id == self.pending_requests.get(method):
+ self.pending_requests[method] = None If we known that a request that got canceled still needs to return from the server and send a response back, Would you consider such approach? Also note, I would not do any changes that I wrote in this PR, |
IMO partial results don't apply in this case because we are only canceling completion requests if another one was made so the results from the canceled one don't apply anymore. (Also ST wouldn't even show results for that completion list since it already made new completions query)
Just using the request method as a way of telling whether request can be canceled is not enough since there can be multiple requests with the same method name but made for different files, for example. In which case we'd need to key by method+uri. And the logic might need to be request-specific to know what to key on. Certainly out of scope of this PR anyway. |
* main: (80 commits) Perform inlay hint action on double instead of single click (#2175) support canceling pending completions request (#2177) Implement type hierarchy request (#2180) fix stale state or lack of updates on changing branches (#2182) Add timestamp and request duration in LSP logs (#2181) don't show diagnostics panel on save by default (#2179) trigger "on_server_response_async" also for "initialize" response (#2172) feat(API): allow setting additional text in permanent server status (#2173) Implement call hierarchy request (#2151) workaround for View Listeners not being attached for new transient view (#2174) Make Document Symbols behavior more consistent with built-in Goto Symbol (#2166) docs: fsautocomplete config - remove redundant argument (#2165) Allow missing window/workDoneProgress/create request from the server (#2159) Use "plaintext" language ID for plain text files (#2164) Improve type annotations (#2162) Don't use "escapeall" extension when formatting with mdpopups (#2163) Cut 1.21.0 Fix inlay hint parts wrapping into multiple lines (#2153) Ensure commands triggered from minihtml run on correct view (#2154) Add "Source Action" entry to the "Edit" main menu (#2149) ...
Adds support for canceling pending completions requests when ST triggers
on_query_completions
handler while one is currently in progress.Created
QueryCompletionsTask
(not one of SaveTask's, the naming similarity is just coincidental) class that encapsulates thetextDocument/completion
requests and makes it possible to cancel them thanks to having enough context.Canceling the completion task means that:
textDocument/completion
requests are canceled with$/cancelRequest
.This can improve typing experience dramatically if the server is slow and especially if the responses are also very big (50MB is not unheard of in Typescript). Previously it was possible to trigger multiple requests one after each other which would queue and get processed as they come, resulting in a big UI lag. With this change only the latest response will be processed.
I will also add support for canceling completion request to typescript-language-server which will further help this case.