@@ -40,6 +40,10 @@ type Client struct {
40 // Configuration for this LSP client
40 // Configuration for this LSP client
41 config config.LSPConfig
41 config config.LSPConfig
43+ // Original context and resolver for recreating the client
45+ resolver config.VariableResolver
43 // Diagnostic change callback
47 // Diagnostic change callback
44 onDiagnosticsChanged func(name string, count int)
48 onDiagnosticsChanged func(name string, count int)
@@ -59,58 +63,22 @@ type Client struct {
61// New creates a new LSP client using the powernap implementation.
65// New creates a new LSP client using the powernap implementation.
62−func New(ctx context.Context, name string, config config.LSPConfig, resolver config.VariableResolver) (*Client, error) {
66+func New(ctx context.Context, name string, cfg config.LSPConfig, resolver config.VariableResolver) (*Client, error) {
63− // Convert working directory to file URI
64− workDir, err := os.Getwd()
66− return nil, fmt.Errorf("failed to get working directory: %w", err)
69− rootURI := string(protocol.URIFromPath(workDir))
71− command, err := resolver.ResolveValue(config.Command)
73− return nil, fmt.Errorf("invalid lsp command: %w", err)
76− // Create powernap client config
77− clientConfig := powernap.ClientConfig{
78− Command: home.Long(command),
81− Environment: func() map[string]string {
82− env := make(map[string]string)
83− maps.Copy(env, config.Env)
86− Settings: config.Options,
87− InitOptions: config.InitOptions,
88− WorkspaceFolders: []protocol.WorkspaceFolder{
91− Name: filepath.Base(workDir),
96− // Create the powernap client
97− powernapClient, err := powernap.NewClient(clientConfig)
99− return nil, fmt.Errorf("failed to create lsp client: %w", err)
102 client := &Client{
67 client := &Client{
103− client: powernapClient,
104 name: name,
68 name: name,
105− fileTypes: config.FileTypes,
69+ fileTypes: cfg.FileTypes,
106 diagnostics: csync.NewVersionedMap[protocol.DocumentURI, []protocol.Diagnostic](),
70 diagnostics: csync.NewVersionedMap[protocol.DocumentURI, []protocol.Diagnostic](),
107 openFiles: csync.NewMap[string, *OpenFileInfo](),
71 openFiles: csync.NewMap[string, *OpenFileInfo](),
108− config: config,
72+ config: cfg,
111− // Initialize server state
112 client.serverState.Store(StateStarting)
76 client.serverState.Store(StateStarting)
78+ if err := client.createPowernapClient(); err != nil {
114 return client, nil
82 return client, nil
@@ -140,13 +108,7 @@ func (c *Client) Initialize(ctx context.Context, workspaceDir string) (*protocol
140 Capabilities: protocolCaps,
108 Capabilities: protocolCaps,
143− c.RegisterServerRequestHandler("workspace/applyEdit", HandleApplyEdit)
111+ c.registerHandlers()
144− c.RegisterServerRequestHandler("workspace/configuration", HandleWorkspaceConfiguration)
145− c.RegisterServerRequestHandler("client/registerCapability", HandleRegisterCapability)
146− c.RegisterNotificationHandler("window/showMessage", HandleServerMessage)
147− c.RegisterNotificationHandler("textDocument/publishDiagnostics", func(_ context.Context, _ string, params json.RawMessage) {
148− HandleDiagnostics(c, params)
151 return result, nil
113 return result, nil
@@ -163,6 +125,102 @@ func (c *Client) Close(ctx context.Context) error {
163 return c.client.Exit()
125 return c.client.Exit()
128+// createPowernapClient creates a new powernap client with the current configuration.
129+func (c *Client) createPowernapClient() error {
130+ workDir, err := os.Getwd()
132+ return fmt.Errorf("failed to get working directory: %w", err)
135+ rootURI := string(protocol.URIFromPath(workDir))
137+ command, err := c.resolver.ResolveValue(c.config.Command)
139+ return fmt.Errorf("invalid lsp command: %w", err)
142+ clientConfig := powernap.ClientConfig{
143+ Command: home.Long(command),
144+ Args: c.config.Args,
146+ Environment: maps.Clone(c.config.Env),
147+ Settings: c.config.Options,
148+ InitOptions: c.config.InitOptions,
149+ WorkspaceFolders: []protocol.WorkspaceFolder{
152+ Name: filepath.Base(workDir),
157+ powernapClient, err := powernap.NewClient(clientConfig)
159+ return fmt.Errorf("failed to create lsp client: %w", err)
162+ c.client = powernapClient
166+// registerHandlers registers the standard LSP notification and request handlers.
167+func (c *Client) registerHandlers() {
168+ c.RegisterServerRequestHandler("workspace/applyEdit", HandleApplyEdit)
169+ c.RegisterServerRequestHandler("workspace/configuration", HandleWorkspaceConfiguration)
170+ c.RegisterServerRequestHandler("client/registerCapability", HandleRegisterCapability)
171+ c.RegisterNotificationHandler("window/showMessage", HandleServerMessage)
172+ c.RegisterNotificationHandler("textDocument/publishDiagnostics", func(_ context.Context, _ string, params json.RawMessage) {
173+ HandleDiagnostics(c, params)
177+// Restart closes the current LSP client and creates a new one with the same configuration.
178+func (c *Client) Restart() error {
179+ var openFiles []string
180+ for uri := range c.openFiles.Seq2() {
181+ openFiles = append(openFiles, string(uri))
184+ closeCtx, cancel := context.WithTimeout(c.ctx, 10*time.Second)
187+ if err := c.Close(closeCtx); err != nil {
188+ slog.Warn("Error closing client during restart", "name", c.name, "error", err)
191+ c.diagCountsCache = DiagnosticCounts{}
192+ c.diagCountsVersion = 0
194+ if err := c.createPowernapClient(); err != nil {
198+ initCtx, cancel := context.WithTimeout(c.ctx, 30*time.Second)
201+ c.SetServerState(StateStarting)
203+ if err := c.client.Initialize(initCtx, false); err != nil {
204+ c.SetServerState(StateError)
205+ return fmt.Errorf("failed to initialize lsp client: %w", err)
208+ c.registerHandlers()
210+ if err := c.WaitForServerReady(initCtx); err != nil {
211+ slog.Error("Server failed to become ready after restart", "name", c.name, "error", err)
212+ c.SetServerState(StateError)
216+ for _, uri := range openFiles {
217+ if err := c.OpenFile(initCtx, uri); err != nil {
218+ slog.Warn("Failed to reopen file after restart", "file", uri, "error", err)
166// ServerState represents the state of an LSP server
224// ServerState represents the state of an LSP server
167type ServerState int
225type ServerState int