1package lsp
2
3import (
4 "context"
5 "encoding/json"
6 "log/slog"
7
8 "github.com/charmbracelet/crush/internal/config"
9 "github.com/charmbracelet/crush/internal/lsp/util"
10 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
11)
12
13// HandleWorkspaceConfiguration handles workspace configuration requests
14func HandleWorkspaceConfiguration(_ context.Context, _ string, params json.RawMessage) (any, error) {
15 return []map[string]any{{}}, nil
16}
17
18// HandleRegisterCapability handles capability registration requests
19func HandleRegisterCapability(_ context.Context, _ string, params json.RawMessage) (any, error) {
20 var registerParams protocol.RegistrationParams
21 if err := json.Unmarshal(params, ®isterParams); err != nil {
22 slog.Error("Error unmarshaling registration params", "error", err)
23 return nil, err
24 }
25
26 for _, reg := range registerParams.Registrations {
27 switch reg.Method {
28 case "workspace/didChangeWatchedFiles":
29 // Parse the registration options
30 optionsJSON, err := json.Marshal(reg.RegisterOptions)
31 if err != nil {
32 slog.Error("Error marshaling registration options", "error", err)
33 continue
34 }
35 var options protocol.DidChangeWatchedFilesRegistrationOptions
36 if err := json.Unmarshal(optionsJSON, &options); err != nil {
37 slog.Error("Error unmarshaling registration options", "error", err)
38 continue
39 }
40 // Store the file watchers registrations
41 notifyFileWatchRegistration(reg.ID, options.Watchers)
42 }
43 }
44 return nil, nil
45}
46
47// HandleApplyEdit handles workspace edit requests
48func HandleApplyEdit(_ context.Context, _ string, params json.RawMessage) (any, error) {
49 var edit protocol.ApplyWorkspaceEditParams
50 if err := json.Unmarshal(params, &edit); err != nil {
51 return nil, err
52 }
53
54 err := util.ApplyWorkspaceEdit(edit.Edit)
55 if err != nil {
56 slog.Error("Error applying workspace edit", "error", err)
57 return protocol.ApplyWorkspaceEditResult{Applied: false, FailureReason: err.Error()}, nil
58 }
59
60 return protocol.ApplyWorkspaceEditResult{Applied: true}, nil
61}
62
63// FileWatchRegistrationHandler is a function that will be called when file watch registrations are received
64type FileWatchRegistrationHandler func(id string, watchers []protocol.FileSystemWatcher)
65
66// fileWatchHandler holds the current handler for file watch registrations
67var fileWatchHandler FileWatchRegistrationHandler
68
69// RegisterFileWatchHandler sets the handler for file watch registrations
70func RegisterFileWatchHandler(handler FileWatchRegistrationHandler) {
71 fileWatchHandler = handler
72}
73
74// notifyFileWatchRegistration notifies the handler about new file watch registrations
75func notifyFileWatchRegistration(id string, watchers []protocol.FileSystemWatcher) {
76 if fileWatchHandler != nil {
77 fileWatchHandler(id, watchers)
78 }
79}
80
81// HandleServerMessage handles server messages
82func HandleServerMessage(ctx context.Context, method string, params json.RawMessage) {
83 cfg, ok := config.FromContext(ctx)
84 if !ok || !cfg.Options.DebugLSP {
85 return
86 }
87
88 var msg protocol.ShowMessageParams
89 if err := json.Unmarshal(params, &msg); err != nil {
90 slog.Debug("Server message", "type", msg.Type, "message", msg.Message)
91 return
92 }
93
94 switch msg.Type {
95 case protocol.Error:
96 slog.Error("LSP Server", "message", msg.Message)
97 case protocol.Warning:
98 slog.Warn("LSP Server", "message", msg.Message)
99 case protocol.Info:
100 slog.Info("LSP Server", "message", msg.Message)
101 case protocol.Log:
102 slog.Debug("LSP Server", "message", msg.Message)
103 }
104}
105
106// HandleDiagnostics handles diagnostic notifications from the LSP server
107func HandleDiagnostics(client *Client, params json.RawMessage) {
108 var diagParams protocol.PublishDiagnosticsParams
109 if err := json.Unmarshal(params, &diagParams); err != nil {
110 slog.Error("Error unmarshaling diagnostics params", "error", err)
111 return
112 }
113
114 client.diagnostics.Set(diagParams.URI, diagParams.Diagnostics)
115
116 // Calculate total diagnostic count
117 totalCount := 0
118 for _, diagnostics := range client.diagnostics.Seq2() {
119 totalCount += len(diagnostics)
120 }
121
122 // Trigger callback if set
123 if client.onDiagnosticsChanged != nil {
124 client.onDiagnosticsChanged(client.name, totalCount)
125 }
126}