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:
parent
dba8ec47d2
commit
5749aebe42
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
266
formatters/tty_indexed.go
Normal 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]})
|
38
formatters/tty_truecolour.go
Normal file
38
formatters/tty_truecolour.go
Normal 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
|
||||
}
|
12
lexer.go
12
lexer.go
@ -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
|
||||
}
|
||||
|
@ -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) {
|
@ -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{
|
||||
|
@ -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)},
|
||||
|
@ -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]})
|
||||
}
|
||||
|
@ -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},
|
||||
|
18
mutators.go
18
mutators.go
@ -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
173
styles/api.go
Normal 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
32
styles/api_test.go
Normal 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
35
styles/borland.go
Normal 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
85
styles/colour.go
Normal 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
18
styles/colour_test.go
Normal 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
20
styles/swapoff.go
Normal 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
40
styles/trac.go
Normal 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",
|
||||
}))
|
Loading…
x
Reference in New Issue
Block a user