Files
diple/markdown_editor_highlight.go
2026-07-28 20:31:27 +02:00

163 lines
4.1 KiB
Go

package main
import (
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/lexers"
)
type editorMarkdownStyle uint8
const (
editorMarkdownPlain editorMarkdownStyle = iota
editorMarkdownHeading
editorMarkdownStrong
editorMarkdownEmphasis
editorMarkdownCode
editorMarkdownLink
editorMarkdownDestination
editorMarkdownQuote
editorMarkdownComment
)
func editorMarkdownStyles(value string) []editorMarkdownStyle {
runes := []rune(value)
styles := make([]editorMarkdownStyle, len(runes))
lexer := lexers.Get("markdown")
if lexer == nil {
highlightEditorHTMLComments(runes, styles)
return styles
}
tokens, err := chroma.Tokenise(lexer, nil, value)
if err != nil {
highlightEditorHTMLComments(runes, styles)
return styles
}
offset := 0
for _, token := range tokens {
style := editorMarkdownStyleForToken(token.Type)
for range []rune(token.Value) {
if offset >= len(styles) {
return styles
}
styles[offset] = style
offset++
}
}
highlightEditorHTMLComments(runes, styles)
return styles
}
func highlightEditorHTMLComments(runes []rune, styles []editorMarkdownStyle) {
startMarker, endMarker := []rune("<!--"), []rune("-->")
for offset := 0; offset < len(runes); {
start := findEditorRunes(runes, startMarker, offset)
if start < 0 {
return
}
end := findEditorRunes(runes, endMarker, start+len(startMarker))
if end < 0 {
end = len(runes)
} else {
end += len(endMarker)
}
for index := start; index < end; index++ {
styles[index] = editorMarkdownComment
}
offset = end
}
}
func findEditorRunes(value, target []rune, offset int) int {
for index := max(0, offset); index+len(target) <= len(value); index++ {
matches := true
for targetIndex := range target {
if value[index+targetIndex] != target[targetIndex] {
matches = false
break
}
}
if matches {
return index
}
}
return -1
}
func editorMarkdownStyleForToken(token chroma.TokenType) editorMarkdownStyle {
switch {
case token == chroma.GenericHeading || token == chroma.GenericSubheading:
return editorMarkdownHeading
case token == chroma.GenericStrong:
return editorMarkdownStrong
case token == chroma.GenericEmph:
return editorMarkdownEmphasis
case token == chroma.LiteralStringBacktick:
return editorMarkdownCode
case token == chroma.NameTag:
return editorMarkdownLink
case token == chroma.NameAttribute:
return editorMarkdownDestination
case token == chroma.Keyword:
return editorMarkdownQuote
case token.InSubCategory(chroma.Comment):
return editorMarkdownComment
default:
return editorMarkdownPlain
}
}
func editorMarkdownStyleStart(style editorMarkdownStyle) string {
if style == editorMarkdownPlain {
return ""
}
if currentThemeName == "no-color" {
switch style {
case editorMarkdownHeading, editorMarkdownStrong:
return "\x1b[1m"
case editorMarkdownEmphasis:
return "\x1b[3m"
case editorMarkdownLink:
return "\x1b[4m"
case editorMarkdownComment:
return "\x1b[2m"
default:
return ""
}
}
authors := editorMarkdownTheme.AuthorPalette
author := func(index int) string {
if len(authors) == 0 {
return editorMarkdownTheme.Text
}
return authors[index%len(authors)]
}
switch style {
case editorMarkdownHeading:
return "\x1b[1m" + foregroundSequence(editorMarkdownTheme.Title)
case editorMarkdownStrong:
return "\x1b[1m" + foregroundSequence(author(1))
case editorMarkdownEmphasis:
return "\x1b[3m" + foregroundSequence(editorMarkdownTheme.Text)
case editorMarkdownCode:
return foregroundSequence(editorMarkdownTheme.Success)
case editorMarkdownLink:
return "\x1b[4m" + foregroundSequence(author(0))
case editorMarkdownDestination:
return foregroundSequence(author(2))
case editorMarkdownQuote:
return foregroundSequence(editorMarkdownTheme.Warning)
case editorMarkdownComment:
return "\x1b[2m" + foregroundSequence(editorMarkdownTheme.Dim)
default:
return ""
}
}
func editorMarkdownStyleEnd(active bool) string {
foreground := "\x1b[39m"
if active && colorEnabled {
foreground = foregroundSequence(editorMarkdownTheme.EditorForeground)
}
return "\x1b[22;23;24m" + foreground
}