Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

112 lines
4.5 KiB
Go

package lsp
import (
"os"
"strings"
)
// SweepEnv is the environment variable that overrides the configured
// per-file enrichment sweep mode. When set to a recognised value it wins
// over the provider's configured mode (and therefore over .gortex.yaml),
// so an operator can dial sweep intensity for one run without editing
// config.
const SweepEnv = "GORTEX_LSP_SWEEP"
// Per-file enrichment sweep modes. The sweep is the whole-repo hover /
// call-hierarchy phase that runs after the tier-deciding confirm and add
// passes: it stamps hover type strings and interrogates the server for
// call/type-hierarchy edges the AST extractor missed, file by file.
//
// On an already-resolved graph (a warm restart) that sweep is pure churn —
// it re-opens and re-hovers every file to confirm zero new edges. The mode
// gates how much of it runs:
//
// - sweepModeDemand (DEFAULT): sweep a file when its declarations still
// carry unresolved same-name call candidates (enrichment demand) OR it
// declares a type / interface whose super/subtype hierarchy the sweep
// interrogates (dispatch-relevant). The dispatch disjunct is load-bearing:
// a type / interface never contributes call demand, yet the sweep is the
// only path that recovers its cross-file / dynamic extends / supertype
// edges, so gating on demand alone would silently drop them. A file with
// neither signal is skipped, so a warm restart pays no sweep for it while
// the already-enriched declarations that are swept skip their redundant
// hover.
// - sweepModeFull: sweep every file of the language — the pre-knob
// behaviour, kept for a cold index that wants maximal hover coverage.
// - sweepModeOff: skip the per-file sweep entirely. The confirm / add /
// interface passes still run, so tiers and recall are unaffected.
const (
sweepModeDemand = "demand"
sweepModeFull = "full"
sweepModeOff = "off"
)
// normalizeSweepMode canonicalises a configured / env sweep-mode string.
// Case- and whitespace-insensitive; "none" is accepted as an alias for
// "off". An empty or unrecognised value returns "" so the caller can fall
// through to the next precedence source (env → config → default).
func normalizeSweepMode(v string) string {
switch strings.ToLower(strings.TrimSpace(v)) {
case sweepModeDemand:
return sweepModeDemand
case sweepModeFull:
return sweepModeFull
case sweepModeOff, "none":
return sweepModeOff
default:
return ""
}
}
// resolveSweepMode picks the effective per-file sweep mode by precedence:
// the GORTEX_LSP_SWEEP env override wins over the operator-configured value,
// which wins over the per-server spec default, which wins over the global
// demand-gated default. Only the two operator-set sources (env, config) can
// override a server's own default, so a server that needs the full sweep
// gets it out of the box while an operator retains the last word. An
// unrecognised value at any level is ignored (falls through) rather than
// failing the pass.
func resolveSweepMode(configured, specDefault string) string {
if env := normalizeSweepMode(os.Getenv(SweepEnv)); env != "" {
return env
}
if cfg := normalizeSweepMode(configured); cfg != "" {
return cfg
}
if sd := normalizeSweepMode(specDefault); sd != "" {
return sd
}
return sweepModeDemand
}
// effectiveSweepMode resolves the sweep mode for this provider, honouring
// the GORTEX_LSP_SWEEP env override over the router-configured field over
// the server spec's own DefaultSweepMode.
func (p *Provider) effectiveSweepMode() string {
specDefault := ""
if p.spec != nil {
specDefault = p.spec.DefaultSweepMode
}
return resolveSweepMode(p.sweepMode, specDefault)
}
// sweepFile reports whether the per-file hover / call-hierarchy sweep should
// run for a file under mode, given its unresolved-demand count and whether it
// carries a dispatch-relevant declaration. Under the demand default a file is
// swept when at least one of its declarations still has unresolved same-name
// call candidates (demand > 0) OR it declares a type / interface whose
// super/subtype hierarchy the sweep interrogates (dispatch) — the latter never
// surfaces as demand, so without this disjunct a type-only file would drop the
// extends / supertype edges only this sweep recovers. "full" always sweeps,
// "off" never does.
func sweepFile(mode string, demand int, dispatch bool) bool {
switch mode {
case sweepModeOff:
return false
case sweepModeFull:
return true
default: // sweepModeDemand and any unrecognised residue
return demand > 0 || dispatch
}
}