1package lsp
2
3import (
4 "context"
5 "encoding/json"
6 "log/slog"
7
8 "github.com/charmbracelet/crush/internal/lsp/util"
9 powernap "github.com/charmbracelet/x/powernap/pkg/lsp"
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(encoding powernap.OffsetEncoding) func(_ context.Context, _ string, params json.RawMessage) (any, error) {
49 return func(_ context.Context, _ string, params json.RawMessage) (any, error) {
50 var edit protocol.ApplyWorkspaceEditParams
51 if err := json.Unmarshal(params, &edit); err != nil {
52 return nil, err
53 }
54
55 err := util.ApplyWorkspaceEdit(edit.Edit, encoding)
56 if err != nil {
57 slog.Error("Error applying workspace edit", "error", err)
58 return protocol.ApplyWorkspaceEditResult{Applied: false, FailureReason: err.Error()}, nil
59 }
60
61 return protocol.ApplyWorkspaceEditResult{Applied: true}, nil
62 }
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// HandleServerMessage handles server messages
84func HandleServerMessage(_ context.Context, method string, params json.RawMessage) {
85 var msg protocol.ShowMessageParams
86 if err := json.Unmarshal(params, &msg); err != nil {
87 slog.Debug("Error unmarshal server message", "error", err)
88 return
89 }
90
91 switch msg.Type {
92 case protocol.Error:
93 slog.Error("LSP Server", "message", msg.Message)
94 case protocol.Warning:
95 slog.Warn("LSP Server", "message", msg.Message)
96 case protocol.Info:
97 slog.Info("LSP Server", "message", msg.Message)
98 case protocol.Log:
99 slog.Debug("LSP Server", "message", msg.Message)
100 }
101}
102
103// HandleDiagnostics handles diagnostic notifications from the LSP server
104func HandleDiagnostics(client *Client, params json.RawMessage) {
105 var diagParams protocol.PublishDiagnosticsParams
106 if err := json.Unmarshal(params, &diagParams); err != nil {
107 slog.Error("Error unmarshaling diagnostics params", "error", err)
108 return
109 }
110
111 client.diagnostics.Set(diagParams.URI, diagParams.Diagnostics)
112
113 // Calculate total diagnostic count
114 totalCount := 0
115 for _, diagnostics := range client.diagnostics.Seq2() {
116 totalCount += len(diagnostics)
117 }
118
119 // Trigger callback if set
120 if client.onDiagnosticsChanged != nil {
121 client.onDiagnosticsChanged(client.name, totalCount)
122 }
123}