1package model
2
3import (
4 "fmt"
5 "strings"
6
7 "charm.land/lipgloss/v2"
8 "github.com/charmbracelet/crush/internal/app"
9 "github.com/charmbracelet/crush/internal/lsp"
10 "github.com/charmbracelet/crush/internal/ui/common"
11 "github.com/charmbracelet/crush/internal/ui/styles"
12 "github.com/charmbracelet/x/powernap/pkg/lsp/protocol"
13)
14
15// LSPInfo wraps LSP client information with diagnostic counts by severity.
16type LSPInfo struct {
17 app.LSPClientInfo
18 Diagnostics map[protocol.DiagnosticSeverity]int
19}
20
21// lspInfo renders the LSP status section showing active LSP clients and their
22// diagnostic counts.
23func (m *UI) lspInfo(width, maxItems int, isSection bool) string {
24 var lsps []LSPInfo
25 t := m.com.Styles
26 lspConfigs := m.com.Config().LSP.Sorted()
27
28 for _, cfg := range lspConfigs {
29 state, ok := m.lspStates[cfg.Name]
30 if !ok {
31 continue
32 }
33
34 client, ok := m.com.App.LSPClients.Get(state.Name)
35 if !ok {
36 continue
37 }
38 counts := client.GetDiagnosticCounts()
39 lspErrs := map[protocol.DiagnosticSeverity]int{
40 protocol.SeverityError: counts.Error,
41 protocol.SeverityWarning: counts.Warning,
42 protocol.SeverityHint: counts.Hint,
43 protocol.SeverityInformation: counts.Information,
44 }
45
46 lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
47 }
48
49 title := t.Subtle.Render("LSPs")
50 if isSection {
51 title = common.Section(t, title, width)
52 }
53 list := t.Subtle.Render("None")
54 if len(lsps) > 0 {
55 list = lspList(t, lsps, width, maxItems)
56 }
57
58 return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
59}
60
61// lspDiagnostics formats diagnostic counts with appropriate icons and colors.
62func lspDiagnostics(t *styles.Styles, diagnostics map[protocol.DiagnosticSeverity]int) string {
63 errs := []string{}
64 if diagnostics[protocol.SeverityError] > 0 {
65 errs = append(errs, t.LSP.ErrorDiagnostic.Render(fmt.Sprintf("%s %d", styles.ErrorIcon, diagnostics[protocol.SeverityError])))
66 }
67 if diagnostics[protocol.SeverityWarning] > 0 {
68 errs = append(errs, t.LSP.WarningDiagnostic.Render(fmt.Sprintf("%s %d", styles.WarningIcon, diagnostics[protocol.SeverityWarning])))
69 }
70 if diagnostics[protocol.SeverityHint] > 0 {
71 errs = append(errs, t.LSP.HintDiagnostic.Render(fmt.Sprintf("%s %d", styles.HintIcon, diagnostics[protocol.SeverityHint])))
72 }
73 if diagnostics[protocol.SeverityInformation] > 0 {
74 errs = append(errs, t.LSP.InfoDiagnostic.Render(fmt.Sprintf("%s %d", styles.InfoIcon, diagnostics[protocol.SeverityInformation])))
75 }
76 return strings.Join(errs, " ")
77}
78
79// lspList renders a list of LSP clients with their status and diagnostics,
80// truncating to maxItems if needed.
81func lspList(t *styles.Styles, lsps []LSPInfo, width, maxItems int) string {
82 if maxItems <= 0 {
83 return ""
84 }
85 var renderedLsps []string
86 for _, l := range lsps {
87 var icon string
88 title := l.Name
89 var description string
90 var diagnostics string
91 switch l.State {
92 case lsp.StateStarting:
93 icon = t.ItemBusyIcon.String()
94 description = t.Subtle.Render("starting...")
95 case lsp.StateReady:
96 icon = t.ItemOnlineIcon.String()
97 diagnostics = lspDiagnostics(t, l.Diagnostics)
98 case lsp.StateError:
99 icon = t.ItemErrorIcon.String()
100 description = t.Subtle.Render("error")
101 if l.Error != nil {
102 description = t.Subtle.Render(fmt.Sprintf("error: %s", l.Error.Error()))
103 }
104 case lsp.StateDisabled:
105 icon = t.ItemOfflineIcon.Foreground(t.Muted.GetBackground()).String()
106 description = t.Subtle.Render("inactive")
107 default:
108 icon = t.ItemOfflineIcon.String()
109 }
110 renderedLsps = append(renderedLsps, common.Status(t, common.StatusOpts{
111 Icon: icon,
112 Title: title,
113 Description: description,
114 ExtraContent: diagnostics,
115 }, width))
116 }
117
118 if len(renderedLsps) > maxItems {
119 visibleItems := renderedLsps[:maxItems-1]
120 remaining := len(renderedLsps) - maxItems
121 visibleItems = append(visibleItems, t.Subtle.Render(fmt.Sprintf("β¦and %d more", remaining)))
122 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
123 }
124 return lipgloss.JoinVertical(lipgloss.Left, renderedLsps...)
125}