mirror of
https://github.com/alecthomas/chroma.git
synced 2025-07-03 00:37:01 +02:00
Move style and formatter API into chroma package.
This commit is contained in:
8
README.md
Normal file
8
README.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Chroma - A general purpose syntax highlighter for Go
|
||||||
|
|
||||||
|
Chroma is inspired by [Pygments](http://pygments.org/).
|
||||||
|
|
||||||
|
|
||||||
|
## Unsupported Pygments features
|
||||||
|
|
||||||
|
- Autodetection from content.
|
@ -1,4 +1,4 @@
|
|||||||
package styles
|
package chroma
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -1,4 +1,4 @@
|
|||||||
package styles
|
package chroma
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
18
formatter.go
Normal file
18
formatter.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package chroma
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A Formatter for Chroma lexers.
|
||||||
|
type Formatter interface {
|
||||||
|
// Format returns a formatting function for tokens.
|
||||||
|
Format(w io.Writer, style *Style) (func(*Token), error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A FormatterFunc is a Formatter implemented as a function.
|
||||||
|
type FormatterFunc func(io.Writer, *Style) (func(*Token), error)
|
||||||
|
|
||||||
|
func (f FormatterFunc) Format(w io.Writer, s *Style) (func(*Token), error) {
|
||||||
|
return f(w, s)
|
||||||
|
}
|
@ -4,35 +4,23 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
"github.com/alecthomas/chroma"
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// A Formatter for Chroma lexers.
|
// NoOp formatter.
|
||||||
type Formatter interface {
|
var NoOp = Register("noop", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style) (func(*chroma.Token), error) {
|
||||||
// Format returns a formatting function for tokens.
|
|
||||||
Format(w io.Writer, style *styles.Style) (func(*chroma.Token), error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type FormatterFunc func(io.Writer, *styles.Style) (func(*chroma.Token), error)
|
|
||||||
|
|
||||||
func (f FormatterFunc) Format(w io.Writer, s *styles.Style) (func(*chroma.Token), error) {
|
|
||||||
return f(w, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
var noop = Register("noop", FormatterFunc(func(w io.Writer, s *styles.Style) (func(*chroma.Token), error) {
|
|
||||||
return func(t *chroma.Token) { io.WriteString(w, t.Value) }, nil
|
return func(t *chroma.Token) { io.WriteString(w, t.Value) }, nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// Fallback formatter.
|
// Fallback formatter.
|
||||||
var Fallback = noop
|
var Fallback = NoOp
|
||||||
|
|
||||||
// Registry of Formatters.
|
// Registry of Formatters.
|
||||||
var Registry = map[string]Formatter{}
|
var Registry = map[string]chroma.Formatter{}
|
||||||
|
|
||||||
// Get formatter by name.
|
// Get formatter by name.
|
||||||
//
|
//
|
||||||
// If the given formatter is not found, the Fallback formatter will be returned.
|
// If the given formatter is not found, the Fallback formatter will be returned.
|
||||||
func Get(name string) Formatter {
|
func Get(name string) chroma.Formatter {
|
||||||
if f, ok := Registry[name]; ok {
|
if f, ok := Registry[name]; ok {
|
||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
@ -40,7 +28,7 @@ func Get(name string) Formatter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Register a named formatter.
|
// Register a named formatter.
|
||||||
func Register(name string, formatter Formatter) Formatter {
|
func Register(name string, formatter chroma.Formatter) chroma.Formatter {
|
||||||
Registry[name] = formatter
|
Registry[name] = formatter
|
||||||
return formatter
|
return formatter
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,10 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
"github.com/alecthomas/chroma"
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tokens formatter outputs the raw token structures.
|
// Tokens formatter outputs the raw token structures.
|
||||||
var Tokens = Register("tokens", FormatterFunc(func(w io.Writer, s *styles.Style) (func(*chroma.Token), error) {
|
var Tokens = Register("tokens", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style) (func(*chroma.Token), error) {
|
||||||
return func(token *chroma.Token) {
|
return func(token *chroma.Token) {
|
||||||
fmt.Fprintln(w, token.GoString())
|
fmt.Fprintln(w, token.GoString())
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -5,26 +5,25 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/alecthomas/chroma" // nolint
|
"github.com/alecthomas/chroma"
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type ttyTable struct {
|
type ttyTable struct {
|
||||||
foreground map[styles.Colour]string
|
foreground map[chroma.Colour]string
|
||||||
background map[styles.Colour]string
|
background map[chroma.Colour]string
|
||||||
}
|
}
|
||||||
|
|
||||||
var c = styles.ParseColour
|
var c = chroma.ParseColour
|
||||||
|
|
||||||
var ttyTables = map[int]*ttyTable{
|
var ttyTables = map[int]*ttyTable{
|
||||||
8: &ttyTable{
|
8: &ttyTable{
|
||||||
foreground: map[styles.Colour]string{
|
foreground: map[chroma.Colour]string{
|
||||||
c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m",
|
c("#000000"): "\033[30m", c("#7f0000"): "\033[31m", c("#007f00"): "\033[32m", c("#7f7fe0"): "\033[33m",
|
||||||
c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m",
|
c("#00007f"): "\033[34m", c("#7f007f"): "\033[35m", c("#007f7f"): "\033[36m", c("#e5e5e5"): "\033[37m",
|
||||||
c("#555555"): "\033[90m", c("#ff0000"): "\033[91m", c("#00ff00"): "\033[92m", c("#ffff00"): "\033[93m",
|
c("#555555"): "\033[90m", c("#ff0000"): "\033[91m", c("#00ff00"): "\033[92m", c("#ffff00"): "\033[93m",
|
||||||
c("#0000ff"): "\033[94m", c("#ff00ff"): "\033[95m", c("#00ffff"): "\033[96m", c("#ffffff"): "\033[97m",
|
c("#0000ff"): "\033[94m", c("#ff00ff"): "\033[95m", c("#00ffff"): "\033[96m", c("#ffffff"): "\033[97m",
|
||||||
},
|
},
|
||||||
background: map[styles.Colour]string{
|
background: map[chroma.Colour]string{
|
||||||
c("#000000"): "\033[40m", c("#7f0000"): "\033[41m", c("#007f00"): "\033[42m", c("#7f7fe0"): "\033[43m",
|
c("#000000"): "\033[40m", c("#7f0000"): "\033[41m", c("#007f00"): "\033[42m", c("#7f7fe0"): "\033[43m",
|
||||||
c("#00007f"): "\033[44m", c("#7f007f"): "\033[45m", c("#007f7f"): "\033[46m", c("#e5e5e5"): "\033[47m",
|
c("#00007f"): "\033[44m", c("#7f007f"): "\033[45m", c("#007f7f"): "\033[46m", c("#e5e5e5"): "\033[47m",
|
||||||
c("#555555"): "\033[100m", c("#ff0000"): "\033[101m", c("#00ff00"): "\033[102m", c("#ffff00"): "\033[103m",
|
c("#555555"): "\033[100m", c("#ff0000"): "\033[101m", c("#00ff00"): "\033[102m", c("#ffff00"): "\033[103m",
|
||||||
@ -32,7 +31,7 @@ var ttyTables = map[int]*ttyTable{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
256: &ttyTable{
|
256: &ttyTable{
|
||||||
foreground: map[styles.Colour]string{
|
foreground: map[chroma.Colour]string{
|
||||||
c("#000000"): "\033[38;5;0m", c("#800000"): "\033[38;5;1m", c("#008000"): "\033[38;5;2m", c("#808000"): "\033[38;5;3m",
|
c("#000000"): "\033[38;5;0m", c("#800000"): "\033[38;5;1m", c("#008000"): "\033[38;5;2m", c("#808000"): "\033[38;5;3m",
|
||||||
c("#000080"): "\033[38;5;4m", c("#800080"): "\033[38;5;5m", c("#008080"): "\033[38;5;6m", c("#c0c0c0"): "\033[38;5;7m",
|
c("#000080"): "\033[38;5;4m", c("#800080"): "\033[38;5;5m", c("#008080"): "\033[38;5;6m", c("#c0c0c0"): "\033[38;5;7m",
|
||||||
c("#808080"): "\033[38;5;8m", c("#ff0000"): "\033[38;5;9m", c("#00ff00"): "\033[38;5;10m", c("#ffff00"): "\033[38;5;11m",
|
c("#808080"): "\033[38;5;8m", c("#ff0000"): "\033[38;5;9m", c("#00ff00"): "\033[38;5;10m", c("#ffff00"): "\033[38;5;11m",
|
||||||
@ -98,7 +97,7 @@ var ttyTables = map[int]*ttyTable{
|
|||||||
c("#a8a8a8"): "\033[38;5;248m", c("#b2b2b2"): "\033[38;5;249m", c("#bcbcbc"): "\033[38;5;250m", c("#c6c6c6"): "\033[38;5;251m",
|
c("#a8a8a8"): "\033[38;5;248m", c("#b2b2b2"): "\033[38;5;249m", c("#bcbcbc"): "\033[38;5;250m", c("#c6c6c6"): "\033[38;5;251m",
|
||||||
c("#d0d0d0"): "\033[38;5;252m", c("#dadada"): "\033[38;5;253m", c("#e4e4e4"): "\033[38;5;254m", c("#eeeeee"): "\033[38;5;255m",
|
c("#d0d0d0"): "\033[38;5;252m", c("#dadada"): "\033[38;5;253m", c("#e4e4e4"): "\033[38;5;254m", c("#eeeeee"): "\033[38;5;255m",
|
||||||
},
|
},
|
||||||
background: map[styles.Colour]string{
|
background: map[chroma.Colour]string{
|
||||||
c("#000000"): "\033[48;5;0m", c("#800000"): "\033[48;5;1m", c("#008000"): "\033[48;5;2m", c("#808000"): "\033[48;5;3m",
|
c("#000000"): "\033[48;5;0m", c("#800000"): "\033[48;5;1m", c("#008000"): "\033[48;5;2m", c("#808000"): "\033[48;5;3m",
|
||||||
c("#000080"): "\033[48;5;4m", c("#800080"): "\033[48;5;5m", c("#008080"): "\033[48;5;6m", c("#c0c0c0"): "\033[48;5;7m",
|
c("#000080"): "\033[48;5;4m", c("#800080"): "\033[48;5;5m", c("#008080"): "\033[48;5;6m", c("#c0c0c0"): "\033[48;5;7m",
|
||||||
c("#808080"): "\033[48;5;8m", c("#ff0000"): "\033[48;5;9m", c("#00ff00"): "\033[48;5;10m", c("#ffff00"): "\033[48;5;11m",
|
c("#808080"): "\033[48;5;8m", c("#ff0000"): "\033[48;5;9m", c("#00ff00"): "\033[48;5;10m", c("#ffff00"): "\033[48;5;11m",
|
||||||
@ -167,7 +166,7 @@ var ttyTables = map[int]*ttyTable{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func entryToEscapeSequence(table *ttyTable, entry *styles.Entry) string {
|
func entryToEscapeSequence(table *ttyTable, entry *chroma.StyleEntry) string {
|
||||||
out := ""
|
out := ""
|
||||||
if entry.Bold {
|
if entry.Bold {
|
||||||
out += "\033[1m"
|
out += "\033[1m"
|
||||||
@ -184,8 +183,8 @@ func entryToEscapeSequence(table *ttyTable, entry *styles.Entry) string {
|
|||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
func findClosest(table *ttyTable, colour styles.Colour) styles.Colour {
|
func findClosest(table *ttyTable, colour chroma.Colour) chroma.Colour {
|
||||||
closestColour := styles.Colour(0)
|
closestColour := chroma.Colour(0)
|
||||||
closest := math.MaxInt32
|
closest := math.MaxInt32
|
||||||
for styleColour := range table.foreground {
|
for styleColour := range table.foreground {
|
||||||
distance := styleColour.Distance(colour)
|
distance := styleColour.Distance(colour)
|
||||||
@ -197,7 +196,7 @@ func findClosest(table *ttyTable, colour styles.Colour) styles.Colour {
|
|||||||
return closestColour
|
return closestColour
|
||||||
}
|
}
|
||||||
|
|
||||||
func styleToEscapeSequence(table *ttyTable, style *styles.Style) map[chroma.TokenType]string {
|
func styleToEscapeSequence(table *ttyTable, style *chroma.Style) map[chroma.TokenType]string {
|
||||||
out := map[chroma.TokenType]string{}
|
out := map[chroma.TokenType]string{}
|
||||||
for ttype, entry := range style.Entries {
|
for ttype, entry := range style.Entries {
|
||||||
out[ttype] = entryToEscapeSequence(table, entry)
|
out[ttype] = entryToEscapeSequence(table, entry)
|
||||||
@ -209,7 +208,7 @@ type indexedTTYFormatter struct {
|
|||||||
table *ttyTable
|
table *ttyTable
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *indexedTTYFormatter) Format(w io.Writer, style *styles.Style) (func(*chroma.Token), error) {
|
func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style) (func(*chroma.Token), error) {
|
||||||
theme := styleToEscapeSequence(c.table, style)
|
theme := styleToEscapeSequence(c.table, style)
|
||||||
return func(token *chroma.Token) {
|
return func(token *chroma.Token) {
|
||||||
// TODO: Cache token lookups?
|
// TODO: Cache token lookups?
|
||||||
@ -219,7 +218,7 @@ func (c *indexedTTYFormatter) Format(w io.Writer, style *styles.Style) (func(*ch
|
|||||||
if !ok {
|
if !ok {
|
||||||
clr, ok = theme[token.Type.Category()]
|
clr, ok = theme[token.Type.Category()]
|
||||||
if !ok {
|
if !ok {
|
||||||
clr = theme[styles.Inherit]
|
clr = theme[chroma.InheritStyle]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,12 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
"github.com/alecthomas/chroma"
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// TTY16m is a true-colour terminal formatter.
|
// TTY16m is a true-colour terminal formatter.
|
||||||
var TTY16m = Register("terminal16m", FormatterFunc(trueColourFormatter))
|
var TTY16m = Register("terminal16m", chroma.FormatterFunc(trueColourFormatter))
|
||||||
|
|
||||||
func trueColourFormatter(w io.Writer, style *styles.Style) (func(*chroma.Token), error) {
|
func trueColourFormatter(w io.Writer, style *chroma.Style) (func(*chroma.Token), error) {
|
||||||
return func(token *chroma.Token) {
|
return func(token *chroma.Token) {
|
||||||
entry := style.Get(token.Type)
|
entry := style.Get(token.Type)
|
||||||
if !entry.IsZero() {
|
if !entry.IsZero() {
|
||||||
|
149
style.go
Normal file
149
style.go
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package chroma
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// InheritStyle from entry with this key.
|
||||||
|
const InheritStyle TokenType = -1
|
||||||
|
|
||||||
|
// A StyleEntry in the Style map.
|
||||||
|
type StyleEntry struct {
|
||||||
|
// Hex colours.
|
||||||
|
Colour Colour
|
||||||
|
Background Colour
|
||||||
|
Border Colour
|
||||||
|
|
||||||
|
Bold bool
|
||||||
|
Italic bool
|
||||||
|
Underline bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StyleEntry) String() string {
|
||||||
|
out := []string{}
|
||||||
|
if e.Bold {
|
||||||
|
out = append(out, "bold")
|
||||||
|
}
|
||||||
|
if e.Italic {
|
||||||
|
out = append(out, "italic")
|
||||||
|
}
|
||||||
|
if e.Underline {
|
||||||
|
out = append(out, "underline")
|
||||||
|
}
|
||||||
|
if e.Colour.IsSet() {
|
||||||
|
out = append(out, e.Colour.String())
|
||||||
|
}
|
||||||
|
if e.Background.IsSet() {
|
||||||
|
out = append(out, "bg:"+e.Background.String())
|
||||||
|
}
|
||||||
|
if e.Border.IsSet() {
|
||||||
|
out = append(out, "border:"+e.Border.String())
|
||||||
|
}
|
||||||
|
return strings.Join(out, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *StyleEntry) IsZero() bool {
|
||||||
|
return e.Colour == 0 && e.Background == 0 && e.Border == 0 && !e.Bold && !e.Italic && !e.Underline
|
||||||
|
}
|
||||||
|
|
||||||
|
// StyleEntries mapping TokenType to colour definition.
|
||||||
|
type StyleEntries map[TokenType]string
|
||||||
|
|
||||||
|
// NewStyle creates a new style definition.
|
||||||
|
func NewStyle(name string, entries StyleEntries) *Style {
|
||||||
|
s := &Style{
|
||||||
|
Name: name,
|
||||||
|
Entries: map[TokenType]*StyleEntry{
|
||||||
|
InheritStyle: &StyleEntry{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for tt, entry := range entries {
|
||||||
|
s.Add(tt, entry)
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a style entry. Will try sub-category or category if an exact match is not found, and
|
||||||
|
// finally return the entry mapped to `InheritStyle`.
|
||||||
|
func (s *Style) Get(ttype TokenType) *StyleEntry {
|
||||||
|
out := s.Entries[ttype]
|
||||||
|
if out == nil {
|
||||||
|
out = s.Entries[ttype.SubCategory()]
|
||||||
|
if out == nil {
|
||||||
|
out = s.Entries[ttype.Category()]
|
||||||
|
if out == nil {
|
||||||
|
out = s.Entries[InheritStyle]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add an StyleEntry to the Style map.
|
||||||
|
//
|
||||||
|
// See http://pygments.org/docs/styles/#style-rules for details.
|
||||||
|
func (s *Style) Add(ttype TokenType, entry string) *Style { // nolint: gocyclo
|
||||||
|
out := &StyleEntry{}
|
||||||
|
dupl := s.Entries[ttype.SubCategory()]
|
||||||
|
if dupl == nil {
|
||||||
|
dupl = s.Entries[ttype.Category()]
|
||||||
|
if dupl == nil {
|
||||||
|
dupl = s.Entries[InheritStyle]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent := &StyleEntry{}
|
||||||
|
// Duplicate ancestor node.
|
||||||
|
*parent = *dupl
|
||||||
|
for _, part := range strings.Fields(entry) {
|
||||||
|
switch {
|
||||||
|
case part == "italic":
|
||||||
|
out.Italic = true
|
||||||
|
case part == "noitalic":
|
||||||
|
out.Italic = false
|
||||||
|
case part == "bold":
|
||||||
|
out.Bold = true
|
||||||
|
case part == "nobold":
|
||||||
|
out.Bold = false
|
||||||
|
case part == "underline":
|
||||||
|
out.Underline = true
|
||||||
|
case part == "nounderline":
|
||||||
|
out.Underline = false
|
||||||
|
case part == "noinherit":
|
||||||
|
parent = &StyleEntry{}
|
||||||
|
case strings.HasPrefix(part, "bg:#"):
|
||||||
|
out.Background = ParseColour(part[3:])
|
||||||
|
case strings.HasPrefix(part, "border:#"):
|
||||||
|
out.Border = ParseColour(part[7:])
|
||||||
|
case strings.HasPrefix(part, "#"):
|
||||||
|
out.Colour = ParseColour(part)
|
||||||
|
default:
|
||||||
|
panic("unsupported style entry " + part)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if parent.Colour != 0 && out.Colour == 0 {
|
||||||
|
out.Colour = parent.Colour
|
||||||
|
}
|
||||||
|
if parent.Background != 0 && out.Background == 0 {
|
||||||
|
out.Background = parent.Background
|
||||||
|
}
|
||||||
|
if parent.Border != 0 && out.Border == 0 {
|
||||||
|
out.Border = parent.Border
|
||||||
|
}
|
||||||
|
if parent.Bold && !out.Bold {
|
||||||
|
out.Bold = true
|
||||||
|
}
|
||||||
|
if parent.Italic && !out.Italic {
|
||||||
|
out.Italic = true
|
||||||
|
}
|
||||||
|
if parent.Underline && !out.Underline {
|
||||||
|
out.Underline = true
|
||||||
|
}
|
||||||
|
s.Entries[ttype] = out
|
||||||
|
return s
|
||||||
|
}
|
30
style_test.go
Normal file
30
style_test.go
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package chroma
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestStyleAddNoInherit(t *testing.T) {
|
||||||
|
s := NewStyle("test", StyleEntries{
|
||||||
|
Name: "bold #f00",
|
||||||
|
NameVariable: "noinherit #fff",
|
||||||
|
})
|
||||||
|
require.Equal(t, &StyleEntry{Colour: 0x1000000}, s.Get(NameVariable))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStyleInherit(t *testing.T) {
|
||||||
|
s := NewStyle("test", StyleEntries{
|
||||||
|
Name: "bold #f00",
|
||||||
|
NameVariable: "#fff",
|
||||||
|
})
|
||||||
|
require.Equal(t, &StyleEntry{Colour: 0x1000000, Bold: true}, s.Get(NameVariable))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestColours(t *testing.T) {
|
||||||
|
s := NewStyle("test", StyleEntries{
|
||||||
|
Name: "#f00 bg:#001 border:#ansiblue",
|
||||||
|
})
|
||||||
|
require.Equal(t, &StyleEntry{Colour: 0xff0001, Background: 0x000012, Border: 0x000100}, s.Get(Name))
|
||||||
|
}
|
160
styles/api.go
160
styles/api.go
@ -1,173 +1,23 @@
|
|||||||
package styles
|
package styles
|
||||||
|
|
||||||
import (
|
import "github.com/alecthomas/chroma"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Registry of Styles.
|
// Registry of Styles.
|
||||||
var Registry = map[string]*Style{}
|
var Registry = map[string]*chroma.Style{}
|
||||||
|
|
||||||
// Fallback style. Reassign to change the default fallback style.
|
// Fallback style. Reassign to change the default fallback style.
|
||||||
var Fallback = SwapOff
|
var Fallback = SwapOff
|
||||||
|
|
||||||
// Register a Style.
|
// Register a chroma.Style.
|
||||||
func Register(style *Style) *Style {
|
func Register(style *chroma.Style) *chroma.Style {
|
||||||
Registry[style.Name] = style
|
Registry[style.Name] = style
|
||||||
return style
|
return style
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get named style, or Fallback.
|
// Get named style, or Fallback.
|
||||||
func Get(name string) *Style {
|
func Get(name string) *chroma.Style {
|
||||||
if style, ok := Registry[name]; ok {
|
if style, ok := Registry[name]; ok {
|
||||||
return style
|
return style
|
||||||
}
|
}
|
||||||
return Fallback
|
return Fallback
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inherit from entry with this key.
|
|
||||||
const Inherit chroma.TokenType = -1
|
|
||||||
|
|
||||||
// An Entry in the Style map.
|
|
||||||
type Entry struct {
|
|
||||||
// Hex colours.
|
|
||||||
Colour Colour
|
|
||||||
Background Colour
|
|
||||||
Border Colour
|
|
||||||
|
|
||||||
Bold bool
|
|
||||||
Italic bool
|
|
||||||
Underline bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *Entry) String() string {
|
|
||||||
out := []string{}
|
|
||||||
if e.Bold {
|
|
||||||
out = append(out, "bold")
|
|
||||||
}
|
|
||||||
if e.Italic {
|
|
||||||
out = append(out, "italic")
|
|
||||||
}
|
|
||||||
if e.Underline {
|
|
||||||
out = append(out, "underline")
|
|
||||||
}
|
|
||||||
if e.Colour.IsSet() {
|
|
||||||
out = append(out, e.Colour.String())
|
|
||||||
}
|
|
||||||
if e.Background.IsSet() {
|
|
||||||
out = append(out, "bg:"+e.Background.String())
|
|
||||||
}
|
|
||||||
if e.Border.IsSet() {
|
|
||||||
out = append(out, "border:"+e.Border.String())
|
|
||||||
}
|
|
||||||
return strings.Join(out, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Entries mapping TokenType to colour definition.
|
|
||||||
type Entries map[chroma.TokenType]string
|
|
||||||
|
|
||||||
func (e *Entry) IsZero() bool {
|
|
||||||
return e.Colour == 0 && e.Background == 0 && e.Border == 0 && !e.Bold && !e.Italic && !e.Underline
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates a new style definition.
|
|
||||||
func New(name string, entries Entries) *Style {
|
|
||||||
s := &Style{
|
|
||||||
Name: name,
|
|
||||||
Entries: map[chroma.TokenType]*Entry{
|
|
||||||
Inherit: &Entry{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for tt, entry := range entries {
|
|
||||||
s.Add(tt, entry)
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// A Style definition.
|
|
||||||
//
|
|
||||||
// See http://pygments.org/docs/styles/ for details. Semantics are intended to be identical.
|
|
||||||
type Style struct {
|
|
||||||
Name string
|
|
||||||
Entries map[chroma.TokenType]*Entry
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a style entry. Will try sub-category or category if an exact match is not found, and
|
|
||||||
// finally return the entry mapped to `Inherit`.
|
|
||||||
func (s *Style) Get(ttype chroma.TokenType) *Entry {
|
|
||||||
out := s.Entries[ttype]
|
|
||||||
if out == nil {
|
|
||||||
out = s.Entries[ttype.SubCategory()]
|
|
||||||
if out == nil {
|
|
||||||
out = s.Entries[ttype.Category()]
|
|
||||||
if out == nil {
|
|
||||||
out = s.Entries[Inherit]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add an Entry to the Style map.
|
|
||||||
//
|
|
||||||
// See http://pygments.org/docs/styles/#style-rules for details.
|
|
||||||
func (s *Style) Add(ttype chroma.TokenType, entry string) *Style { // nolint: gocyclo
|
|
||||||
out := &Entry{}
|
|
||||||
dupl := s.Entries[ttype.SubCategory()]
|
|
||||||
if dupl == nil {
|
|
||||||
dupl = s.Entries[ttype.Category()]
|
|
||||||
if dupl == nil {
|
|
||||||
dupl = s.Entries[Inherit]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parent := &Entry{}
|
|
||||||
// Duplicate ancestor node.
|
|
||||||
*parent = *dupl
|
|
||||||
for _, part := range strings.Fields(entry) {
|
|
||||||
switch {
|
|
||||||
case part == "italic":
|
|
||||||
out.Italic = true
|
|
||||||
case part == "noitalic":
|
|
||||||
out.Italic = false
|
|
||||||
case part == "bold":
|
|
||||||
out.Bold = true
|
|
||||||
case part == "nobold":
|
|
||||||
out.Bold = false
|
|
||||||
case part == "underline":
|
|
||||||
out.Underline = true
|
|
||||||
case part == "nounderline":
|
|
||||||
out.Underline = false
|
|
||||||
case part == "noinherit":
|
|
||||||
parent = &Entry{}
|
|
||||||
case strings.HasPrefix(part, "bg:#"):
|
|
||||||
out.Background = ParseColour(part[3:])
|
|
||||||
case strings.HasPrefix(part, "border:#"):
|
|
||||||
out.Border = ParseColour(part[7:])
|
|
||||||
case strings.HasPrefix(part, "#"):
|
|
||||||
out.Colour = ParseColour(part)
|
|
||||||
default:
|
|
||||||
panic("unsupported style entry " + part)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if parent.Colour != 0 && out.Colour == 0 {
|
|
||||||
out.Colour = parent.Colour
|
|
||||||
}
|
|
||||||
if parent.Background != 0 && out.Background == 0 {
|
|
||||||
out.Background = parent.Background
|
|
||||||
}
|
|
||||||
if parent.Border != 0 && out.Border == 0 {
|
|
||||||
out.Border = parent.Border
|
|
||||||
}
|
|
||||||
if parent.Bold && !out.Bold {
|
|
||||||
out.Bold = true
|
|
||||||
}
|
|
||||||
if parent.Italic && !out.Italic {
|
|
||||||
out.Italic = true
|
|
||||||
}
|
|
||||||
if parent.Underline && !out.Underline {
|
|
||||||
out.Underline = true
|
|
||||||
}
|
|
||||||
s.Entries[ttype] = out
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
package styles
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStyleAddNoInherit(t *testing.T) {
|
|
||||||
s := New("test", Entries{
|
|
||||||
chroma.Name: "bold #f00",
|
|
||||||
chroma.NameVariable: "noinherit #fff",
|
|
||||||
})
|
|
||||||
require.Equal(t, &Entry{Colour: 0x1000000}, s.Get(chroma.NameVariable))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStyleInherit(t *testing.T) {
|
|
||||||
s := New("test", Entries{
|
|
||||||
chroma.Name: "bold #f00",
|
|
||||||
chroma.NameVariable: "#fff",
|
|
||||||
})
|
|
||||||
require.Equal(t, &Entry{Colour: 0x1000000, Bold: true}, s.Get(chroma.NameVariable))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestColours(t *testing.T) {
|
|
||||||
s := New("test", Entries{
|
|
||||||
chroma.Name: "#f00 bg:#001 border:#ansiblue",
|
|
||||||
})
|
|
||||||
require.Equal(t, &Entry{Colour: 0xff0001, Background: 0x000012, Border: 0x000100}, s.Get(chroma.Name))
|
|
||||||
}
|
|
@ -1,35 +1,35 @@
|
|||||||
package styles
|
package styles
|
||||||
|
|
||||||
import (
|
import (
|
||||||
. "github.com/alecthomas/chroma" // nolint: golint
|
"github.com/alecthomas/chroma"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Borland style.
|
// Borland style.
|
||||||
var Borland = Register(New("borland", Entries{
|
var Borland = Register(chroma.NewStyle("borland", chroma.StyleEntries{
|
||||||
Whitespace: "#bbbbbb",
|
chroma.Whitespace: "#bbbbbb",
|
||||||
|
|
||||||
Comment: "italic #008800",
|
chroma.Comment: "italic #008800",
|
||||||
CommentPreproc: "noitalic #008080",
|
chroma.CommentPreproc: "noitalic #008080",
|
||||||
CommentSpecial: "noitalic bold",
|
chroma.CommentSpecial: "noitalic bold",
|
||||||
|
|
||||||
String: "#0000FF",
|
chroma.String: "#0000FF",
|
||||||
StringChar: "#800080",
|
chroma.StringChar: "#800080",
|
||||||
Number: "#0000FF",
|
chroma.Number: "#0000FF",
|
||||||
Keyword: "bold #000080",
|
chroma.Keyword: "bold #000080",
|
||||||
OperatorWord: "bold",
|
chroma.OperatorWord: "bold",
|
||||||
NameTag: "bold #000080",
|
chroma.NameTag: "bold #000080",
|
||||||
NameAttribute: "#FF0000",
|
chroma.NameAttribute: "#FF0000",
|
||||||
|
|
||||||
GenericHeading: "#999999",
|
chroma.GenericHeading: "#999999",
|
||||||
GenericSubheading: "#aaaaaa",
|
chroma.GenericSubheading: "#aaaaaa",
|
||||||
GenericDeleted: "bg:#ffdddd #000000",
|
chroma.GenericDeleted: "bg:#ffdddd #000000",
|
||||||
GenericInserted: "bg:#ddffdd #000000",
|
chroma.GenericInserted: "bg:#ddffdd #000000",
|
||||||
GenericError: "#aa0000",
|
chroma.GenericError: "#aa0000",
|
||||||
GenericEmph: "italic",
|
chroma.GenericEmph: "italic",
|
||||||
GenericStrong: "bold",
|
chroma.GenericStrong: "bold",
|
||||||
GenericPrompt: "#555555",
|
chroma.GenericPrompt: "#555555",
|
||||||
GenericOutput: "#888888",
|
chroma.GenericOutput: "#888888",
|
||||||
GenericTraceback: "#aa0000",
|
chroma.GenericTraceback: "#aa0000",
|
||||||
|
|
||||||
Error: "bg:#e3d2d2 #a61717",
|
chroma.Error: "bg:#e3d2d2 #a61717",
|
||||||
}))
|
}))
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// SwapOff theme.
|
// SwapOff theme.
|
||||||
var SwapOff = Register(New("swapoff", map[chroma.TokenType]string{
|
var SwapOff = Register(chroma.NewStyle("swapoff", map[chroma.TokenType]string{
|
||||||
chroma.Number: "bold #ansiyellow",
|
chroma.Number: "bold #ansiyellow",
|
||||||
chroma.Comment: "#ansiteal",
|
chroma.Comment: "#ansiteal",
|
||||||
chroma.CommentPreproc: "bold #ansigreen",
|
chroma.CommentPreproc: "bold #ansigreen",
|
||||||
|
@ -5,7 +5,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Trac colour scheme.
|
// Trac colour scheme.
|
||||||
var Trac = Register(New("trac", map[chroma.TokenType]string{
|
var Trac = Register(chroma.NewStyle("trac", map[chroma.TokenType]string{
|
||||||
chroma.Whitespace: "#bbbbbb",
|
chroma.Whitespace: "#bbbbbb",
|
||||||
chroma.Comment: "italic #999988",
|
chroma.Comment: "italic #999988",
|
||||||
chroma.CommentPreproc: "bold noitalic #999999",
|
chroma.CommentPreproc: "bold noitalic #999999",
|
||||||
|
Reference in New Issue
Block a user