@@ -3,41 +3,108 @@ package app
33import (
44 "context"
55 "log/slog"
6+ "os/exec"
7+ "slices"
68 "time"
79
810 "github.com/charmbracelet/crush/internal/config"
911 "github.com/charmbracelet/crush/internal/lsp"
12+ powernapconfig "github.com/charmbracelet/x/powernap/pkg/config"
1013)
1114
1215// initLSPClients initializes LSP clients.
1316func (app *App) initLSPClients(ctx context.Context) {
17+ slog.Info("LSP clients initialization started")
18+
19+ manager := powernapconfig.NewManager()
20+ manager.LoadDefaults()
21+
22+ var userConfiguredLSPs []string
1423 for name, clientConfig := range app.config.LSP {
1524 if clientConfig.Disabled {
1625 slog.Info("Skipping disabled LSP client", "name", name)
26+ manager.RemoveServer(name)
1727 continue
1828 }
19− go app.createAndStartLSPClient(ctx, name, clientConfig)
29+
30+ // HACK: the user might have the command name in their config, instead
31+ // of the actual name. This finds out these cases, and adjusts the name
32+ // accordingly.
33+ if _, ok := manager.GetServer(name); !ok {
34+ for sname, server := range manager.GetServers() {
35+ if server.Command == name {
36+ name = sname
37+ break
38+ }
39+ }
40+ }
41+ userConfiguredLSPs = append(userConfiguredLSPs, name)
42+ manager.AddServer(name, &powernapconfig.ServerConfig{
43+ Command: clientConfig.Command,
44+ Args: clientConfig.Args,
45+ Environment: clientConfig.Env,
46+ FileTypes: clientConfig.FileTypes,
47+ RootMarkers: clientConfig.RootMarkers,
48+ InitOptions: clientConfig.InitOptions,
49+ Settings: clientConfig.Options,
50+ })
51+ }
52+
53+ servers := manager.GetServers()
54+ filtered := lsp.FilterMatching(app.config.WorkingDir(), servers)
55+
56+ for _, name := range userConfiguredLSPs {
57+ if _, ok := filtered[name]; !ok {
58+ updateLSPState(name, lsp.StateDisabled, nil, nil, 0)
59+ }
60+ }
61+ for name, server := range filtered {
62+ if app.config.Options.AutoLSP != nil && !*app.config.Options.AutoLSP && !slices.Contains(userConfiguredLSPs, name) {
63+ slog.Debug("Ignoring non user-define LSP client due to AutoLSP being disabled", "name", name)
64+ continue
65+ }
66+ go app.createAndStartLSPClient(
67+ ctx, name,
68+ toOurConfig(server),
69+ slices.Contains(userConfiguredLSPs, name),
70+ )
2071 }
21− slog.Info("LSP clients initialization started in background")
2272}
2373
24−// createAndStartLSPClient creates a new LSP client, initializes it, and starts its workspace watcher
25−func (app *App) createAndStartLSPClient(ctx context.Context, name string, config config.LSPConfig) {
26− slog.Debug("Creating LSP client", "name", name, "command", config.Command, "fileTypes", config.FileTypes, "args", config.Args)
74+func toOurConfig(in *powernapconfig.ServerConfig) config.LSPConfig {
75+ return config.LSPConfig{
76+ Command: in.Command,
77+ Args: in.Args,
78+ Env: in.Environment,
79+ FileTypes: in.FileTypes,
80+ RootMarkers: in.RootMarkers,
81+ InitOptions: in.InitOptions,
82+ Options: in.Settings,
83+ }
84+}
2785
28− // Check if any root markers exist in the working directory (config now has defaults)
29− if !lsp.HasRootMarkers(app.config.WorkingDir(), config.RootMarkers) {
30− slog.Debug("Skipping LSP client: no root markers found", "name", name, "rootMarkers", config.RootMarkers)
31− updateLSPState(name, lsp.StateDisabled, nil, nil, 0)
32− return
86+// createAndStartLSPClient creates a new LSP client, initializes it, and starts its workspace watcher.
87+func (app *App) createAndStartLSPClient(ctx context.Context, name string, config config.LSPConfig, userConfigured bool) {
88+ if !userConfigured {
89+ if _, err := exec.LookPath(config.Command); err != nil {
90+ slog.Warn("Default LSP config skipped: server not installed", "name", name, "error", err)
91+ return
92+ }
3393 }
3494
35− // Update state to starting
95+ slog.Debug("Creating LSP client", "name", name, "command", config.Command, "fileTypes", config.FileTypes, "args", config.Args)
96+
97+ // Update state to starting.
3698 updateLSPState(name, lsp.StateStarting, nil, nil, 0)
3799
38100 // Create LSP client.
39101 lspClient, err := lsp.New(ctx, name, config, app.config.Resolver())
40102 if err != nil {
103+ if !userConfigured {
104+ slog.Warn("Default LSP config skipped due to error", "name", name, "error", err)
105+ updateLSPState(name, lsp.StateDisabled, nil, nil, 0)
106+ return
107+ }
41108 slog.Error("Failed to create LSP client for", "name", name, "error", err)
42109 updateLSPState(name, lsp.StateError, err, nil, 0)
43110 return