a06f331eb8
install-script / powershell-syntax (push) Waiting to run
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
install-script / install (macos-14) (push) Waiting to run
install-script / install (ubuntu-latest) (push) Waiting to run
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
150 lines
4.3 KiB
Go
150 lines
4.3 KiB
Go
package serverstack
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/zzet/gortex/internal/config"
|
|
"github.com/zzet/gortex/internal/resolver"
|
|
"github.com/zzet/gortex/internal/semantic/lsp"
|
|
)
|
|
|
|
// IsFalsyEnv reports whether the named env var holds a falsy value
|
|
// (0/false/no/off/n, case-insensitive). Empty/unset is NOT falsy.
|
|
func IsFalsyEnv(name string) bool {
|
|
switch strings.ToLower(strings.TrimSpace(os.Getenv(name))) {
|
|
case "0", "false", "no", "off", "n":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// LspDisabledSet builds the set of LSP spec names that should NOT be
|
|
// auto-registered by Router.RegisterAvailable, merging per-spec config
|
|
// opt-outs (semantic.providers with enabled:false that resolve to a
|
|
// known LSP spec) with the comma-separated GORTEX_LSP_DISABLE env var.
|
|
// The special key "__all__" (from the literal "all"/"*") signals
|
|
// "skip auto-register everywhere" and is checked separately by callers.
|
|
func LspDisabledSet(providers []config.SemanticProviderConfig, envVar string) map[string]bool {
|
|
out := map[string]bool{}
|
|
for _, pc := range providers {
|
|
if pc.Enabled {
|
|
continue
|
|
}
|
|
if lsp.SpecByName(pc.Name) != nil {
|
|
out[pc.Name] = true
|
|
}
|
|
}
|
|
for _, raw := range strings.Split(envVar, ",") {
|
|
name := strings.TrimSpace(raw)
|
|
if name == "" {
|
|
continue
|
|
}
|
|
if strings.EqualFold(name, "all") || name == "*" {
|
|
out["__all__"] = true
|
|
continue
|
|
}
|
|
out[name] = true
|
|
}
|
|
return out
|
|
}
|
|
|
|
// RepoLikelyHasTypeScriptIntent reports whether a repo root carries one
|
|
// of the canonical TS/JS project markers, used to decide whether to wire
|
|
// the resolve-time tsserver helper for a tracked repo.
|
|
func RepoLikelyHasTypeScriptIntent(absRoot string) bool {
|
|
for _, marker := range []string{"tsconfig.json", "jsconfig.json", "package.json"} {
|
|
if _, err := os.Stat(filepath.Join(absRoot, marker)); err == nil {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// RepoLikelyHasPythonIntent reports whether a repo root carries one of the
|
|
// standard Python project markers, used to decide whether to wire the
|
|
// resolve-time pyright helper for a tracked repo.
|
|
func RepoLikelyHasPythonIntent(absRoot string) bool {
|
|
for _, marker := range []string{
|
|
"pyproject.toml",
|
|
"setup.py",
|
|
"setup.cfg",
|
|
"requirements.txt",
|
|
"requirements.in",
|
|
"Pipfile",
|
|
"poetry.lock",
|
|
"uv.lock",
|
|
"tox.ini",
|
|
".python-version",
|
|
} {
|
|
if _, err := os.Stat(filepath.Join(absRoot, marker)); err == nil {
|
|
return true
|
|
}
|
|
}
|
|
if matches, err := filepath.Glob(filepath.Join(absRoot, "*.py")); err == nil && len(matches) > 0 {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// BuildResolverLSPHelper constructs the resolve-time LSP helper for a
|
|
// workspace, choosing the router-cached lazy path (poolSize <= 1, reuses
|
|
// the router's idle reaper) or the fresh-spawn pool path (poolSize > 1,
|
|
// opt-in via GORTEX_LSP_POOL_SIZE; keeps every spawn alive, so only for
|
|
// small tracked-workspace counts).
|
|
func BuildResolverLSPHelper(router *lsp.Router, spec *lsp.ServerSpec, absRoot string, poolSize int, logger *zap.Logger) *lsp.ResolverHelper {
|
|
if poolSize <= 1 {
|
|
return lsp.NewLazyResolverHelper(
|
|
func() (*lsp.Provider, error) {
|
|
return router.ForSpecWorkspace(spec, absRoot)
|
|
},
|
|
absRoot,
|
|
spec.Extensions,
|
|
0,
|
|
logger,
|
|
)
|
|
}
|
|
return lsp.NewPooledResolverHelper(
|
|
func() (*lsp.Provider, error) {
|
|
return lsp.SpawnProviderForResolver(spec, absRoot, logger)
|
|
},
|
|
absRoot,
|
|
spec.Extensions,
|
|
0,
|
|
poolSize,
|
|
logger,
|
|
)
|
|
}
|
|
|
|
// BuildResolverLSPHelperForRepo constructs a per-repo helper that can route
|
|
// each file extension to the appropriate language server. The returned spec
|
|
// names are the helpers included in the mux, for logging and diagnostics.
|
|
func BuildResolverLSPHelperForRepo(router *lsp.Router, absRoot string, poolSize int, logger *zap.Logger) (resolver.LSPHelper, []string) {
|
|
if router == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
var helpers []resolver.LSPHelper
|
|
var specs []string
|
|
add := func(name string, enabled bool) {
|
|
if !enabled {
|
|
return
|
|
}
|
|
spec := lsp.SpecByName(name)
|
|
if spec == nil || !router.Available(spec) {
|
|
return
|
|
}
|
|
helpers = append(helpers, BuildResolverLSPHelper(router, spec, absRoot, poolSize, logger))
|
|
specs = append(specs, name)
|
|
}
|
|
|
|
add("typescript-language-server", RepoLikelyHasTypeScriptIntent(absRoot))
|
|
add("pyright", RepoLikelyHasPythonIntent(absRoot))
|
|
|
|
return lsp.NewResolverHelperMux(helpers...), specs
|
|
}
|