@@ -10,10 +10,9 @@ import (
1010 "github.com/charmbracelet/crush/internal/lsp/watcher"
1111)
1212
13+// initLSPClients initializes LSP clients.
1314func (app *App) initLSPClients(ctx context.Context) {
14− // Initialize LSP clients
1515 for name, clientConfig := range app.config.LSP {
16− // Start each client initialization in its own goroutine
1716 go app.createAndStartLSPClient(ctx, name, clientConfig.Command, clientConfig.Args...)
1817 }
1918 slog.Info("LSP clients initialization started in background")
@@ -21,68 +20,68 @@ func (app *App) initLSPClients(ctx context.Context) {
2120
2221// createAndStartLSPClient creates a new LSP client, initializes it, and starts its workspace watcher
2322func (app *App) createAndStartLSPClient(ctx context.Context, name string, command string, args ...string) {
24− // Create a specific context for initialization with a timeout
2523 slog.Info("Creating LSP client", "name", name, "command", command, "args", args)
2624
27− // Create the LSP client
25+ // Create LSP client.
2826 lspClient, err := lsp.NewClient(ctx, command, args...)
2927 if err != nil {
3028 slog.Error("Failed to create LSP client for", name, err)
3129 return
3230 }
3331
34− // Create a longer timeout for initialization (some servers take time to start)
32+ // Increase initialization timeout as some servers take more time to start.
3533 initCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
3634 defer cancel()
3735
38− // Initialize with the initialization context
36+ // Initialize LSP client.
3937 _, err = lspClient.InitializeLSPClient(initCtx, app.config.WorkingDir())
4038 if err != nil {
4139 slog.Error("Initialize failed", "name", name, "error", err)
42− // Clean up the client to prevent resource leaks
4340 lspClient.Close()
4441 return
4542 }
4643
47− // Wait for the server to be ready
44+ // Wait for the server to be ready.
4845 if err := lspClient.WaitForServerReady(initCtx); err != nil {
4946 slog.Error("Server failed to become ready", "name", name, "error", err)
50− // We'll continue anyway, as some functionality might still work
47+ // Server never reached a ready state, but let's continue anyway, as
48+ // some functionality might still work.
5149 lspClient.SetServerState(lsp.StateError)
5250 } else {
51+ // Server reached a ready state scuccessfully.
5352 slog.Info("LSP server is ready", "name", name)
5453 lspClient.SetServerState(lsp.StateReady)
5554 }
5655
5756 slog.Info("LSP client initialized", "name", name)
5857
59− // Create a child context that can be canceled when the app is shutting down
58+ // Create a child context that can be canceled when the app is shutting
59+ // down.
6060 watchCtx, cancelFunc := context.WithCancel(ctx)
6161
62− // Create the workspace watcher
62+ // Create the workspace watcher.
6363 workspaceWatcher := watcher.NewWorkspaceWatcher(name, lspClient)
6464
65− // Store the cancel function to be called during cleanup
65+ // Store the cancel function to be called during cleanup.
6666 app.cancelFuncsMutex.Lock()
6767 app.watcherCancelFuncs = append(app.watcherCancelFuncs, cancelFunc)
6868 app.cancelFuncsMutex.Unlock()
6969
70− // Add the watcher to a WaitGroup to track active goroutines
71− app.lspWatcherWG.Add(1)
72−
7370 // Add to map with mutex protection before starting goroutine
7471 app.clientsMutex.Lock()
7572 app.LSPClients[name] = lspClient
7673 app.clientsMutex.Unlock()
7774
75+ // Run workspace watcher.
76+ app.lspWatcherWG.Add(1)
7877 go app.runWorkspaceWatcher(watchCtx, name, workspaceWatcher)
7978}
8079
81−// runWorkspaceWatcher executes the workspace watcher for an LSP client
80+// runWorkspaceWatcher executes the workspace watcher for an LSP client.
8281func (app *App) runWorkspaceWatcher(ctx context.Context, name string, workspaceWatcher *watcher.WorkspaceWatcher) {
8382 defer app.lspWatcherWG.Done()
8483 defer log.RecoverPanic("LSP-"+name, func() {
85− // Try to restart the client
84+ // Try to restart the client.
8685 app.restartLSPClient(ctx, name)
8786 })
8887
@@ -90,31 +89,32 @@ func (app *App) runWorkspaceWatcher(ctx context.Context, name string, workspaceW
9089 slog.Info("Workspace watcher stopped", "client", name)
9190}
9291
93−// restartLSPClient attempts to restart a crashed or failed LSP client
92+// restartLSPClient attempts to restart a crashed or failed LSP client.
9493func (app *App) restartLSPClient(ctx context.Context, name string) {
95− // Get the original configuration
94+ // Get the original configuration.
9695 clientConfig, exists := app.config.LSP[name]
9796 if !exists {
9897 slog.Error("Cannot restart client, configuration not found", "client", name)
9998 return
10099 }
101100
102− // Clean up the old client if it exists
101+ // Clean up the old client if it exists.
103102 app.clientsMutex.Lock()
104103 oldClient, exists := app.LSPClients[name]
105104 if exists {
106− delete(app.LSPClients, name) // Remove from map before potentially slow shutdown
105+ // Remove from map before potentially slow shutdown.
106+ delete(app.LSPClients, name)
107107 }
108108 app.clientsMutex.Unlock()
109109
110110 if exists && oldClient != nil {
111− // Try to shut it down gracefully, but don't block on errors
111+ // Try to shut down client gracefully, but don't block on errors.
112112 shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
113113 _ = oldClient.Shutdown(shutdownCtx)
114114 cancel()
115115 }
116116
117− // Create a new client using the shared function
117+ // Create a new client using the shared function.
118118 app.createAndStartLSPClient(ctx, name, clientConfig.Command, clientConfig.Args...)
119119 slog.Info("Successfully restarted LSP client", "client", name)
120120}