@@ -6,6 +6,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "io"
"log/slog"
"maps"
"net/http"
@@ -257,7 +258,10 @@ func updateMCPState(name string, state MCPState, err error, client *mcp.ClientSe
func CloseMCPClients() error {
var errs []error
for name, c := range mcpClients.Seq2() {
- if err := c.Close(); err != nil {
+ if err := c.Close(); err != nil &&
+ !errors.Is(err, io.EOF) &&
+ !errors.Is(err, context.Canceled) &&
+ err.Error() != "signal: killed" {
errs = append(errs, fmt.Errorf("close mcp: %s: %w", name, err))
}
}
@@ -389,7 +393,6 @@ func createMCPSession(ctx context.Context, name string, m config.MCPConfig, reso
if err != nil {
updateMCPState(name, MCPStateError, maybeTimeoutErr(err, timeout), nil, MCPCounts{})
slog.Error("error starting mcp client", "error", err, "name", name)
- _ = session.Close()
cancel()
return nil, err
}
@@ -56,32 +56,7 @@ func RenderLSPList(lspClients *csync.Map[string, *lsp.Client], opts RenderOption
break
}
- // Determine icon color and description based on state
- icon := t.ItemOfflineIcon
- description := l.LSP.Command
-
- if l.LSP.Disabled {
- description = t.S().Subtle.Render("disabled")
- } else if state, exists := lspStates[l.Name]; exists {
- switch state.State {
- case lsp.StateStarting:
- icon = t.ItemBusyIcon
- description = t.S().Subtle.Render("starting...")
- case lsp.StateReady:
- icon = t.ItemOnlineIcon
- description = l.LSP.Command
- case lsp.StateError:
- icon = t.ItemErrorIcon
- if state.Error != nil {
- description = t.S().Subtle.Render(fmt.Sprintf("error: %s", state.Error.Error()))
- } else {
- description = t.S().Subtle.Render("error")
- }
- case lsp.StateDisabled:
- icon = t.ItemOfflineIcon.Foreground(t.FgMuted)
- description = t.S().Base.Foreground(t.FgMuted).Render("no root markers found")
- }
- }
+ icon, description := iconAndDescription(l, t, lspStates)
// Calculate diagnostic counts if we have LSP clients
var extraContent string
@@ -134,6 +109,30 @@ func RenderLSPList(lspClients *csync.Map[string, *lsp.Client], opts RenderOption
return lspList
}
+func iconAndDescription(l config.LSP, t *styles.Theme, states map[string]app.LSPClientInfo) (lipgloss.Style, string) {
+ if l.LSP.Disabled {
+ return t.ItemOfflineIcon.Foreground(t.FgMuted), t.S().Subtle.Render("disabled")
+ }
+
+ info := states[l.Name]
+ switch info.State {
+ case lsp.StateStarting:
+ return t.ItemBusyIcon, t.S().Subtle.Render("starting...")
+ case lsp.StateReady:
+ return t.ItemOnlineIcon, ""
+ case lsp.StateError:
+ description := t.S().Subtle.Render("error")
+ if info.Error != nil {
+ description = t.S().Subtle.Render(fmt.Sprintf("error: %s", info.Error.Error()))
+ }
+ return t.ItemErrorIcon, description
+ case lsp.StateDisabled:
+ return t.ItemOfflineIcon.Foreground(t.FgMuted), t.S().Subtle.Render("inactive")
+ default:
+ return t.ItemOfflineIcon, ""
+ }
+}
+
// RenderLSPBlock renders a complete LSP block with optional truncation indicator.
func RenderLSPBlock(lspClients *csync.Map[string, *lsp.Client], opts RenderOptions, showTruncationIndicator bool) string {
t := styles.CurrentTheme()