-
Notifications
You must be signed in to change notification settings - Fork 324
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
Semantic highlighting flickers #576
Comments
Monaco itself seems to be okay based on some random testing I just did in my editor so it may be something at the LSP level? |
Found another related with content-modified cancellation: if highlighting is in progress and then some outside change occures (for example, file on disk changes), it gets canceled and is not retried. This is diffrenet from the case where the modification is triggered by the user, because in that case we retry and get the correct highlighting. For rust-analyzer, the first case happens on startup: when we ingest project files from disk, we trigger internal modifications and cancel highlighting request. This specific case can and should be handled on our side by delaying requests until the whole project is loaded, but the general issue of missed highlighting due to unrelated changes still exists. |
The VS Code editor actually shifts the tokens automatically. @alexdima what is the suggested way to inform the editor about a rejected sematnic token request. Should I simply rethrow or do you have a special error code that I can use to inform you to retrigger the request later (e.g. the busy case we discussed). |
the current implementationReturning about the 'busy'@dbaeumer I've talked with @jrieken and in the vscode API we don't have a clear way to specify this "busy" situation. You mentioned you also think LSP should have a "busy" concept, not sure what would be the way to express that in LSP? How should that be better expressed in the vscode API? I am not very happy with an error with a certain message... |
Confirmed that throwing busy fixes the fickering: rust-lang/rust-analyzer#3339. Also confirmed that it doesn't fix the second problem with the absence of retry behavior. Ie, the client sends one semanticTokens request, it fails with busy, and the file remains not highlighted until I type something, thus triggering the request again. |
@matklad You're correct. After an error, a new request is issued only if text changes occurred in the meantime -- https://github.com/microsoft/vscode/blob/8758dc9dddf95f358eb93c969353e5cf245ed1e4/src/vs/editor/common/services/modelServiceImpl.ts#L796-L810 But why do you throw |
@alexdima the fundamental issue here is that highlighting of a file might be invalidated by changes to other files. For example, while the server computes the highlighting, the client might change git branches in the terminal (which modify some unrelated files), and the server gives up highlighting because it is now obsolete. Another hypothetical case is "I can't highlight this file right now, I am waiting for this library to be downloaded from the Internet". In the specific case of rust-analyzer, this behavior is an artifact of an implementation. We have a single data structure, global state, which is protected by rw lock. Highlighting and other features work under r lock, any modification needs a w lock. When a new change arrives, we cancel all in-process r operations, which causes all pending requests to fail with "content-modified" error. One motivation for this is that, if the state has changed, the results of requests could be obsoleted anyway. What is the correct retry behavior is an interesting question. The answer is clear in the push model: if the client registers an interest in a file, server responds with highlights as soon as quiescent state is reached (ie, there are no pending in-flight changes). For pull model, I am not sure what's the right behavior. "retry indefinitely without delays", paradoxically, seems the theoretically right answer at least for rust-analyzer's architecture? Requests only get cancelled if modification arrives afterwards; if there are some pending modifications, and a new requests arrives, it's just scheduled after the modifications. |
For the pull model we usually solve this by allowing providers to have an event that signals a change. The |
That event still isn't available in the LSP though / requires nasty hacks. :( microsoft/language-server-protocol#192 |
@jrieken we discussed that a while back ago and I somehow thought that having a busy indication to signal that a server is currently not able to answer the questions is a good idea. IMO this is even unrelated to having an event to invalidate persented data and ask the client to refetch the data. @alexdima a model that would make sense to me is as follows:
I would indicate Busy through a special Error class (comparable to I can of course simulated this with a change event. However this has the disadvantage that the exension has no knowledge about part visibility and therefore might trigger change events although no data is presented. |
Sure, I am still in agreement with that but we decided to accept "any" error and not clear semantic highlights in its presence. However, I don't see how the error solves the case of two editors where typing in A causes changes in B - for that I can only image the event. |
With microsoft/vscode@c2a604a , I've added support for a change event from the provider. |
Regarding the busy indication: I discussed this with @alexdima again and noticed that naming it busy is pretty misleading. A better name would very likely be server side cancelation of an operation. @jrieken: what is your take on this. However it still doesn't solve how we would communicate this (e.g. using an exception or a special return object). |
One possible way out of this design question is to hide the retry loop inside the |
I still think that this might not work since even the LSP library needs to give up after some tries and the problem remains what to return then. If the provider has registered an edit provider I think it is easy since we could return an empty edit. If it didn't then all that remains is throwing an exception. So I would still like to agree on what to do if a NON edit request times out. One possible idea would be to allow |
I don't think The VS Code request clearly expects the reply for a certain project state, the project state at which the request was made. The whole point of the server canceling is that the server believes it makes no sense to answer the request at that certain state and the client should recreate a request at a new project state. This is nothing For example, for semantic tokens, when a request is made, text buffer edits are collected, such that when the response comes back, the response is adjusted against the user edits that occurred in the meantime. When a request is canceled (completed via a busy error), the collected text buffer edits are dropped and a new request is made, which is expected to return results at that new state. |
@alexdima makes perfect sense. So we don't to agree on how to cancel. Should that be an exception or |
@jrieken @dbaeumer Here is an idea. Currently, when a request is canceled by the client, we kind of expect and ignore (in error telemetry, etc.) any error with the message So what if we just extend this and allow that a |
Well, only kind of. The canceled-error is usually generated by a utility that we own and generally all provider triggered errors are ignored/dropped. Anyways, it doesn't invalidate your idea of supporting "cancellation from both ends", e.g today only client cancels but with that the server can also cancel. But it does require us to spec this kind of error. |
Tracking in microsoft/vscode#93686 ... We will bolt it on after March since technically it isn't a breaking change, we will for now support errors with the message |
@alexdima thanks. |
@matklad do we still need the workaround: async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken, next: DocumentSemanticsTokensSignature) {
const res = await next(document, token);
if (res === undefined) throw new Error('busy');
return res;
} Now that microsoft/vscode@c2a604a is in? |
I din‘t think so
…On Sunday, 9 August 2020, Jeremy Kolb ***@***.***> wrote:
@matklad <https://github.com/matklad> do we still need the workaround:
async provideDocumentSemanticTokens(document: vscode.TextDocument, token: vscode.CancellationToken, next: DocumentSemanticsTokensSignature) {
const res = await next(document, token);
if (res === undefined) throw new Error('busy');
return res;
}
Now that ***@***.***
<microsoft/vscode@c2a604a>
is in?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#576 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AANB3M24D6UFVH7EUFL5WPDR74HETANCNFSM4K4IJASQ>
.
|
See: microsoft/vscode-languageserver-node#576 (comment) This has been fixed since vscode 1.44
5697: Remove workaround for semantic token flickering r=jonas-schievink a=kjeremy See: microsoft/vscode-languageserver-node#576 (comment) This has been fixed since vscode 1.44 Co-authored-by: Jeremy Kolb <[email protected]>
@kjeremy but the event is not yet in the protocol. |
@alexdima do we have a final story for the |
Not fully finished -- microsoft/vscode#93686 |
@alexdima can we make progress on this in September so that I can do that in LSP. |
@jrieken To be honest, this is more on your plate, since such an error would be relevant to any of the other providers. Would you be willing to drive this since it has so much more impact than the semantic tokens provider? |
@jrieken any additional insights here? |
No update, work will happen here: microsoft/vscode#93686 |
Moving to 3.17 since no progress in VS Code so far. |
@kjeremy yes it should have improved it. I still need to convert the code to the new CancelError from VS Code API. |
Throwing |
microsoft/vscode-languageserver-node#576 has been closed with the latest vscode-languageclient release.
This picks up the fix for microsoft/vscode-languageserver-node#576 which fixes an issue where semantic highlighting flickers. The patch also makes a corresponding update to API usage.
This picks up the fix for microsoft/vscode-languageserver-node#576 which fixes an issue where semantic highlighting flickers. The patch also makes a corresponding update to API usage.
I've tried new semantic highlighting API in rust-analyzer (which was implemented by @kjeremy). I am super excited to see this feature finally in the LSP!
However, test drive shows this flickering:
I am not exactly sure, but I think the reason here is content modifies errors:
The editor asks for highlights, and then continues with sending modifications. Server than cancels the in-flight highlighting request. This causes the provider to return
undefined
, and that apparently clears the previous highlights. Because user continues typing, the highlighting is not re-applied until the user stops and the server is able to catch up.It seems like the better behavior in this case is to just shift previous highlighting ranges, instead of clearing them. Not sure if this something to be fixed in vscode-languageserver-node, vscode itself, or the API for SemanticTokensProvider.
The text was updated successfully, but these errors were encountered: