lsp_restart.go

 1package tools
 2
 3import (
 4	"context"
 5	_ "embed"
 6	"fmt"
 7	"log/slog"
 8	"maps"
 9	"strings"
10	"sync"
11
12	"charm.land/fantasy"
13	"github.com/charmbracelet/crush/internal/lsp"
14)
15
16const LSPRestartToolName = "lsp_restart"
17
18//go:embed lsp_restart.md
19var lspRestartDescription []byte
20
21type LSPRestartParams struct {
22	// Name is the optional name of a specific LSP client to restart.
23	// If empty, all LSP clients will be restarted.
24	Name string `json:"name,omitempty"`
25}
26
27func NewLSPRestartTool(lspManager *lsp.Manager) fantasy.AgentTool {
28	return fantasy.NewAgentTool(
29		LSPRestartToolName,
30		string(lspRestartDescription),
31		func(ctx context.Context, params LSPRestartParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
32			if lspManager.Clients().Len() == 0 {
33				return fantasy.NewTextErrorResponse("no LSP clients available to restart"), nil
34			}
35
36			clientsToRestart := make(map[string]*lsp.Client)
37			if params.Name == "" {
38				maps.Insert(clientsToRestart, lspManager.Clients().Seq2())
39			} else {
40				client, exists := lspManager.Clients().Get(params.Name)
41				if !exists {
42					return fantasy.NewTextErrorResponse(fmt.Sprintf("LSP client '%s' not found", params.Name)), nil
43				}
44				clientsToRestart[params.Name] = client
45			}
46
47			var restarted []string
48			var failed []string
49			var mu sync.Mutex
50			var wg sync.WaitGroup
51			for name, client := range clientsToRestart {
52				wg.Go(func() {
53					if err := client.Restart(); err != nil {
54						slog.Error("Failed to restart LSP client", "name", name, "error", err)
55						mu.Lock()
56						failed = append(failed, name)
57						mu.Unlock()
58						return
59					}
60					mu.Lock()
61					restarted = append(restarted, name)
62					mu.Unlock()
63				})
64			}
65
66			wg.Wait()
67
68			var output string
69			if len(restarted) > 0 {
70				output = fmt.Sprintf("Successfully restarted %d LSP client(s): %s\n", len(restarted), strings.Join(restarted, ", "))
71			}
72			if len(failed) > 0 {
73				output += fmt.Sprintf("Failed to restart %d LSP client(s): %s\n", len(failed), strings.Join(failed, ", "))
74				return fantasy.NewTextErrorResponse(output), nil
75			}
76
77			return fantasy.NewTextResponse(output), nil
78		})
79}