diff --git a/internal/ui/common/markdown.go b/internal/ui/common/markdown.go index f5af8121d1667658725b4424a4ab303804c75b42..86b5690655ccac0a10c095f5cf6d9d6170c706dd 100644 --- a/internal/ui/common/markdown.go +++ b/internal/ui/common/markdown.go @@ -1,16 +1,30 @@ package common import ( + "image/color" + "charm.land/glamour/v2" + "github.com/alecthomas/chroma/v2/formatters" "github.com/charmbracelet/crush/internal/ui/styles" + "github.com/charmbracelet/crush/internal/ui/xchroma" ) +const formatterName = "crush" + +func init() { + // NOTE: Glamour does not offer us an option to pass the formatter + // implementation directly. We need to register and use by name. + var zero color.Color + formatters.Register(formatterName, xchroma.Formatter(zero, nil)) +} + // MarkdownRenderer returns a glamour [glamour.TermRenderer] configured with // the given styles and width. func MarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer { r, _ := glamour.NewTermRenderer( glamour.WithStyles(sty.Markdown), glamour.WithWordWrap(width), + glamour.WithChromaFormatter(formatterName), ) return r } @@ -21,6 +35,7 @@ func PlainMarkdownRenderer(sty *styles.Styles, width int) *glamour.TermRenderer r, _ := glamour.NewTermRenderer( glamour.WithStyles(sty.PlainMarkdown), glamour.WithWordWrap(width), + glamour.WithChromaFormatter(formatterName), ) return r } diff --git a/internal/ui/diffview/chroma.go b/internal/ui/diffview/chroma.go deleted file mode 100644 index d66cf39712a5e9a42a6021bec96ad155e0ffbd76..0000000000000000000000000000000000000000 --- a/internal/ui/diffview/chroma.go +++ /dev/null @@ -1,57 +0,0 @@ -package diffview - -import ( - "fmt" - "image/color" - "io" - "strings" - - "charm.land/lipgloss/v2" - "github.com/alecthomas/chroma/v2" - "github.com/charmbracelet/crush/internal/ansiext" -) - -var _ chroma.Formatter = chromaFormatter{} - -// chromaFormatter is a custom formatter for Chroma that uses Lip Gloss for -// foreground styling, while keeping a forced background color. -type chromaFormatter struct { - bgColor color.Color -} - -// Format implements the chroma.Formatter interface. -func (c chromaFormatter) Format(w io.Writer, style *chroma.Style, it chroma.Iterator) error { - for token := it(); token != chroma.EOF; token = it() { - value := strings.TrimRight(token.Value, "\n") - value = ansiext.Escape(value) - - entry := style.Get(token.Type) - if entry.IsZero() { - if _, err := fmt.Fprint(w, value); err != nil { - return err - } - continue - } - - s := lipgloss.NewStyle(). - Background(c.bgColor) - - if entry.Bold == chroma.Yes { - s = s.Bold(true) - } - if entry.Underline == chroma.Yes { - s = s.Underline(true) - } - if entry.Italic == chroma.Yes { - s = s.Italic(true) - } - if entry.Colour.IsSet() { - s = s.Foreground(lipgloss.Color(entry.Colour.String())) - } - - if _, err := fmt.Fprint(w, s.Render(value)); err != nil { - return err - } - } - return nil -} diff --git a/internal/ui/diffview/diffview.go b/internal/ui/diffview/diffview.go index a215ac15809521bc089369969e8c763c631af454..db311593e0f57384f0417ba018d5c7cf0f88df5f 100644 --- a/internal/ui/diffview/diffview.go +++ b/internal/ui/diffview/diffview.go @@ -10,6 +10,8 @@ import ( "github.com/alecthomas/chroma/v2" "github.com/alecthomas/chroma/v2/lexers" "github.com/aymanbagabas/go-udiff" + "github.com/charmbracelet/crush/internal/ansiext" + "github.com/charmbracelet/crush/internal/ui/xchroma" "github.com/charmbracelet/x/ansi" "github.com/zeebo/xxh3" ) @@ -768,7 +770,11 @@ func (dv *DiffView) getChromaLexer() chroma.Lexer { } func (dv *DiffView) getChromaFormatter(bgColor color.Color) chroma.Formatter { - return chromaFormatter{ - bgColor: bgColor, - } + return xchroma.Formatter(bgColor, processChromaValue) +} + +func processChromaValue(value string) string { + value = strings.TrimRight(value, "\n") + value = ansiext.Escape(value) + return value } diff --git a/internal/ui/xchroma/chroma.go b/internal/ui/xchroma/chroma.go new file mode 100644 index 0000000000000000000000000000000000000000..3ca6d90be902ac68e8892a4c29ff71be505c7261 --- /dev/null +++ b/internal/ui/xchroma/chroma.go @@ -0,0 +1,52 @@ +package xchroma + +import ( + "fmt" + "image/color" + "io" + + "charm.land/lipgloss/v2" + "github.com/alecthomas/chroma/v2" +) + +// Formatter is func that returns a custom formatter for Chroma that uses +// Lip Gloss for foreground styling, while keeping a forced background color. +func Formatter(bgColor color.Color, processValue func(string) string) chroma.Formatter { + return chroma.FormatterFunc(func(w io.Writer, style *chroma.Style, it chroma.Iterator) error { + for token := it(); token != chroma.EOF; token = it() { + value := token.Value + if processValue != nil { + value = processValue(value) + } + + entry := style.Get(token.Type) + if entry.IsZero() { + if _, err := fmt.Fprint(w, value); err != nil { + return err + } + continue + } + + s := lipgloss.NewStyle(). + Background(bgColor) + + if entry.Bold == chroma.Yes { + s = s.Bold(true) + } + if entry.Underline == chroma.Yes { + s = s.Underline(true) + } + if entry.Italic == chroma.Yes { + s = s.Italic(true) + } + if entry.Colour.IsSet() { + s = s.Foreground(lipgloss.Color(entry.Colour.String())) + } + + if _, err := fmt.Fprint(w, s.Render(value)); err != nil { + return err + } + } + return nil + }) +}