Skip to content
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

Add support for explicitly requesting diagnostics #737

Closed
w0rp opened this issue May 1, 2019 · 32 comments
Closed

Add support for explicitly requesting diagnostics #737

w0rp opened this issue May 1, 2019 · 32 comments
Labels
diagnostics feature-request Request for new features or functionality implemented
Milestone

Comments

@w0rp
Copy link

w0rp commented May 1, 2019

The Language Server Protocol sends diagnostics to clients as notifications. These notifications are typically sent in response to textDocument/didChange or textDocument/didSave, but they could arrive at any time. Clients have no control over when or if diagnostics will be sent to them. This lack of control causes problems for clients.

  1. Clients cannot indicate if there there are pending diagnostics. Because they can arrive at any time, or may never arrive, you cannot indicate if there are diagnostics being calculated or not.
  2. Clients cannot easily control when diagnostics should be displayed. A document update in order to retrieve accurate completion results may result in diagnostics being sent, for example.
  3. Servers can send clients diagnostics for files they might not be interested in, such as files that are not open in client editors. This results in clients having to read more data than they need.

I think it would be a good idea to add an optional mode to the protocol to disable sending diagnostics to clients unless they are explicitly requested for a file. An optional attribute could be added to InitializeParams which when enabled would tell servers not to send diagnostics until a new message type textDocument/getDiagonistics is sent. ServerCapabilities could include an attribute for indicating if this mode is supported. Servers would then be required to respond to clients with textDocument/publishDiagnostics with either an empty diagnostics array, or previously computed results.

For my LSP client ALE, this would mean that ALE will be able to update the Vim status bar to indicate that diagnostic results from language servers are pending, and to avoid requesting diagnostics when the user only wants to get some completion results. Currently ALE is able to do this for tsserver, because the tsserver protocol requires the use of a geterr command, but this is unfortunately not possible with language servers.

Let me know what you think. Thank you for the protocol.

@dbaeumer
Copy link
Member

dbaeumer commented May 7, 2019

@aeschli we discussed something like this awhile back as well.

@w0rp the guarding of such a capability would need to be done in the client and server capabilities, not in the InitializeParams. However if servers support that mode they shouldn't push any diagnostics anymore.

One tricky part could be is how to clear diagnostics on document close. Clients would need to send a corresponding request in this case.

@aeschli
Copy link
Contributor

aeschli commented May 7, 2019

IMO we need:
a. a new request textDocument/getDiagonistics for the client to request diagnostics for a specific URL from the server
b. a way to configure the server whether and for which URLs the server it should publish diagnostics. This will likely be a selector as the client doesn't know all URIs that can get diagnostics.

However if servers support that mode they shouldn't push any diagnostics anymore.

I don't think a. and b. should be coupled. The user can configure it that way, but you can also think of a usage where the problems view is fed by published diagnostics (as now) but there are also programmatic requests for diagnosics of a specific document for other another use cases.

@dbaeumer
Copy link
Member

I don't think a. and b. should be coupled.

After reading this again I agree.

@w0rp
Copy link
Author

w0rp commented May 23, 2019

I agree that the explicit diagnostic requests and disabling automatic publishing of diagnostics in response to notifications like textDocument/didChange can be implemented as two separate features.

@kdvolder
Copy link

FWIW: The setup where server 'pushes' diagnostics works just fine from our point of view. And this also fits with situations where the server performs something like workspace builds, and then pushes any build-related problems as 'diagnostics' to the client. Such workflow is not driven by the client, but by the server.

I do also agree that there may be situations where a pull model makes more sense. I'm fine with adding that as long as its a 'opt-in' sort of thing and we also keep the 'push' model as it exists now.

@w0rp
Copy link
Author

w0rp commented May 23, 2019

I think both are valid use cases, for different purposes.

@dbaeumer
Copy link
Member

Just to clarify: it is not our intention to deprecate the push model. Pull would be simply another way to get to diagnostics.

@apexskier
Copy link
Contributor

apexskier commented Aug 25, 2020

I have another use case for this. For https://github.com/apexskier/nova-typescript/, the editor manages the language server entirely, and intercepts diagnostic notifications. When requesting code actions, I need to pass diagnostics to the server to receive all actions, but I can't because I currently don't have a way to know the diagnostics. I'd say this is more of a Nova issue - it should provide a way for me to get a list of diagnostics, since it's already managing them, but this would be another way to solve it.

