diff vendor/github.com/alecthomas/chroma/v2/style.go @ 66:787b5ee0289d draft

Use vendored modules Signed-off-by: Izuru Yakumo <yakumo.izuru@chaotic.ninja>
author yakumo.izuru
date Sun, 23 Jul 2023 13:18:53 +0000
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/vendor/github.com/alecthomas/chroma/v2/style.go	Sun Jul 23 13:18:53 2023 +0000
@@ -0,0 +1,366 @@
+package chroma
+
+import (
+	"fmt"
+	"strings"
+)
+
+// Trilean value for StyleEntry value inheritance.
+type Trilean uint8
+
+// Trilean states.
+const (
+	Pass Trilean = iota
+	Yes
+	No
+)
+
+func (t Trilean) String() string {
+	switch t {
+	case Yes:
+		return "Yes"
+	case No:
+		return "No"
+	default:
+		return "Pass"
+	}
+}
+
+// Prefix returns s with "no" as a prefix if Trilean is no.
+func (t Trilean) Prefix(s string) string {
+	if t == Yes {
+		return s
+	} else if t == No {
+		return "no" + s
+	}
+	return ""
+}
+
+// A StyleEntry in the Style map.
+type StyleEntry struct {
+	// Hex colours.
+	Colour     Colour
+	Background Colour
+	Border     Colour
+
+	Bold      Trilean
+	Italic    Trilean
+	Underline Trilean
+	NoInherit bool
+}
+
+func (s StyleEntry) String() string {
+	out := []string{}
+	if s.Bold != Pass {
+		out = append(out, s.Bold.Prefix("bold"))
+	}
+	if s.Italic != Pass {
+		out = append(out, s.Italic.Prefix("italic"))
+	}
+	if s.Underline != Pass {
+		out = append(out, s.Underline.Prefix("underline"))
+	}
+	if s.NoInherit {
+		out = append(out, "noinherit")
+	}
+	if s.Colour.IsSet() {
+		out = append(out, s.Colour.String())
+	}
+	if s.Background.IsSet() {
+		out = append(out, "bg:"+s.Background.String())
+	}
+	if s.Border.IsSet() {
+		out = append(out, "border:"+s.Border.String())
+	}
+	return strings.Join(out, " ")
+}
+
+// Sub subtracts e from s where elements match.
+func (s StyleEntry) Sub(e StyleEntry) StyleEntry {
+	out := StyleEntry{}
+	if e.Colour != s.Colour {
+		out.Colour = s.Colour
+	}
+	if e.Background != s.Background {
+		out.Background = s.Background
+	}
+	if e.Bold != s.Bold {
+		out.Bold = s.Bold
+	}
+	if e.Italic != s.Italic {
+		out.Italic = s.Italic
+	}
+	if e.Underline != s.Underline {
+		out.Underline = s.Underline
+	}
+	if e.Border != s.Border {
+		out.Border = s.Border
+	}
+	return out
+}
+
+// Inherit styles from ancestors.
+//
+// Ancestors should be provided from oldest to newest.
+func (s StyleEntry) Inherit(ancestors ...StyleEntry) StyleEntry {
+	out := s
+	for i := len(ancestors) - 1; i >= 0; i-- {
+		if out.NoInherit {
+			return out
+		}
+		ancestor := ancestors[i]
+		if !out.Colour.IsSet() {
+			out.Colour = ancestor.Colour
+		}
+		if !out.Background.IsSet() {
+			out.Background = ancestor.Background
+		}
+		if !out.Border.IsSet() {
+			out.Border = ancestor.Border
+		}
+		if out.Bold == Pass {
+			out.Bold = ancestor.Bold
+		}
+		if out.Italic == Pass {
+			out.Italic = ancestor.Italic
+		}
+		if out.Underline == Pass {
+			out.Underline = ancestor.Underline
+		}
+	}
+	return out
+}
+
+func (s StyleEntry) IsZero() bool {
+	return s.Colour == 0 && s.Background == 0 && s.Border == 0 && s.Bold == Pass && s.Italic == Pass &&
+		s.Underline == Pass && !s.NoInherit
+}
+
+// A StyleBuilder is a mutable structure for building styles.
+//
+// Once built, a Style is immutable.
+type StyleBuilder struct {
+	entries map[TokenType]string
+	name    string
+	parent  *Style
+}
+
+func NewStyleBuilder(name string) *StyleBuilder {
+	return &StyleBuilder{name: name, entries: map[TokenType]string{}}
+}
+
+func (s *StyleBuilder) AddAll(entries StyleEntries) *StyleBuilder {
+	for ttype, entry := range entries {
+		s.entries[ttype] = entry
+	}
+	return s
+}
+
+func (s *StyleBuilder) Get(ttype TokenType) StyleEntry {
+	// This is less than ideal, but it's the price for not having to check errors on each Add().
+	entry, _ := ParseStyleEntry(s.entries[ttype])
+	if s.parent != nil {
+		entry = entry.Inherit(s.parent.Get(ttype))
+	}
+	return entry
+}
+
+// Add an entry to the Style map.
+//
+// See http://pygments.org/docs/styles/#style-rules for details.
+func (s *StyleBuilder) Add(ttype TokenType, entry string) *StyleBuilder { // nolint: gocyclo
+	s.entries[ttype] = entry
+	return s
+}
+
+func (s *StyleBuilder) AddEntry(ttype TokenType, entry StyleEntry) *StyleBuilder {
+	s.entries[ttype] = entry.String()
+	return s
+}
+
+// Transform passes each style entry currently defined in the builder to the supplied
+// function and saves the returned value. This can be used to adjust a style's colours;
+// see Colour's ClampBrightness function, for example.
+func (s *StyleBuilder) Transform(transform func(StyleEntry) StyleEntry) *StyleBuilder {
+	types := make(map[TokenType]struct{})
+	for tt := range s.entries {
+		types[tt] = struct{}{}
+	}
+	if s.parent != nil {
+		for _, tt := range s.parent.Types() {
+			types[tt] = struct{}{}
+		}
+	}
+	for tt := range types {
+		s.AddEntry(tt, transform(s.Get(tt)))
+	}
+	return s
+}
+
+func (s *StyleBuilder) Build() (*Style, error) {
+	style := &Style{
+		Name:    s.name,
+		entries: map[TokenType]StyleEntry{},
+		parent:  s.parent,
+	}
+	for ttype, descriptor := range s.entries {
+		entry, err := ParseStyleEntry(descriptor)
+		if err != nil {
+			return nil, fmt.Errorf("invalid entry for %s: %s", ttype, err)
+		}
+		style.entries[ttype] = entry
+	}
+	return style, nil
+}
+
+// StyleEntries mapping TokenType to colour definition.
+type StyleEntries map[TokenType]string
+
+// NewStyle creates a new style definition.
+func NewStyle(name string, entries StyleEntries) (*Style, error) {
+	return NewStyleBuilder(name).AddAll(entries).Build()
+}
+
+// MustNewStyle creates a new style or panics.
+func MustNewStyle(name string, entries StyleEntries) *Style {
+	style, err := NewStyle(name, entries)
+	if err != nil {
+		panic(err)
+	}
+	return style
+}
+
+// A Style definition.
+//
+// See http://pygments.org/docs/styles/ for details. Semantics are intended to be identical.
+type Style struct {
+	Name    string
+	entries map[TokenType]StyleEntry
+	parent  *Style
+}
+
+// Types that are styled.
+func (s *Style) Types() []TokenType {
+	dedupe := map[TokenType]bool{}
+	for tt := range s.entries {
+		dedupe[tt] = true
+	}
+	if s.parent != nil {
+		for _, tt := range s.parent.Types() {
+			dedupe[tt] = true
+		}
+	}
+	out := make([]TokenType, 0, len(dedupe))
+	for tt := range dedupe {
+		out = append(out, tt)
+	}
+	return out
+}
+
+// Builder creates a mutable builder from this Style.
+//
+// The builder can then be safely modified. This is a cheap operation.
+func (s *Style) Builder() *StyleBuilder {
+	return &StyleBuilder{
+		name:    s.Name,
+		entries: map[TokenType]string{},
+		parent:  s,
+	}
+}
+
+// Has checks if an exact style entry match exists for a token type.
+//
+// This is distinct from Get() which will merge parent tokens.
+func (s *Style) Has(ttype TokenType) bool {
+	return !s.get(ttype).IsZero() || s.synthesisable(ttype)
+}
+
+// Get a style entry. Will try sub-category or category if an exact match is not found, and
+// finally return the Background.
+func (s *Style) Get(ttype TokenType) StyleEntry {
+	return s.get(ttype).Inherit(
+		s.get(Background),
+		s.get(Text),
+		s.get(ttype.Category()),
+		s.get(ttype.SubCategory()))
+}
+
+func (s *Style) get(ttype TokenType) StyleEntry {
+	out := s.entries[ttype]
+	if out.IsZero() && s.parent != nil {
+		return s.parent.get(ttype)
+	}
+	if out.IsZero() && s.synthesisable(ttype) {
+		out = s.synthesise(ttype)
+	}
+	return out
+}
+
+func (s *Style) synthesise(ttype TokenType) StyleEntry {
+	bg := s.get(Background)
+	text := StyleEntry{Colour: bg.Colour}
+	text.Colour = text.Colour.BrightenOrDarken(0.5)
+
+	switch ttype {
+	// If we don't have a line highlight colour, make one that is 10% brighter/darker than the background.
+	case LineHighlight:
+		return StyleEntry{Background: bg.Background.BrightenOrDarken(0.1)}
+
+	// If we don't have line numbers, use the text colour but 20% brighter/darker
+	case LineNumbers, LineNumbersTable:
+		return text
+
+	default:
+		return StyleEntry{}
+	}
+}
+
+func (s *Style) synthesisable(ttype TokenType) bool {
+	return ttype == LineHighlight || ttype == LineNumbers || ttype == LineNumbersTable
+}
+
+// ParseStyleEntry parses a Pygments style entry.
+func ParseStyleEntry(entry string) (StyleEntry, error) { // nolint: gocyclo
+	out := StyleEntry{}
+	parts := strings.Fields(entry)
+	for _, part := range parts {
+		switch {
+		case part == "italic":
+			out.Italic = Yes
+		case part == "noitalic":
+			out.Italic = No
+		case part == "bold":
+			out.Bold = Yes
+		case part == "nobold":
+			out.Bold = No
+		case part == "underline":
+			out.Underline = Yes
+		case part == "nounderline":
+			out.Underline = No
+		case part == "inherit":
+			out.NoInherit = false
+		case part == "noinherit":
+			out.NoInherit = true
+		case part == "bg:":
+			out.Background = 0
+		case strings.HasPrefix(part, "bg:#"):
+			out.Background = ParseColour(part[3:])
+			if !out.Background.IsSet() {
+				return StyleEntry{}, fmt.Errorf("invalid background colour %q", part)
+			}
+		case strings.HasPrefix(part, "border:#"):
+			out.Border = ParseColour(part[7:])
+			if !out.Border.IsSet() {
+				return StyleEntry{}, fmt.Errorf("invalid border colour %q", part)
+			}
+		case strings.HasPrefix(part, "#"):
+			out.Colour = ParseColour(part)
+			if !out.Colour.IsSet() {
+				return StyleEntry{}, fmt.Errorf("invalid colour %q", part)
+			}
+		default:
+			return StyleEntry{}, fmt.Errorf("unknown style element %q", part)
+		}
+	}
+	return out, nil
+}