85 lines
2.1 KiB
Go
85 lines
2.1 KiB
Go
//go:build !no_syntax_highlighting
|
|
|
|
package markdown
|
|
|
|
import (
|
|
"github.com/alecthomas/chroma/v2"
|
|
"github.com/alecthomas/chroma/v2/lexers"
|
|
"github.com/alecthomas/chroma/v2/styles"
|
|
"github.com/ayn2op/tview"
|
|
"github.com/gdamore/tcell/v3"
|
|
)
|
|
|
|
func (r *Renderer) writeCodeBlock(builder *Builder, code string, language string, base tview.Style) {
|
|
lexer := lexers.Get(language)
|
|
declaredLanguageSupported := lexer != nil
|
|
|
|
// Detect the language from its content.
|
|
var analyzed bool
|
|
if lexer == nil {
|
|
lexer = lexers.Analyse(code)
|
|
analyzed = lexer != nil
|
|
}
|
|
if lexer == nil {
|
|
lexer = lexers.Fallback
|
|
}
|
|
|
|
// Some lexers emit many tiny tokens; coalesce runs of the same type.
|
|
lexer = chroma.Coalesce(lexer)
|
|
|
|
var header string
|
|
switch {
|
|
case analyzed:
|
|
header = "code: analyzed"
|
|
case language == "":
|
|
header = "code"
|
|
case !declaredLanguageSupported:
|
|
header = "code: " + language
|
|
}
|
|
if header != "" {
|
|
builder.Write(codeBlockIndent+header, base.Dim(true))
|
|
builder.NewLine()
|
|
}
|
|
|
|
iterator, err := lexer.Tokenise(nil, code)
|
|
if err != nil {
|
|
builder.Write(codeBlockIndent, base)
|
|
writeCode(builder, code, base, base)
|
|
return
|
|
}
|
|
|
|
theme := styles.Get(r.cfg.Markdown.Theme)
|
|
if theme == nil {
|
|
theme = styles.Fallback
|
|
}
|
|
|
|
builder.Write(codeBlockIndent, base)
|
|
for token := iterator(); token != chroma.EOF; token = iterator() {
|
|
style := applyChromaStyle(base, theme.Get(token.Type))
|
|
writeCode(builder, token.Value, style, base)
|
|
}
|
|
}
|
|
|
|
func applyChromaStyle(base tview.Style, entry chroma.StyleEntry) tview.Style {
|
|
style := base
|
|
if entry.Colour.IsSet() {
|
|
style = style.Foreground(tcell.NewRGBColor(
|
|
int32(entry.Colour.Red()),
|
|
int32(entry.Colour.Green()),
|
|
int32(entry.Colour.Blue()),
|
|
))
|
|
}
|
|
// Token background colors are not applied, so code blocks keep the user's terminal/chat background.
|
|
|
|
// An attribute chroma passes on is left as it is.
|
|
if entry.Bold != chroma.Pass {
|
|
style = style.Bold(entry.Bold == chroma.Yes)
|
|
}
|
|
if entry.Italic != chroma.Pass {
|
|
style = style.Italic(entry.Italic == chroma.Yes)
|
|
}
|
|
if entry.Underline != chroma.Pass {
|
|
style = style.Underline(entry.Underline == chroma.Yes)
|
|
}
|
|
return style
|
|
}
|