Detailed changes
@@ -126,9 +126,14 @@ func New(ctx context.Context, conn *sql.DB, cfg *config.Config) (*App, error) {
// Set up callback for LSP state updates.
app.LSPManager.SetCallback(func(name string, client *lsp.Client) {
+ if client == nil {
+ updateLSPState(name, lsp.StateUnstarted, nil, nil, 0)
+ return
+ }
client.SetDiagnosticsCallback(updateLSPDiagnostics)
updateLSPState(name, client.GetServerState(), nil, client, 0)
})
+ go app.LSPManager.TrackConfigured()
return app, nil
}
@@ -241,10 +241,11 @@ func (c *Client) Restart() error {
type ServerState int
const (
- StateStopped ServerState = iota
+ StateUnstarted ServerState = iota
StateStarting
StateReady
StateError
+ StateStopped
StateDisabled
)
@@ -77,6 +77,24 @@ func (s *Manager) SetCallback(cb func(name string, client *Client)) {
s.callback = cb
}
+// TrackConfigured will callback the user-configured LSPs, but will not create
+// any clients.
+func (s *Manager) TrackConfigured() {
+ s.mu.Lock()
+ defer s.mu.Unlock()
+
+ var wg sync.WaitGroup
+ for name := range s.manager.GetServers() {
+ if !s.isUserConfigured(name) {
+ continue
+ }
+ wg.Go(func() {
+ s.callback(name, nil)
+ })
+ }
+ wg.Wait()
+}
+
// Start starts an LSP server that can handle the given file path.
// If an appropriate LSP is already running, this is a no-op.
func (s *Manager) Start(ctx context.Context, path string) {
@@ -31,26 +31,23 @@ func (m *UI) lspInfo(width, maxItems int, isSection bool) string {
var lsps []LSPInfo
for _, state := range states {
- client, ok := m.com.App.LSPManager.Clients().Get(state.Name)
- if !ok {
- continue
- }
- counts := client.GetDiagnosticCounts()
- lspErrs := map[protocol.DiagnosticSeverity]int{
- protocol.SeverityError: counts.Error,
- protocol.SeverityWarning: counts.Warning,
- protocol.SeverityHint: counts.Hint,
- protocol.SeverityInformation: counts.Information,
+ lspErrs := map[protocol.DiagnosticSeverity]int{}
+ if client, ok := m.com.App.LSPManager.Clients().Get(state.Name); ok {
+ counts := client.GetDiagnosticCounts()
+ lspErrs[protocol.SeverityError] = counts.Error
+ lspErrs[protocol.SeverityWarning] = counts.Warning
+ lspErrs[protocol.SeverityHint] = counts.Hint
+ lspErrs[protocol.SeverityInformation] = counts.Information
}
lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
}
- title := t.Subtle.Render("LSPs")
+ title := t.ResourceGroupTitle.Render("LSPs")
if isSection {
title = common.Section(t, title, width)
}
- list := t.Subtle.Render("None")
+ list := t.ResourceAdditionalText.Render("None")
if len(lsps) > 0 {
list = lspList(t, lsps, width, maxItems)
}
@@ -85,30 +82,33 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
var renderedLsps []string
for _, l := range lsps {
var icon string
- title := l.Name
+ title := t.ResourceName.Render(l.Name)
var description string
var diagnostics string
switch l.State {
+ case lsp.StateUnstarted:
+ icon = t.ResourceOfflineIcon.String()
+ description = t.ResourceStatus.Render("unstarted")
case lsp.StateStopped:
- icon = t.ItemOfflineIcon.Foreground(t.Muted.GetBackground()).String()
- description = t.Subtle.Render("stopped")
+ icon = t.ResourceOfflineIcon.String()
+ description = t.ResourceStatus.Render("stopped")
case lsp.StateStarting:
- icon = t.ItemBusyIcon.String()
- description = t.Subtle.Render("starting...")
+ icon = t.ResourceBusyIcon.String()
+ description = t.ResourceStatus.Render("starting...")
case lsp.StateReady:
- icon = t.ItemOnlineIcon.String()
+ icon = t.ResourceOnlineIcon.String()
diagnostics = lspDiagnostics(t, l.Diagnostics)
case lsp.StateError:
- icon = t.ItemErrorIcon.String()
- description = t.Subtle.Render("error")
+ icon = t.ResourceErrorIcon.String()
+ description = t.ResourceStatus.Render("error")
if l.Error != nil {
- description = t.Subtle.Render(fmt.Sprintf("error: %s", l.Error.Error()))
+ description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", l.Error.Error()))
}
case lsp.StateDisabled:
- icon = t.ItemOfflineIcon.Foreground(t.Muted.GetBackground()).String()
- description = t.Subtle.Render("disabled")
+ icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
+ description = t.ResourceStatus.Render("disabled")
default:
- icon = t.ItemOfflineIcon.String()
+ icon = t.ResourceOfflineIcon.String()
}
renderedLsps = append(renderedLsps, common.Status(t, common.StatusOpts{
Icon: icon,
@@ -121,7 +121,7 @@ func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
if len(renderedLsps) > maxItems {
visibleItems := renderedLsps[:maxItems-1]
remaining := len(renderedLsps) - maxItems
- visibleItems = append(visibleItems, t.Subtle.Render(fmt.Sprintf("β¦and %d more", remaining)))
+ visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("β¦and %d more", remaining)))
return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
}
return lipgloss.JoinVertical(lipgloss.Left, renderedLsps...)
@@ -22,11 +22,11 @@ func (m *UI) mcpInfo(width, maxItems int, isSection bool) string {
}
}
- title := t.Subtle.Render("MCPs")
+ title := t.ResourceGroupTitle.Render("MCPs")
if isSection {
title = common.Section(t, title, width)
}
- list := t.Subtle.Render("None")
+ list := t.ResourceAdditionalText.Render("None")
if len(mcps) > 0 {
list = mcpList(t, mcps, width, maxItems)
}
@@ -59,28 +59,28 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
for _, m := range mcps {
var icon string
- title := m.Name
+ title := t.ResourceName.Render(m.Name)
var description string
var extraContent string
switch m.State {
case mcp.StateStarting:
- icon = t.ItemBusyIcon.String()
- description = t.Subtle.Render("starting...")
+ icon = t.ResourceBusyIcon.String()
+ description = t.ResourceStatus.Render("starting...")
case mcp.StateConnected:
- icon = t.ItemOnlineIcon.String()
+ icon = t.ResourceOnlineIcon.String()
extraContent = mcpCounts(t, m.Counts)
case mcp.StateError:
- icon = t.ItemErrorIcon.String()
- description = t.Subtle.Render("error")
+ icon = t.ResourceErrorIcon.String()
+ description = t.ResourceStatus.Render("error")
if m.Error != nil {
- description = t.Subtle.Render(fmt.Sprintf("error: %s", m.Error.Error()))
+ description = t.ResourceStatus.Render(fmt.Sprintf("error: %s", m.Error.Error()))
}
case mcp.StateDisabled:
- icon = t.ItemOfflineIcon.Foreground(t.Muted.GetBackground()).String()
- description = t.Subtle.Render("disabled")
+ icon = t.ResourceOfflineIcon.Foreground(t.Muted.GetBackground()).String()
+ description = t.ResourceStatus.Render("disabled")
default:
- icon = t.ItemOfflineIcon.String()
+ icon = t.ResourceOfflineIcon.String()
}
renderedMcps = append(renderedMcps, common.Status(t, common.StatusOpts{
@@ -94,7 +94,7 @@ func mcpList(t *styles.Styles, mcps []mcp.ClientInfo, width, maxItems int) strin
if len(renderedMcps) > maxItems {
visibleItems := renderedMcps[:maxItems-1]
remaining := len(renderedMcps) - maxItems
- visibleItems = append(visibleItems, t.Subtle.Render(fmt.Sprintf("β¦and %d more", remaining)))
+ visibleItems = append(visibleItems, t.ResourceAdditionalText.Render(fmt.Sprintf("β¦and %d more", remaining)))
return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
}
return lipgloss.JoinVertical(lipgloss.Left, renderedMcps...)
@@ -109,10 +109,14 @@ type Styles struct {
TextSelection lipgloss.Style
// LSP and MCP status indicators
- ItemOfflineIcon lipgloss.Style
- ItemBusyIcon lipgloss.Style
- ItemErrorIcon lipgloss.Style
- ItemOnlineIcon lipgloss.Style
+ ResourceGroupTitle lipgloss.Style
+ ResourceOfflineIcon lipgloss.Style
+ ResourceBusyIcon lipgloss.Style
+ ResourceErrorIcon lipgloss.Style
+ ResourceOnlineIcon lipgloss.Style
+ ResourceName lipgloss.Style
+ ResourceStatus lipgloss.Style
+ ResourceAdditionalText lipgloss.Style
// Markdown & Chroma
Markdown ansi.StyleConfig
@@ -1199,10 +1203,14 @@ func DefaultStyles() Styles {
s.Initialize.Accent = s.Base.Foreground(greenDark)
// LSP and MCP status.
- s.ItemOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Squid).SetString("β")
- s.ItemBusyIcon = s.ItemOfflineIcon.Foreground(charmtone.Citron)
- s.ItemErrorIcon = s.ItemOfflineIcon.Foreground(charmtone.Coral)
- s.ItemOnlineIcon = s.ItemOfflineIcon.Foreground(charmtone.Guac)
+ s.ResourceGroupTitle = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+ s.ResourceOfflineIcon = lipgloss.NewStyle().Foreground(charmtone.Iron).SetString("β")
+ s.ResourceBusyIcon = s.ResourceOfflineIcon.Foreground(charmtone.Citron)
+ s.ResourceErrorIcon = s.ResourceOfflineIcon.Foreground(charmtone.Coral)
+ s.ResourceOnlineIcon = s.ResourceOfflineIcon.Foreground(charmtone.Guac)
+ s.ResourceName = lipgloss.NewStyle().Foreground(charmtone.Squid)
+ s.ResourceStatus = lipgloss.NewStyle().Foreground(charmtone.Oyster)
+ s.ResourceAdditionalText = lipgloss.NewStyle().Foreground(charmtone.Oyster)
// LSP
s.LSP.ErrorDiagnostic = s.Base.Foreground(redDark)