purefunctor / purefunctor/purescript-iris
Queue analysis requests until the workspace is prepared
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 102
- Forks
- 11
- Avg merge
- 3h 30m
- Merged PRs (30d)
- 138
Description
Context
iris lsp prepares the Spago workspace on startup (spago fetch, then discovery and the initial compilation) before serving analysis. While it prepares, analysis requests are answered with ContentModified and the message Workspace is loading, which is the correct retryable response.
Problem
Not every client retries. VS Code advertises general.staleRequestSupport.retryOnContentModified only for semanticTokens/full|range|delta, so textDocument/documentSymbol receives the default value instead. VS Code's outline model service then caches an empty outline for that document version, and LSP defines no document-symbol refresh, so the outline stays empty until the document changes.
Proposal
Queue the requests that the editor issues proactively and caches until the workspace is installed, then answer them from the real snapshot.
- Add a readiness value owned by the workspace runtime:
Loading | Ready(Arc<Analysis>) | Failed. installpublishesReady;failpublishesFailed.- A deferred request awaits readiness, then takes a snapshot and runs its handler on a blocking thread.
- Route
textDocument/documentSymbolthrough the deferred path, and checktextDocument/semanticTokens/full. - Keep
ContentModifiedfor user-initiated methods (hover,definition,references,completion,rename,workspace/symbol), where a fast retryable response is better than a long-pending request.
enum Readiness {
Loading,
Ready(Arc<Analysis>),
Failed,
}
async fn wait_for_readiness(
mut readiness: watch::Receiver<Readiness>,
) -> Result<Arc<Analysis>, LspError> {
let readiness = readiness
.wait_for(|readiness| !matches!(readiness, Readiness::Loading))
.await
.map_err(|_| LspError::WorkspaceFailed)?;
if let Readiness::Ready(analysis) = &*readiness {
return Ok(Arc::clone(analysis));
}
Err(LspError::WorkspaceFailed)
}
watch::Receiver::wait_for parks the waiter on the channel until a value satisfies the predicate, so a request does not poll while preparation runs. The receiver sees the transition at most once, because Readiness only moves from Loading to Ready or Failed. Dropping the sender on shutdown closes the channel, which makes wait_for return RecvError.
Behaviour
Failedresolves waiters withRequestFailed; shutdown drops the sender and waiters fail out.- Client cancellation aborts the pending future; async-lsp's concurrency layer already answers it with
RequestCancelled.
Tests
- Gated e2e: hold
spago fetch, issue a realtextDocument/documentSymbol, assert it stays pending, release, and assert it returns the expected symbol without an edit. - Unit: readiness waiters resolve on install and on failure.
References
- LSP 3.17 cancellation and
ContentModified. vscode-languageclienthandleFailedRequest,RequestsToCancelOnContentModified.- VS Code outline model service caching by model id and version id.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in the workspace runtime around the existing install and fail transitions, then inspect the documentSymbol and semanticTokens/full request handlers and their ContentModified behavior. Use the proposed readiness tests and gated end-to-end test as the initial checks; done means deferred requests wait during preparation, resolve or fail correctly, and document symbols arrive without an edit.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, devtools
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100