1
0
mirror of https://github.com/alecthomas/chroma.git synced 2025-03-25 21:39:02 +02:00

Generalise and support 8, 256 and 16m colour terminals.

This commit is contained in:
Alec Thomas 2017-06-06 15:59:48 +10:00
parent dba8ec47d2
commit 5749aebe42
20 changed files with 845 additions and 98 deletions

View File

@ -13,17 +13,26 @@ import (
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/formatters"
"github.com/alecthomas/chroma/lexers"
"github.com/alecthomas/chroma/styles"
)
var (
profileFlag = kingpin.Flag("profile", "Enable profiling to file.").PlaceHolder("FILE").String()
tokensFlag = kingpin.Flag("tokens", "Dump raw tokens.").Bool()
lexerFlag = kingpin.Flag("lexer", "Lexer to use when formatting (default is to autodetect).").Short('l').String()
filesArgs = kingpin.Arg("files", "Files to highlight.").ExistingFiles()
listFlag = kingpin.Flag("list", "List lexers, styles and formatters.").Bool()
tokensFlag = kingpin.Flag("tokens", "Dump raw tokens.").Bool()
lexerFlag = kingpin.Flag("lexer", "Lexer to use when formatting.").Default("autodetect").Short('l').String()
styleFlag = kingpin.Flag("style", "Style to use for formatting.").Short('s').Default("swapoff").String()
formatterFlag = kingpin.Flag("formatter", "Formatter to use.").Default("terminal").Short('f').String()
filesArgs = kingpin.Arg("files", "Files to highlight.").ExistingFiles()
)
func main() {
kingpin.Parse()
if *listFlag {
listAll()
return
}
if *profileFlag != "" {
f, err := os.Create(*profileFlag)
kingpin.FatalIfError(err, "")
@ -34,32 +43,61 @@ func main() {
defer w.Flush()
writer := getWriter(w)
if len(*filesArgs) == 0 {
lexer := lexers.Registry.Get(*lexerFlag)
contents, err := ioutil.ReadAll(os.Stdin)
kingpin.FatalIfError(err, "")
err = lexer.Tokenise(nil, string(contents), writer)
kingpin.FatalIfError(err, "")
lex("", os.Stdin, writer)
} else {
for _, filename := range *filesArgs {
lexers := lexers.Registry.Match(filename)
lexer := lexers[0]
lexer = chroma.Coalesce(lexer)
contents, err := ioutil.ReadFile(filename)
kingpin.FatalIfError(err, "")
err = lexer.Tokenise(nil, string(contents), writer)
r, err := os.Open(filename)
kingpin.FatalIfError(err, "")
lex(filename, r, writer)
r.Close()
}
}
}
func listAll() {
fmt.Printf("lexers:")
for _, l := range lexers.Registry.Lexers {
fmt.Printf(" %s", l.Config().Name)
}
fmt.Println()
fmt.Printf("styles:")
for name := range styles.Registry {
fmt.Printf(" %s", name)
}
fmt.Println()
fmt.Printf("formatters:")
for name := range formatters.Registry {
fmt.Printf(" %s", name)
}
fmt.Println()
}
func lex(path string, r io.Reader, writer func(*chroma.Token)) {
contents, err := ioutil.ReadAll(r)
kingpin.FatalIfError(err, "")
kingpin.FatalIfError(err, "")
lexer := chroma.Coalesce(selexer(path))
err = lexer.Tokenise(nil, string(contents), writer)
kingpin.FatalIfError(err, "")
}
func selexer(path string) chroma.Lexer {
if *lexerFlag != "autodetect" {
return lexers.Get(*lexerFlag)
}
return lexers.Match(path)[0]
}
func getWriter(w io.Writer) func(*chroma.Token) {
if *tokensFlag {
return func(token *chroma.Token) {
fmt.Printf("%#v\n", token)
}
}
formatter := formatters.Console(formatters.DefaultConsoleTheme)
writer, err := formatter.Format(w)
style := styles.Get(*styleFlag)
formatter := formatters.Get(*formatterFlag)
// formatter := formatters.TTY8
writer, err := formatter.Format(w, style)
kingpin.FatalIfError(err, "")
return writer
}

View File

@ -16,7 +16,7 @@ func (d *coalescer) Tokenise(options *TokeniseOptions, text string, out func(*To
if last == nil {
last = token
} else {
if last.Type == token.Type {
if last.Type == token.Type && len(last.Value) < 8192 {
last.Value += token.Value
} else {
out(last)

View File

@ -4,9 +4,43 @@ import (
"io"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/styles"
)
// Formatter returns a formatting function for tokens.
// A Formatter for Chroma lexers.
type Formatter interface {
Format(w io.Writer) (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
}))
// Fallback formatter.
var Fallback = noop
// Registry of Formatters.
var Registry = map[string]Formatter{}
// Get formatter by name.
//
// If the given formatter is not found, the Fallback formatter will be returned.
func Get(name string) Formatter {
if f, ok := Registry[name]; ok {
return f
}
return Fallback
}
// Register a named formatter.
func Register(name string, formatter Formatter) Formatter {
Registry[name] = formatter
return formatter
}

View File

@ -1,57 +0,0 @@
package formatters
import (
"fmt"
"io"
. "github.com/alecthomas/chroma" // nolint
)
var DefaultConsoleTheme = map[TokenType]string{
Number: "\033[1m\033[33m",
Comment: "\033[36m",
CommentPreproc: "\033[1m\033[32m",
String: "\033[1m\033[36m",
Keyword: "\033[1m\033[37m",
GenericHeading: "\033[1m",
GenericSubheading: "\033[1m",
GenericStrong: "\033[1m",
GenericUnderline: "\033[4m",
GenericDeleted: "\033[9m",
}
// Console formatter.
//
// formatter := Console(nil)
func Console(theme map[TokenType]string) Formatter {
if theme == nil {
theme = DefaultConsoleTheme
}
return &consoleFormatter{theme}
}
type consoleFormatter struct {
theme map[TokenType]string
}
func (c *consoleFormatter) Format(w io.Writer) (func(*Token), error) {
return func(token *Token) {
clr, ok := c.theme[token.Type]
if !ok {
clr, ok = c.theme[token.Type.SubCategory()]
if !ok {
clr, ok = c.theme[token.Type.Category()]
if !ok {
clr = ""
}
}
}
if clr != "" {
fmt.Fprint(w, clr)
}
fmt.Fprint(w, token.Value)
if clr != "" {
fmt.Fprintf(w, "\033[0m")
}
}, nil
}

266
formatters/tty_indexed.go Normal file
View File

@ -0,0 +1,266 @@
package formatters
import (
"fmt"
"io"
"math"
"github.com/lucasb-eyer/go-colorful"
"github.com/alecthomas/chroma" // nolint
"github.com/alecthomas/chroma/styles"
)
type ttyTable struct {
foreground map[styles.Colour]string
background map[styles.Colour]string
distance map[styles.Colour]colorful.Color
}
var c = styles.ParseColour
var ttyTables = map[int]*ttyTable{
8: &ttyTable{
foreground: map[styles.Colour]string{
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("#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",
},
background: map[styles.Colour]string{
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("#555555"): "\033[100m", c("#ff0000"): "\033[101m", c("#00ff00"): "\033[102m", c("#ffff00"): "\033[103m",
c("#0000ff"): "\033[104m", c("#ff00ff"): "\033[105m", c("#00ffff"): "\033[106m", c("#ffffff"): "\033[107m",
},
},
256: &ttyTable{
foreground: map[styles.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("#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("#0000ff"): "\033[38;5;12m", c("#ff00ff"): "\033[38;5;13m", c("#00ffff"): "\033[38;5;14m", c("#ffffff"): "\033[38;5;15m",
c("#000000"): "\033[38;5;16m", c("#00005f"): "\033[38;5;17m", c("#000087"): "\033[38;5;18m", c("#0000af"): "\033[38;5;19m",
c("#0000d7"): "\033[38;5;20m", c("#0000ff"): "\033[38;5;21m", c("#005f00"): "\033[38;5;22m", c("#005f5f"): "\033[38;5;23m",
c("#005f87"): "\033[38;5;24m", c("#005faf"): "\033[38;5;25m", c("#005fd7"): "\033[38;5;26m", c("#005fff"): "\033[38;5;27m",
c("#008700"): "\033[38;5;28m", c("#00875f"): "\033[38;5;29m", c("#008787"): "\033[38;5;30m", c("#0087af"): "\033[38;5;31m",
c("#0087d7"): "\033[38;5;32m", c("#0087ff"): "\033[38;5;33m", c("#00af00"): "\033[38;5;34m", c("#00af5f"): "\033[38;5;35m",
c("#00af87"): "\033[38;5;36m", c("#00afaf"): "\033[38;5;37m", c("#00afd7"): "\033[38;5;38m", c("#00afff"): "\033[38;5;39m",
c("#00d700"): "\033[38;5;40m", c("#00d75f"): "\033[38;5;41m", c("#00d787"): "\033[38;5;42m", c("#00d7af"): "\033[38;5;43m",
c("#00d7d7"): "\033[38;5;44m", c("#00d7ff"): "\033[38;5;45m", c("#00ff00"): "\033[38;5;46m", c("#00ff5f"): "\033[38;5;47m",
c("#00ff87"): "\033[38;5;48m", c("#00ffaf"): "\033[38;5;49m", c("#00ffd7"): "\033[38;5;50m", c("#00ffff"): "\033[38;5;51m",
c("#5f0000"): "\033[38;5;52m", c("#5f005f"): "\033[38;5;53m", c("#5f0087"): "\033[38;5;54m", c("#5f00af"): "\033[38;5;55m",
c("#5f00d7"): "\033[38;5;56m", c("#5f00ff"): "\033[38;5;57m", c("#5f5f00"): "\033[38;5;58m", c("#5f5f5f"): "\033[38;5;59m",
c("#5f5f87"): "\033[38;5;60m", c("#5f5faf"): "\033[38;5;61m", c("#5f5fd7"): "\033[38;5;62m", c("#5f5fff"): "\033[38;5;63m",
c("#5f8700"): "\033[38;5;64m", c("#5f875f"): "\033[38;5;65m", c("#5f8787"): "\033[38;5;66m", c("#5f87af"): "\033[38;5;67m",
c("#5f87d7"): "\033[38;5;68m", c("#5f87ff"): "\033[38;5;69m", c("#5faf00"): "\033[38;5;70m", c("#5faf5f"): "\033[38;5;71m",
c("#5faf87"): "\033[38;5;72m", c("#5fafaf"): "\033[38;5;73m", c("#5fafd7"): "\033[38;5;74m", c("#5fafff"): "\033[38;5;75m",
c("#5fd700"): "\033[38;5;76m", c("#5fd75f"): "\033[38;5;77m", c("#5fd787"): "\033[38;5;78m", c("#5fd7af"): "\033[38;5;79m",
c("#5fd7d7"): "\033[38;5;80m", c("#5fd7ff"): "\033[38;5;81m", c("#5fff00"): "\033[38;5;82m", c("#5fff5f"): "\033[38;5;83m",
c("#5fff87"): "\033[38;5;84m", c("#5fffaf"): "\033[38;5;85m", c("#5fffd7"): "\033[38;5;86m", c("#5fffff"): "\033[38;5;87m",
c("#870000"): "\033[38;5;88m", c("#87005f"): "\033[38;5;89m", c("#870087"): "\033[38;5;90m", c("#8700af"): "\033[38;5;91m",
c("#8700d7"): "\033[38;5;92m", c("#8700ff"): "\033[38;5;93m", c("#875f00"): "\033[38;5;94m", c("#875f5f"): "\033[38;5;95m",
c("#875f87"): "\033[38;5;96m", c("#875faf"): "\033[38;5;97m", c("#875fd7"): "\033[38;5;98m", c("#875fff"): "\033[38;5;99m",
c("#878700"): "\033[38;5;100m", c("#87875f"): "\033[38;5;101m", c("#878787"): "\033[38;5;102m", c("#8787af"): "\033[38;5;103m",
c("#8787d7"): "\033[38;5;104m", c("#8787ff"): "\033[38;5;105m", c("#87af00"): "\033[38;5;106m", c("#87af5f"): "\033[38;5;107m",
c("#87af87"): "\033[38;5;108m", c("#87afaf"): "\033[38;5;109m", c("#87afd7"): "\033[38;5;110m", c("#87afff"): "\033[38;5;111m",
c("#87d700"): "\033[38;5;112m", c("#87d75f"): "\033[38;5;113m", c("#87d787"): "\033[38;5;114m", c("#87d7af"): "\033[38;5;115m",
c("#87d7d7"): "\033[38;5;116m", c("#87d7ff"): "\033[38;5;117m", c("#87ff00"): "\033[38;5;118m", c("#87ff5f"): "\033[38;5;119m",
c("#87ff87"): "\033[38;5;120m", c("#87ffaf"): "\033[38;5;121m", c("#87ffd7"): "\033[38;5;122m", c("#87ffff"): "\033[38;5;123m",
c("#af0000"): "\033[38;5;124m", c("#af005f"): "\033[38;5;125m", c("#af0087"): "\033[38;5;126m", c("#af00af"): "\033[38;5;127m",
c("#af00d7"): "\033[38;5;128m", c("#af00ff"): "\033[38;5;129m", c("#af5f00"): "\033[38;5;130m", c("#af5f5f"): "\033[38;5;131m",
c("#af5f87"): "\033[38;5;132m", c("#af5faf"): "\033[38;5;133m", c("#af5fd7"): "\033[38;5;134m", c("#af5fff"): "\033[38;5;135m",
c("#af8700"): "\033[38;5;136m", c("#af875f"): "\033[38;5;137m", c("#af8787"): "\033[38;5;138m", c("#af87af"): "\033[38;5;139m",
c("#af87d7"): "\033[38;5;140m", c("#af87ff"): "\033[38;5;141m", c("#afaf00"): "\033[38;5;142m", c("#afaf5f"): "\033[38;5;143m",
c("#afaf87"): "\033[38;5;144m", c("#afafaf"): "\033[38;5;145m", c("#afafd7"): "\033[38;5;146m", c("#afafff"): "\033[38;5;147m",
c("#afd700"): "\033[38;5;148m", c("#afd75f"): "\033[38;5;149m", c("#afd787"): "\033[38;5;150m", c("#afd7af"): "\033[38;5;151m",
c("#afd7d7"): "\033[38;5;152m", c("#afd7ff"): "\033[38;5;153m", c("#afff00"): "\033[38;5;154m", c("#afff5f"): "\033[38;5;155m",
c("#afff87"): "\033[38;5;156m", c("#afffaf"): "\033[38;5;157m", c("#afffd7"): "\033[38;5;158m", c("#afffff"): "\033[38;5;159m",
c("#d70000"): "\033[38;5;160m", c("#d7005f"): "\033[38;5;161m", c("#d70087"): "\033[38;5;162m", c("#d700af"): "\033[38;5;163m",
c("#d700d7"): "\033[38;5;164m", c("#d700ff"): "\033[38;5;165m", c("#d75f00"): "\033[38;5;166m", c("#d75f5f"): "\033[38;5;167m",
c("#d75f87"): "\033[38;5;168m", c("#d75faf"): "\033[38;5;169m", c("#d75fd7"): "\033[38;5;170m", c("#d75fff"): "\033[38;5;171m",
c("#d78700"): "\033[38;5;172m", c("#d7875f"): "\033[38;5;173m", c("#d78787"): "\033[38;5;174m", c("#d787af"): "\033[38;5;175m",
c("#d787d7"): "\033[38;5;176m", c("#d787ff"): "\033[38;5;177m", c("#d7af00"): "\033[38;5;178m", c("#d7af5f"): "\033[38;5;179m",
c("#d7af87"): "\033[38;5;180m", c("#d7afaf"): "\033[38;5;181m", c("#d7afd7"): "\033[38;5;182m", c("#d7afff"): "\033[38;5;183m",
c("#d7d700"): "\033[38;5;184m", c("#d7d75f"): "\033[38;5;185m", c("#d7d787"): "\033[38;5;186m", c("#d7d7af"): "\033[38;5;187m",
c("#d7d7d7"): "\033[38;5;188m", c("#d7d7ff"): "\033[38;5;189m", c("#d7ff00"): "\033[38;5;190m", c("#d7ff5f"): "\033[38;5;191m",
c("#d7ff87"): "\033[38;5;192m", c("#d7ffaf"): "\033[38;5;193m", c("#d7ffd7"): "\033[38;5;194m", c("#d7ffff"): "\033[38;5;195m",
c("#ff0000"): "\033[38;5;196m", c("#ff005f"): "\033[38;5;197m", c("#ff0087"): "\033[38;5;198m", c("#ff00af"): "\033[38;5;199m",
c("#ff00d7"): "\033[38;5;200m", c("#ff00ff"): "\033[38;5;201m", c("#ff5f00"): "\033[38;5;202m", c("#ff5f5f"): "\033[38;5;203m",
c("#ff5f87"): "\033[38;5;204m", c("#ff5faf"): "\033[38;5;205m", c("#ff5fd7"): "\033[38;5;206m", c("#ff5fff"): "\033[38;5;207m",
c("#ff8700"): "\033[38;5;208m", c("#ff875f"): "\033[38;5;209m", c("#ff8787"): "\033[38;5;210m", c("#ff87af"): "\033[38;5;211m",
c("#ff87d7"): "\033[38;5;212m", c("#ff87ff"): "\033[38;5;213m", c("#ffaf00"): "\033[38;5;214m", c("#ffaf5f"): "\033[38;5;215m",
c("#ffaf87"): "\033[38;5;216m", c("#ffafaf"): "\033[38;5;217m", c("#ffafd7"): "\033[38;5;218m", c("#ffafff"): "\033[38;5;219m",
c("#ffd700"): "\033[38;5;220m", c("#ffd75f"): "\033[38;5;221m", c("#ffd787"): "\033[38;5;222m", c("#ffd7af"): "\033[38;5;223m",
c("#ffd7d7"): "\033[38;5;224m", c("#ffd7ff"): "\033[38;5;225m", c("#ffff00"): "\033[38;5;226m", c("#ffff5f"): "\033[38;5;227m",
c("#ffff87"): "\033[38;5;228m", c("#ffffaf"): "\033[38;5;229m", c("#ffffd7"): "\033[38;5;230m", c("#ffffff"): "\033[38;5;231m",
c("#080808"): "\033[38;5;232m", c("#121212"): "\033[38;5;233m", c("#1c1c1c"): "\033[38;5;234m", c("#262626"): "\033[38;5;235m",
c("#303030"): "\033[38;5;236m", c("#3a3a3a"): "\033[38;5;237m", c("#444444"): "\033[38;5;238m", c("#4e4e4e"): "\033[38;5;239m",
c("#585858"): "\033[38;5;240m", c("#626262"): "\033[38;5;241m", c("#6c6c6c"): "\033[38;5;242m", c("#767676"): "\033[38;5;243m",
c("#808080"): "\033[38;5;244m", c("#8a8a8a"): "\033[38;5;245m", c("#949494"): "\033[38;5;246m", c("#9e9e9e"): "\033[38;5;247m",
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",
},
background: map[styles.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("#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("#0000ff"): "\033[48;5;12m", c("#ff00ff"): "\033[48;5;13m", c("#00ffff"): "\033[48;5;14m", c("#ffffff"): "\033[48;5;15m",
c("#000000"): "\033[48;5;16m", c("#00005f"): "\033[48;5;17m", c("#000087"): "\033[48;5;18m", c("#0000af"): "\033[48;5;19m",
c("#0000d7"): "\033[48;5;20m", c("#0000ff"): "\033[48;5;21m", c("#005f00"): "\033[48;5;22m", c("#005f5f"): "\033[48;5;23m",
c("#005f87"): "\033[48;5;24m", c("#005faf"): "\033[48;5;25m", c("#005fd7"): "\033[48;5;26m", c("#005fff"): "\033[48;5;27m",
c("#008700"): "\033[48;5;28m", c("#00875f"): "\033[48;5;29m", c("#008787"): "\033[48;5;30m", c("#0087af"): "\033[48;5;31m",
c("#0087d7"): "\033[48;5;32m", c("#0087ff"): "\033[48;5;33m", c("#00af00"): "\033[48;5;34m", c("#00af5f"): "\033[48;5;35m",
c("#00af87"): "\033[48;5;36m", c("#00afaf"): "\033[48;5;37m", c("#00afd7"): "\033[48;5;38m", c("#00afff"): "\033[48;5;39m",
c("#00d700"): "\033[48;5;40m", c("#00d75f"): "\033[48;5;41m", c("#00d787"): "\033[48;5;42m", c("#00d7af"): "\033[48;5;43m",
c("#00d7d7"): "\033[48;5;44m", c("#00d7ff"): "\033[48;5;45m", c("#00ff00"): "\033[48;5;46m", c("#00ff5f"): "\033[48;5;47m",
c("#00ff87"): "\033[48;5;48m", c("#00ffaf"): "\033[48;5;49m", c("#00ffd7"): "\033[48;5;50m", c("#00ffff"): "\033[48;5;51m",
c("#5f0000"): "\033[48;5;52m", c("#5f005f"): "\033[48;5;53m", c("#5f0087"): "\033[48;5;54m", c("#5f00af"): "\033[48;5;55m",
c("#5f00d7"): "\033[48;5;56m", c("#5f00ff"): "\033[48;5;57m", c("#5f5f00"): "\033[48;5;58m", c("#5f5f5f"): "\033[48;5;59m",
c("#5f5f87"): "\033[48;5;60m", c("#5f5faf"): "\033[48;5;61m", c("#5f5fd7"): "\033[48;5;62m", c("#5f5fff"): "\033[48;5;63m",
c("#5f8700"): "\033[48;5;64m", c("#5f875f"): "\033[48;5;65m", c("#5f8787"): "\033[48;5;66m", c("#5f87af"): "\033[48;5;67m",
c("#5f87d7"): "\033[48;5;68m", c("#5f87ff"): "\033[48;5;69m", c("#5faf00"): "\033[48;5;70m", c("#5faf5f"): "\033[48;5;71m",
c("#5faf87"): "\033[48;5;72m", c("#5fafaf"): "\033[48;5;73m", c("#5fafd7"): "\033[48;5;74m", c("#5fafff"): "\033[48;5;75m",
c("#5fd700"): "\033[48;5;76m", c("#5fd75f"): "\033[48;5;77m", c("#5fd787"): "\033[48;5;78m", c("#5fd7af"): "\033[48;5;79m",
c("#5fd7d7"): "\033[48;5;80m", c("#5fd7ff"): "\033[48;5;81m", c("#5fff00"): "\033[48;5;82m", c("#5fff5f"): "\033[48;5;83m",
c("#5fff87"): "\033[48;5;84m", c("#5fffaf"): "\033[48;5;85m", c("#5fffd7"): "\033[48;5;86m", c("#5fffff"): "\033[48;5;87m",
c("#870000"): "\033[48;5;88m", c("#87005f"): "\033[48;5;89m", c("#870087"): "\033[48;5;90m", c("#8700af"): "\033[48;5;91m",
c("#8700d7"): "\033[48;5;92m", c("#8700ff"): "\033[48;5;93m", c("#875f00"): "\033[48;5;94m", c("#875f5f"): "\033[48;5;95m",
c("#875f87"): "\033[48;5;96m", c("#875faf"): "\033[48;5;97m", c("#875fd7"): "\033[48;5;98m", c("#875fff"): "\033[48;5;99m",
c("#878700"): "\033[48;5;100m", c("#87875f"): "\033[48;5;101m", c("#878787"): "\033[48;5;102m", c("#8787af"): "\033[48;5;103m",
c("#8787d7"): "\033[48;5;104m", c("#8787ff"): "\033[48;5;105m", c("#87af00"): "\033[48;5;106m", c("#87af5f"): "\033[48;5;107m",
c("#87af87"): "\033[48;5;108m", c("#87afaf"): "\033[48;5;109m", c("#87afd7"): "\033[48;5;110m", c("#87afff"): "\033[48;5;111m",
c("#87d700"): "\033[48;5;112m", c("#87d75f"): "\033[48;5;113m", c("#87d787"): "\033[48;5;114m", c("#87d7af"): "\033[48;5;115m",
c("#87d7d7"): "\033[48;5;116m", c("#87d7ff"): "\033[48;5;117m", c("#87ff00"): "\033[48;5;118m", c("#87ff5f"): "\033[48;5;119m",
c("#87ff87"): "\033[48;5;120m", c("#87ffaf"): "\033[48;5;121m", c("#87ffd7"): "\033[48;5;122m", c("#87ffff"): "\033[48;5;123m",
c("#af0000"): "\033[48;5;124m", c("#af005f"): "\033[48;5;125m", c("#af0087"): "\033[48;5;126m", c("#af00af"): "\033[48;5;127m",
c("#af00d7"): "\033[48;5;128m", c("#af00ff"): "\033[48;5;129m", c("#af5f00"): "\033[48;5;130m", c("#af5f5f"): "\033[48;5;131m",
c("#af5f87"): "\033[48;5;132m", c("#af5faf"): "\033[48;5;133m", c("#af5fd7"): "\033[48;5;134m", c("#af5fff"): "\033[48;5;135m",
c("#af8700"): "\033[48;5;136m", c("#af875f"): "\033[48;5;137m", c("#af8787"): "\033[48;5;138m", c("#af87af"): "\033[48;5;139m",
c("#af87d7"): "\033[48;5;140m", c("#af87ff"): "\033[48;5;141m", c("#afaf00"): "\033[48;5;142m", c("#afaf5f"): "\033[48;5;143m",
c("#afaf87"): "\033[48;5;144m", c("#afafaf"): "\033[48;5;145m", c("#afafd7"): "\033[48;5;146m", c("#afafff"): "\033[48;5;147m",
c("#afd700"): "\033[48;5;148m", c("#afd75f"): "\033[48;5;149m", c("#afd787"): "\033[48;5;150m", c("#afd7af"): "\033[48;5;151m",
c("#afd7d7"): "\033[48;5;152m", c("#afd7ff"): "\033[48;5;153m", c("#afff00"): "\033[48;5;154m", c("#afff5f"): "\033[48;5;155m",
c("#afff87"): "\033[48;5;156m", c("#afffaf"): "\033[48;5;157m", c("#afffd7"): "\033[48;5;158m", c("#afffff"): "\033[48;5;159m",
c("#d70000"): "\033[48;5;160m", c("#d7005f"): "\033[48;5;161m", c("#d70087"): "\033[48;5;162m", c("#d700af"): "\033[48;5;163m",
c("#d700d7"): "\033[48;5;164m", c("#d700ff"): "\033[48;5;165m", c("#d75f00"): "\033[48;5;166m", c("#d75f5f"): "\033[48;5;167m",
c("#d75f87"): "\033[48;5;168m", c("#d75faf"): "\033[48;5;169m", c("#d75fd7"): "\033[48;5;170m", c("#d75fff"): "\033[48;5;171m",
c("#d78700"): "\033[48;5;172m", c("#d7875f"): "\033[48;5;173m", c("#d78787"): "\033[48;5;174m", c("#d787af"): "\033[48;5;175m",
c("#d787d7"): "\033[48;5;176m", c("#d787ff"): "\033[48;5;177m", c("#d7af00"): "\033[48;5;178m", c("#d7af5f"): "\033[48;5;179m",
c("#d7af87"): "\033[48;5;180m", c("#d7afaf"): "\033[48;5;181m", c("#d7afd7"): "\033[48;5;182m", c("#d7afff"): "\033[48;5;183m",
c("#d7d700"): "\033[48;5;184m", c("#d7d75f"): "\033[48;5;185m", c("#d7d787"): "\033[48;5;186m", c("#d7d7af"): "\033[48;5;187m",
c("#d7d7d7"): "\033[48;5;188m", c("#d7d7ff"): "\033[48;5;189m", c("#d7ff00"): "\033[48;5;190m", c("#d7ff5f"): "\033[48;5;191m",
c("#d7ff87"): "\033[48;5;192m", c("#d7ffaf"): "\033[48;5;193m", c("#d7ffd7"): "\033[48;5;194m", c("#d7ffff"): "\033[48;5;195m",
c("#ff0000"): "\033[48;5;196m", c("#ff005f"): "\033[48;5;197m", c("#ff0087"): "\033[48;5;198m", c("#ff00af"): "\033[48;5;199m",
c("#ff00d7"): "\033[48;5;200m", c("#ff00ff"): "\033[48;5;201m", c("#ff5f00"): "\033[48;5;202m", c("#ff5f5f"): "\033[48;5;203m",
c("#ff5f87"): "\033[48;5;204m", c("#ff5faf"): "\033[48;5;205m", c("#ff5fd7"): "\033[48;5;206m", c("#ff5fff"): "\033[48;5;207m",
c("#ff8700"): "\033[48;5;208m", c("#ff875f"): "\033[48;5;209m", c("#ff8787"): "\033[48;5;210m", c("#ff87af"): "\033[48;5;211m",
c("#ff87d7"): "\033[48;5;212m", c("#ff87ff"): "\033[48;5;213m", c("#ffaf00"): "\033[48;5;214m", c("#ffaf5f"): "\033[48;5;215m",
c("#ffaf87"): "\033[48;5;216m", c("#ffafaf"): "\033[48;5;217m", c("#ffafd7"): "\033[48;5;218m", c("#ffafff"): "\033[48;5;219m",
c("#ffd700"): "\033[48;5;220m", c("#ffd75f"): "\033[48;5;221m", c("#ffd787"): "\033[48;5;222m", c("#ffd7af"): "\033[48;5;223m",
c("#ffd7d7"): "\033[48;5;224m", c("#ffd7ff"): "\033[48;5;225m", c("#ffff00"): "\033[48;5;226m", c("#ffff5f"): "\033[48;5;227m",
c("#ffff87"): "\033[48;5;228m", c("#ffffaf"): "\033[48;5;229m", c("#ffffd7"): "\033[48;5;230m", c("#ffffff"): "\033[48;5;231m",
c("#080808"): "\033[48;5;232m", c("#121212"): "\033[48;5;233m", c("#1c1c1c"): "\033[48;5;234m", c("#262626"): "\033[48;5;235m",
c("#303030"): "\033[48;5;236m", c("#3a3a3a"): "\033[48;5;237m", c("#444444"): "\033[48;5;238m", c("#4e4e4e"): "\033[48;5;239m",
c("#585858"): "\033[48;5;240m", c("#626262"): "\033[48;5;241m", c("#6c6c6c"): "\033[48;5;242m", c("#767676"): "\033[48;5;243m",
c("#808080"): "\033[48;5;244m", c("#8a8a8a"): "\033[48;5;245m", c("#949494"): "\033[48;5;246m", c("#9e9e9e"): "\033[48;5;247m",
c("#a8a8a8"): "\033[48;5;248m", c("#b2b2b2"): "\033[48;5;249m", c("#bcbcbc"): "\033[48;5;250m", c("#c6c6c6"): "\033[48;5;251m",
c("#d0d0d0"): "\033[48;5;252m", c("#dadada"): "\033[48;5;253m", c("#e4e4e4"): "\033[48;5;254m", c("#eeeeee"): "\033[48;5;255m",
},
},
}
func computeDistance(table map[styles.Colour]string) map[styles.Colour]colorful.Color {
out := map[styles.Colour]colorful.Color{}
for colour := range table {
out[colour] = toColourful(colour)
}
return out
}
func init() {
for _, table := range ttyTables {
table.distance = computeDistance(table.foreground)
}
}
func toColourful(colour styles.Colour) colorful.Color {
return colorful.Color{
float64(colour.Red()) / 256.0,
float64(colour.Green()) / 256.0,
float64(colour.Blue()) / 256.0,
}
}
func entryToEscapeSequence(table *ttyTable, entry *styles.Entry) string {
out := ""
if entry.Bold {
out += "\033[1m"
}
if entry.Underline {
out += "\033[4m"
}
if entry.Colour.IsSet() {
out += table.foreground[findClosest(table, entry.Colour)]
}
if entry.Background.IsSet() {
out += table.background[findClosest(table, entry.Background)]
}
return out
}
func findClosest(table *ttyTable, colour styles.Colour) styles.Colour {
closestColour := styles.Colour(0)
seeking := toColourful(colour)
closest := float64(math.MaxFloat64)
for styleColour, colour := range table.distance {
distance := colour.DistanceCIE76(seeking)
if distance < closest {
closest = distance
closestColour = styleColour
}
}
return closestColour
}
func styleToEscapeSequence(table *ttyTable, style *styles.Style) map[chroma.TokenType]string {
out := map[chroma.TokenType]string{}
for ttype, entry := range style.Entries {
out[ttype] = entryToEscapeSequence(table, entry)
}
return out
}
type indexedTTYFormatter struct {
table *ttyTable
}
func (c *indexedTTYFormatter) Format(w io.Writer, style *styles.Style) (func(*chroma.Token), error) {
theme := styleToEscapeSequence(c.table, style)
return func(token *chroma.Token) {
// TODO: Cache token lookups?
clr, ok := theme[token.Type]
if !ok {
clr, ok = theme[token.Type.SubCategory()]
if !ok {
clr, ok = theme[token.Type.Category()]
if !ok {
clr = theme[styles.Inherit]
}
}
}
if clr != "" {
fmt.Fprint(w, clr)
}
fmt.Fprint(w, token.Value)
if clr != "" {
fmt.Fprintf(w, "\033[0m")
}
}, nil
}
// TTY8 is an 8-colour terminal formatter.
var TTY8 = Register("terminal", &indexedTTYFormatter{ttyTables[8]})
// TTY256 is a 256-colour terminal formatter.
var TTY256 = Register("terminal256", &indexedTTYFormatter{ttyTables[256]})

View File

@ -0,0 +1,38 @@
package formatters
import (
"fmt"
"io"
"github.com/alecthomas/chroma"
"github.com/alecthomas/chroma/styles"
)
// TTY16m is a true-colour terminal formatter.
var TTY16m = Register("terminal16m", FormatterFunc(trueColourFormatter))
func trueColourFormatter(w io.Writer, style *styles.Style) (func(*chroma.Token), error) {
return func(token *chroma.Token) {
entry := style.Get(token.Type)
if !entry.IsZero() {
out := ""
if entry.Bold {
out += "\033[1m"
}
if entry.Underline {
out += "\033[4m"
}
if entry.Colour.IsSet() {
out += fmt.Sprintf("\033[38:2:%d:%d:%dm", entry.Colour.Red(), entry.Colour.Green(), entry.Colour.Blue())
}
if entry.Background.IsSet() {
out += fmt.Sprintf("\033[48:2:%d:%d:%dm", entry.Background.Red(), entry.Background.Green(), entry.Background.Blue())
}
fmt.Fprint(w, out)
}
fmt.Fprint(w, token.Value)
if !entry.IsZero() {
fmt.Fprint(w, "\033[0m")
}
}, nil
}

View File

@ -201,6 +201,8 @@ type LexerState struct {
Stack []string
State string
Rule int
// Group matches.
Groups []string
}
type regexLexer struct {
@ -233,24 +235,24 @@ func (r *regexLexer) Tokenise(options *TokeniseOptions, text string, out func(*T
}
state.Rule = ruleIndex
groups := make([]string, len(index)/2)
state.Groups = make([]string, len(index)/2)
for i := 0; i < len(index); i += 2 {
start := state.Pos + index[i]
end := state.Pos + index[i+1]
if start == -1 || end == -1 {
continue
}
groups[i/2] = text[start:end]
state.Groups[i/2] = text[start:end]
}
state.Pos += index[1]
if rule.Type != nil {
rule.Type.Emit(groups, r, out)
}
if rule.Mutator != nil {
if err := rule.Mutator.Mutate(state); err != nil {
return err
}
}
if rule.Type != nil {
rule.Type.Emit(state.Groups, r, out)
}
}
return nil
}

View File

@ -1,6 +1,7 @@
package lexers
import (
"path/filepath"
"sort"
"github.com/danwakefield/fnmatch"
@ -14,18 +15,18 @@ func (p prioritisedLexers) Len() int { return len(p) }
func (p prioritisedLexers) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p prioritisedLexers) Less(i, j int) bool { return p[i].Config().Priority < p[j].Config().Priority }
// Registry is the global Lexer registry.
var Registry = registry{byName: map[string]chroma.Lexer{}}
type registry struct {
// Registry of Lexers.
var Registry = struct {
Lexers []chroma.Lexer
byName map[string]chroma.Lexer
}{
byName: map[string]chroma.Lexer{},
}
// Names of all lexers, optionally including aliases.
func (r *registry) Names(withAliases bool) []string {
func Names(withAliases bool) []string {
out := []string{}
for _, lexer := range r.Lexers {
for _, lexer := range Registry.Lexers {
config := lexer.Config()
out = append(out, config.Name)
if withAliases {
@ -36,8 +37,8 @@ func (r *registry) Names(withAliases bool) []string {
}
// Get a Lexer by name.
func (r *registry) Get(name string) chroma.Lexer {
lexer, ok := r.byName[name]
func Get(name string) chroma.Lexer {
lexer, ok := Registry.byName[name]
if ok {
return lexer
}
@ -45,9 +46,10 @@ func (r *registry) Get(name string) chroma.Lexer {
}
// Match returns all lexers matching filename.
func (r *registry) Match(filename string) []chroma.Lexer {
func Match(filename string) []chroma.Lexer {
filename = filepath.Base(filename)
lexers := prioritisedLexers{}
for _, lexer := range r.Lexers {
for _, lexer := range Registry.Lexers {
config := lexer.Config()
for _, glob := range config.Filenames {
if fnmatch.Match(glob, filename, 0) {

View File

@ -7,9 +7,12 @@ import (
// Bash lexer.
var Bash = Register(NewLexer(
&Config{
Name: "Bash",
Aliases: []string{"bash", "sh", "ksh", "zsh", "shell"},
Filenames: []string{"*.sh", "*.ksh", "*.bash", "*.ebuild", "*.eclass", "*.exheres-0", "*.exlib", "*.zsh", ".bashrc", "bashrc", ".bash_*", "bash_*", "zshrc", ".zshrc", "PKGBUILD"},
Name: "Bash",
Aliases: []string{"bash", "sh", "ksh", "zsh", "shell"},
Filenames: []string{
"*.sh", "*.ksh", "*.bash", "*.ebuild", "*.eclass", "*.exheres-0", "*.exlib",
"*.zsh", ".bashrc", "bashrc", ".bash_*", "bash_*", "zshrc", ".zshrc", "PKGBUILD",
},
MimeTypes: []string{"application/x-sh", "application/x-shellscript"},
},
Rules{

View File

@ -41,11 +41,11 @@ var HTML = Register(NewLexer(
},
"script-content": {
{`(<)(\s*)(/)(\s*)(script)(\s*)(>)`, ByGroups(Punctuation, Text, Punctuation, Text, NameTag, Text, Punctuation), Pop(1)},
{`.+?`, Using(JavaScript, nil), nil},
{`(.+?)(<\s*/\s*script\s*>)`, Using(JavaScript, nil), Rewind()},
},
"style-content": {
{`(<)(\s*)(/)(\s*)(style)(\s*)(>)`, ByGroups(Punctuation, Text, Punctuation, Text, NameTag, Text, Punctuation), Pop(1)},
{`.+?`, Using(CSS, nil), nil},
{`(.+?)(<\s*/\s*style\s*>)`, Using(CSS, nil), Rewind()},
},
"attr": {
{`".*?"`, LiteralString, Pop(1)},

View File

@ -44,7 +44,7 @@ func handleCodeblock(groups []string, lexer Lexer, out func(*Token)) {
out(&Token{String, groups[2]})
out(&Token{Text, groups[3]})
code := groups[4]
lexer = Registry.Get(groups[2])
lexer = Get(groups[2])
lexer.Tokenise(nil, code, out)
out(&Token{String, groups[5]})
}

View File

@ -34,7 +34,7 @@ var Php = Register(NewLexer(
{`\?`, Operator, nil},
{`[\[\]{}();,]+`, Punctuation, nil},
{`(class)(\s+)`, ByGroups(Keyword, Text), Push("classname")},
{`(function)(\s*)`, ByGroups(Keyword, Text), nil},
{`(function)(\s*)(\()`, ByGroups(Keyword, Text), Rewind()},
{`(function)(\s+)(&?)(\s*)`, ByGroups(Keyword, Text, Operator, Text), Push("functionname")},
{`(const)(\s+)((?:[\\_a-z]|[^\x00-\x7f])(?:[\\\w]|[^\x00-\x7f])*)`, ByGroups(Keyword, Text, NameConstant), nil},
{`(and|E_PARSE|old_function|E_ERROR|or|as|E_WARNING|parent|eval|PHP_OS|break|exit|case|extends|PHP_VERSION|cfunction|FALSE|print|for|require|continue|foreach|require_once|declare|return|default|static|do|switch|die|stdClass|echo|else|TRUE|elseif|var|empty|if|xor|enddeclare|include|virtual|endfor|include_once|while|endforeach|global|endif|list|endswitch|new|endwhile|not|array|E_ALL|NULL|final|php_user_filter|interface|implements|public|private|protected|abstract|clone|try|catch|throw|this|use|namespace|trait|yield|finally)\b`, Keyword, nil},

View File

@ -28,6 +28,24 @@ func Mutators(modifiers ...Mutator) MutatorFunc {
}
}
// Rewind rewinds the cursor to before the last match group.
//
// This is typically used to replace lookahead assertions.
//
// eg. this {`([a-z]+)(?=\()`, Name, nil} would become {`([a-z]+)(\()`, Name, Rewind()}
func Rewind() MutatorFunc {
return func(state *LexerState) error {
if len(state.Groups) <= 2 {
return fmt.Errorf("Rewind() needs more than one capture group")
}
tail := len(state.Groups[len(state.Groups)-1])
state.Groups[0] = state.Groups[0][:len(state.Groups[0])-tail]
state.Groups = state.Groups[:len(state.Groups)-1]
state.Pos -= tail
return nil
}
}
// Include the given state.
func Include(state string) Rule {
return Rule{

173
styles/api.go Normal file
View File

@ -0,0 +1,173 @@
package styles
import (
"strings"
"github.com/alecthomas/chroma"
)
// Registry of Styles.
var Registry = map[string]*Style{}
// Fallback style. Reassign to change the default fallback style.
var Fallback = SwapOff
// Register a Style.
func Register(style *Style) *Style {
Registry[style.Name] = style
return style
}
// Get named style, or Fallback.
func Get(name string) *Style {
if style, ok := Registry[name]; ok {
return style
}
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
}

32
styles/api_test.go Normal file
View File

@ -0,0 +1,32 @@
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))
}

35
styles/borland.go Normal file
View File

@ -0,0 +1,35 @@
package styles
import (
. "github.com/alecthomas/chroma" // nolint: golint
)
// Borland style.
var Borland = Register(New("borland", Entries{
Whitespace: "#bbbbbb",
Comment: "italic #008800",
CommentPreproc: "noitalic #008080",
CommentSpecial: "noitalic bold",
String: "#0000FF",
StringChar: "#800080",
Number: "#0000FF",
Keyword: "bold #000080",
OperatorWord: "bold",
NameTag: "bold #000080",
NameAttribute: "#FF0000",
GenericHeading: "#999999",
GenericSubheading: "#aaaaaa",
GenericDeleted: "bg:#ffdddd #000000",
GenericInserted: "bg:#ddffdd #000000",
GenericError: "#aa0000",
GenericEmph: "italic",
GenericStrong: "bold",
GenericPrompt: "#555555",
GenericOutput: "#888888",
GenericTraceback: "#aa0000",
Error: "bg:#e3d2d2 #a61717",
}))

85
styles/colour.go Normal file
View File

@ -0,0 +1,85 @@
package styles
import (
"fmt"
"strconv"
"strings"
)
// ANSI2RGB maps ANSI colour names, as supported by Chroma, to hex RGB values.
var ANSI2RGB = map[string]string{
"#ansiblack": "000000",
"#ansidarkred": "7f0000",
"#ansidarkgreen": "007f00",
"#ansibrown": "7f7fe0",
"#ansidarkblue": "00007f",
"#ansipurple": "7f007f",
"#ansiteal": "007f7f",
"#ansilightgray": "e5e5e5",
// Normal
"#ansidarkgray": "555555",
"#ansired": "ff0000",
"#ansigreen": "00ff00",
"#ansiyellow": "ffff00",
"#ansiblue": "0000ff",
"#ansifuchsia": "ff00ff",
"#ansiturquoise": "00ffff",
"#ansiwhite": "ffffff",
}
// Colour represents an RGB colour.
type Colour int32
// ParseColour in the forms #rgb, #rrggbb, or #ansi<colour>. Will panic
// if colour is in an invalid format.
func ParseColour(colour string) Colour {
colour = normaliseColour(colour)
n, err := strconv.ParseUint(colour, 16, 32)
if err != nil {
panic(err)
}
return Colour(n + 1)
}
func (c Colour) IsSet() bool { return c != 0 }
func (c Colour) String() string { return fmt.Sprintf("#%06x", int(c-1)) }
func (c Colour) GoString() string { return fmt.Sprintf("Colour(0x%06x)", int(c-1)) }
// Red component of colour.
func (c Colour) Red() uint8 { return uint8(((c - 1) >> 16) & 0xff) }
// Green component of colour.
func (c Colour) Green() uint8 { return uint8(((c - 1) >> 8) & 0xff) }
// Blue component of colour.
func (c Colour) Blue() uint8 { return uint8((c - 1) & 0xff) }
// Distance squared between two colours.
func (c Colour) Distance(other Colour) int {
rd := int(c.Red() - other.Red())
gd := int(c.Green() - other.Green())
bd := int(c.Blue() - other.Blue())
return rd*rd + gd*gd + bd*bd
}
// Colours is an orderable set of colours.
type Colours []Colour
func (c Colours) Len() int { return len(c) }
func (c Colours) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c Colours) Less(i, j int) bool { return c[i] < c[j] }
// Convert colours to #rrggbb.
func normaliseColour(colour string) string {
if ansi, ok := ANSI2RGB[colour]; ok {
return ansi
}
if strings.HasPrefix(colour, "#") {
colour = colour[1:]
if len(colour) == 3 {
return colour[0:1] + colour[0:1] + colour[1:2] + colour[1:2] + colour[2:3] + colour[2:3]
}
}
return colour
}

18
styles/colour_test.go Normal file
View File

@ -0,0 +1,18 @@
package styles
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestColourRGB(t *testing.T) {
colour := ParseColour("#8913af")
require.Equal(t, uint8(0x89), colour.Red())
require.Equal(t, uint8(0x13), colour.Green())
require.Equal(t, uint8(0xaf), colour.Blue())
}
func TestColourString(t *testing.T) {
require.Equal(t, "#8913af", ParseColour("#8913af").String())
}

20
styles/swapoff.go Normal file
View File

@ -0,0 +1,20 @@
package styles
import (
"github.com/alecthomas/chroma"
)
// SwapOff theme.
var SwapOff = Register(New("swapoff", map[chroma.TokenType]string{
chroma.Number: "bold #ansiyellow",
chroma.Comment: "#ansiteal",
chroma.CommentPreproc: "bold #ansigreen",
chroma.String: "bold #ansiteal",
chroma.Keyword: "bold #ansiwhite",
chroma.GenericHeading: "bold",
chroma.GenericSubheading: "bold",
chroma.GenericStrong: "bold",
chroma.GenericUnderline: "underline",
chroma.NameTag: "bold",
chroma.NameAttribute: "#ansiteal",
}))

40
styles/trac.go Normal file
View File

@ -0,0 +1,40 @@
package styles
import (
"github.com/alecthomas/chroma"
)
// Trac colour scheme.
var Trac = Register(New("trac", map[chroma.TokenType]string{
chroma.Whitespace: "#bbbbbb",
chroma.Comment: "italic #999988",
chroma.CommentPreproc: "bold noitalic #999999",
chroma.CommentSpecial: "bold #999999",
chroma.Operator: "bold",
chroma.String: "#bb8844",
chroma.StringRegex: "#808000",
chroma.Number: "#009999",
chroma.Keyword: "bold",
chroma.KeywordType: "#445588",
chroma.NameBuiltin: "#999999",
chroma.NameFunction: "bold #990000",
chroma.NameClass: "bold #445588",
chroma.NameException: "bold #990000",
chroma.NameNamespace: "#555555",
chroma.NameVariable: "#008080",
chroma.NameConstant: "#008080",
chroma.NameTag: "#000080",
chroma.NameAttribute: "#008080",
chroma.NameEntity: "#800080",
chroma.GenericHeading: "#999999",
chroma.GenericSubheading: "#aaaaaa",
chroma.GenericDeleted: "bg:#ffdddd #000000",
chroma.GenericInserted: "bg:#ddffdd #000000",
chroma.GenericError: "#aa0000",
chroma.GenericEmph: "italic",
chroma.GenericStrong: "bold",
chroma.GenericPrompt: "#555555",
chroma.GenericOutput: "#888888",
chroma.GenericTraceback: "#aa0000",
chroma.Error: "bg:#e3d2d2 #a61717",
}))