Files
glamour-temp-MUTN/ansi/style.go
T

280 lines
8.0 KiB
Go

package ansi
import (
"github.com/lucasb-eyer/go-colorful"
)
// StylePrimitive holds all the basic style settings.
type StylePrimitive struct {
BlockPrefix string `json:"block_prefix"`
BlockSuffix string `json:"block_suffix"`
Prefix string `json:"prefix"`
Suffix string `json:"suffix"`
Color *string `json:"color"`
BackgroundColor *string `json:"background_color"`
Underline *bool `json:"underline"`
Bold *bool `json:"bold"`
Italic *bool `json:"italic"`
CrossedOut *bool `json:"crossed_out"`
Faint *bool `json:"faint"`
Conceal *bool `json:"conceal"`
Overlined *bool `json:"overlined"`
Inverse *bool `json:"inverse"`
Blink *bool `json:"blink"`
Format string `json:"format"`
}
// StyleTask holds the style settings for a task item.
type StyleTask struct {
StylePrimitive
Ticked string `json:"ticked"`
Unticked string `json:"unticked"`
}
// StyleBlock holds the basic style settings for block elements.
type StyleBlock struct {
StylePrimitive
Indent *uint `json:"indent"`
IndentToken *string `json:"indent_token"`
Margin *uint `json:"margin"`
}
// StyleCodeBlock holds the style settings for a code block.
type StyleCodeBlock struct {
StyleBlock
Theme string `json:"theme"`
Chroma *struct {
Text StylePrimitive `json:"text"`
Error StylePrimitive `json:"error"`
Comment StylePrimitive `json:"comment"`
CommentPreproc StylePrimitive `json:"comment_preproc"`
Keyword StylePrimitive `json:"keyword"`
KeywordReserved StylePrimitive `json:"keyword_reserved"`
KeywordNamespace StylePrimitive `json:"keyword_namespace"`
KeywordType StylePrimitive `json:"keyword_type"`
Operator StylePrimitive `json:"operator"`
Punctuation StylePrimitive `json:"punctuation"`
Name StylePrimitive `json:"name"`
NameBuiltin StylePrimitive `json:"name_builtin"`
NameTag StylePrimitive `json:"name_tag"`
NameAttribute StylePrimitive `json:"name_attribute"`
NameClass StylePrimitive `json:"name_class"`
NameConstant StylePrimitive `json:"name_constant"`
NameDecorator StylePrimitive `json:"name_decorator"`
NameException StylePrimitive `json:"name_exception"`
NameFunction StylePrimitive `json:"name_function"`
NameOther StylePrimitive `json:"name_other"`
Literal StylePrimitive `json:"literal"`
LiteralNumber StylePrimitive `json:"literal_number"`
LiteralDate StylePrimitive `json:"literal_date"`
LiteralString StylePrimitive `json:"literal_string"`
LiteralStringEscape StylePrimitive `json:"literal_string_escape"`
GenericDeleted StylePrimitive `json:"generic_deleted"`
GenericEmph StylePrimitive `json:"generic_emph"`
GenericInserted StylePrimitive `json:"generic_inserted"`
GenericStrong StylePrimitive `json:"generic_strong"`
GenericSubheading StylePrimitive `json:"generic_subheading"`
Background StylePrimitive `json:"background"`
} `json:"chroma"`
}
// StyleList holds the style settings for a list.
type StyleList struct {
StyleBlock
LevelIndent uint `json:"level_indent"`
}
// StyleTable holds the style settings for a table.
type StyleTable struct {
StyleBlock
CenterSeparator *string `json:"center_separator"`
ColumnSeparator *string `json:"column_separator"`
RowSeparator *string `json:"row_separator"`
}
// StyleConfig is used to configure the styling behavior of an ANSIRenderer.
type StyleConfig struct {
Document StyleBlock `json:"document"`
BlockQuote StyleBlock `json:"block_quote"`
Paragraph StyleBlock `json:"paragraph"`
List StyleList `json:"list"`
Heading StyleBlock `json:"heading"`
H1 StyleBlock `json:"h1"`
H2 StyleBlock `json:"h2"`
H3 StyleBlock `json:"h3"`
H4 StyleBlock `json:"h4"`
H5 StyleBlock `json:"h5"`
H6 StyleBlock `json:"h6"`
Text StylePrimitive `json:"text"`
Strikethrough StylePrimitive `json:"strike_through"`
Emph StylePrimitive `json:"emph"`
Strong StylePrimitive `json:"strong"`
HorizontalRule StylePrimitive `json:"hr"`
Item StylePrimitive `json:"item"`
Enumeration StylePrimitive `json:"enumeration"`
Task StyleTask `json:"task"`
Link StylePrimitive `json:"link"`
LinkText StylePrimitive `json:"link_text"`
Image StylePrimitive `json:"image"`
ImageText StylePrimitive `json:"image_text"`
Code StyleBlock `json:"code"`
CodeBlock StyleCodeBlock `json:"code_block"`
Table StyleTable `json:"table"`
DefinitionList StyleBlock `json:"definition_list"`
DefinitionTerm StylePrimitive `json:"definition_term"`
DefinitionDescription StylePrimitive `json:"definition_description"`
HTMLBlock StyleBlock `json:"html_block"`
HTMLSpan StyleBlock `json:"html_span"`
}
func cascadeStyles(onlyColors bool, s ...StyleBlock) StyleBlock {
var r StyleBlock
for _, v := range s {
r = cascadeStyle(r, v, onlyColors)
}
return r
}
func cascadeStyle(parent StyleBlock, child StyleBlock, onlyColors bool) StyleBlock {
s := child
s.Color = parent.Color
s.BackgroundColor = parent.BackgroundColor
if !onlyColors {
s.Indent = parent.Indent
s.Margin = parent.Margin
s.Underline = parent.Underline
s.Bold = parent.Bold
s.Italic = parent.Italic
s.CrossedOut = parent.CrossedOut
s.Faint = parent.Faint
s.Conceal = parent.Conceal
s.Overlined = parent.Overlined
s.Inverse = parent.Inverse
s.Blink = parent.Blink
s.BlockPrefix = parent.BlockPrefix
s.BlockSuffix = parent.BlockSuffix
s.Prefix = parent.Prefix
s.Suffix = parent.Suffix
s.Format = parent.Format
}
if child.Color != nil {
s.Color = child.Color
}
if child.BackgroundColor != nil {
s.BackgroundColor = child.BackgroundColor
}
if child.Indent != nil {
s.Indent = child.Indent
}
if child.Margin != nil {
s.Margin = child.Margin
}
if child.Underline != nil {
s.Underline = child.Underline
}
if child.Bold != nil {
s.Bold = child.Bold
}
if child.Italic != nil {
s.Italic = child.Italic
}
if child.CrossedOut != nil {
s.CrossedOut = child.CrossedOut
}
if child.Faint != nil {
s.Faint = child.Faint
}
if child.Conceal != nil {
s.Conceal = child.Conceal
}
if child.Overlined != nil {
s.Overlined = child.Overlined
}
if child.Inverse != nil {
s.Inverse = child.Inverse
}
if child.Blink != nil {
s.Blink = child.Blink
}
if child.BlockPrefix != "" {
s.BlockPrefix = child.BlockPrefix
}
if child.BlockSuffix != "" {
s.BlockSuffix = child.BlockSuffix
}
if child.Prefix != "" {
s.Prefix = child.Prefix
}
if child.Suffix != "" {
s.Suffix = child.Suffix
}
if child.Format != "" {
s.Format = child.Format
}
return s
}
func hexToANSIColor(h string) (int, error) {
c, err := colorful.Hex(h)
if err != nil {
return 0, err
}
v2ci := func(v float64) int {
if v < 48 {
return 0
}
if v < 115 {
return 1
}
return int((v - 35) / 40)
}
// Calculate the nearest 0-based color index at 16..231
r := v2ci(c.R * 255.0) // 0..5 each
g := v2ci(c.G * 255.0)
b := v2ci(c.B * 255.0)
ci := 36*r + 6*g + b /* 0..215 */
// Calculate the represented colors back from the index
i2cv := [6]int{0, 0x5f, 0x87, 0xaf, 0xd7, 0xff}
cr := i2cv[r] // r/g/b, 0..255 each
cg := i2cv[g]
cb := i2cv[b]
// Calculate the nearest 0-based gray index at 232..255
var grayIdx int
average := (r + g + b) / 3
if average > 238 {
grayIdx = 23
} else {
grayIdx = (average - 3) / 10 // 0..23
}
gv := 8 + 10*grayIdx // same value for r/g/b, 0..255
// Return the one which is nearer to the original input rgb value
c2 := colorful.Color{R: float64(cr) / 255.0, G: float64(cg) / 255.0, B: float64(cb) / 255.0}
g2 := colorful.Color{R: float64(gv) / 255.0, G: float64(gv) / 255.0, B: float64(gv) / 255.0}
colorDist := c.DistanceLab(c2)
grayDist := c.DistanceLab(g2)
if colorDist <= grayDist {
return 16 + ci, nil
}
return 232 + grayIdx, nil
}