on main help sign in
fix(lsp): prevent nil client from being stored in clients map (#2262)
0a2a3feAustin Cherry
internal/lsp/manager.go+28−14
@@ -178,24 +178,38 @@ func (s *Manager) startServer(ctx context.Context, name, filepath string, server
178 return
178 return
179 }
179 }
180
180
181− // check again in case another goroutine started it in the meantime
181+ // Check again in case another goroutine started it in the meantime.
182− var err error
183− client := s.clients.GetOrSet(name, func() *Client {
182+ if client, ok := s.clients.Get(name); ok {
184− var cli *Client
183+ switch client.GetServerState() {
184+ case StateReady, StateStarting, StateDisabled:
185+ s.callback(name, client)
186+ return
187+ }
188+ }
189+
185− cli, err = New(
190+ client, err := New(
186− ctx,
191+ ctx,
187− name,
192+ name,
188− cfg,
193+ cfg,
189− s.cfg.Resolver(),
194+ s.cfg.Resolver(),
190− s.cfg.WorkingDir(),
195+ s.cfg.WorkingDir(),
191− s.cfg.Options.DebugLSP,
196+ s.cfg.Options.DebugLSP,
192− )
193− return cli
194− })
197+ )
195 if err != nil {
198 if err != nil {
196 slog.Error("Failed to create LSP client", "name", name, "error", err)
199 slog.Error("Failed to create LSP client", "name", name, "error", err)
197 return
200 return
198 }
201 }
202+ // Only store non-nil clients. If another goroutine raced us,
203+ // prefer the already-stored client.
204+ if existing, ok := s.clients.Get(name); ok {
205+ switch existing.GetServerState() {
206+ case StateReady, StateStarting, StateDisabled:
207+ client.Close(ctx)
208+ s.callback(name, existing)
209+ return
210+ }
211+ }
212+ s.clients.Set(name, client)
199 defer func() {
213 defer func() {
200 s.callback(name, client)
214 s.callback(name, client)
201 }()
215 }()