@gwerbin
Copy link

gwerbin commented Feb 19, 2021

Would it make sense if the client could "filter" diagnostics, in addition to adding the "client pull" capability?

For example, the client could ask the server to not pull diagnostics matching tag = "Linting" and code ∈ (E328, E089), and then could later specifically pull an unfiltered diagnostic when the user wants it.

This is like the difference between a "signal" and a "method" in D-Bus.

@w0rp
Copy link
Author

w0rp commented Feb 19, 2021

If there's ever the ability to filter which type of diagnostics are received, I think it's worth implementing that after being able to just pull everything at first, so we can see that by itself finished sooner.

@karlhorky
Copy link

@dbaeumer since an implementation "got published", is there any documentation about how Diagnostic Pull proposal can be used?

Would be interesting for extensions such as Error Lens usernamehw/vscode-error-lens#77

@dbaeumer
Copy link
Member

Since the implementation is still proposed I leep the doc with the implementation. It is here: https://github.com/microsoft/vscode-languageserver-node/blob/main/protocol/src/common/proposed.diagnostics.md#L1

@dbaeumer dbaeumer modified the milestones: Backlog, 3.17 Oct 28, 2021
@resolritter
Copy link

resolritter commented Dec 21, 2021

I've noticed a problem with the current protocol: a client is not able to know for sure if a diagnostics message reflects the latest document changes. I was following this ticket in hopes it would be a solution, but upon revisiting the proposal, I don't think it will.

From https://github.com/microsoft/vscode-languageserver-node/blob/2020cb4254abe91de1749cecad8283d397eea0b7/protocol/src/common/proposed.diagnostics.md?plain=1#L76:

As with other pull requests the server is asked to compute the diagnostics for the currently synced version of the document.

"Currently synced version" is imprecise. Is there no way for the server to communicate which version of the document the diagnostics are for? Consider the following scheme:

  1. Client detects changes to the document
  2. Client updates the document and generates a new ID for latest document version
  3. Client notifies the server
  4. Server computes the diagnostics for the document
  5. Server responds with diagnostics + the document ID for which the diagnostics were computed

That way the client can ignore the diagnostics if it is not matching the latest ID (effectively means the diagnostics are outdated).

Without a way of precisely knowing which version of the document the diagnostics are for, the client is forced to rely on the assumptions of:

The latest diagnostics are up-to-date because

  1. The server should have cancelled any previous diagnostic processing as soon as it was notified of the document update and
  1. The server should have received the latest document updates before it responded

Therefore this message can't possibly refer to an old version

As far as I am aware those assumptions are not guaranteed by the protocol, nor it means the latest diagnostics surely incorporate the latest document changes, as demonstrated by the following scheme:

  1. Client detects changes to the document
  2. Client updates the document
  3. Server is already computing some diagnostics in the meantime
  4. Client notifies the server
  5. Before the client's message arrives, the server responds with outdated diagnostics

I was thinking the pull model could be a solution to this problem because the client could request diagnostics again if the document had changed before the server replied. Even despite of that, as I understood the proposal, however, the major flaw of "a client is not able to know if the diagnostics incorporate the latest document changes" still exists and is subject to transport delays and failures in the client <-> server communication.

@sam-mccall
Copy link
Contributor

"Currently synced version" is imprecise. Is there no way for the server to communicate which version of the document the diagnostics are for?

With all request/response pairs on a document, the response pertains to the version of the file that was valid when the request was sent (e.g. the last didChange notification before the request).

As far as I am aware those assumptions are not guaranteed by the protocol

I assure you that implementations rely on them. If that guarantee isn't clear enough then it should be made clearer, but adding a second way to determine the version is going to create confusing.

@resolritter
Copy link

resolritter commented Dec 21, 2021

With all request/response pairs on a document, the response pertains to the version of the file that was valid when the request was sent (e.g. the last didChange notification before the request).

So the client first has to flush all change events related to the document before sending the request. Makes sense. Now here's the question, does the client always know that the changes' messages are successfully delivered before the diagnostics request is made? Please bear in mind I'm no expert in the supported network protocols, but as far as I'm aware not all of them have an "ACK" mechanism like TCP does.

I assure you that implementations rely on them. If that guarantee isn't clear enough then it should be made clearer, but adding a second way to determine the version is going to create confusing.

