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
59 lines
2.5 KiB
Go
59 lines
2.5 KiB
Go
package resolver
|
|
|
|
import "github.com/zzet/gortex/internal/graph"
|
|
|
|
// Incremental single-file resolve: skip re-resolving a file's references that
|
|
// were already unresolved before the edit.
|
|
//
|
|
// On a save the whole file is evicted, re-parsed (every edge unresolved), and
|
|
// re-resolved. For a reference-heavy file most of those edges are stdlib /
|
|
// external calls (strings.HasPrefix, fmt.Sprintf, …) that never bind to an
|
|
// in-repo symbol — yet the resolver re-runs its full candidate fetch + cascade
|
|
// on each of them every save. That re-work dominates edit latency.
|
|
//
|
|
// An edge that was unresolved before the edit and is unchanged will not bind
|
|
// now either: it still points at the same stub, and the incoming pass already
|
|
// rebinds it if a matching symbol later appears (when that symbol's file is
|
|
// indexed). So the single-file path captures those prior-unresolved shapes and
|
|
// the forward pass skips them, touching only references the edit actually
|
|
// added or changed.
|
|
|
|
// SetIncrementalSkip installs the prior-unresolved out-edge shapes for the
|
|
// file about to be re-resolved (nil to clear afterwards). Only the forward
|
|
// pass honours it; the incoming pass still rebinds other files' references to
|
|
// this file's symbols. The per-file resolve runs single-goroutine under r.mu,
|
|
// so this field needs no extra synchronisation.
|
|
func (r *Resolver) SetIncrementalSkip(priorUnresolved []*graph.Edge) {
|
|
if len(priorUnresolved) == 0 {
|
|
r.incrementalSkip = nil
|
|
return
|
|
}
|
|
skip := make(map[string]struct{}, len(priorUnresolved))
|
|
for _, e := range priorUnresolved {
|
|
if e != nil {
|
|
skip[r.edgeShape(e)] = struct{}{}
|
|
}
|
|
}
|
|
r.incrementalSkip = skip
|
|
}
|
|
|
|
// edgeShape canonicalises an edge by its source-side identity — origin symbol,
|
|
// kind, receiver type, referenced name — independent of line number and of
|
|
// whether the edge is currently resolved. The captured prior edges and the
|
|
// freshly re-parsed ones run through the same function, so an unchanged
|
|
// reference produces the same key and is recognised as a carry-over.
|
|
func (r *Resolver) edgeShape(e *graph.Edge) string {
|
|
name := identifierFromTarget(graph.UnresolvedName(e.To))
|
|
return e.From + "\x1f" + string(e.Kind) + "\x1f" + edgeReceiverType(e) + "\x1f" + name
|
|
}
|
|
|
|
// incrementalSkipped reports whether an unresolved edge should be left for the
|
|
// incoming pass instead of re-running the forward cascade on it.
|
|
func (r *Resolver) incrementalSkipped(e *graph.Edge) bool {
|
|
if r.incrementalSkip == nil {
|
|
return false
|
|
}
|
|
_, ok := r.incrementalSkip[r.edgeShape(e)]
|
|
return ok
|
|
}
|