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("") 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 }