1package lsp
2
3import (
4 "encoding/json"
5 "log/slog"
6
7 "github.com/charmbracelet/crush/internal/config"
8
9 "github.com/charmbracelet/crush/internal/lsp/protocol"
10 "github.com/charmbracelet/crush/internal/lsp/util"
11)
12
13// Requests
14
15func HandleWorkspaceConfiguration(params json.RawMessage) (any, error) {
16 return []map[string]any{{}}, nil
17}
18
19func HandleRegisterCapability(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
36 var options protocol.DidChangeWatchedFilesRegistrationOptions
37 if err := json.Unmarshal(optionsJSON, &options); err != nil {
38 slog.Error("Error unmarshaling registration options", "error", err)
39 continue
40 }
41
42 // Store the file watchers registrations
43 notifyFileWatchRegistration(reg.ID, options.Watchers)
44 }
45 }
46
47 return nil, nil
48}
49
50func HandleApplyEdit(params json.RawMessage) (any, error) {
51 var edit protocol.ApplyWorkspaceEditParams
52 if err := json.Unmarshal(params, &edit); err != nil {
53 return nil, err
54 }
55
56 err := util.ApplyWorkspaceEdit(edit.Edit)
57 if err != nil {
58 slog.Error("Error applying workspace edit", "error", err)
59 return protocol.ApplyWorkspaceEditResult{Applied: false, FailureReason: err.Error()}, nil
60 }
61
62 return protocol.ApplyWorkspaceEditResult{Applied: true}, nil
63}
64
65// FileWatchRegistrationHandler is a function that will be called when file watch registrations are received
66type FileWatchRegistrationHandler func(id string, watchers []protocol.FileSystemWatcher)
67
68// fileWatchHandler holds the current handler for file watch registrations
69var fileWatchHandler FileWatchRegistrationHandler
70
71// RegisterFileWatchHandler sets the handler for file watch registrations
72func RegisterFileWatchHandler(handler FileWatchRegistrationHandler) {
73 fileWatchHandler = handler
74}
75
76// notifyFileWatchRegistration notifies the handler about new file watch registrations
77func notifyFileWatchRegistration(id string, watchers []protocol.FileSystemWatcher) {
78 if fileWatchHandler != nil {
79 fileWatchHandler(id, watchers)
80 }
81}
82
83// Notifications
84
85func HandleServerMessage(params json.RawMessage) {
86 cfg := config.Get()
87 var msg struct {
88 Type int `json:"type"`
89 Message string `json:"message"`
90 }
91 if err := json.Unmarshal(params, &msg); err == nil {
92 if cfg.Options.DebugLSP {
93 slog.Debug("Server message", "type", msg.Type, "message", msg.Message)
94 }
95 }
96}
97
98func HandleDiagnostics(client *Client, params json.RawMessage) {
99 var diagParams protocol.PublishDiagnosticsParams
100 if err := json.Unmarshal(params, &diagParams); err != nil {
101 slog.Error("Error unmarshaling diagnostics params", "error", err)
102 return
103 }
104
105 client.diagnosticsMu.Lock()
106 client.diagnostics[diagParams.URI] = diagParams.Diagnostics
107
108 // Calculate total diagnostic count
109 totalCount := 0
110 for _, diagnostics := range client.diagnostics {
111 totalCount += len(diagnostics)
112 }
113 client.diagnosticsMu.Unlock()
114
115 // Trigger callback if set
116 if client.onDiagnosticsChanged != nil {
117 client.onDiagnosticsChanged(client.name, totalCount)
118 }
119}