On the server pushes model, even if "The server should have cancelled any previous diagnostic processing as soon as it was notified of the document update" is being followed in the wild (unclear if it is; server implementers might think it is fine to send the ongoing ones first and queue the latest ones for later), in the scenario I brought up above:

  1. Client detects changes to the document
  2. Client updates the document
  3. Server is already computing some diagnostics in the meantime
  4. Client notifies the server
  5. Before the client's message arrives, the server responds with outdated diagnostics

Suppose the client is able to work around this scenario through the following strategy: "As soon as I detect document changes locally, then ignore all diagnostics the server pushes for this document until I have flushed all changes notifications" which sounds fine but then, again, do we have this "ACK" delivery guarantee on all supported transport methods?

For all the problems I brought up so far, a document content ID seems like the simplest solution. Perhaps it's not required though.

@sam-mccall
Copy link
Contributor

So the client first has to flush all change events related to the document before sending the request. Makes sense. Now here's the question, does the client always know that the changes' messages are successfully delivered before the diagnostics request is made? Please bear in mind I'm no expert in the supported network protocols, but as far as I'm aware not all of them have an "ACK" mechanism like TCP does.

My interpretation: the client gets to assume that the change messages are delivered before the diagnostics request is delivered.
This requires that:

  • the client sends the change messages first.
  • the network protocol preserves the order of client->server messages, and guarantees delivery or an error state.
  • the server handles messages in order. (Even if it ultimately does async work and responds out-of-order)

The spec doesn't explicitly say what protocols are supported, but it mentions stdin/stdout, windows pipes, unix domain sockets, and network sockets. These protocols do preserve order of messages in each direction (unless we're talking about UDP sockets). Note that there's no requirement that the client->server message stream and the server->client message stream are synchronized in any way.

I think it would be great if the spec said something about the constraints on the transport protocol, because JSON-RPC itself doesn't have this requirement (it seems perfectly sensible to have a stateless JSON-RPC server over UDP or something).

@resolritter

This comment has been minimized.

@sam-mccall
Copy link
Contributor

does not know if the response refers to the first or second request.

JSON-RPC requests include a mandatory request ID, and responses include the ID of the request they're associated with.

@resolritter
Copy link

JSON-RPC requests include a mandatory request ID, and responses include the ID of the request they're associated with.

I was not aware of that. Thank you.

All explanations considered, my takeaway for the time being is that it'll be possible to get away without content IDs. I'll keep thinking about the scenarios to see if there's one which is not covered by the current proposal.

@dbaeumer
Copy link
Member

@sam-mccall thanks a lot for helping @resolritter with this!

@aral
Copy link

aral commented Feb 8, 2022

@dbaeumer Hey Dirk, do you have any idea when 3.17 might land in VSCode?

(Going with the request forwarding method for embedded languages for my extension that splits a page into server-side JS and Svelte virtual documents and was just wondering when-ish I could expect to implement diagnostics with this approach.)

@rcjsuen
Copy link
Contributor

rcjsuen commented Feb 8, 2022

@dbaeumer Hey Dirk, do you have any idea when 3.17 might land in VSCode?

There is a client/src/common/proposed.diagnostic.ts file. Are you encountering bugs with the implementation?

@dbaeumer
Copy link
Member

Added to 3.17 which shipped today :-)

@w0rp
Copy link
Author

w0rp commented May 12, 2022

I just saw this now, and this is great news. This will be a game changer for editors.

@ejgallego
Copy link

That's great! IIUC however the Document Diagnostic request doesn't include a document point.

Do you think that is something that could be added in 3.18? It would be useful for us as to be able to check the document part that is near the cursor (a common use case in our context, due to slow checking times)

@dbaeumer
Copy link
Member

You usually can infer that from the latest changes made to the document.

In general, in LSP we stay away from syncing UI state (cursor, open views, ...)

@ejgallego
Copy link

I had understood from #718 (comment) that the current visible range would be acceptable; IMHO that's a good hint. [So I assume the range here would be akin to the position that is sent in most other requests, like hover]

Inferring the view from changes is actually quite brittle, systems that implement viewport diagnostics usually want them refreshed on scroll, and for the use cases of Isabelle and Coq that's what you want, as the user moves ahead in a document you want to schedule the checking for that part.

@dbaeumer
Copy link
Member

@ejgallego yes, that is something we can add since we sent this in other situations as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
diagnostics feature-request Request for new features or functionality implemented
Projects
None yet
Development

No branches or pull requests