diff --git a/E2E_BUGS.md b/E2E_BUGS.md index 08b04f3..c78958d 100644 --- a/E2E_BUGS.md +++ b/E2E_BUGS.md @@ -32,11 +32,32 @@ regression still fails loudly. typescript-language-server (js/ts). This is the single most common exception across the suite. -2. **Workspace-symbol search (`grep`) returns nothing before indexing - finishes.** clangd, vtsls, pylyzer, pyright, EmmyLua all report empty - `matches` for `workspace/symbol` queries issued immediately after startup, - since none exposes a synchronous "ready" signal — the server hasn't - indexed the workspace yet when the query fires. +2. ~~**Workspace-symbol search (`grep`) returns nothing before indexing + finishes.**~~ **Fixed for clangd, vtsls, and pyright.** clangd, vtsls, + pylyzer, pyright, and EmmyLua used to report empty `matches` for + `workspace/symbol` queries issued immediately after startup, since none + exposes a synchronous "ready" signal. This turned out to be the same + class of bug as item 3 below rather than a pure server quirk: + `run_workspace_symbol_query` (`src/commands/symbol_query.rs`) already had + a prime-and-retry path (`prime_workspace_document`, opening a workspace + file to give the server something to index) but it only triggered when + the first `workspace/symbol` call *errored* — an empty-but-successful + `[]` response was accepted as final. Changed the condition to also prime + and retry on an empty result, with a short poll (a few hundred-ms-spaced + attempts) after priming since indexing an opened document is still + asynchronous. Verified against live clangd (`E2E_CASES="cpp/clangd,c/clangd"`, + also spot-checked `objc`/`objcpp`/`cuda`, which share clangd's exact code + path): `grep Order` now reliably returns real matches instead of `[]`, + even from a cold index, across repeated runs. Removed the `grep` + empty-matches exceptions for clangd (cpp/c/objc/objcpp/cuda), vtsls + (typescript/javascript), and pyright/pylyzer (python) — the same fix + applies architecturally to all of them since they're all document-driven + analyzers with no true "ready" signal. vtsls, pyright, and pylyzer + weren't available to verify locally in this sandbox, so those removals + are provisional pending CI; if any disagrees, revert just that server's + exception (same discipline as the Rust `callees` revert below). EmmyLua's + `grep` exception was left in place — untouched pending separate + investigation. 3. ~~**ts_ls (typescript-language-server) "No Project" errors.**~~ **Fixed.** For both TS and JS, ts_ls used to throw `No Project` on `grep`, diff --git a/src/commands/symbol_query.rs b/src/commands/symbol_query.rs index 4e208fc..ac0916a 100644 --- a/src/commands/symbol_query.rs +++ b/src/commands/symbol_query.rs @@ -25,6 +25,9 @@ mod tests; use kinds::{CallHierarchyDirection, LocationQueryKind}; +const PRIME_RETRY_ATTEMPTS: u32 = 5; +const PRIME_RETRY_DELAY: Duration = Duration::from_millis(750); + pub(super) use render::{ render_file_list_json, render_list_symbols_json, render_paths_text, render_symbol_match_paths_text, render_symbol_matches_text, render_symbol_names_text, @@ -66,15 +69,36 @@ pub(super) fn run_workspace_symbol_query( config, |workspace, initialize, client| { ensure_workspace_symbol_support(initialize)?; - let response = if let Ok(response) = client.workspace_symbol(query) { - response - } else { - prime_workspace_document(&args.query.directory, config, workspace, client)?; - client.workspace_symbol(query).map_err(|error| { + let matches = client + .workspace_symbol(query) + .ok() + .map(|response| symbol_matches_from_response(&response)) + .transpose()? + .unwrap_or_default(); + if !matches.is_empty() { + return Ok(matches); + } + + prime_workspace_document(&args.query.directory, config, workspace, client)?; + + // A freshly opened document can take a moment for the server to + // fold into its workspace-wide symbol index (e.g. clangd indexes + // headers pulled in by the opened file asynchronously), so poll + // briefly rather than giving up on the first still-empty result. + let mut matches = Vec::new(); + for attempt in 0..PRIME_RETRY_ATTEMPTS { + if attempt > 0 { + std::thread::sleep(PRIME_RETRY_DELAY); + } + let response = client.workspace_symbol(query).map_err(|error| { error.with_prefix(format!("failed to query {}", workspace.server.server)) - })? - }; - symbol_matches_from_response(&response) + })?; + matches = symbol_matches_from_response(&response)?; + if !matches.is_empty() { + break; + } + } + Ok(matches) }, )?; diff --git a/tests/e2e/cases/c.yaml b/tests/e2e/cases/c.yaml index 861f1b6..38dede4 100644 --- a/tests/e2e/cases/c.yaml +++ b/tests/e2e/cases/c.yaml @@ -16,9 +16,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: clangd may not publish workspace symbols before background indexing completes - command: build-index outcome: failure message: background-work progress diff --git a/tests/e2e/cases/cpp.yaml b/tests/e2e/cases/cpp.yaml index 35920f1..2056c75 100644 --- a/tests/e2e/cases/cpp.yaml +++ b/tests/e2e/cases/cpp.yaml @@ -16,9 +16,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: clangd may not publish workspace symbols before background indexing completes - command: build-index outcome: failure message: background-work progress diff --git a/tests/e2e/cases/cuda.yaml b/tests/e2e/cases/cuda.yaml index 386360e..6eb64d9 100644 --- a/tests/e2e/cases/cuda.yaml +++ b/tests/e2e/cases/cuda.yaml @@ -16,9 +16,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: clangd may not publish workspace symbols before background indexing completes - command: build-index outcome: failure message: background-work progress diff --git a/tests/e2e/cases/javascript.yaml b/tests/e2e/cases/javascript.yaml index b8cd83f..ff64dcf 100644 --- a/tests/e2e/cases/javascript.yaml +++ b/tests/e2e/cases/javascript.yaml @@ -33,9 +33,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: vtsls returns no workspace symbols before project analysis completes - command: build-index outcome: failure message: background-work progress diff --git a/tests/e2e/cases/objc.yaml b/tests/e2e/cases/objc.yaml index 42c40dc..e05ee61 100644 --- a/tests/e2e/cases/objc.yaml +++ b/tests/e2e/cases/objc.yaml @@ -15,9 +15,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: clangd may not publish workspace symbols before background indexing completes - command: build-index outcome: failure message: background-work progress diff --git a/tests/e2e/cases/objcpp.yaml b/tests/e2e/cases/objcpp.yaml index 5121274..f51c2ec 100644 --- a/tests/e2e/cases/objcpp.yaml +++ b/tests/e2e/cases/objcpp.yaml @@ -15,9 +15,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: clangd may not publish workspace symbols before background indexing completes - command: build-index outcome: failure message: background-work progress diff --git a/tests/e2e/cases/python.yaml b/tests/e2e/cases/python.yaml index 3eab2ff..b80259f 100644 --- a/tests/e2e/cases/python.yaml +++ b/tests/e2e/cases/python.yaml @@ -32,9 +32,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: pylyzer returns no immediate workspace-symbol matches - command: callers outcome: empty-matches reason: pylyzer returns no incoming call-hierarchy edges for the fixture function @@ -53,9 +50,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: pyright advertises workspace symbols but returns no matches before background analysis completes - command: build-index outcome: failure message: background-work progress diff --git a/tests/e2e/cases/typescript.yaml b/tests/e2e/cases/typescript.yaml index e6f3421..00bb34f 100644 --- a/tests/e2e/cases/typescript.yaml +++ b/tests/e2e/cases/typescript.yaml @@ -38,9 +38,6 @@ pairs: smoke: status: queries exceptions: - - command: grep - outcome: empty-matches - reason: vtsls returns no workspace symbols before project analysis completes - command: build-index outcome: failure message: background-work progress