150 lines
3.8 KiB
Go
150 lines
3.8 KiB
Go
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"))
|
||
var markdownStyleName = "dark"
|
||
|
||
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
|
||
if markdownStyleName == "light" {
|
||
style = styles.LightStyleConfig
|
||
}
|
||
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
|
||
}
|