Skip to content

feat(lsp): default rust-analyzer to the full enrichment sweep#243

Merged
zzet merged 1 commit into
mainfrom
feat/per-server-sweep-default
Jul 4, 2026
Merged

feat(lsp): default rust-analyzer to the full enrichment sweep#243
zzet merged 1 commit into
mainfrom
feat/per-server-sweep-default

Conversation

@zzet

@zzet zzet commented Jul 4, 2026

Copy link
Copy Markdown
Owner

What

Adds a per-server DefaultSweepMode to ServerSpec and sets it to full for rust-analyzer, so Rust gets the full enrichment sweep out of the box while every other language keeps the demand-gated default.

New sweep-mode precedence (highest first): GORTEX_LSP_SWEEP env → semantic.lsp_sweep config → per-server spec default → global demand. Both operator sources still win, so semantic.lsp_sweep: demand (or the env) puts rust-analyzer back on the demand gate.

Why

The demand gate skips a file whose declarations are already statically resolved, on the premise that its call edges are recoverable without the language server. That premise holds for Go / TS / Python but not Rust: measured on ripgrep, Rust's method calls bind overwhelmingly to standard-library receiver types (Vec/String/Option/&[u8]) that the graph never indexes, so static resolution can't recover them and rust-analyzer's net-new call-hierarchy edges come precisely from the sweep the demand gate skips.

Concretely, across a language matrix (baseline vs demand vs full):

  • Go / TS / Python: demand mode trims a negligible-to-small share of net-new edges (−3% / −2% / −10%) for a 1.5–2.4× enrichment speedup — demand is the right default.
  • Rust: demand mode trims ~23% of rust-analyzer's net-new call edges while saving only ~5% wall time (rust-analyzer's cost is dominated by reference/hover volume the gate doesn't reduce, not the incoming calls it gates). So Rust pays the most recall for the least speed under demand — full is the better default.

A separate investigation confirmed this gap is not closable by improving Gortex's native Rust resolution: the residual ambiguity is standard-library-shaped, which is inherently language-server territory. So defaulting rust-analyzer to full is the right lever.

Tests

  • resolveSweepMode precedence including the new spec-default level (env > config > spec default > demand); unrecognised spec default falls through.
  • effectiveSweepMode: a spec DefaultSweepMode is honoured, the router field and env both override it, and the real rust-analyzer registry spec resolves to full.
  • go build, go test -race ./internal/semantic/lsp/, golangci-lint all green. docs/lsp.md updated with the new precedence and rust-analyzer note.

The demand-gated sweep skips a file whose declarations are already
statically resolved, on the premise that its call edges are recoverable
without the language server. That holds for most languages, but Rust
method calls bind overwhelmingly to standard-library receiver types the
graph never indexes, so rust-analyzer's net-new call-hierarchy edges come
from the sweep the demand gate skips — leaving recall on the table.

Add a per-server DefaultSweepMode on ServerSpec, inserted into the sweep
precedence between the operator config and the global demand default, and
set it to full for rust-analyzer. Both operator sources still win, so
GORTEX_LSP_SWEEP or semantic.lsp_sweep put rust-analyzer back on the
demand gate; every other server is unchanged.
@zzet zzet merged commit e7ab959 into main Jul 4, 2026
9 checks passed
@zzet zzet deleted the feat/per-server-sweep-default branch July 6, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant