Files
diple/markdown_editor_highlight.go
2026-07-28 14:54:19 +02:00

183 lines
4.4 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 ""
}
}
if currentThemeName == "light" {
switch style {
case editorMarkdownHeading:
return "\x1b[1;38;2;154;103;0m"
case editorMarkdownStrong:
return "\x1b[1;38;2;130;80;223m"
case editorMarkdownEmphasis:
return "\x1b[3;38;2;87;96;106m"
case editorMarkdownCode:
return "\x1b[38;2;17;99;41m"
case editorMarkdownLink:
return "\x1b[4;38;2;9;105;218m"
case editorMarkdownDestination:
return "\x1b[38;2;10;112;111m"
case editorMarkdownQuote:
return "\x1b[38;2;154;103;0m"
case editorMarkdownComment:
return "\x1b[2;38;2;101;109;118m"
}
}
switch style {
case editorMarkdownHeading:
return "\x1b[1;38;2;240;183;47m"
case editorMarkdownStrong:
return "\x1b[1;38;2;198;120;221m"
case editorMarkdownEmphasis:
return "\x1b[3;38;2;215;218;232m"
case editorMarkdownCode:
return "\x1b[38;2;152;195;121m"
case editorMarkdownLink:
return "\x1b[4;38;2;97;175;239m"
case editorMarkdownDestination:
return "\x1b[38;2;86;182;194m"
case editorMarkdownQuote:
return "\x1b[38;2;229;192;123m"
case editorMarkdownComment:
return "\x1b[2;38;2;119;119;119m"
default:
return ""
}
}
func editorMarkdownStyleEnd(active bool) string {
foreground := "\x1b[39m"
if active {
switch currentThemeName {
case "dark":
foreground = "\x1b[38;2;215;218;232m"
case "light":
foreground = "\x1b[38;2;36;41;47m"
case "high-contrast":
foreground = "\x1b[38;2;255;255;255m"
}
}
return "\x1b[22;23;24m" + foreground
}