fix: remove healthcheck

Raphael Amorim created

Change summary

internal/lsp/client.go  | 181 +-----------------------------------------
internal/lsp/methods.go |   1 
2 files changed, 5 insertions(+), 177 deletions(-)

Detailed changes

internal/lsp/client.go 🔗

@@ -72,7 +72,6 @@ type Client struct {
 	shutdownChan       chan struct{}
 	stderrDone         chan struct{}
 	messageHandlerDone chan struct{}
-	healthCheckDone    chan struct{}
 }
 
 // NewClient creates a new LSP client.
@@ -113,7 +112,6 @@ func NewClient(ctx context.Context, name string, config config.LSPConfig) (*Clie
 		shutdownChan:          make(chan struct{}),
 		stderrDone:            make(chan struct{}),
 		messageHandlerDone:    make(chan struct{}),
-		healthCheckDone:       make(chan struct{}),
 	}
 
 	// Initialize server state
@@ -150,12 +148,6 @@ func NewClient(ctx context.Context, name string, config config.LSPConfig) (*Clie
 		client.handleMessages()
 	}()
 
-	// Start health check and cleanup goroutine
-	go func() {
-		defer close(client.healthCheckDone)
-		client.startHealthCheckAndCleanup(ctx)
-	}()
-
 	return client, nil
 }
 
