2
0

lsp.go 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. package app
  2. import (
  3. "context"
  4. "log/slog"
  5. "time"
  6. "github.com/charmbracelet/crush/internal/config"
  7. "github.com/charmbracelet/crush/internal/lsp"
  8. )
  9. // initLSPClients initializes LSP clients.
  10. func (app *App) initLSPClients(ctx context.Context) {
  11. for name, clientConfig := range app.config.LSP {
  12. if clientConfig.Disabled {
  13. slog.Info("Skipping disabled LSP client", "name", name)
  14. continue
  15. }
  16. go app.createAndStartLSPClient(ctx, name, clientConfig)
  17. }
  18. slog.Info("LSP clients initialization started in background")
  19. }
  20. // createAndStartLSPClient creates a new LSP client, initializes it, and starts its workspace watcher
  21. func (app *App) createAndStartLSPClient(ctx context.Context, name string, config config.LSPConfig) {
  22. slog.Info("Creating LSP client", "name", name, "command", config.Command, "fileTypes", config.FileTypes, "args", config.Args)
  23. // Check if any root markers exist in the working directory (config now has defaults)
  24. if !lsp.HasRootMarkers(app.config.WorkingDir(), config.RootMarkers) {
  25. slog.Info("Skipping LSP client - no root markers found", "name", name, "rootMarkers", config.RootMarkers)
  26. updateLSPState(name, lsp.StateDisabled, nil, nil, 0)
  27. return
  28. }
  29. // Update state to starting
  30. updateLSPState(name, lsp.StateStarting, nil, nil, 0)
  31. // Create LSP client.
  32. lspClient, err := lsp.New(ctx, name, config, app.config.Resolver())
  33. if err != nil {
  34. slog.Error("Failed to create LSP client for", name, err)
  35. updateLSPState(name, lsp.StateError, err, nil, 0)
  36. return
  37. }
  38. // Set diagnostics callback
  39. lspClient.SetDiagnosticsCallback(updateLSPDiagnostics)
  40. // Increase initialization timeout as some servers take more time to start.
  41. initCtx, cancel := context.WithTimeout(ctx, 30*time.Second)
  42. defer cancel()
  43. // Initialize LSP client.
  44. _, err = lspClient.Initialize(initCtx, app.config.WorkingDir())
  45. if err != nil {
  46. slog.Error("Initialize failed", "name", name, "error", err)
  47. updateLSPState(name, lsp.StateError, err, lspClient, 0)
  48. lspClient.Close(ctx)
  49. return
  50. }
  51. // Wait for the server to be ready.
  52. if err := lspClient.WaitForServerReady(initCtx); err != nil {
  53. slog.Error("Server failed to become ready", "name", name, "error", err)
  54. // Server never reached a ready state, but let's continue anyway, as
  55. // some functionality might still work.
  56. lspClient.SetServerState(lsp.StateError)
  57. updateLSPState(name, lsp.StateError, err, lspClient, 0)
  58. } else {
  59. // Server reached a ready state scuccessfully.
  60. slog.Info("LSP server is ready", "name", name)
  61. lspClient.SetServerState(lsp.StateReady)
  62. updateLSPState(name, lsp.StateReady, nil, lspClient, 0)
  63. }
  64. slog.Info("LSP client initialized", "name", name)
  65. // Add to map with mutex protection before starting goroutine
  66. app.LSPClients.Set(name, lspClient)
  67. }