Repository navigation
Conversation
Since text synchronization notifications were moved to the worker queue (Shopify#3637), the reader thread could parse a document for an incoming request before the didChange edits queued ahead of it were applied. The worker then applied the edits (clearing the cache) and ran the request against the outdated parse result, caching the stale response until the next edit. This shows up as syntax error diagnostics that persist after the code is fixed. Move the parse (and the ERB virtual state delegation) into the worker, right before the request is processed, so it always reflects every edit received before the request. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Since #3637, text synchronization notifications (
didOpen/didChange/didClose) go through the worker queue so they are processed in order with requests. But the document is still parsed in the reader thread inBaseServer#start, as soon as a request is read. If that request arrives while adidChangeahead of it is still waiting in the queue, this happens:parse!on the document's old source and sets@needs_parsing = false.@needs_parsing = trueand clears the cache.parse_result. FortextDocument/diagnostic, the stale response is also stored withcache_fetch.Because of the cache, pulling diagnostics again returns the same stale result until the next edit. In practice, a syntax error from Prism stays visible after the code is fixed. It's most noticeable with edits that make the code invalid for a moment and then fix it right away, like adding surrounding delimiters. Clients that send
didChangeand then pull diagnostics immediately, such as Neovim's built-in pull diagnostics, hit this regularly.Implementation
textDocument.urito aURIobject.delegate/textDocument/virtualStatenotification that depends on it, moved into a new privateBaseServer#parse_document. The worker calls it inhandle_incoming_message, after the cancellation check and right beforeprocess_message. Edits and requests are processed in order on the same worker, so the parse always sees every edit received before the request. The parse still runs under@global_state.synchronize.Store::NonExistingDocumentError.One change for reviewers: the parse now runs when the worker picks up the request instead of when the message is read. Requests that are cancelled while waiting in the queue are no longer parsed at all.
Automated Tests
Added
ServerTest#test_requests_parse_the_document_after_queued_edits_are_applied. It sendsdidOpen(def foo), adidChangethat adds\nend, and atextDocument/diagnosticrequest through the realServer#startreader loop. It asserts that no diagnostics are returned. Without the fix the test fails every time withexpected a delimiter to close the parameters. With the fix it passes.Manual Tests
The bug depends on timing, so it's easiest to reproduce with a client that pulls diagnostics right after each change:
def fooon its own, which produces a syntax error.end, or use a surround or auto-pair action that inserts both delimiters together.🤖 Generated with Claude Code