@@ -390,8 +382,11 @@ func (c *Client) WaitForServerReady(ctx context.Context) error {
 			c.SetServerState(StateError)
 			return fmt.Errorf("timeout waiting for LSP server to be ready")
 		case <-ticker.C:
-			// Try a ping method appropriate for this server type
-			err := c.pingServerByType(ctx, serverType)
+			// Try a simple workspace/symbol request to check if server is ready
+			var symbols []protocol.WorkspaceSymbol
+			err := c.Call(ctx, "workspace/symbol", protocol.WorkspaceSymbolParams{
+				Query: "",
+			}, &symbols)
 			if err == nil {
 				// Server responded successfully
 				c.SetServerState(StateReady)
@@ -399,8 +394,6 @@ func (c *Client) WaitForServerReady(ctx context.Context) error {
 					slog.Debug("LSP server is ready")
 				}
 				return nil
-			} else {
-				slog.Debug("LSP server not ready yet", "error", err, "serverType", serverType)
 			}
 
 			if cfg.Options.DebugLSP {
@@ -487,88 +480,6 @@ func (c *Client) openKeyConfigFiles(ctx context.Context) {
 	}
 }
 
-// pingServerByType sends a ping request appropriate for the server type
-func (c *Client) pingServerByType(ctx context.Context, serverType ServerType) error {
-	switch serverType {
-	case ServerTypeTypeScript:
-		// For TypeScript, try a document symbol request on an open file
-		return c.pingTypeScriptServer(ctx)
-	case ServerTypeGo:
-		// For Go, workspace/symbol works well
-		return c.pingWithWorkspaceSymbol(ctx)
-	case ServerTypeRust:
-		// For Rust, workspace/symbol works well
-		return c.pingWithWorkspaceSymbol(ctx)
-	default:
-		// Default ping method
-		return c.pingWithWorkspaceSymbol(ctx)
-	}
-}
-
-// pingTypeScriptServer tries to ping a TypeScript server with appropriate methods
-func (c *Client) pingTypeScriptServer(ctx context.Context) error {
-	// First try workspace/symbol which works for many servers
-	if err := c.pingWithWorkspaceSymbol(ctx); err == nil {
-		return nil
-	}
-
-	// If that fails, try to find an open file and request document symbols
-	c.openFilesMu.RLock()
-	defer c.openFilesMu.RUnlock()
-
-	// If we have any open files, try to get document symbols for one
-	for uri := range c.openFiles {
-		filePath, err := protocol.DocumentURI(uri).Path()
-		if err != nil {
-			slog.Error("Failed to convert URI to path for TypeScript symbol collection", "uri", uri, "error", err)
-			continue
-		}
-
-		if strings.HasSuffix(filePath, ".ts") || strings.HasSuffix(filePath, ".js") ||
-			strings.HasSuffix(filePath, ".tsx") || strings.HasSuffix(filePath, ".jsx") {
-			var symbols []protocol.DocumentSymbol
-			err := c.Call(ctx, "textDocument/documentSymbol", protocol.DocumentSymbolParams{
-				TextDocument: protocol.TextDocumentIdentifier{
-					URI: protocol.DocumentURI(uri),
-				},
-			}, &symbols)
-			if err == nil {
-				return nil
-			}
-		}
-	}
-
-	// If we have no open TypeScript files, try to find and open one
-	workDir := config.Get().WorkingDir()
-	err := filepath.WalkDir(workDir, func(path string, d os.DirEntry, err error) error {
-		if err != nil {
-			return err
-		}
-
-		// Skip directories and non-TypeScript files
-		if d.IsDir() {
-			return nil
-		}
-
-		ext := filepath.Ext(path)
-		if ext == ".ts" || ext == ".js" || ext == ".tsx" || ext == ".jsx" {
-			// Found a TypeScript file, try to open it
-			if err := c.OpenFile(ctx, path); err == nil {
-				// Successfully opened, stop walking
-				return filepath.SkipAll
-			}
-		}
-
-		return nil
-	})
-	if err != nil {
-		slog.Debug("Error walking directory for TypeScript files", "error", err)
-	}
-
-	// Final fallback - just try a generic capability
-	return c.pingWithServerCapabilities(ctx)
-}
-
 // openTypeScriptFiles finds and opens TypeScript files to help initialize the server
 func (c *Client) openTypeScriptFiles(ctx context.Context, workDir string) {
 	cfg := config.Get()
@@ -641,20 +552,6 @@ func shouldSkipDir(path string) bool {
 	return skipDirs[dirName]
 }
 
-// pingWithWorkspaceSymbol tries a workspace/symbol request
-func (c *Client) pingWithWorkspaceSymbol(ctx context.Context) error {
-	var result []protocol.SymbolInformation
-	return c.Call(ctx, "workspace/symbol", protocol.WorkspaceSymbolParams{
-		Query: "",
-	}, &result)
-}
-
-// pingWithServerCapabilities tries to get server capabilities
-func (c *Client) pingWithServerCapabilities(ctx context.Context) error {
-	// This is a very lightweight request that should work for most servers
-	return c.Notify(ctx, "$/cancelRequest", struct{ ID int }{ID: -1})
-}
-
 type OpenFileInfo struct {
 	Version int32
 	URI     protocol.DocumentURI
@@ -891,71 +788,3 @@ func (c *Client) ClearDiagnosticsForURI(uri protocol.DocumentURI) {
 	defer c.diagnosticsMu.Unlock()
 	delete(c.diagnostics, uri)
 }
-
-// startHealthCheckAndCleanup runs periodic health checks and cleans up stale handlers
-func (c *Client) startHealthCheckAndCleanup(ctx context.Context) {
-	healthTicker := time.NewTicker(30 * time.Second)
-	cleanupTicker := time.NewTicker(60 * time.Second)
-	defer healthTicker.Stop()
-	defer cleanupTicker.Stop()
-
-	for {
-		select {
-		case <-ctx.Done():
-			return
-		case <-c.shutdownChan:
-			return
-		case <-healthTicker.C:
-			// Perform health check
-			if c.GetServerState() == StateReady {
-				// Try a simple ping to check if server is still responsive
-				pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
-				err := c.pingServerByType(pingCtx, c.detectServerType())
-				cancel()
-				if err != nil {
-					slog.Warn("LSP server health check failed", "name", c.name, "error", err)
-					c.SetServerState(StateError)
-				}
-			}
-		case <-cleanupTicker.C:
-			// Clean up stale pending requests
-			c.cleanupStaleHandlers()
-		}
-	}
-}
-
-// cleanupStaleHandlers removes handlers for requests that have been pending too long
-func (c *Client) cleanupStaleHandlers() {
-	threshold := time.Now().Add(-5 * time.Minute)
-	var staleIDs []int32
-
-	// Find stale requests
-	c.pendingRequestsMu.RLock()
-	for id, timestamp := range c.pendingRequests {
-		if timestamp.Before(threshold) {
-			staleIDs = append(staleIDs, id)
-		}
-	}
-	c.pendingRequestsMu.RUnlock()
-
-	if len(staleIDs) == 0 {
-		return
-	}
-
-	// Clean up stale handlers
-	c.handlersMu.Lock()
-	c.pendingRequestsMu.Lock()
-	for _, id := range staleIDs {
-		if ch, exists := c.handlers[id]; exists {
-			close(ch)
-			delete(c.handlers, id)
-		}
-		delete(c.pendingRequests, id)
-	}
-	c.pendingRequestsMu.Unlock()
-	c.handlersMu.Unlock()
-
-	if len(staleIDs) > 0 {
-		slog.Debug("Cleaned up stale LSP handlers", "count", len(staleIDs), "name", c.name)
-	}
-}

internal/lsp/methods.go 🔗

@@ -283,7 +283,6 @@ func (c *Client) Shutdown(ctx context.Context) error {
 		go func() {
 			<-c.stderrDone
 			<-c.messageHandlerDone
-			<-c.healthCheckDone
 			close(done)
 		}()