lsp.go

  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}