handlers.go

  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, &registerParams); 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}