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
15type LSPInfo struct {
16 app.LSPClientInfo
17 Diagnostics map[protocol.DiagnosticSeverity]int
18}
19
20func (m *UI) lspInfo(t *styles.Styles, width, height int) string {
21 var lsps []LSPInfo
22
23 for _, state := range m.lspStates {
24 client, ok := m.com.App.LSPClients.Get(state.Name)
25 if !ok {
26 continue
27 }
28 lspErrs := map[protocol.DiagnosticSeverity]int{
29 protocol.SeverityError: 0,
30 protocol.SeverityWarning: 0,
31 protocol.SeverityHint: 0,
32 protocol.SeverityInformation: 0,
33 }
34
35 for _, diagnostics := range client.GetDiagnostics() {
36 for _, diagnostic := range diagnostics {
37 if severity, ok := lspErrs[diagnostic.Severity]; ok {
38 lspErrs[diagnostic.Severity] = severity + 1
39 }
40 }
41 }
42
43 lsps = append(lsps, LSPInfo{LSPClientInfo: state, Diagnostics: lspErrs})
44 }
45 title := t.Subtle.Render("LSPs")
46 list := t.Subtle.Render("None")
47 if len(lsps) > 0 {
48 height = max(0, height-2) // remove title and space
49 list = lspList(t, lsps, width, height)
50 }
51
52 return lipgloss.NewStyle().Width(width).Render(fmt.Sprintf("%s\n\n%s", title, list))
53}
54
55func lspDiagnostics(t *styles.Styles, diagnostics map[protocol.DiagnosticSeverity]int) string {
56 errs := []string{}
57 if diagnostics[protocol.SeverityError] > 0 {
58 errs = append(errs, t.LSP.ErrorDiagnostic.Render(fmt.Sprintf("%s %d", styles.ErrorIcon, diagnostics[protocol.SeverityError])))
59 }
60 if diagnostics[protocol.SeverityWarning] > 0 {
61 errs = append(errs, t.LSP.WarningDiagnostic.Render(fmt.Sprintf("%s %d", styles.WarningIcon, diagnostics[protocol.SeverityWarning])))
62 }
63 if diagnostics[protocol.SeverityHint] > 0 {
64 errs = append(errs, t.LSP.HintDiagnostic.Render(fmt.Sprintf("%s %d", styles.HintIcon, diagnostics[protocol.SeverityHint])))
65 }
66 if diagnostics[protocol.SeverityInformation] > 0 {
67 errs = append(errs, t.LSP.InfoDiagnostic.Render(fmt.Sprintf("%s %d", styles.InfoIcon, diagnostics[protocol.SeverityInformation])))
68 }
69 return strings.Join(errs, " ")
70}
71
72func lspList(t *styles.Styles, lsps []LSPInfo, width, height int) string {
73 var renderedLsps []string
74 for _, l := range lsps {
75 var icon string
76 title := l.Name
77 var description string
78 var diagnostics string
79 switch l.State {
80 case lsp.StateStarting:
81 icon = t.ItemBusyIcon.String()
82 description = t.Subtle.Render("starting...")
83 case lsp.StateReady:
84 icon = t.ItemOnlineIcon.String()
85 diagnostics = lspDiagnostics(t, l.Diagnostics)
86 case lsp.StateError:
87 icon = t.ItemErrorIcon.String()
88 description = t.Subtle.Render("error")
89 if l.Error != nil {
90 description = t.Subtle.Render(fmt.Sprintf("error: %s", l.Error.Error()))
91 }
92 case lsp.StateDisabled:
93 icon = t.ItemOfflineIcon.Foreground(t.Muted.GetBackground()).String()
94 description = t.Subtle.Render("inactive")
95 default:
96 icon = t.ItemOfflineIcon.String()
97 }
98 renderedLsps = append(renderedLsps, common.Status(t, common.StatusOpts{
99 Icon: icon,
100 Title: title,
101 Description: description,
102 ExtraContent: diagnostics,
103 }, width))
104 }
105
106 if len(renderedLsps) > height {
107 visibleItems := renderedLsps[:height-1]
108 remaining := len(renderedLsps) - (height - 1)
109 visibleItems = append(visibleItems, t.Subtle.Render(fmt.Sprintf("β¦and %d more", remaining)))
110 return lipgloss.JoinVertical(lipgloss.Left, visibleItems...)
111 }
112 return lipgloss.JoinVertical(lipgloss.Left, renderedLsps...)
113}