Files
diple/markdown.go

146 lines
3.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"strings"
"sync"
"github.com/charmbracelet/glamour"
"github.com/charmbracelet/glamour/styles"
"github.com/charmbracelet/lipgloss"
)
var commentMarkdownRenderers sync.Map
var quoteRailStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("#777777"))
func renderCommentMarkdown(markdown string, width int) []string {
if strings.TrimSpace(markdown) == "" {
return nil
}
width = max(10, width)
markdown = normalizeGitHubAlerts(markdown)
var (
result []string
block []string
blockQuote bool
haveBlock bool
)
flush := func() {
if len(block) == 0 {
return
}
blockWidth := width
if blockQuote {
blockWidth = max(8, width-2)
}
lines := renderMarkdownFragment(strings.Join(block, "\n"), blockWidth)
if haveBlock && len(lines) > 0 {
result = append(result, "")
}
if blockQuote {
rail := quoteRailStyle.Render("│ ")
for i := range lines {
lines[i] = rail + lines[i]
}
}
result = append(result, lines...)
haveBlock = haveBlock || len(lines) > 0
block = nil
}
for _, line := range strings.Split(markdown, "\n") {
content, quoted := stripQuoteMarker(line)
if len(block) > 0 && quoted != blockQuote {
flush()
}
blockQuote = quoted
block = append(block, content)
}
flush()
return trimMarkdownLines(result)
}
func renderMarkdownFragment(markdown string, width int) []string {
if strings.TrimSpace(markdown) == "" {
return nil
}
renderer, err := commentMarkdownRenderer(width)
if err != nil {
return fallbackCommentLines(markdown, width)
}
rendered, err := renderer.Render(markdown)
if err != nil {
return fallbackCommentLines(markdown, width)
}
return trimMarkdownLines(strings.Split(strings.Trim(rendered, "\n"), "\n"))
}
func commentMarkdownRenderer(width int) (*glamour.TermRenderer, error) {
if cached, ok := commentMarkdownRenderers.Load(width); ok {
return cached.(*glamour.TermRenderer), nil
}
style := styles.DarkStyleConfig
zero := uint(0)
style.Document.Margin = &zero
style.Code.Prefix = ""
style.Code.Suffix = ""
renderer, err := glamour.NewTermRenderer(
glamour.WithStyles(style),
glamour.WithWordWrap(width),
glamour.WithTableWrap(true),
glamour.WithPreservedNewLines(),
glamour.WithEmoji(),
)
if err != nil {
return nil, err
}
actual, _ := commentMarkdownRenderers.LoadOrStore(width, renderer)
return actual.(*glamour.TermRenderer), nil
}
func stripQuoteMarker(line string) (string, bool) {
trimmed := strings.TrimLeft(line, " \t")
if !strings.HasPrefix(trimmed, ">") {
return line, false
}
content := strings.TrimPrefix(trimmed, ">")
content = strings.TrimPrefix(content, " ")
return content, true
}
func normalizeGitHubAlerts(markdown string) string {
alerts := map[string]string{
"[!NOTE]": " **Note**",
"[!TIP]": "◆ **Tip**",
"[!IMPORTANT]": "❗ **Important**",
"[!WARNING]": "⚠ **Warning**",
"[!CAUTION]": "⛔ **Caution**",
}
lines := strings.Split(markdown, "\n")
for i, line := range lines {
trimmed := strings.TrimSpace(line)
if !strings.HasPrefix(trimmed, ">") {
continue
}
label := strings.TrimSpace(strings.TrimPrefix(trimmed, ">"))
if replacement, ok := alerts[strings.ToUpper(label)]; ok {
prefix := line[:strings.Index(line, ">")+1]
lines[i] = prefix + " " + replacement
}
}
return strings.Join(lines, "\n")
}
func fallbackCommentLines(markdown string, width int) []string {
return strings.Split(wrap(markdown, max(10, width)), "\n")
}
func trimMarkdownLines(lines []string) []string {
for len(lines) > 0 && strings.TrimSpace(lines[0]) == "" {
lines = lines[1:]
}
for len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) == "" {
lines = lines[:len(lines)-1]
}
return lines
}