Use better dep version
This version of dep is better at pruning unused source code from dependencies, so we don't need to carry around such a large vendor directory.
This commit is contained in:
parent
d441569025
commit
5b9795d676
|
@ -1,3 +1,7 @@
|
||||||
|
[prune]
|
||||||
|
unused-packages = true
|
||||||
|
go-tests = true
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
name = "github.com/alecthomas/chroma"
|
name = "github.com/alecthomas/chroma"
|
||||||
revision = "v0.2.1"
|
revision = "v0.2.1"
|
||||||
|
|
|
@ -1,136 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
"text/template"
|
|
||||||
|
|
||||||
"github.com/aymerick/douceur/css"
|
|
||||||
"github.com/aymerick/douceur/parser"
|
|
||||||
"gopkg.in/alecthomas/kingpin.v3-unstable"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
outputTemplate = `package styles
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
// {{.Name}} style.
|
|
||||||
var {{.Name}} = Register(chroma.MustNewStyle("{{.Name|Lower}}", chroma.StyleEntries{
|
|
||||||
{{- range .Rules}}
|
|
||||||
{{- if .Prelude|TokenType}}
|
|
||||||
chroma.{{.Prelude|TokenType}}: "{{.Declarations|TranslateDecls}}",
|
|
||||||
{{- end}}
|
|
||||||
{{- end}}
|
|
||||||
}))
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
typeByClass = map[string]chroma.TokenType{
|
|
||||||
".hll": chroma.Background,
|
|
||||||
}
|
|
||||||
|
|
||||||
cssNamedColours = map[string]string{
|
|
||||||
"black": "#000000", "silver": "#c0c0c0", "gray": "#808080", "white": "#ffffff",
|
|
||||||
"maroon": "#800000", "red": "#ff0000", "purple": "#800080", "fuchsia": "#ff00ff",
|
|
||||||
"green": "#008000", "lime": "#00ff00", "olive": "#808000", "yellow": "#ffff00",
|
|
||||||
"navy": "#000080", "blue": "#0000ff", "teal": "#008080", "aqua": "#00ffff",
|
|
||||||
"orange": "#ffa500", "aliceblue": "#f0f8ff", "antiquewhite": "#faebd7", "aquamarine": "#7fffd4",
|
|
||||||
"azure": "#f0ffff", "beige": "#f5f5dc", "bisque": "#ffe4c4", "blanchedalmond": "#ffebcd",
|
|
||||||
"blueviolet": "#8a2be2", "brown": "#a52a2a", "burlywood": "#deb887", "cadetblue": "#5f9ea0",
|
|
||||||
"chartreuse": "#7fff00", "chocolate": "#d2691e", "coral": "#ff7f50", "cornflowerblue": "#6495ed",
|
|
||||||
"cornsilk": "#fff8dc", "crimson": "#dc143c", "cyan": "#00ffff", "darkblue": "#00008b",
|
|
||||||
"darkcyan": "#008b8b", "darkgoldenrod": "#b8860b", "darkgray": "#a9a9a9", "darkgreen": "#006400",
|
|
||||||
"darkgrey": "#a9a9a9", "darkkhaki": "#bdb76b", "darkmagenta": "#8b008b", "darkolivegreen": "#556b2f",
|
|
||||||
"darkorange": "#ff8c00", "darkorchid": "#9932cc", "darkred": "#8b0000", "darksalmon": "#e9967a",
|
|
||||||
"darkseagreen": "#8fbc8f", "darkslateblue": "#483d8b", "darkslategray": "#2f4f4f", "darkslategrey": "#2f4f4f",
|
|
||||||
"darkturquoise": "#00ced1", "darkviolet": "#9400d3", "deeppink": "#ff1493", "deepskyblue": "#00bfff",
|
|
||||||
"dimgray": "#696969", "dimgrey": "#696969", "dodgerblue": "#1e90ff", "firebrick": "#b22222",
|
|
||||||
"floralwhite": "#fffaf0", "forestgreen": "#228b22", "gainsboro": "#dcdcdc", "ghostwhite": "#f8f8ff",
|
|
||||||
"gold": "#ffd700", "goldenrod": "#daa520", "greenyellow": "#adff2f", "grey": "#808080",
|
|
||||||
"honeydew": "#f0fff0", "hotpink": "#ff69b4", "indianred": "#cd5c5c", "indigo": "#4b0082",
|
|
||||||
"ivory": "#fffff0", "khaki": "#f0e68c", "lavender": "#e6e6fa", "lavenderblush": "#fff0f5",
|
|
||||||
"lawngreen": "#7cfc00", "lemonchiffon": "#fffacd", "lightblue": "#add8e6", "lightcoral": "#f08080",
|
|
||||||
"lightcyan": "#e0ffff", "lightgoldenrodyellow": "#fafad2", "lightgray": "#d3d3d3", "lightgreen": "#90ee90",
|
|
||||||
"lightgrey": "#d3d3d3", "lightpink": "#ffb6c1", "lightsalmon": "#ffa07a", "lightseagreen": "#20b2aa",
|
|
||||||
"lightskyblue": "#87cefa", "lightslategray": "#778899", "lightslategrey": "#778899", "lightsteelblue": "#b0c4de",
|
|
||||||
"lightyellow": "#ffffe0", "limegreen": "#32cd32", "linen": "#faf0e6", "magenta": "#ff00ff",
|
|
||||||
"mediumaquamarine": "#66cdaa", "mediumblue": "#0000cd", "mediumorchid": "#ba55d3", "mediumpurple": "#9370db",
|
|
||||||
"mediumseagreen": "#3cb371", "mediumslateblue": "#7b68ee", "mediumspringgreen": "#00fa9a", "mediumturquoise": "#48d1cc",
|
|
||||||
"mediumvioletred": "#c71585", "midnightblue": "#191970", "mintcream": "#f5fffa", "mistyrose": "#ffe4e1",
|
|
||||||
"moccasin": "#ffe4b5", "navajowhite": "#ffdead", "oldlace": "#fdf5e6", "olivedrab": "#6b8e23",
|
|
||||||
"orangered": "#ff4500", "orchid": "#da70d6", "palegoldenrod": "#eee8aa", "palegreen": "#98fb98",
|
|
||||||
"paleturquoise": "#afeeee", "palevioletred": "#db7093", "papayawhip": "#ffefd5", "peachpuff": "#ffdab9",
|
|
||||||
"peru": "#cd853f", "pink": "#ffc0cb", "plum": "#dda0dd", "powderblue": "#b0e0e6",
|
|
||||||
"rosybrown": "#bc8f8f", "royalblue": "#4169e1", "saddlebrown": "#8b4513", "salmon": "#fa8072",
|
|
||||||
"sandybrown": "#f4a460", "seagreen": "#2e8b57", "seashell": "#fff5ee", "sienna": "#a0522d",
|
|
||||||
"skyblue": "#87ceeb", "slateblue": "#6a5acd", "slategray": "#708090", "slategrey": "#708090",
|
|
||||||
"snow": "#fffafa", "springgreen": "#00ff7f", "steelblue": "#4682b4", "tan": "#d2b48c",
|
|
||||||
"thistle": "#d8bfd8", "tomato": "#ff6347", "turquoise": "#40e0d0", "violet": "#ee82ee",
|
|
||||||
"wheat": "#f5deb3", "whitesmoke": "#f5f5f5", "yellowgreen": "#9acd32", "rebeccapurple": "#663399",
|
|
||||||
}
|
|
||||||
|
|
||||||
nameArg = kingpin.Arg("name", "Name of output style.").Required().String()
|
|
||||||
fileArg = kingpin.Arg("stylesheets", ".css file to import").Required().ExistingFile()
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
for tt, str := range chroma.StandardTypes {
|
|
||||||
typeByClass["."+str] = tt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func translateDecls(decls []*css.Declaration) string {
|
|
||||||
out := []string{}
|
|
||||||
for _, decl := range decls {
|
|
||||||
switch decl.Property {
|
|
||||||
case "color":
|
|
||||||
clr := decl.Value
|
|
||||||
if c, ok := cssNamedColours[clr]; ok {
|
|
||||||
clr = c
|
|
||||||
}
|
|
||||||
out = append(out, clr)
|
|
||||||
case "background-color":
|
|
||||||
out = append(out, "bg:"+decl.Value)
|
|
||||||
case "font-style":
|
|
||||||
if strings.Contains(decl.Value, "italic") {
|
|
||||||
out = append(out, "italic")
|
|
||||||
}
|
|
||||||
case "font-weight":
|
|
||||||
if strings.Contains(decl.Value, "bold") {
|
|
||||||
out = append(out, "bold")
|
|
||||||
}
|
|
||||||
case "text-decoration":
|
|
||||||
if strings.Contains(decl.Value, "underline") {
|
|
||||||
out = append(out, "underline")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(out, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
kingpin.Parse()
|
|
||||||
source, err := ioutil.ReadFile(*fileArg)
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
css, err := parser.Parse(string(source))
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
|
|
||||||
context := map[string]interface{}{
|
|
||||||
"Name": *nameArg,
|
|
||||||
"Rules": css.Rules,
|
|
||||||
}
|
|
||||||
tmpl := template.Must(template.New("style").Funcs(template.FuncMap{
|
|
||||||
"Lower": strings.ToLower,
|
|
||||||
"TranslateDecls": translateDecls,
|
|
||||||
"TokenType": func(s string) chroma.TokenType { return typeByClass[s] },
|
|
||||||
}).Parse(outputTemplate))
|
|
||||||
err = tmpl.Execute(os.Stdout, context)
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma/formatters"
|
|
||||||
"github.com/alecthomas/chroma/lexers"
|
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
"gopkg.in/alecthomas/kingpin.v3-unstable"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
filesArgs = kingpin.Arg("file", "Files to use to exercise lexers.").Required().ExistingFiles()
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
kingpin.CommandLine.Help = "Exercise linters against a list of files."
|
|
||||||
kingpin.Parse()
|
|
||||||
|
|
||||||
for _, file := range *filesArgs {
|
|
||||||
lexer := lexers.Match(file)
|
|
||||||
if lexer == nil {
|
|
||||||
fmt.Printf("warning: could not find lexer for %q\n", file)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fmt.Printf("%s: ", file)
|
|
||||||
os.Stdout.Sync()
|
|
||||||
text, err := ioutil.ReadFile(file)
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
it, err := lexer.Tokenise(nil, string(text))
|
|
||||||
kingpin.FatalIfError(err, "%s failed to tokenise %q", lexer.Config().Name, file)
|
|
||||||
err = formatters.NoOp.Format(ioutil.Discard, styles.SwapOff, it)
|
|
||||||
kingpin.FatalIfError(err, "%s failed to format %q", lexer.Config().Name, file)
|
|
||||||
fmt.Printf("ok\n")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,196 +0,0 @@
|
||||||
import functools
|
|
||||||
import importlib
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import types
|
|
||||||
|
|
||||||
import pystache
|
|
||||||
from pygments import lexer as pygments_lexer
|
|
||||||
from pygments.token import _TokenType
|
|
||||||
|
|
||||||
|
|
||||||
TEMPLATE = r'''
|
|
||||||
package lexers
|
|
||||||
|
|
||||||
import (
|
|
||||||
. "github.com/alecthomas/chroma" // nolint
|
|
||||||
)
|
|
||||||
|
|
||||||
// {{upper_name}} lexer.
|
|
||||||
var {{upper_name}} = Register(MustNewLexer(
|
|
||||||
&Config{
|
|
||||||
Name: "{{name}}",
|
|
||||||
Aliases: []string{ {{#aliases}}"{{.}}", {{/aliases}} },
|
|
||||||
Filenames: []string{ {{#filenames}}"{{.}}", {{/filenames}} },
|
|
||||||
MimeTypes: []string{ {{#mimetypes}}"{{.}}", {{/mimetypes}} },
|
|
||||||
{{#re_not_multiline}}
|
|
||||||
NotMultiline: true,
|
|
||||||
{{/re_not_multiline}}
|
|
||||||
{{#re_dotall}}
|
|
||||||
DotAll: true,
|
|
||||||
{{/re_dotall}}
|
|
||||||
{{#re_ignorecase}}
|
|
||||||
CaseInsensitive: true,
|
|
||||||
{{/re_ignorecase}}
|
|
||||||
},
|
|
||||||
Rules{
|
|
||||||
{{#tokens}}
|
|
||||||
"{{state}}": {
|
|
||||||
{{#rules}}
|
|
||||||
{{{.}}},
|
|
||||||
{{/rules}}
|
|
||||||
},
|
|
||||||
{{/tokens}}
|
|
||||||
},
|
|
||||||
))
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
def go_regex(s):
|
|
||||||
return go_string(s)
|
|
||||||
|
|
||||||
|
|
||||||
def go_string(s):
|
|
||||||
if '`' not in s:
|
|
||||||
return '`' + s + '`'
|
|
||||||
return json.dumps(s)
|
|
||||||
|
|
||||||
|
|
||||||
def to_camel_case(snake_str):
|
|
||||||
components = snake_str.split('_')
|
|
||||||
return ''.join(x.title() for x in components)
|
|
||||||
|
|
||||||
|
|
||||||
def warning(message):
|
|
||||||
print('warning: ' + message, file=sys.stderr)
|
|
||||||
|
|
||||||
|
|
||||||
def resolve_emitter(emitter):
|
|
||||||
if isinstance(emitter, types.FunctionType):
|
|
||||||
if repr(emitter).startswith('<function bygroups.'):
|
|
||||||
args = emitter.__closure__[0].cell_contents
|
|
||||||
emitter = 'ByGroups(%s)' % ', '.join(resolve_emitter(e) for e in args)
|
|
||||||
elif repr(emitter).startswith('<function using.'):
|
|
||||||
args = emitter.__closure__[0].cell_contents
|
|
||||||
if isinstance(args, dict):
|
|
||||||
state = 'root'
|
|
||||||
if 'stack' in args:
|
|
||||||
state = args['stack'][1]
|
|
||||||
args.pop('stack')
|
|
||||||
assert args == {}, args
|
|
||||||
emitter = 'UsingSelf("%s")' % state
|
|
||||||
elif issubclass(args, pygments_lexer.Lexer):
|
|
||||||
name = args.__name__
|
|
||||||
if name.endswith('Lexer'):
|
|
||||||
name = name[:-5]
|
|
||||||
emitter = 'Using(%s, nil)' % name
|
|
||||||
else:
|
|
||||||
raise ValueError('only support "using" with lexer classes, not %r' % args)
|
|
||||||
else:
|
|
||||||
warning('unsupported emitter function %r' % emitter)
|
|
||||||
emitter = '?? %r ??' % emitter
|
|
||||||
elif isinstance(emitter, _TokenType):
|
|
||||||
emitter = str(emitter).replace('.', '')[5:]
|
|
||||||
elif emitter is None:
|
|
||||||
# This generally only occurs when a lookahead/behind assertion is used, so we just allow it
|
|
||||||
# through.
|
|
||||||
return 'None'
|
|
||||||
else:
|
|
||||||
raise ValueError('unsupported emitter type %r' % emitter)
|
|
||||||
assert isinstance(emitter, str)
|
|
||||||
return emitter
|
|
||||||
|
|
||||||
|
|
||||||
def process_state_action(action):
|
|
||||||
if isinstance(action, tuple):
|
|
||||||
return functools.reduce(lambda a, b: a + b, (process_state_action(a) for a in action))
|
|
||||||
if action.startswith('#'):
|
|
||||||
action = action[1:]
|
|
||||||
if action== 'pop':
|
|
||||||
action = 'Pop(1)'
|
|
||||||
elif action.startswith('pop:'):
|
|
||||||
action = 'Pop(%s)' % action[4:]
|
|
||||||
elif action == 'push':
|
|
||||||
action = 'Push()'
|
|
||||||
elif action.startswith('push:'):
|
|
||||||
action = 'Push("%s")' % action[5:]
|
|
||||||
else:
|
|
||||||
raise ValueError('unsupported action %r' % (action,))
|
|
||||||
else:
|
|
||||||
action = 'Push("%s")' % action
|
|
||||||
return (action,)
|
|
||||||
|
|
||||||
|
|
||||||
def translate_rules(rules):
|
|
||||||
out = []
|
|
||||||
for rule in rules:
|
|
||||||
if isinstance(rule, tuple):
|
|
||||||
regex = rule[0]
|
|
||||||
if isinstance(regex, str):
|
|
||||||
regex = go_regex(regex)
|
|
||||||
elif isinstance(regex, pygments_lexer.words):
|
|
||||||
regex = 'Words(%s, %s, %s)' % (go_string(regex.prefix),
|
|
||||||
go_string(regex.suffix),
|
|
||||||
', '.join(go_string(w) for w in regex.words))
|
|
||||||
else:
|
|
||||||
raise ValueError('expected regex string but got %r' % regex)
|
|
||||||
emitter = resolve_emitter(rule[1])
|
|
||||||
if len(rule) == 2:
|
|
||||||
modifier = 'nil'
|
|
||||||
elif type(rule[2]) is str:
|
|
||||||
modifier = process_state_action(rule[2])[0]
|
|
||||||
elif isinstance(rule[2], pygments_lexer.combined):
|
|
||||||
modifier = 'Combined("%s")' % '", "'.join(rule[2])
|
|
||||||
elif type(rule[2]) is tuple:
|
|
||||||
modifier = 'Push("%s")' % '", "'.join(rule[2])
|
|
||||||
else:
|
|
||||||
raise ValueError('unsupported modifier %r' % (rule[2],))
|
|
||||||
out.append('{{ {}, {}, {} }}'.format(regex, emitter, modifier))
|
|
||||||
elif isinstance(rule, pygments_lexer.include):
|
|
||||||
out.append('Include("{}")'.format(rule))
|
|
||||||
elif isinstance(rule, pygments_lexer.default):
|
|
||||||
out.append('Default({})'.format(', '.join(process_state_action(rule.state))))
|
|
||||||
else:
|
|
||||||
raise ValueError('unsupported rule %r' % (rule,))
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
class TemplateView(object):
|
|
||||||
def __init__(self, **kwargs):
|
|
||||||
for key, value in kwargs.items():
|
|
||||||
setattr(self, key, value)
|
|
||||||
|
|
||||||
def re_not_multiline(self):
|
|
||||||
return not (self.regex_flags & re.MULTILINE)
|
|
||||||
|
|
||||||
def re_dotall(self):
|
|
||||||
return self.regex_flags & re.DOTALL
|
|
||||||
|
|
||||||
def re_ignorecase(self):
|
|
||||||
return self.regex_flags & re.IGNORECASE
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
package_name, symbol_name = sys.argv[1].rsplit(sep=".", maxsplit=1)
|
|
||||||
|
|
||||||
package = importlib.import_module(package_name)
|
|
||||||
|
|
||||||
lexer_cls = getattr(package, symbol_name)
|
|
||||||
|
|
||||||
assert issubclass(lexer_cls, pygments_lexer.RegexLexer), 'can only translate from RegexLexer'
|
|
||||||
|
|
||||||
print(pystache.render(TEMPLATE, TemplateView(
|
|
||||||
name=lexer_cls.name,
|
|
||||||
regex_flags=lexer_cls.flags,
|
|
||||||
upper_name=to_camel_case(lexer_cls.name),
|
|
||||||
aliases=lexer_cls.aliases,
|
|
||||||
filenames=lexer_cls.filenames,
|
|
||||||
mimetypes=lexer_cls.mimetypes,
|
|
||||||
tokens=[{'state': state, 'rules': translate_rules(rules)} for (state, rules) in lexer_cls.get_tokendefs().items()],
|
|
||||||
)))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,62 +0,0 @@
|
||||||
import importlib
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import pystache
|
|
||||||
from pygments.style import Style
|
|
||||||
from pygments.token import Token
|
|
||||||
|
|
||||||
|
|
||||||
TEMPLATE = r'''
|
|
||||||
package styles
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
// {{upper_name}} style.
|
|
||||||
var {{upper_name}} = Register(chroma.MustNewStyle("{{name}}", chroma.StyleEntries{
|
|
||||||
{{#styles}}
|
|
||||||
chroma.{{type}}: "{{style}}",
|
|
||||||
{{/styles}}
|
|
||||||
}))
|
|
||||||
'''
|
|
||||||
|
|
||||||
|
|
||||||
def to_camel_case(snake_str):
|
|
||||||
components = snake_str.split('_')
|
|
||||||
return ''.join(x.title() for x in components)
|
|
||||||
|
|
||||||
|
|
||||||
def translate_token_type(t):
|
|
||||||
if t == Token:
|
|
||||||
t = Token.Background
|
|
||||||
return "".join(map(str, t))
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
name = sys.argv[1]
|
|
||||||
package_name, symbol_name = sys.argv[2].rsplit(sep=".", maxsplit=1)
|
|
||||||
|
|
||||||
package = importlib.import_module(package_name)
|
|
||||||
|
|
||||||
style_cls = getattr(package, symbol_name)
|
|
||||||
|
|
||||||
assert issubclass(style_cls, Style), 'can only translate from Style subclass'
|
|
||||||
|
|
||||||
styles = dict(style_cls.styles)
|
|
||||||
bg = "bg:" + style_cls.background_color
|
|
||||||
if Token in styles:
|
|
||||||
styles[Token] += " " + bg
|
|
||||||
else:
|
|
||||||
styles[Token] = bg
|
|
||||||
context = {
|
|
||||||
'upper_name': style_cls.__name__[:-5],
|
|
||||||
'name': name,
|
|
||||||
'styles': [{'type': translate_token_type(t), 'style': s}
|
|
||||||
for t, s in styles.items() if s],
|
|
||||||
}
|
|
||||||
print(pystache.render(TEMPLATE, context))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -1,269 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"runtime"
|
|
||||||
"runtime/pprof"
|
|
||||||
"sort"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/mattn/go-colorable"
|
|
||||||
"github.com/mattn/go-isatty"
|
|
||||||
"gopkg.in/alecthomas/kingpin.v3-unstable"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
"github.com/alecthomas/chroma/formatters"
|
|
||||||
"github.com/alecthomas/chroma/formatters/html"
|
|
||||||
"github.com/alecthomas/chroma/lexers"
|
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// Populated by goreleaser.
|
|
||||||
version = "?"
|
|
||||||
commit = "?"
|
|
||||||
date = "?"
|
|
||||||
|
|
||||||
profileFlag = kingpin.Flag("profile", "Enable profiling to file.").Hidden().String()
|
|
||||||
listFlag = kingpin.Flag("list", "List lexers, styles and formatters.").Bool()
|
|
||||||
unbufferedFlag = kingpin.Flag("unbuffered", "Do not buffer output.").Bool()
|
|
||||||
traceFlag = kingpin.Flag("trace", "Trace lexer states as they are traversed.").Bool()
|
|
||||||
checkFlag = kingpin.Flag("check", "Do not format, check for tokenization errors instead.").Bool()
|
|
||||||
filenameFlag = kingpin.Flag("filename", "Filename to use for selecting a lexer when reading from stdin.").String()
|
|
||||||
|
|
||||||
lexerFlag = kingpin.Flag("lexer", "Lexer to use when formatting.").PlaceHolder("autodetect").Short('l').Enum(lexers.Names(true)...)
|
|
||||||
styleFlag = kingpin.Flag("style", "Style to use for formatting.").Short('s').Default("swapoff").Enum(styles.Names()...)
|
|
||||||
formatterFlag = kingpin.Flag("formatter", "Formatter to use.").Default("terminal").Short('f').Enum(formatters.Names()...)
|
|
||||||
|
|
||||||
jsonFlag = kingpin.Flag("json", "Output JSON representation of tokens.").Bool()
|
|
||||||
|
|
||||||
htmlFlag = kingpin.Flag("html", "Enable HTML mode (equivalent to '--formatter html').").Bool()
|
|
||||||
htmlPrefixFlag = kingpin.Flag("html-prefix", "HTML CSS class prefix.").PlaceHolder("PREFIX").String()
|
|
||||||
htmlStylesFlag = kingpin.Flag("html-styles", "Output HTML CSS styles.").Bool()
|
|
||||||
htmlOnlyFlag = kingpin.Flag("html-only", "Output HTML fragment.").Bool()
|
|
||||||
htmlInlineStyleFlag = kingpin.Flag("html-inline-styles", "Output HTML with inline styles (no classes).").Bool()
|
|
||||||
htmlTabWidthFlag = kingpin.Flag("html-tab-width", "Set the HTML tab width.").Default("8").Int()
|
|
||||||
htmlLinesFlag = kingpin.Flag("html-lines", "Include line numbers in output.").Bool()
|
|
||||||
htmlLinesTableFlag = kingpin.Flag("html-lines-table", "Split line numbers and code in a HTML table").Bool()
|
|
||||||
htmlLinesStyleFlag = kingpin.Flag("html-lines-style", "Style for line numbers.").String()
|
|
||||||
htmlHighlightFlag = kingpin.Flag("html-highlight", "Highlight these lines.").PlaceHolder("N[:M][,...]").String()
|
|
||||||
htmlHighlightStyleFlag = kingpin.Flag("html-highlight-style", "Style used for highlighting lines.").String()
|
|
||||||
htmlBaseLineFlag = kingpin.Flag("html-base-line", "Base line number.").Default("1").Int()
|
|
||||||
|
|
||||||
filesArgs = kingpin.Arg("files", "Files to highlight.").ExistingFiles()
|
|
||||||
)
|
|
||||||
|
|
||||||
type flushableWriter interface {
|
|
||||||
io.Writer
|
|
||||||
Flush() error
|
|
||||||
}
|
|
||||||
|
|
||||||
type nopFlushableWriter struct{ io.Writer }
|
|
||||||
|
|
||||||
func (n *nopFlushableWriter) Flush() error { return nil }
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
kingpin.CommandLine.Version(fmt.Sprintf("%s-%s-%s", version, commit, date))
|
|
||||||
kingpin.CommandLine.Help = `
|
|
||||||
Chroma is a general purpose syntax highlighting library and corresponding
|
|
||||||
command, for Go.
|
|
||||||
`
|
|
||||||
kingpin.Parse()
|
|
||||||
if *listFlag {
|
|
||||||
listAll()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if *profileFlag != "" {
|
|
||||||
f, err := os.Create(*profileFlag)
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
pprof.StartCPUProfile(f)
|
|
||||||
signals := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(signals, os.Interrupt)
|
|
||||||
go func() {
|
|
||||||
<-signals
|
|
||||||
pprof.StopCPUProfile()
|
|
||||||
os.Exit(128 + 3)
|
|
||||||
}()
|
|
||||||
defer pprof.StopCPUProfile()
|
|
||||||
}
|
|
||||||
|
|
||||||
var out io.Writer = os.Stdout
|
|
||||||
if runtime.GOOS == "windows" && isatty.IsTerminal(os.Stdout.Fd()) {
|
|
||||||
out = colorable.NewColorableStdout()
|
|
||||||
}
|
|
||||||
var w flushableWriter
|
|
||||||
if *unbufferedFlag {
|
|
||||||
w = &nopFlushableWriter{out}
|
|
||||||
} else {
|
|
||||||
w = bufio.NewWriterSize(out, 16384)
|
|
||||||
}
|
|
||||||
defer w.Flush()
|
|
||||||
|
|
||||||
if *jsonFlag {
|
|
||||||
*formatterFlag = "json"
|
|
||||||
}
|
|
||||||
|
|
||||||
if *htmlFlag {
|
|
||||||
*formatterFlag = "html"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve user-specified style, clone it, and add some overrides.
|
|
||||||
builder := styles.Get(*styleFlag).Builder()
|
|
||||||
if *htmlHighlightStyleFlag != "" {
|
|
||||||
builder.Add(chroma.LineHighlight, *htmlHighlightStyleFlag)
|
|
||||||
}
|
|
||||||
if *htmlLinesStyleFlag != "" {
|
|
||||||
builder.Add(chroma.LineNumbers, *htmlLinesStyleFlag)
|
|
||||||
}
|
|
||||||
style, err := builder.Build()
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
|
|
||||||
if *formatterFlag == "html" {
|
|
||||||
options := []html.Option{
|
|
||||||
html.TabWidth(*htmlTabWidthFlag),
|
|
||||||
html.BaseLineNumber(*htmlBaseLineFlag),
|
|
||||||
}
|
|
||||||
if *htmlPrefixFlag != "" {
|
|
||||||
options = append(options, html.ClassPrefix(*htmlPrefixFlag))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dump styles.
|
|
||||||
if *htmlStylesFlag {
|
|
||||||
formatter := html.New(html.WithClasses())
|
|
||||||
formatter.WriteCSS(w, style)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if !*htmlInlineStyleFlag {
|
|
||||||
options = append(options, html.WithClasses())
|
|
||||||
}
|
|
||||||
if !*htmlOnlyFlag {
|
|
||||||
options = append(options, html.Standalone())
|
|
||||||
}
|
|
||||||
if *htmlLinesFlag {
|
|
||||||
options = append(options, html.WithLineNumbers())
|
|
||||||
}
|
|
||||||
if *htmlLinesTableFlag {
|
|
||||||
options = append(options, html.LineNumbersInTable())
|
|
||||||
}
|
|
||||||
if len(*htmlHighlightFlag) > 0 {
|
|
||||||
ranges := [][2]int{}
|
|
||||||
for _, span := range strings.Split(*htmlHighlightFlag, ",") {
|
|
||||||
parts := strings.Split(span, ":")
|
|
||||||
if len(parts) > 2 {
|
|
||||||
kingpin.Fatalf("range should be N[:M], not %q", span)
|
|
||||||
}
|
|
||||||
start, err := strconv.ParseInt(parts[0], 10, 64)
|
|
||||||
kingpin.FatalIfError(err, "min value of range should be integer not %q", parts[0])
|
|
||||||
end := start
|
|
||||||
if len(parts) == 2 {
|
|
||||||
end, err = strconv.ParseInt(parts[1], 10, 64)
|
|
||||||
kingpin.FatalIfError(err, "max value of range should be integer not %q", parts[1])
|
|
||||||
}
|
|
||||||
ranges = append(ranges, [2]int{int(start), int(end)})
|
|
||||||
}
|
|
||||||
options = append(options, html.HighlightLines(ranges))
|
|
||||||
}
|
|
||||||
formatters.Register("html", html.New(options...))
|
|
||||||
}
|
|
||||||
if len(*filesArgs) == 0 {
|
|
||||||
contents, err := ioutil.ReadAll(os.Stdin)
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
format(w, style, lex(*filenameFlag, string(contents)))
|
|
||||||
} else {
|
|
||||||
for _, filename := range *filesArgs {
|
|
||||||
contents, err := ioutil.ReadFile(filename)
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
if *checkFlag {
|
|
||||||
check(filename, lex(filename, string(contents)))
|
|
||||||
} else {
|
|
||||||
format(w, style, lex(filename, string(contents)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func listAll() {
|
|
||||||
fmt.Println("lexers:")
|
|
||||||
sort.Sort(lexers.Registry.Lexers)
|
|
||||||
for _, l := range lexers.Registry.Lexers {
|
|
||||||
config := l.Config()
|
|
||||||
fmt.Printf(" %s\n", config.Name)
|
|
||||||
filenames := []string{}
|
|
||||||
filenames = append(filenames, config.Filenames...)
|
|
||||||
filenames = append(filenames, config.AliasFilenames...)
|
|
||||||
if len(config.Aliases) > 0 {
|
|
||||||
fmt.Printf(" aliases: %s\n", strings.Join(config.Aliases, " "))
|
|
||||||
}
|
|
||||||
if len(filenames) > 0 {
|
|
||||||
fmt.Printf(" filenames: %s\n", strings.Join(filenames, " "))
|
|
||||||
}
|
|
||||||
if len(config.MimeTypes) > 0 {
|
|
||||||
fmt.Printf(" mimetypes: %s\n", strings.Join(config.MimeTypes, " "))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Printf("styles:")
|
|
||||||
for _, name := range styles.Names() {
|
|
||||||
fmt.Printf(" %s", name)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
fmt.Printf("formatters:")
|
|
||||||
for _, name := range formatters.Names() {
|
|
||||||
fmt.Printf(" %s", name)
|
|
||||||
}
|
|
||||||
fmt.Println()
|
|
||||||
}
|
|
||||||
|
|
||||||
func lex(path string, contents string) chroma.Iterator {
|
|
||||||
lexer := selexer(path, contents)
|
|
||||||
if lexer == nil {
|
|
||||||
lexer = lexers.Fallback
|
|
||||||
}
|
|
||||||
if rel, ok := lexer.(*chroma.RegexLexer); ok {
|
|
||||||
rel.Trace(*traceFlag)
|
|
||||||
}
|
|
||||||
lexer = chroma.Coalesce(lexer)
|
|
||||||
it, err := lexer.Tokenise(nil, string(contents))
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
|
|
||||||
func selexer(path, contents string) (lexer chroma.Lexer) {
|
|
||||||
if *lexerFlag != "" {
|
|
||||||
return lexers.Get(*lexerFlag)
|
|
||||||
}
|
|
||||||
if path != "" {
|
|
||||||
lexer := lexers.Match(path)
|
|
||||||
if lexer != nil {
|
|
||||||
return lexer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lexers.Analyse(contents)
|
|
||||||
}
|
|
||||||
|
|
||||||
func format(w io.Writer, style *chroma.Style, it chroma.Iterator) {
|
|
||||||
formatter := formatters.Get(*formatterFlag)
|
|
||||||
err := formatter.Format(w, style, it)
|
|
||||||
kingpin.FatalIfError(err, "")
|
|
||||||
}
|
|
||||||
|
|
||||||
func check(filename string, it chroma.Iterator) {
|
|
||||||
line, col := 1, 0
|
|
||||||
for token := it(); token != nil; token = it() {
|
|
||||||
if token.Type == chroma.Error {
|
|
||||||
fmt.Printf("%s:%d:%d %q\n", filename, line, col, token.String())
|
|
||||||
}
|
|
||||||
for _, c := range token.String() {
|
|
||||||
col++
|
|
||||||
if c == '\n' {
|
|
||||||
line, col = line+1, 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCoalesce(t *testing.T) {
|
|
||||||
lexer := Coalesce(MustNewLexer(nil, Rules{
|
|
||||||
"root": []Rule{
|
|
||||||
{`[!@#$%^&*()]`, Punctuation, nil},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
actual, err := Tokenise(lexer, nil, "!@#$")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
expected := []*Token{{Punctuation, "!@#$"}}
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestColourRGB(t *testing.T) {
|
|
||||||
colour := ParseColour("#8913af")
|
|
||||||
assert.Equal(t, uint8(0x89), colour.Red())
|
|
||||||
assert.Equal(t, uint8(0x13), colour.Green())
|
|
||||||
assert.Equal(t, uint8(0xaf), colour.Blue())
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestColourString(t *testing.T) {
|
|
||||||
assert.Equal(t, "#8913af", ParseColour("#8913af").String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func distance(a, b uint8) uint8 {
|
|
||||||
if a < b {
|
|
||||||
return b - a
|
|
||||||
}
|
|
||||||
return a - b
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestColourBrighten(t *testing.T) {
|
|
||||||
actual := NewColour(128, 128, 128).Brighten(0.5)
|
|
||||||
// Closeish to what we expect is fine.
|
|
||||||
assert.True(t, distance(192, actual.Red()) <= 2)
|
|
||||||
assert.True(t, distance(192, actual.Blue()) <= 2)
|
|
||||||
assert.True(t, distance(192, actual.Green()) <= 2)
|
|
||||||
actual = NewColour(128, 128, 128).Brighten(-0.5)
|
|
||||||
assert.True(t, distance(65, actual.Red()) <= 2)
|
|
||||||
assert.True(t, distance(65, actual.Blue()) <= 2)
|
|
||||||
assert.True(t, distance(65, actual.Green()) <= 2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestColourBrightess(t *testing.T) {
|
|
||||||
actual := NewColour(128, 128, 128).Brightness()
|
|
||||||
assert.True(t, distance(128, uint8(actual*255.0)) <= 2)
|
|
||||||
}
|
|
|
@ -1,55 +0,0 @@
|
||||||
package formatters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
"github.com/alecthomas/chroma/formatters/html"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// NoOp formatter.
|
|
||||||
NoOp = Register("noop", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, iterator chroma.Iterator) error {
|
|
||||||
for t := iterator(); t != nil; t = iterator() {
|
|
||||||
if _, err := io.WriteString(w, t.Value); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
||||||
// Default HTML formatter outputs self-contained HTML.
|
|
||||||
htmlFull = Register("html", html.New(html.Standalone(), html.WithClasses()))
|
|
||||||
)
|
|
||||||
|
|
||||||
// Fallback formatter.
|
|
||||||
var Fallback = NoOp
|
|
||||||
|
|
||||||
// Registry of Formatters.
|
|
||||||
var Registry = map[string]chroma.Formatter{}
|
|
||||||
|
|
||||||
// Names of registered formatters.
|
|
||||||
func Names() []string {
|
|
||||||
out := []string{}
|
|
||||||
for name := range Registry {
|
|
||||||
out = append(out, name)
|
|
||||||
}
|
|
||||||
sort.Strings(out)
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get formatter by name.
|
|
||||||
//
|
|
||||||
// If the given formatter is not found, the Fallback formatter will be returned.
|
|
||||||
func Get(name string) chroma.Formatter {
|
|
||||||
if f, ok := Registry[name]; ok {
|
|
||||||
return f
|
|
||||||
}
|
|
||||||
return Fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register a named formatter.
|
|
||||||
func Register(name string, formatter chroma.Formatter) chroma.Formatter {
|
|
||||||
Registry[name] = formatter
|
|
||||||
return formatter
|
|
||||||
}
|
|
|
@ -1,77 +0,0 @@
|
||||||
package html
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"io/ioutil"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
"github.com/alecthomas/chroma/lexers"
|
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCompressStyle(t *testing.T) {
|
|
||||||
style := "color: #888888; background-color: #faffff"
|
|
||||||
actual := compressStyle(style)
|
|
||||||
expected := "color:#888;background-color:#faffff"
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkHTMLFormatter(b *testing.B) {
|
|
||||||
formatter := New()
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
it, err := lexers.Go.Tokenise(nil, "package main\nfunc main()\n{\nprintln(`hello world`)\n}\n")
|
|
||||||
assert.NoError(b, err)
|
|
||||||
err = formatter.Format(ioutil.Discard, styles.Fallback, it)
|
|
||||||
assert.NoError(b, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSplitTokensIntoLines(t *testing.T) {
|
|
||||||
in := []*chroma.Token{
|
|
||||||
{Value: "hello", Type: chroma.NameKeyword},
|
|
||||||
{Value: " world\nwhat?\n", Type: chroma.NameKeyword},
|
|
||||||
}
|
|
||||||
expected := [][]*chroma.Token{
|
|
||||||
{
|
|
||||||
{Type: chroma.NameKeyword, Value: "hello"},
|
|
||||||
{Type: chroma.NameKeyword, Value: " world\n"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{Type: chroma.NameKeyword, Value: "what?\n"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{Type: chroma.NameKeyword},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
actual := splitTokensIntoLines(in)
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIteratorPanicRecovery(t *testing.T) {
|
|
||||||
it := func() *chroma.Token {
|
|
||||||
panic(errors.New("bad"))
|
|
||||||
}
|
|
||||||
err := New().Format(ioutil.Discard, styles.Fallback, it)
|
|
||||||
assert.Error(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFormatter_styleToCSS(t *testing.T) {
|
|
||||||
builder := styles.Get("github").Builder()
|
|
||||||
builder.Add(chroma.LineHighlight, "bg:#ffffcc")
|
|
||||||
builder.Add(chroma.LineNumbers, "bold")
|
|
||||||
style, err := builder.Build()
|
|
||||||
if err != nil {
|
|
||||||
t.Error(err)
|
|
||||||
}
|
|
||||||
formatter := New(WithClasses())
|
|
||||||
css := formatter.styleToCSS(style)
|
|
||||||
for _, s := range css {
|
|
||||||
if strings.HasPrefix(strings.TrimSpace(s), ";") {
|
|
||||||
t.Errorf("rule starts with semicolon - expected valid css rule without semicolon: %v", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
package formatters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
// JSON formatter outputs the raw token structures as JSON.
|
|
||||||
var JSON = Register("json", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
|
||||||
fmt.Fprintln(w, "[")
|
|
||||||
i := 0
|
|
||||||
for t := it(); t != nil; t = it() {
|
|
||||||
if i > 0 {
|
|
||||||
fmt.Fprintln(w, ",")
|
|
||||||
}
|
|
||||||
i++
|
|
||||||
bytes, err := json.Marshal(t)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := fmt.Fprint(w, " "+string(bytes)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Fprintln(w)
|
|
||||||
fmt.Fprintln(w, "]")
|
|
||||||
return nil
|
|
||||||
}))
|
|
|
@ -1,18 +0,0 @@
|
||||||
package formatters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Tokens formatter outputs the raw token structures.
|
|
||||||
var Tokens = Register("tokens", chroma.FormatterFunc(func(w io.Writer, s *chroma.Style, it chroma.Iterator) error {
|
|
||||||
for t := it(); t != nil; t = it() {
|
|
||||||
if _, err := fmt.Fprintln(w, t.GoString()); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}))
|
|
|
@ -1,250 +0,0 @@
|
||||||
package formatters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ttyTable struct {
|
|
||||||
foreground map[chroma.Colour]string
|
|
||||||
background map[chroma.Colour]string
|
|
||||||
}
|
|
||||||
|
|
||||||
var c = chroma.MustParseColour
|
|
||||||
|
|
||||||
var ttyTables = map[int]*ttyTable{
|
|
||||||
8: {
|
|
||||||
foreground: map[chroma.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[chroma.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: {
|
|
||||||
foreground: map[chroma.Colour]string{
|
|
||||||
c("#000000"): "\033[38;5;0m", c("#800000"): "\033[38;5;1m", c("#008000"): "\033[38;5;2m", c("#808000"): "\033[38;5;3m",
|
|
||||||
c("#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[chroma.Colour]string{
|
|
||||||
c("#000000"): "\033[48;5;0m", c("#800000"): "\033[48;5;1m", c("#008000"): "\033[48;5;2m", c("#808000"): "\033[48;5;3m",
|
|
||||||
c("#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 entryToEscapeSequence(table *ttyTable, entry chroma.StyleEntry) string {
|
|
||||||
out := ""
|
|
||||||
if entry.Bold == chroma.Yes {
|
|
||||||
out += "\033[1m"
|
|
||||||
}
|
|
||||||
if entry.Underline == chroma.Yes {
|
|
||||||
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, seeking chroma.Colour) chroma.Colour {
|
|
||||||
closestColour := chroma.Colour(0)
|
|
||||||
closest := float64(math.MaxFloat64)
|
|
||||||
for colour := range table.foreground {
|
|
||||||
distance := colour.Distance(seeking)
|
|
||||||
if distance < closest {
|
|
||||||
closest = distance
|
|
||||||
closestColour = colour
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return closestColour
|
|
||||||
}
|
|
||||||
|
|
||||||
func styleToEscapeSequence(table *ttyTable, style *chroma.Style) map[chroma.TokenType]string {
|
|
||||||
out := map[chroma.TokenType]string{}
|
|
||||||
for _, ttype := range style.Types() {
|
|
||||||
entry := style.Get(ttype)
|
|
||||||
out[ttype] = entryToEscapeSequence(table, entry)
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
type indexedTTYFormatter struct {
|
|
||||||
table *ttyTable
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *indexedTTYFormatter) Format(w io.Writer, style *chroma.Style, it chroma.Iterator) (err error) {
|
|
||||||
defer func() {
|
|
||||||
if perr := recover(); perr != nil {
|
|
||||||
err = perr.(error)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
theme := styleToEscapeSequence(c.table, style)
|
|
||||||
for token := it(); token != nil; token = it() {
|
|
||||||
// TODO: Cache token lookups?
|
|
||||||
clr, ok := theme[token.Type]
|
|
||||||
if !ok {
|
|
||||||
clr, ok = theme[token.Type.SubCategory()]
|
|
||||||
if !ok {
|
|
||||||
clr = theme[token.Type.Category()]
|
|
||||||
// if !ok {
|
|
||||||
// clr = theme[chroma.InheritStyle]
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if clr != "" {
|
|
||||||
fmt.Fprint(w, clr)
|
|
||||||
}
|
|
||||||
fmt.Fprint(w, token.Value)
|
|
||||||
if clr != "" {
|
|
||||||
fmt.Fprintf(w, "\033[0m")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TTY8 is an 8-colour terminal formatter.
|
|
||||||
//
|
|
||||||
// The Lab colour space is used to map RGB values to the most appropriate index colour.
|
|
||||||
var TTY8 = Register("terminal", &indexedTTYFormatter{ttyTables[8]})
|
|
||||||
|
|
||||||
// TTY256 is a 256-colour terminal formatter.
|
|
||||||
//
|
|
||||||
// The Lab colour space is used to map RGB values to the most appropriate index colour.
|
|
||||||
var TTY256 = Register("terminal256", &indexedTTYFormatter{ttyTables[256]})
|
|
|
@ -1,38 +0,0 @@
|
||||||
package formatters
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TTY16m is a true-colour terminal formatter.
|
|
||||||
var TTY16m = Register("terminal16m", chroma.FormatterFunc(trueColourFormatter))
|
|
||||||
|
|
||||||
func trueColourFormatter(w io.Writer, style *chroma.Style, it chroma.Iterator) error {
|
|
||||||
for token := it(); token != nil; token = it() {
|
|
||||||
entry := style.Get(token.Type)
|
|
||||||
if !entry.IsZero() {
|
|
||||||
out := ""
|
|
||||||
if entry.Bold == chroma.Yes {
|
|
||||||
out += "\033[1m"
|
|
||||||
}
|
|
||||||
if entry.Underline == chroma.Yes {
|
|
||||||
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTokenTypeClassifiers(t *testing.T) {
|
|
||||||
assert.True(t, GenericDeleted.InCategory(Generic))
|
|
||||||
assert.True(t, LiteralStringBacktick.InSubCategory(String))
|
|
||||||
assert.Equal(t, LiteralStringBacktick.String(), "LiteralStringBacktick")
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleLexer(t *testing.T) {
|
|
||||||
lexer, err := NewLexer(
|
|
||||||
&Config{
|
|
||||||
Name: "INI",
|
|
||||||
Aliases: []string{"ini", "cfg"},
|
|
||||||
Filenames: []string{"*.ini", "*.cfg"},
|
|
||||||
},
|
|
||||||
map[string][]Rule{
|
|
||||||
"root": {
|
|
||||||
{`\s+`, Whitespace, nil},
|
|
||||||
{`;.*?$`, Comment, nil},
|
|
||||||
{`\[.*?\]$`, Keyword, nil},
|
|
||||||
{`(.*?)(\s*)(=)(\s*)(.*?)$`, ByGroups(Name, Whitespace, Operator, Whitespace, String), nil},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
actual, err := Tokenise(lexer, nil, `
|
|
||||||
; this is a comment
|
|
||||||
[section]
|
|
||||||
a = 10
|
|
||||||
`)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
expected := []*Token{
|
|
||||||
{Whitespace, "\n\t"},
|
|
||||||
{Comment, "; this is a comment"},
|
|
||||||
{Whitespace, "\n\t"},
|
|
||||||
{Keyword, "[section]"},
|
|
||||||
{Whitespace, "\n\t"},
|
|
||||||
{Name, "a"},
|
|
||||||
{Whitespace, " "},
|
|
||||||
{Operator, "="},
|
|
||||||
{Whitespace, " "},
|
|
||||||
{LiteralString, "10"},
|
|
||||||
{Whitespace, "\n"},
|
|
||||||
}
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
package lexers_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma/formatters"
|
|
||||||
"github.com/alecthomas/chroma/lexers"
|
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestCompileAllRegexes(t *testing.T) {
|
|
||||||
for _, lexer := range lexers.Registry.Lexers {
|
|
||||||
it, err := lexer.Tokenise(nil, "")
|
|
||||||
assert.NoError(t, err, "%s failed", lexer.Config().Name)
|
|
||||||
err = formatters.NoOp.Format(ioutil.Discard, styles.SwapOff, it)
|
|
||||||
assert.NoError(t, err, "%s failed", lexer.Config().Name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGet(t *testing.T) {
|
|
||||||
t.Run("ByName", func(t *testing.T) {
|
|
||||||
assert.Equal(t, lexers.Get("xml"), lexers.XML)
|
|
||||||
})
|
|
||||||
t.Run("ByAlias", func(t *testing.T) {
|
|
||||||
assert.Equal(t, lexers.Get("as"), lexers.Actionscript)
|
|
||||||
})
|
|
||||||
t.Run("ViaFilename", func(t *testing.T) {
|
|
||||||
assert.Equal(t, lexers.Get("svg"), lexers.XML)
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
package lexers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestDiffLexerWithoutTralingNewLine(t *testing.T) {
|
|
||||||
diffLexer := Get("diff")
|
|
||||||
it, err := diffLexer.Tokenise(nil, "-foo\n+bar")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
actual := it.Tokens()
|
|
||||||
expected := []*chroma.Token{
|
|
||||||
&chroma.Token{chroma.GenericDeleted, "-foo\n"},
|
|
||||||
&chroma.Token{chroma.GenericInserted, "+bar\n"},
|
|
||||||
}
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package lexers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
const lexerBenchSource = `package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
// A Formatter for Chroma lexers.
|
|
||||||
type Formatter interface {
|
|
||||||
// Format returns a formatting function for tokens.
|
|
||||||
Format(w io.Writer, style *Style) (func(*Token), error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A FormatterFunc is a Formatter implemented as a function.
|
|
||||||
type FormatterFunc func(io.Writer, *Style) (func(*Token), error)
|
|
||||||
|
|
||||||
func (f FormatterFunc) Format(w io.Writer, s *Style) (func(*Token), error) {
|
|
||||||
return f(w, s)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
func Benchmark(b *testing.B) {
|
|
||||||
b.ReportAllocs()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
it, err := Go.Tokenise(nil, lexerBenchSource)
|
|
||||||
assert.NoError(b, err)
|
|
||||||
for t := it(); t != nil; t = it() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,63 +0,0 @@
|
||||||
package lexers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Test source files are in the form <key>.<key> and validation data is in the form <key>.<key>.expected.
|
|
||||||
func TestLexers(t *testing.T) {
|
|
||||||
files, err := ioutil.ReadDir("testdata")
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
for _, file := range files {
|
|
||||||
ext := filepath.Ext(file.Name())[1:]
|
|
||||||
if ext != "actual" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer := Get(strings.TrimSuffix(file.Name(), filepath.Ext(file.Name())))
|
|
||||||
if !assert.NotNil(t, lexer) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
filename := filepath.Join("testdata", file.Name())
|
|
||||||
expectedFilename := strings.TrimSuffix(filename, filepath.Ext(filename)) + ".expected"
|
|
||||||
|
|
||||||
lexer = chroma.Coalesce(lexer)
|
|
||||||
t.Run(lexer.Config().Name, func(t *testing.T) {
|
|
||||||
// Read and tokenise source text.
|
|
||||||
actualText, err := ioutil.ReadFile(filename)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
actual, err := chroma.Tokenise(lexer, nil, string(actualText))
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read expected JSON into token slice.
|
|
||||||
expected := []*chroma.Token{}
|
|
||||||
r, err := os.Open(expectedFilename)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
err = json.NewDecoder(r).Decode(&expected)
|
|
||||||
if !assert.NoError(t, err) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equal?
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
# Lexer tests
|
|
||||||
|
|
||||||
This directory contains input source and expected output lexer tokens.
|
|
||||||
|
|
||||||
Input filenames for lexers are in the form `<name>.actual`. Expected output filenames are in the form `<name>.expected`.
|
|
||||||
|
|
||||||
Each input filename is parsed by the corresponding lexer and checked against the expected JSON-encoded token list.
|
|
||||||
|
|
||||||
|
|
||||||
To add/update tests do the following:
|
|
||||||
|
|
||||||
1. `export LEXER=csharp`
|
|
||||||
1. Create/edit a file `lexers/testdata/${LEXER}.actual` (eg. `csharp.actual`).
|
|
||||||
2. Run `go run ./cmd/chroma/main.go --lexer ${LEXER} --json lexers/testdata/${LEXER}.actual > lexers/testdata/${LEXER}.expected`.
|
|
||||||
3. Run `go test -v -run TestLexers ./lexers`.
|
|
||||||
|
|
||||||
|
|
||||||
eg.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ export LEXER=csharp
|
|
||||||
$ go run ./cmd/chroma/main.go --lexer ${LEXER} --json lexers/testdata/${LEXER}.${LEXER} > lexers/testdata/${LEXER}.expected
|
|
||||||
$ go test -v -run TestLexers ./lexers
|
|
||||||
=== RUN TestLexers
|
|
||||||
=== RUN TestLexers/C#
|
|
||||||
=== RUN TestLexers/CSS
|
|
||||||
--- PASS: TestLexers (0.01s)
|
|
||||||
--- PASS: TestLexers/C# (0.00s)
|
|
||||||
--- PASS: TestLexers/CSS (0.00s)
|
|
||||||
PASS
|
|
||||||
ok github.com/alecthomas/chroma/lexers 0.032s
|
|
||||||
```
|
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
DriveInfo[] drives = DriveInfo.GetDrives();
|
|
||||||
foreach (DriveInfo drive in drives)
|
|
||||||
{
|
|
||||||
IEnumerable<string> driveFolders =
|
|
||||||
Directory.EnumerateDirectories(drive.RootDirectory.ToString());
|
|
||||||
|
|
||||||
foreach (string dir in driveFolders)
|
|
||||||
{
|
|
||||||
Console.WriteLine(dir);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
[
|
|
||||||
{"type":"Name","value":"DriveInfo"},
|
|
||||||
{"type":"NameAttribute","value":"[]"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Name","value":"drives"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Punctuation","value":"="},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"NameClass","value":"DriveInfo"},
|
|
||||||
{"type":"Punctuation","value":"."},
|
|
||||||
{"type":"Name","value":"GetDrives"},
|
|
||||||
{"type":"Punctuation","value":"();"},
|
|
||||||
{"type":"Text","value":"\n"},
|
|
||||||
{"type":"Keyword","value":"foreach"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Punctuation","value":"("},
|
|
||||||
{"type":"Name","value":"DriveInfo"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Name","value":"drive"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Keyword","value":"in"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Name","value":"drives"},
|
|
||||||
{"type":"Punctuation","value":")"},
|
|
||||||
{"type":"Text","value":"\n"},
|
|
||||||
{"type":"Punctuation","value":"{"},
|
|
||||||
{"type":"Text","value":"\n "},
|
|
||||||
{"type":"Name","value":"IEnumerable"},
|
|
||||||
{"type":"Punctuation","value":"\u003c"},
|
|
||||||
{"type":"KeywordType","value":"string"},
|
|
||||||
{"type":"Punctuation","value":"\u003e"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Name","value":"driveFolders"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Punctuation","value":"="},
|
|
||||||
{"type":"Text","value":"\n "},
|
|
||||||
{"type":"NameClass","value":"Directory"},
|
|
||||||
{"type":"Punctuation","value":"."},
|
|
||||||
{"type":"Name","value":"EnumerateDirectories"},
|
|
||||||
{"type":"Punctuation","value":"("},
|
|
||||||
{"type":"NameClass","value":"drive"},
|
|
||||||
{"type":"Punctuation","value":"."},
|
|
||||||
{"type":"NameClass","value":"RootDirectory"},
|
|
||||||
{"type":"Punctuation","value":"."},
|
|
||||||
{"type":"Name","value":"ToString"},
|
|
||||||
{"type":"Punctuation","value":"());"},
|
|
||||||
{"type":"Text","value":"\n\n "},
|
|
||||||
{"type":"Keyword","value":"foreach"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Punctuation","value":"("},
|
|
||||||
{"type":"KeywordType","value":"string"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Name","value":"dir"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Keyword","value":"in"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Name","value":"driveFolders"},
|
|
||||||
{"type":"Punctuation","value":")"},
|
|
||||||
{"type":"Text","value":"\n "},
|
|
||||||
{"type":"Punctuation","value":"{"},
|
|
||||||
{"type":"Text","value":"\n "},
|
|
||||||
{"type":"NameClass","value":"Console"},
|
|
||||||
{"type":"Punctuation","value":"."},
|
|
||||||
{"type":"Name","value":"WriteLine"},
|
|
||||||
{"type":"Punctuation","value":"("},
|
|
||||||
{"type":"Name","value":"dir"},
|
|
||||||
{"type":"Punctuation","value":");"},
|
|
||||||
{"type":"Text","value":"\n "},
|
|
||||||
{"type":"Punctuation","value":"}"},
|
|
||||||
{"type":"Text","value":"\n"},
|
|
||||||
{"type":"Punctuation","value":"}"},
|
|
||||||
{"type":"Text","value":"\n"}
|
|
||||||
]
|
|
|
@ -1,3 +0,0 @@
|
||||||
:root {
|
|
||||||
--variable-name: #fff;
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
[
|
|
||||||
{"type":"Punctuation","value":":"},
|
|
||||||
{"type":"NameDecorator","value":"root"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"Punctuation","value":"{"},
|
|
||||||
{"type":"Text","value":"\n "},
|
|
||||||
{"type":"NameVariable","value":"--variable-name"},
|
|
||||||
{"type":"Text","value":""},
|
|
||||||
{"type":"Punctuation","value":":"},
|
|
||||||
{"type":"Text","value":" "},
|
|
||||||
{"type":"LiteralNumberHex","value":"#fff"},
|
|
||||||
{"type":"Punctuation","value":";"},
|
|
||||||
{"type":"Text","value":"\n"},
|
|
||||||
{"type":"Punctuation","value":"}"},
|
|
||||||
{"type":"Text","value":"\n"}
|
|
||||||
]
|
|
|
@ -1,57 +0,0 @@
|
||||||
package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestInclude(t *testing.T) {
|
|
||||||
include := Include("other")
|
|
||||||
actual := CompiledRules{
|
|
||||||
"root": {{Rule: include}},
|
|
||||||
"other": {
|
|
||||||
{Rule: Rule{Pattern: "//.+", Type: Comment}},
|
|
||||||
{Rule: Rule{Pattern: `"[^"]*"`, Type: String}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
lexer := &RegexLexer{rules: actual}
|
|
||||||
err := include.Mutator.(LexerMutator).MutateLexer(lexer.rules, "root", 0)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
expected := CompiledRules{
|
|
||||||
"root": {
|
|
||||||
{Rule: Rule{
|
|
||||||
Pattern: "//.+",
|
|
||||||
Type: Comment,
|
|
||||||
}},
|
|
||||||
{Rule: Rule{
|
|
||||||
Pattern: `"[^"]*"`,
|
|
||||||
Type: String,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
"other": {
|
|
||||||
{Rule: Rule{
|
|
||||||
Pattern: "//.+",
|
|
||||||
Type: Comment,
|
|
||||||
}},
|
|
||||||
{Rule: Rule{
|
|
||||||
Pattern: `"[^"]*"`,
|
|
||||||
Type: String,
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCombine(t *testing.T) {
|
|
||||||
l := MustNewLexer(nil, Rules{
|
|
||||||
"root": {{`hello`, String, Combined("world", "bye", "space")}},
|
|
||||||
"world": {{`world`, Name, nil}},
|
|
||||||
"bye": {{`bye`, Name, nil}},
|
|
||||||
"space": {{`\s+`, Whitespace, nil}},
|
|
||||||
})
|
|
||||||
it, err := l.Tokenise(nil, "hello world")
|
|
||||||
assert.NoError(t, err)
|
|
||||||
expected := []*Token{{String, `hello`}, {Whitespace, ` `}, {Name, `world`}}
|
|
||||||
assert.Equal(t, expected, it.Tokens())
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
package quick_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma/quick"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Example() {
|
|
||||||
code := `package main
|
|
||||||
|
|
||||||
func main() { }
|
|
||||||
`
|
|
||||||
err := quick.Highlight(os.Stdout, code, "go", "html", "monokai")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
// Package quick provides simple, no-configuration source code highlighting.
|
|
||||||
package quick
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/alecthomas/chroma"
|
|
||||||
"github.com/alecthomas/chroma/formatters"
|
|
||||||
"github.com/alecthomas/chroma/lexers"
|
|
||||||
"github.com/alecthomas/chroma/styles"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Highlight some text.
|
|
||||||
//
|
|
||||||
// Lexer, formatter and style may be empty, in which case a best-effort is made.
|
|
||||||
func Highlight(w io.Writer, source, lexer, formatter, style string) error {
|
|
||||||
// Determine lexer.
|
|
||||||
l := lexers.Get(lexer)
|
|
||||||
if l == nil {
|
|
||||||
l = lexers.Analyse(source)
|
|
||||||
}
|
|
||||||
if l == nil {
|
|
||||||
l = lexers.Fallback
|
|
||||||
}
|
|
||||||
l = chroma.Coalesce(l)
|
|
||||||
|
|
||||||
// Determine formatter.
|
|
||||||
f := formatters.Get(formatter)
|
|
||||||
if f == nil {
|
|
||||||
f = formatters.Fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine style.
|
|
||||||
s := styles.Get(style)
|
|
||||||
if s == nil {
|
|
||||||
s = styles.Fallback
|
|
||||||
}
|
|
||||||
|
|
||||||
it, err := l.Tokenise(nil, source)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return f.Format(w, s, it)
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNewlineAtEndOfFile(t *testing.T) {
|
|
||||||
l := Coalesce(MustNewLexer(&Config{EnsureNL: true}, Rules{
|
|
||||||
"root": {
|
|
||||||
{`(\w+)(\n)`, ByGroups(Keyword, Whitespace), nil},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
it, err := l.Tokenise(nil, `hello`)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, []*Token{{Keyword, "hello"}, {Whitespace, "\n"}}, it.Tokens())
|
|
||||||
|
|
||||||
l = Coalesce(MustNewLexer(nil, Rules{
|
|
||||||
"root": {
|
|
||||||
{`(\w+)(\n)`, ByGroups(Keyword, Whitespace), nil},
|
|
||||||
},
|
|
||||||
}))
|
|
||||||
it, err = l.Tokenise(nil, `hello`)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, []*Token{{Error, "hello"}}, it.Tokens())
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRemappingLexer(t *testing.T) {
|
|
||||||
var lexer Lexer = MustNewLexer(nil, Rules{
|
|
||||||
"root": {
|
|
||||||
{`\s+`, Whitespace, nil},
|
|
||||||
{`\w+`, Name, nil},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
lexer = TypeRemappingLexer(lexer, TypeMapping{
|
|
||||||
{Name, Keyword, []string{"if", "else"}},
|
|
||||||
})
|
|
||||||
|
|
||||||
it, err := lexer.Tokenise(nil, `if true then print else end`)
|
|
||||||
assert.NoError(t, err)
|
|
||||||
expected := []*Token{
|
|
||||||
{Keyword, "if"}, {TextWhitespace, " "}, {Name, "true"}, {TextWhitespace, " "}, {Name, "then"},
|
|
||||||
{TextWhitespace, " "}, {Name, "print"}, {TextWhitespace, " "}, {Keyword, "else"},
|
|
||||||
{TextWhitespace, " "}, {Name, "end"},
|
|
||||||
}
|
|
||||||
actual := it.Tokens()
|
|
||||||
assert.Equal(t, expected, actual)
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
package chroma
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/alecthomas/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStyleInherit(t *testing.T) {
|
|
||||||
s, err := NewStyle("test", StyleEntries{
|
|
||||||
Name: "bold #f00",
|
|
||||||
NameVariable: "#fff",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, StyleEntry{Colour: 0x1000000, Bold: Yes}, s.Get(NameVariable))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStyleColours(t *testing.T) {
|
|
||||||
s, err := NewStyle("test", StyleEntries{
|
|
||||||
Name: "#f00 bg:#001 border:#ansiblue",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
assert.Equal(t, StyleEntry{Colour: 0xff0001, Background: 0x000012, Border: 0x000100}, s.Get(Name))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStyleClone(t *testing.T) {
|
|
||||||
parent, err := NewStyle("test", StyleEntries{
|
|
||||||
Background: "bg:#ffffff",
|
|
||||||
})
|
|
||||||
assert.NoError(t, err)
|
|
||||||
clone, err := parent.Builder().Add(Comment, "#0f0").Build()
|
|
||||||
assert.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "bg:#ffffff", clone.Get(Background).String())
|
|
||||||
assert.Equal(t, "#00ff00 bg:#ffffff", clone.Get(Comment).String())
|
|
||||||
assert.Equal(t, "bg:#ffffff", parent.Get(Comment).String())
|
|
||||||
}
|
|
|
@ -1,351 +0,0 @@
|
||||||
package fnmatch_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/danwakefield/fnmatch"
|
|
||||||
)
|
|
||||||
|
|
||||||
// This is a set of tests ported from a set of tests for C fnmatch
|
|
||||||
// found at http://www.mail-archive.com/bug-gnulib@gnu.org/msg14048.html
|
|
||||||
func TestMatch(t *testing.T) {
|
|
||||||
assert := func(p, s string) {
|
|
||||||
if !fnmatch.Match(p, s, 0) {
|
|
||||||
t.Errorf("Assertion failed: Match(%#v, %#v, 0)", p, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert("", "")
|
|
||||||
assert("*", "")
|
|
||||||
assert("*", "foo")
|
|
||||||
assert("*", "bar")
|
|
||||||
assert("*", "*")
|
|
||||||
assert("**", "f")
|
|
||||||
assert("**", "foo.txt")
|
|
||||||
assert("*.*", "foo.txt")
|
|
||||||
assert("foo*.txt", "foobar.txt")
|
|
||||||
assert("foo.txt", "foo.txt")
|
|
||||||
assert("foo\\.txt", "foo.txt")
|
|
||||||
if fnmatch.Match("foo\\.txt", "foo.txt", fnmatch.FNM_NOESCAPE) {
|
|
||||||
t.Errorf("Assertion failed: Match(%#v, %#v, FNM_NOESCAPE) == false", "foo\\.txt", "foo.txt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWildcard(t *testing.T) {
|
|
||||||
// A wildcard pattern "*" should match anything
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"*", "", 0, true},
|
|
||||||
{"*", "foo", 0, true},
|
|
||||||
{"*", "*", 0, true},
|
|
||||||
{"*", " ", 0, true},
|
|
||||||
{"*", ".foo", 0, true},
|
|
||||||
{"*", "わたし", 0, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWildcardSlash(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// Should match / when flags are 0
|
|
||||||
{"*", "foo/bar", 0, true},
|
|
||||||
{"*", "/", 0, true},
|
|
||||||
{"*", "/foo", 0, true},
|
|
||||||
{"*", "foo/", 0, true},
|
|
||||||
// Shouldnt match / when flags include FNM_PATHNAME
|
|
||||||
{"*", "foo/bar", fnmatch.FNM_PATHNAME, false},
|
|
||||||
{"*", "/", fnmatch.FNM_PATHNAME, false},
|
|
||||||
{"*", "/foo", fnmatch.FNM_PATHNAME, false},
|
|
||||||
{"*", "foo/", fnmatch.FNM_PATHNAME, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestWildcardFNMPeriod(t *testing.T) {
|
|
||||||
// FNM_PERIOD means that . is not matched in some circumstances.
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"*", ".foo", fnmatch.FNM_PERIOD, false},
|
|
||||||
{"/*", "/.foo", fnmatch.FNM_PERIOD, true},
|
|
||||||
{"/*", "/.foo", fnmatch.FNM_PERIOD | fnmatch.FNM_PATHNAME, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQuestionMark(t *testing.T) {
|
|
||||||
//A question mark pattern "?" should match a single character
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"?", "", 0, false},
|
|
||||||
{"?", "f", 0, true},
|
|
||||||
{"?", ".", 0, true},
|
|
||||||
{"?", "?", 0, true},
|
|
||||||
{"?", "foo", 0, false},
|
|
||||||
{"?", "わ", 0, true},
|
|
||||||
{"?", "わた", 0, false},
|
|
||||||
// Even '/' when flags are 0
|
|
||||||
{"?", "/", 0, true},
|
|
||||||
// Except '/' when flags include FNM_PATHNAME
|
|
||||||
{"?", "/", fnmatch.FNM_PATHNAME, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestQuestionMarkExceptions(t *testing.T) {
|
|
||||||
//When flags include FNM_PERIOD a '?' might not match a '.' character.
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
{"?", ".", fnmatch.FNM_PERIOD, false},
|
|
||||||
{"foo?", "foo.", fnmatch.FNM_PERIOD, true},
|
|
||||||
{"/?", "/.", fnmatch.FNM_PERIOD, true},
|
|
||||||
{"/?", "/.", fnmatch.FNM_PERIOD | fnmatch.FNM_PATHNAME, false},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRange(t *testing.T) {
|
|
||||||
azPat := "[a-z]"
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// Should match a single character inside its range
|
|
||||||
{azPat, "a", 0, true},
|
|
||||||
{azPat, "q", 0, true},
|
|
||||||
{azPat, "z", 0, true},
|
|
||||||
{"[わ]", "わ", 0, true},
|
|
||||||
|
|
||||||
// Should not match characters outside its range
|
|
||||||
{azPat, "-", 0, false},
|
|
||||||
{azPat, " ", 0, false},
|
|
||||||
{azPat, "D", 0, false},
|
|
||||||
{azPat, "é", 0, false},
|
|
||||||
|
|
||||||
//Should only match one character
|
|
||||||
{azPat, "ab", 0, false},
|
|
||||||
{azPat, "", 0, false},
|
|
||||||
|
|
||||||
// Should not consume more of the pattern than necessary
|
|
||||||
{azPat + "foo", "afoo", 0, true},
|
|
||||||
|
|
||||||
// Should match '-' if it is the first/last character or is
|
|
||||||
// backslash escaped
|
|
||||||
{"[-az]", "-", 0, true},
|
|
||||||
{"[-az]", "a", 0, true},
|
|
||||||
{"[-az]", "b", 0, false},
|
|
||||||
{"[az-]", "-", 0, true},
|
|
||||||
{"[a\\-z]", "-", 0, true},
|
|
||||||
{"[a\\-z]", "b", 0, false},
|
|
||||||
|
|
||||||
// ignore '\\' when FNM_NOESCAPE is given
|
|
||||||
{"[a\\-z]", "\\", fnmatch.FNM_NOESCAPE, true},
|
|
||||||
{"[a\\-z]", "-", fnmatch.FNM_NOESCAPE, false},
|
|
||||||
|
|
||||||
// Should be negated if starting with ^ or !"
|
|
||||||
{"[^a-z]", "a", 0, false},
|
|
||||||
{"[!a-z]", "b", 0, false},
|
|
||||||
{"[!a-z]", "é", 0, true},
|
|
||||||
{"[!a-z]", "わ", 0, true},
|
|
||||||
|
|
||||||
// Still match '-' if following the negation character
|
|
||||||
{"[^-az]", "-", 0, false},
|
|
||||||
{"[^-az]", "b", 0, true},
|
|
||||||
|
|
||||||
// Should support multiple characters/ranges
|
|
||||||
{"[abc]", "a", 0, true},
|
|
||||||
{"[abc]", "c", 0, true},
|
|
||||||
{"[abc]", "d", 0, false},
|
|
||||||
{"[a-cg-z]", "c", 0, true},
|
|
||||||
{"[a-cg-z]", "h", 0, true},
|
|
||||||
{"[a-cg-z]", "d", 0, false},
|
|
||||||
|
|
||||||
//Should not match '/' when flags is FNM_PATHNAME
|
|
||||||
{"[abc/def]", "/", 0, true},
|
|
||||||
{"[abc/def]", "/", fnmatch.FNM_PATHNAME, false},
|
|
||||||
{"[.-0]", "/", 0, true}, // The range [.-0] includes /
|
|
||||||
{"[.-0]", "/", fnmatch.FNM_PATHNAME, false},
|
|
||||||
|
|
||||||
// Should normally be case-sensitive
|
|
||||||
{"[a-z]", "A", 0, false},
|
|
||||||
{"[A-Z]", "a", 0, false},
|
|
||||||
//Except when FNM_CASEFOLD is given
|
|
||||||
{"[a-z]", "A", fnmatch.FNM_CASEFOLD, true},
|
|
||||||
{"[A-Z]", "a", fnmatch.FNM_CASEFOLD, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestBackSlash(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
//A backslash should escape the following characters
|
|
||||||
{"\\\\", "\\", 0, true},
|
|
||||||
{"\\*", "*", 0, true},
|
|
||||||
{"\\*", "foo", 0, false},
|
|
||||||
{"\\?", "?", 0, true},
|
|
||||||
{"\\?", "f", 0, false},
|
|
||||||
{"\\[a-z]", "[a-z]", 0, true},
|
|
||||||
{"\\[a-z]", "a", 0, false},
|
|
||||||
{"\\foo", "foo", 0, true},
|
|
||||||
{"\\わ", "わ", 0, true},
|
|
||||||
|
|
||||||
// Unless FNM_NOESCAPE is given
|
|
||||||
{"\\\\", "\\", fnmatch.FNM_NOESCAPE, false},
|
|
||||||
{"\\\\", "\\\\", fnmatch.FNM_NOESCAPE, true},
|
|
||||||
{"\\*", "foo", fnmatch.FNM_NOESCAPE, false},
|
|
||||||
{"\\*", "\\*", fnmatch.FNM_NOESCAPE, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestLiteral(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
//Literal characters should match themselves
|
|
||||||
{"foo", "foo", 0, true},
|
|
||||||
{"foo", "foobar", 0, false},
|
|
||||||
{"foobar", "foo", 0, false},
|
|
||||||
{"foo", "Foo", 0, false},
|
|
||||||
{"わたし", "わたし", 0, true},
|
|
||||||
// And perform case-folding when FNM_CASEFOLD is given
|
|
||||||
{"foo", "FOO", fnmatch.FNM_CASEFOLD, true},
|
|
||||||
{"FoO", "fOo", fnmatch.FNM_CASEFOLD, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFNMLeadingDir(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
flags int
|
|
||||||
want bool
|
|
||||||
}{
|
|
||||||
// FNM_LEADING_DIR should ignore trailing '/*'
|
|
||||||
{"foo", "foo/bar", 0, false},
|
|
||||||
{"foo", "foo/bar", fnmatch.FNM_LEADING_DIR, true},
|
|
||||||
{"*", "foo/bar", fnmatch.FNM_PATHNAME, false},
|
|
||||||
{"*", "foo/bar", fnmatch.FNM_PATHNAME | fnmatch.FNM_LEADING_DIR, true},
|
|
||||||
}
|
|
||||||
|
|
||||||
for tc, c := range cases {
|
|
||||||
got := fnmatch.Match(c.pattern, c.input, c.flags)
|
|
||||||
if got != c.want {
|
|
||||||
t.Errorf(
|
|
||||||
"Testcase #%d failed: fnmatch.Match('%s', '%s', %d) should be %v not %v",
|
|
||||||
tc, c.pattern, c.input, c.flags, c.want, got,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,43 +0,0 @@
|
||||||
package regexp2
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestIgnoreCase_Simple(t *testing.T) {
|
|
||||||
r := MustCompile("aaamatch thisbbb", IgnoreCase)
|
|
||||||
m, err := r.FindStringMatch("AaAMatch thisBBb")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("no match when one was expected")
|
|
||||||
}
|
|
||||||
if want, got := "AaAMatch thisBBb", m.String(); want != got {
|
|
||||||
t.Fatalf("group 0 wanted '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIgnoreCase_Inline(t *testing.T) {
|
|
||||||
r := MustCompile("aaa(?i:match this)bbb", 0)
|
|
||||||
m, err := r.FindStringMatch("aaaMaTcH ThIsbbb")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("no match when one was expected")
|
|
||||||
}
|
|
||||||
if want, got := "aaaMaTcH ThIsbbb", m.String(); want != got {
|
|
||||||
t.Fatalf("group 0 wanted '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIgnoreCase_Revert(t *testing.T) {
|
|
||||||
|
|
||||||
r := MustCompile("aaa(?-i:match this)bbb", IgnoreCase)
|
|
||||||
m, err := r.FindStringMatch("AaAMatch thisBBb")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if m != nil {
|
|
||||||
t.Fatalf("had a match but expected no match")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,409 +0,0 @@
|
||||||
package regexp2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Process the file "testoutput1" from PCRE2 v10.21 (public domain)
|
|
||||||
var totalCount, failCount = 0, 0
|
|
||||||
|
|
||||||
func TestPcre_Basics(t *testing.T) {
|
|
||||||
defer func() {
|
|
||||||
if failCount > 0 {
|
|
||||||
t.Logf("%v of %v patterns failed", failCount, totalCount)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
// open our test patterns file and run through it
|
|
||||||
// validating results as we go
|
|
||||||
file, err := os.Open("testoutput1")
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
// the high level structure of the file:
|
|
||||||
// #comments - ignore only outside of the pattern
|
|
||||||
// pattern (could be multi-line, could be surrounded by "" or //) after the / there are the options some we understand, some we don't
|
|
||||||
// test case
|
|
||||||
// 0: success case
|
|
||||||
// \= Expect no match (ignored)
|
|
||||||
// another test case
|
|
||||||
// No Match
|
|
||||||
//
|
|
||||||
// another pattern ...etc
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
// main pattern loop
|
|
||||||
for scanner.Scan() {
|
|
||||||
// reading the file a line at a time
|
|
||||||
line := scanner.Text()
|
|
||||||
|
|
||||||
if trim := strings.TrimSpace(line); trim == "" || strings.HasPrefix(trim, "#") {
|
|
||||||
// skip blanks and comments
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
patternStart := line[0]
|
|
||||||
if patternStart != '/' && patternStart != '"' {
|
|
||||||
// an error! expected a pattern but we didn't understand what was in the file
|
|
||||||
t.Fatalf("Unknown file format, expected line to start with '/' or '\"', line in: %v", line)
|
|
||||||
}
|
|
||||||
|
|
||||||
// start building our pattern, handling multi-line patterns
|
|
||||||
pattern := line
|
|
||||||
totalCount++
|
|
||||||
|
|
||||||
// keep appending the lines to our pattern string until we
|
|
||||||
// find our closing tag, don't allow the first char to match on the
|
|
||||||
// line start, but subsequent lines could end on the first char
|
|
||||||
allowFirst := false
|
|
||||||
for !containsEnder(line, patternStart, allowFirst) {
|
|
||||||
if !scanner.Scan() {
|
|
||||||
// an error! expected more pattern, but got eof
|
|
||||||
t.Fatalf("Unknown file format, expected more pattern text, but got EOF, pattern so far: %v", pattern)
|
|
||||||
}
|
|
||||||
line = scanner.Text()
|
|
||||||
pattern += fmt.Sprintf("\n%s", line)
|
|
||||||
allowFirst = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have our raw pattern! -- we need to convert this to a compiled regex
|
|
||||||
re := compileRawPattern(t, pattern)
|
|
||||||
|
|
||||||
var (
|
|
||||||
capsIdx map[int]int
|
|
||||||
m *Match
|
|
||||||
toMatch string
|
|
||||||
)
|
|
||||||
// now we need to parse the test cases if there are any
|
|
||||||
// they start with 4 spaces -- if we don't get a 4-space start then
|
|
||||||
// we're back out to our next pattern
|
|
||||||
for scanner.Scan() {
|
|
||||||
line = scanner.Text()
|
|
||||||
|
|
||||||
// blank line is our separator for a new pattern
|
|
||||||
if strings.TrimSpace(line) == "" {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
// could be either " " or "\= Expect"
|
|
||||||
if strings.HasPrefix(line, "\\= Expect") {
|
|
||||||
continue
|
|
||||||
} else if strings.HasPrefix(line, " ") {
|
|
||||||
// trim off leading spaces for our text to match
|
|
||||||
toMatch = line[4:]
|
|
||||||
// trim off trailing spaces too
|
|
||||||
toMatch = strings.TrimRight(toMatch, " ")
|
|
||||||
|
|
||||||
m = matchString(t, re, toMatch)
|
|
||||||
|
|
||||||
capsIdx = make(map[int]int)
|
|
||||||
continue
|
|
||||||
//t.Fatalf("Expected match text to start with 4 spaces, instead got: '%v'", line)
|
|
||||||
} else if strings.HasPrefix(line, "No match") {
|
|
||||||
validateNoMatch(t, re, m)
|
|
||||||
// no match means we're done
|
|
||||||
continue
|
|
||||||
} else if subs := matchGroup.FindStringSubmatch(line); len(subs) == 3 {
|
|
||||||
gIdx, _ := strconv.Atoi(subs[1])
|
|
||||||
if _, ok := capsIdx[gIdx]; !ok {
|
|
||||||
capsIdx[gIdx] = 0
|
|
||||||
}
|
|
||||||
validateMatch(t, re, m, toMatch, subs[2], gIdx, capsIdx[gIdx])
|
|
||||||
capsIdx[gIdx]++
|
|
||||||
continue
|
|
||||||
} else {
|
|
||||||
// no match -- problem
|
|
||||||
t.Fatalf("Unknown file format, expected match or match group but got '%v'", line)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var matchGroup = regexp.MustCompile(`^\s*(\d+): (.*)`)
|
|
||||||
|
|
||||||
func problem(t *testing.T, input string, args ...interface{}) {
|
|
||||||
failCount++
|
|
||||||
t.Errorf(input, args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateNoMatch(t *testing.T, re *Regexp, m *Match) {
|
|
||||||
if re == nil || m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
problem(t, "Expected no match for pattern '%v', but got '%v'", re.pattern, m.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateMatch(t *testing.T, re *Regexp, m *Match, toMatch, value string, idx, capIdx int) {
|
|
||||||
if re == nil {
|
|
||||||
// already error'd earlier up stream
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if m == nil {
|
|
||||||
// we didn't match, but should have
|
|
||||||
problem(t, "Expected match for pattern '%v' with input '%v', but got no match", re.pattern, toMatch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
g := m.Groups()
|
|
||||||
if len(g) <= idx {
|
|
||||||
problem(t, "Expected group %v does not exist in pattern '%v' with input '%v'", idx, re.pattern, toMatch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if value == "<unset>" {
|
|
||||||
// this means we shouldn't have a cap for this group
|
|
||||||
if len(g[idx].Captures) > 0 {
|
|
||||||
problem(t, "Expected no cap %v in group %v in pattern '%v' with input '%v'", g[idx].Captures[capIdx].String(), idx, re.pattern, toMatch)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(g[idx].Captures) <= capIdx {
|
|
||||||
problem(t, "Expected cap %v does not exist in group %v in pattern '%v' with input '%v'", capIdx, idx, re.pattern, toMatch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
escp := unEscapeGroup(g[idx].String())
|
|
||||||
//escp := unEscapeGroup(g[idx].Captures[capIdx].String())
|
|
||||||
if escp != value {
|
|
||||||
problem(t, "Expected '%v' but got '%v' for cap %v, group %v for pattern '%v' with input '%v'", value, escp, capIdx, idx, re.pattern, toMatch)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func compileRawPattern(t *testing.T, pattern string) *Regexp {
|
|
||||||
// check our end for RegexOptions -trim them off
|
|
||||||
index := strings.LastIndexAny(pattern, "/\"")
|
|
||||||
//
|
|
||||||
// Append "= Debug" to compare details between corefx and regexp2 on the PCRE test suite
|
|
||||||
//
|
|
||||||
var opts RegexOptions
|
|
||||||
|
|
||||||
if index+1 < len(pattern) {
|
|
||||||
textOptions := pattern[index+1:]
|
|
||||||
pattern = pattern[:index+1]
|
|
||||||
// there are lots of complex options here
|
|
||||||
for _, textOpt := range strings.Split(textOptions, ",") {
|
|
||||||
switch textOpt {
|
|
||||||
case "dupnames":
|
|
||||||
// we don't know how to handle this...
|
|
||||||
default:
|
|
||||||
if strings.Contains(textOpt, "i") {
|
|
||||||
opts |= IgnoreCase
|
|
||||||
}
|
|
||||||
if strings.Contains(textOpt, "s") {
|
|
||||||
opts |= Singleline
|
|
||||||
}
|
|
||||||
if strings.Contains(textOpt, "m") {
|
|
||||||
opts |= Multiline
|
|
||||||
}
|
|
||||||
if strings.Contains(textOpt, "x") {
|
|
||||||
opts |= IgnorePatternWhitespace
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// trim off first and last char
|
|
||||||
pattern = pattern[1 : len(pattern)-1]
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if rec := recover(); rec != nil {
|
|
||||||
problem(t, "PANIC in compiling \"%v\": %v", pattern, rec)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
re, err := Compile(pattern, opts)
|
|
||||||
if err != nil {
|
|
||||||
problem(t, "Error parsing \"%v\": %v", pattern, err)
|
|
||||||
}
|
|
||||||
return re
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchString(t *testing.T, re *Regexp, toMatch string) *Match {
|
|
||||||
if re == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
re.MatchTimeout = time.Second * 1
|
|
||||||
|
|
||||||
escp := ""
|
|
||||||
var err error
|
|
||||||
if toMatch != "\\" {
|
|
||||||
escp = unEscapeToMatch(toMatch)
|
|
||||||
}
|
|
||||||
m, err := re.FindStringMatch(escp)
|
|
||||||
if err != nil {
|
|
||||||
problem(t, "Error matching \"%v\" in pattern \"%v\": %v", toMatch, re.pattern, err)
|
|
||||||
}
|
|
||||||
return m
|
|
||||||
}
|
|
||||||
|
|
||||||
func containsEnder(line string, ender byte, allowFirst bool) bool {
|
|
||||||
index := strings.LastIndexByte(line, ender)
|
|
||||||
if index > 0 {
|
|
||||||
return true
|
|
||||||
} else if index == 0 && allowFirst {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func unEscapeToMatch(line string) string {
|
|
||||||
idx := strings.IndexRune(line, '\\')
|
|
||||||
// no slashes means no unescape needed
|
|
||||||
if idx == -1 {
|
|
||||||
return line
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := bytes.NewBufferString(line[:idx])
|
|
||||||
// get the runes for the rest of the string -- we're going full parser scan on this
|
|
||||||
|
|
||||||
inEscape := false
|
|
||||||
// take any \'s and convert them
|
|
||||||
for i := idx; i < len(line); i++ {
|
|
||||||
ch := line[i]
|
|
||||||
if ch == '\\' {
|
|
||||||
if inEscape {
|
|
||||||
buf.WriteByte(ch)
|
|
||||||
}
|
|
||||||
inEscape = !inEscape
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if inEscape {
|
|
||||||
switch ch {
|
|
||||||
case 'x':
|
|
||||||
buf.WriteByte(scanHex(line, &i))
|
|
||||||
case 'a':
|
|
||||||
buf.WriteByte(0x07)
|
|
||||||
case 'b':
|
|
||||||
buf.WriteByte('\b')
|
|
||||||
case 'e':
|
|
||||||
buf.WriteByte(0x1b)
|
|
||||||
case 'f':
|
|
||||||
buf.WriteByte('\f')
|
|
||||||
case 'n':
|
|
||||||
buf.WriteByte('\n')
|
|
||||||
case 'r':
|
|
||||||
buf.WriteByte('\r')
|
|
||||||
case 't':
|
|
||||||
buf.WriteByte('\t')
|
|
||||||
case 'v':
|
|
||||||
buf.WriteByte(0x0b)
|
|
||||||
default:
|
|
||||||
if ch >= '0' && ch <= '7' {
|
|
||||||
buf.WriteByte(scanOctal(line, &i))
|
|
||||||
} else {
|
|
||||||
buf.WriteByte(ch)
|
|
||||||
//panic(fmt.Sprintf("unexpected escape '%v' in %v", string(ch), line))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
inEscape = false
|
|
||||||
} else {
|
|
||||||
buf.WriteByte(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func unEscapeGroup(val string) string {
|
|
||||||
// use hex for chars 0x00-0x1f, 0x7f-0xff
|
|
||||||
buf := &bytes.Buffer{}
|
|
||||||
|
|
||||||
for i := 0; i < len(val); i++ {
|
|
||||||
ch := val[i]
|
|
||||||
if ch <= 0x1f || ch >= 0x7f {
|
|
||||||
//write it as a \x00
|
|
||||||
fmt.Fprintf(buf, "\\x%.2x", ch)
|
|
||||||
} else {
|
|
||||||
// write as-is
|
|
||||||
buf.WriteByte(ch)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func scanHex(line string, idx *int) byte {
|
|
||||||
if *idx >= len(line)-2 {
|
|
||||||
panic(fmt.Sprintf("not enough hex chars in %v at %v", line, *idx))
|
|
||||||
}
|
|
||||||
(*idx)++
|
|
||||||
d1 := hexDigit(line[*idx])
|
|
||||||
(*idx)++
|
|
||||||
d2 := hexDigit(line[*idx])
|
|
||||||
if d1 < 0 || d2 < 0 {
|
|
||||||
panic("bad hex chars")
|
|
||||||
}
|
|
||||||
|
|
||||||
return byte(d1*0x10 + d2)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns n <= 0xF for a hex digit.
|
|
||||||
func hexDigit(ch byte) int {
|
|
||||||
|
|
||||||
if d := uint(ch - '0'); d <= 9 {
|
|
||||||
return int(d)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d := uint(ch - 'a'); d <= 5 {
|
|
||||||
return int(d + 0xa)
|
|
||||||
}
|
|
||||||
|
|
||||||
if d := uint(ch - 'A'); d <= 5 {
|
|
||||||
return int(d + 0xa)
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scans up to three octal digits (stops before exceeding 0377).
|
|
||||||
func scanOctal(line string, idx *int) byte {
|
|
||||||
// Consume octal chars only up to 3 digits and value 0377
|
|
||||||
|
|
||||||
// octals can be 3,2, or 1 digit
|
|
||||||
c := 3
|
|
||||||
|
|
||||||
if diff := len(line) - *idx; c > diff {
|
|
||||||
c = diff
|
|
||||||
}
|
|
||||||
|
|
||||||
i := 0
|
|
||||||
d := int(line[*idx] - '0')
|
|
||||||
for c > 0 && d <= 7 {
|
|
||||||
i *= 8
|
|
||||||
i += d
|
|
||||||
|
|
||||||
c--
|
|
||||||
(*idx)++
|
|
||||||
if *idx < len(line) {
|
|
||||||
d = int(line[*idx] - '0')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(*idx)--
|
|
||||||
|
|
||||||
// Octal codes only go up to 255. Any larger and the behavior that Perl follows
|
|
||||||
// is simply to truncate the high bits.
|
|
||||||
i &= 0xFF
|
|
||||||
|
|
||||||
return byte(i)
|
|
||||||
}
|
|
|
@ -1,307 +0,0 @@
|
||||||
package regexp2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkLiteral(b *testing.B) {
|
|
||||||
x := strings.Repeat("x", 50) + "y"
|
|
||||||
b.StopTimer()
|
|
||||||
re := MustCompile("y", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNotLiteral(b *testing.B) {
|
|
||||||
x := strings.Repeat("x", 50) + "y"
|
|
||||||
b.StopTimer()
|
|
||||||
re := MustCompile(".y", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMatchClass(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := strings.Repeat("xxxx", 20) + "w"
|
|
||||||
re := MustCompile("[abcdw]", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkMatchClass_InRange(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
// 'b' is between 'a' and 'c', so the charclass
|
|
||||||
// range checking is no help here.
|
|
||||||
x := strings.Repeat("bbbb", 20) + "c"
|
|
||||||
re := MustCompile("[ac]", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func BenchmarkReplaceAll(b *testing.B) {
|
|
||||||
x := "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
b.StopTimer()
|
|
||||||
re := MustCompile("[cjrw]", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
re.ReplaceAllString(x, "")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
func BenchmarkAnchoredLiteralShortNonMatch(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
re := MustCompile("^zbc(d|e)", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); m || err != nil {
|
|
||||||
b.Fatalf("unexpected match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAnchoredLiteralLongNonMatch(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
|
|
||||||
data := "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
x := make([]rune, 32768*len(data))
|
|
||||||
for i := 0; i < 32768; /*(2^15)*/ i++ {
|
|
||||||
for j := 0; j < len(data); j++ {
|
|
||||||
x[i*len(data)+j] = rune(data[j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
re := MustCompile("^zbc(d|e)", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchRunes(x); m || err != nil {
|
|
||||||
b.Fatalf("unexpected match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAnchoredShortMatch(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
re := MustCompile("^.bc(d|e)", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkAnchoredLongMatch(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
data := "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
x := make([]rune, 32768*len(data))
|
|
||||||
for i := 0; i < 32768; /*(2^15)*/ i++ {
|
|
||||||
for j := 0; j < len(data); j++ {
|
|
||||||
x[i*len(data)+j] = rune(data[j])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
re := MustCompile("^.bc(d|e)", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchRunes(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkOnePassShortA(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcddddddeeeededd"
|
|
||||||
re := MustCompile("^.bc(d|e)*$", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNotOnePassShortA(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcddddddeeeededd"
|
|
||||||
re := MustCompile(".bc(d|e)*$", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkOnePassShortB(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcddddddeeeededd"
|
|
||||||
re := MustCompile("^.bc(?:d|e)*$", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkNotOnePassShortB(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcddddddeeeededd"
|
|
||||||
re := MustCompile(".bc(?:d|e)*$", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkOnePassLongPrefix(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
re := MustCompile("^abcdefghijklmnopqrstuvwxyz.*$", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkOnePassLongNotPrefix(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
x := "abcdefghijklmnopqrstuvwxyz"
|
|
||||||
re := MustCompile("^.bcdefghijklmnopqrstuvwxyz.*$", 0)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(x); !m || err != nil {
|
|
||||||
b.Fatalf("no match or error! %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var text []rune
|
|
||||||
|
|
||||||
func makeText(n int) []rune {
|
|
||||||
if len(text) >= n {
|
|
||||||
return text[:n]
|
|
||||||
}
|
|
||||||
text = make([]rune, n)
|
|
||||||
x := ^uint32(0)
|
|
||||||
for i := range text {
|
|
||||||
x += x
|
|
||||||
x ^= 1
|
|
||||||
if int32(x) < 0 {
|
|
||||||
x ^= 0x88888eef
|
|
||||||
}
|
|
||||||
if x%31 == 0 {
|
|
||||||
text[i] = '\n'
|
|
||||||
} else {
|
|
||||||
text[i] = rune(x%(0x7E+1-0x20) + 0x20)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
func benchmark(b *testing.B, re string, n int) {
|
|
||||||
r := MustCompile(re, 0)
|
|
||||||
t := makeText(n)
|
|
||||||
b.ResetTimer()
|
|
||||||
b.SetBytes(int64(n))
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := r.MatchRunes(t); m {
|
|
||||||
b.Fatal("match!")
|
|
||||||
} else if err != nil {
|
|
||||||
b.Fatalf("Err %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
easy0 = "ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
|
|
||||||
easy1 = "A[AB]B[BC]C[CD]D[DE]E[EF]F[FG]G[GH]H[HI]I[IJ]J$"
|
|
||||||
medium = "[XYZ]ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
|
|
||||||
hard = "[ -~]*ABCDEFGHIJKLMNOPQRSTUVWXYZ$"
|
|
||||||
hard1 = "ABCD|CDEF|EFGH|GHIJ|IJKL|KLMN|MNOP|OPQR|QRST|STUV|UVWX|WXYZ"
|
|
||||||
parens = "([ -~])*(A)(B)(C)(D)(E)(F)(G)(H)(I)(J)(K)(L)(M)" +
|
|
||||||
"(N)(O)(P)(Q)(R)(S)(T)(U)(V)(W)(X)(Y)(Z)$"
|
|
||||||
)
|
|
||||||
|
|
||||||
func BenchmarkMatchEasy0_32(b *testing.B) { benchmark(b, easy0, 32<<0) }
|
|
||||||
func BenchmarkMatchEasy0_1K(b *testing.B) { benchmark(b, easy0, 1<<10) }
|
|
||||||
func BenchmarkMatchEasy0_32K(b *testing.B) { benchmark(b, easy0, 32<<10) }
|
|
||||||
func BenchmarkMatchEasy0_1M(b *testing.B) { benchmark(b, easy0, 1<<20) }
|
|
||||||
func BenchmarkMatchEasy0_32M(b *testing.B) { benchmark(b, easy0, 32<<20) }
|
|
||||||
func BenchmarkMatchEasy1_32(b *testing.B) { benchmark(b, easy1, 32<<0) }
|
|
||||||
func BenchmarkMatchEasy1_1K(b *testing.B) { benchmark(b, easy1, 1<<10) }
|
|
||||||
func BenchmarkMatchEasy1_32K(b *testing.B) { benchmark(b, easy1, 32<<10) }
|
|
||||||
func BenchmarkMatchEasy1_1M(b *testing.B) { benchmark(b, easy1, 1<<20) }
|
|
||||||
func BenchmarkMatchEasy1_32M(b *testing.B) { benchmark(b, easy1, 32<<20) }
|
|
||||||
func BenchmarkMatchMedium_32(b *testing.B) { benchmark(b, medium, 32<<0) }
|
|
||||||
func BenchmarkMatchMedium_1K(b *testing.B) { benchmark(b, medium, 1<<10) }
|
|
||||||
func BenchmarkMatchMedium_32K(b *testing.B) { benchmark(b, medium, 32<<10) }
|
|
||||||
func BenchmarkMatchMedium_1M(b *testing.B) { benchmark(b, medium, 1<<20) }
|
|
||||||
func BenchmarkMatchMedium_32M(b *testing.B) { benchmark(b, medium, 32<<20) }
|
|
||||||
func BenchmarkMatchHard_32(b *testing.B) { benchmark(b, hard, 32<<0) }
|
|
||||||
func BenchmarkMatchHard_1K(b *testing.B) { benchmark(b, hard, 1<<10) }
|
|
||||||
func BenchmarkMatchHard_32K(b *testing.B) { benchmark(b, hard, 32<<10) }
|
|
||||||
func BenchmarkMatchHard_1M(b *testing.B) { benchmark(b, hard, 1<<20) }
|
|
||||||
func BenchmarkMatchHard_32M(b *testing.B) { benchmark(b, hard, 32<<20) }
|
|
||||||
func BenchmarkMatchHard1_32(b *testing.B) { benchmark(b, hard1, 32<<0) }
|
|
||||||
func BenchmarkMatchHard1_1K(b *testing.B) { benchmark(b, hard1, 1<<10) }
|
|
||||||
func BenchmarkMatchHard1_32K(b *testing.B) { benchmark(b, hard1, 32<<10) }
|
|
||||||
func BenchmarkMatchHard1_1M(b *testing.B) { benchmark(b, hard1, 1<<20) }
|
|
||||||
func BenchmarkMatchHard1_32M(b *testing.B) { benchmark(b, hard1, 32<<20) }
|
|
||||||
|
|
||||||
// TestProgramTooLongForBacktrack tests that a regex which is too long
|
|
||||||
// for the backtracker still executes properly.
|
|
||||||
func TestProgramTooLongForBacktrack(t *testing.T) {
|
|
||||||
longRegex := MustCompile(`(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty|twentyone|twentytwo|twentythree|twentyfour|twentyfive|twentysix|twentyseven|twentyeight|twentynine|thirty|thirtyone|thirtytwo|thirtythree|thirtyfour|thirtyfive|thirtysix|thirtyseven|thirtyeight|thirtynine|forty|fortyone|fortytwo|fortythree|fortyfour|fortyfive|fortysix|fortyseven|fortyeight|fortynine|fifty|fiftyone|fiftytwo|fiftythree|fiftyfour|fiftyfive|fiftysix|fiftyseven|fiftyeight|fiftynine|sixty|sixtyone|sixtytwo|sixtythree|sixtyfour|sixtyfive|sixtysix|sixtyseven|sixtyeight|sixtynine|seventy|seventyone|seventytwo|seventythree|seventyfour|seventyfive|seventysix|seventyseven|seventyeight|seventynine|eighty|eightyone|eightytwo|eightythree|eightyfour|eightyfive|eightysix|eightyseven|eightyeight|eightynine|ninety|ninetyone|ninetytwo|ninetythree|ninetyfour|ninetyfive|ninetysix|ninetyseven|ninetyeight|ninetynine|onehundred)`, 0)
|
|
||||||
|
|
||||||
if m, err := longRegex.MatchString("two"); !m {
|
|
||||||
t.Errorf("longRegex.MatchString(\"two\") was false, want true")
|
|
||||||
} else if err != nil {
|
|
||||||
t.Errorf("Error: %v", err)
|
|
||||||
}
|
|
||||||
if m, err := longRegex.MatchString("xxx"); m {
|
|
||||||
t.Errorf("longRegex.MatchString(\"xxx\") was true, want false")
|
|
||||||
} else if err != nil {
|
|
||||||
t.Errorf("Error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkLeading(b *testing.B) {
|
|
||||||
b.StopTimer()
|
|
||||||
r := MustCompile("[a-q][^u-z]{13}x", 0)
|
|
||||||
inp := makeText(1000000)
|
|
||||||
b.StartTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := r.MatchRunes(inp); !m {
|
|
||||||
b.Errorf("Expected match")
|
|
||||||
} else if err != nil {
|
|
||||||
b.Errorf("Error: %v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,915 +0,0 @@
|
||||||
package regexp2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"reflect"
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dlclark/regexp2/syntax"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBacktrack_CatastrophicTimeout(t *testing.T) {
|
|
||||||
r, err := Compile("(.+)*\\?", 0)
|
|
||||||
r.MatchTimeout = time.Millisecond * 1
|
|
||||||
t.Logf("code dump: %v", r.code.Dump())
|
|
||||||
m, err := r.FindStringMatch("Do you think you found the problem string!")
|
|
||||||
if err == nil {
|
|
||||||
t.Errorf("expected timeout err")
|
|
||||||
}
|
|
||||||
if m != nil {
|
|
||||||
t.Errorf("Expected no match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetPrefix(t *testing.T) {
|
|
||||||
r := MustCompile(`^\s*-TEST`, 0)
|
|
||||||
if r.code.FcPrefix == nil {
|
|
||||||
t.Fatalf("Expected prefix set [-\\s] but was nil")
|
|
||||||
}
|
|
||||||
if r.code.FcPrefix.PrefixSet.String() != "[-\\s]" {
|
|
||||||
t.Fatalf("Expected prefix set [\\s-] but was %v", r.code.FcPrefix.PrefixSet.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSetInCode(t *testing.T) {
|
|
||||||
r := MustCompile(`(?<body>\s*(?<name>.+))`, 0)
|
|
||||||
t.Logf("code dump: %v", r.code.Dump())
|
|
||||||
if want, got := 1, len(r.code.Sets); want != got {
|
|
||||||
t.Fatalf("r.code.Sets wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
if want, got := "[\\s]", r.code.Sets[0].String(); want != got {
|
|
||||||
t.Fatalf("first set wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRegexp_Basic(t *testing.T) {
|
|
||||||
r, err := Compile("test(?<named>ing)?", 0)
|
|
||||||
//t.Logf("code dump: %v", r.code.Dump())
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected compile err: %v", err)
|
|
||||||
}
|
|
||||||
m, err := r.FindStringMatch("this is a testing stuff")
|
|
||||||
if err != nil {
|
|
||||||
t.Errorf("unexpected match err: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Error("Nil match, expected success")
|
|
||||||
} else {
|
|
||||||
//t.Logf("Match: %v", m.dump())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check all our functions and properties around basic capture groups and referential for Group 0
|
|
||||||
func TestCapture_Basic(t *testing.T) {
|
|
||||||
r := MustCompile(`.*\B(SUCCESS)\B.*`, 0)
|
|
||||||
m, err := r.FindStringMatch("adfadsfSUCCESSadsfadsf")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected match error: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Should have matched")
|
|
||||||
}
|
|
||||||
if want, got := "adfadsfSUCCESSadsfadsf", m.String(); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := 0, m.Index; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := 22, m.Length; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := 1, len(m.Captures); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
if want, got := m.String(), m.Captures[0].String(); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := 0, m.Captures[0].Index; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := 22, m.Captures[0].Length; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
g := m.Groups()
|
|
||||||
if want, got := 2, len(g); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
// group 0 is always the match
|
|
||||||
if want, got := m.String(), g[0].String(); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := 1, len(g[0].Captures); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
// group 0's capture is always the match
|
|
||||||
if want, got := m.Captures[0].String(), g[0].Captures[0].String(); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// group 1 is our first explicit group (unnamed)
|
|
||||||
if want, got := 7, g[1].Index; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := 7, g[1].Length; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := "SUCCESS", g[1].String(); want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEscapeUnescape_Basic(t *testing.T) {
|
|
||||||
s1 := "#$^*+(){}<>\\|. "
|
|
||||||
s2 := Escape(s1)
|
|
||||||
s3, err := Unescape(s2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error during unescape: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
//confirm one way
|
|
||||||
if want, got := `\#\$\^\*\+\(\)\{\}<>\\\|\.\ `, s2; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
//confirm round-trip
|
|
||||||
if want, got := s1, s3; want != got {
|
|
||||||
t.Fatalf("Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestGroups_Basic(t *testing.T) {
|
|
||||||
type d struct {
|
|
||||||
p string
|
|
||||||
s string
|
|
||||||
name []string
|
|
||||||
num []int
|
|
||||||
strs []string
|
|
||||||
}
|
|
||||||
data := []d{
|
|
||||||
d{"(?<first_name>\\S+)\\s(?<last_name>\\S+)", // example
|
|
||||||
"Ryan Byington",
|
|
||||||
[]string{"0", "first_name", "last_name"},
|
|
||||||
[]int{0, 1, 2},
|
|
||||||
[]string{"Ryan Byington", "Ryan", "Byington"}},
|
|
||||||
d{"((?<One>abc)\\d+)?(?<Two>xyz)(.*)", // example
|
|
||||||
"abc208923xyzanqnakl",
|
|
||||||
[]string{"0", "1", "2", "One", "Two"},
|
|
||||||
[]int{0, 1, 2, 3, 4},
|
|
||||||
[]string{"abc208923xyzanqnakl", "abc208923", "anqnakl", "abc", "xyz"}},
|
|
||||||
d{"((?<256>abc)\\d+)?(?<16>xyz)(.*)", // numeric names
|
|
||||||
"0272saasdabc8978xyz][]12_+-",
|
|
||||||
[]string{"0", "1", "2", "16", "256"},
|
|
||||||
[]int{0, 1, 2, 16, 256},
|
|
||||||
[]string{"abc8978xyz][]12_+-", "abc8978", "][]12_+-", "xyz", "abc"}},
|
|
||||||
d{"((?<4>abc)(?<digits>\\d+))?(?<2>xyz)(?<everything_else>.*)", // mix numeric and string names
|
|
||||||
"0272saasdabc8978xyz][]12_+-",
|
|
||||||
[]string{"0", "1", "2", "digits", "4", "everything_else"},
|
|
||||||
[]int{0, 1, 2, 3, 4, 5},
|
|
||||||
[]string{"abc8978xyz][]12_+-", "abc8978", "xyz", "8978", "abc", "][]12_+-"}},
|
|
||||||
d{"(?<first_name>\\S+)\\s(?<first_name>\\S+)", // dupe string names
|
|
||||||
"Ryan Byington",
|
|
||||||
[]string{"0", "first_name"},
|
|
||||||
[]int{0, 1},
|
|
||||||
[]string{"Ryan Byington", "Byington"}},
|
|
||||||
d{"(?<15>\\S+)\\s(?<15>\\S+)", // dupe numeric names
|
|
||||||
"Ryan Byington",
|
|
||||||
[]string{"0", "15"},
|
|
||||||
[]int{0, 15},
|
|
||||||
[]string{"Ryan Byington", "Byington"}},
|
|
||||||
// *** repeated from above, but with alt cap syntax ***
|
|
||||||
d{"(?'first_name'\\S+)\\s(?'last_name'\\S+)", //example
|
|
||||||
"Ryan Byington",
|
|
||||||
[]string{"0", "first_name", "last_name"},
|
|
||||||
[]int{0, 1, 2},
|
|
||||||
[]string{"Ryan Byington", "Ryan", "Byington"}},
|
|
||||||
d{"((?'One'abc)\\d+)?(?'Two'xyz)(.*)", // example
|
|
||||||
"abc208923xyzanqnakl",
|
|
||||||
[]string{"0", "1", "2", "One", "Two"},
|
|
||||||
[]int{0, 1, 2, 3, 4},
|
|
||||||
[]string{"abc208923xyzanqnakl", "abc208923", "anqnakl", "abc", "xyz"}},
|
|
||||||
d{"((?'256'abc)\\d+)?(?'16'xyz)(.*)", // numeric names
|
|
||||||
"0272saasdabc8978xyz][]12_+-",
|
|
||||||
[]string{"0", "1", "2", "16", "256"},
|
|
||||||
[]int{0, 1, 2, 16, 256},
|
|
||||||
[]string{"abc8978xyz][]12_+-", "abc8978", "][]12_+-", "xyz", "abc"}},
|
|
||||||
d{"((?'4'abc)(?'digits'\\d+))?(?'2'xyz)(?'everything_else'.*)", // mix numeric and string names
|
|
||||||
"0272saasdabc8978xyz][]12_+-",
|
|
||||||
[]string{"0", "1", "2", "digits", "4", "everything_else"},
|
|
||||||
[]int{0, 1, 2, 3, 4, 5},
|
|
||||||
[]string{"abc8978xyz][]12_+-", "abc8978", "xyz", "8978", "abc", "][]12_+-"}},
|
|
||||||
d{"(?'first_name'\\S+)\\s(?'first_name'\\S+)", // dupe string names
|
|
||||||
"Ryan Byington",
|
|
||||||
[]string{"0", "first_name"},
|
|
||||||
[]int{0, 1},
|
|
||||||
[]string{"Ryan Byington", "Byington"}},
|
|
||||||
d{"(?'15'\\S+)\\s(?'15'\\S+)", // dupe numeric names
|
|
||||||
"Ryan Byington",
|
|
||||||
[]string{"0", "15"},
|
|
||||||
[]int{0, 15},
|
|
||||||
[]string{"Ryan Byington", "Byington"}},
|
|
||||||
}
|
|
||||||
|
|
||||||
fatalf := func(re *Regexp, v d, format string, args ...interface{}) {
|
|
||||||
args = append(args, v, re.code.Dump())
|
|
||||||
|
|
||||||
t.Fatalf(format+" using test data: %#v\ndump:%v", args...)
|
|
||||||
}
|
|
||||||
|
|
||||||
validateGroupNamesNumbers := func(re *Regexp, v d) {
|
|
||||||
if len(v.name) != len(v.num) {
|
|
||||||
fatalf(re, v, "Invalid data, group name count and number count must match")
|
|
||||||
}
|
|
||||||
|
|
||||||
groupNames := re.GetGroupNames()
|
|
||||||
if !reflect.DeepEqual(groupNames, v.name) {
|
|
||||||
fatalf(re, v, "group names expected: %v, actual: %v", v.name, groupNames)
|
|
||||||
}
|
|
||||||
groupNums := re.GetGroupNumbers()
|
|
||||||
if !reflect.DeepEqual(groupNums, v.num) {
|
|
||||||
fatalf(re, v, "group numbers expected: %v, actual: %v", v.num, groupNums)
|
|
||||||
}
|
|
||||||
// make sure we can freely get names and numbers from eachother
|
|
||||||
for i := range groupNums {
|
|
||||||
if want, got := groupNums[i], re.GroupNumberFromName(groupNames[i]); want != got {
|
|
||||||
fatalf(re, v, "group num from name Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
if want, got := groupNames[i], re.GroupNameFromNumber(groupNums[i]); want != got {
|
|
||||||
fatalf(re, v, "group name from num Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range data {
|
|
||||||
// compile the regex
|
|
||||||
re := MustCompile(v.p, 0)
|
|
||||||
|
|
||||||
// validate our group name/num info before execute
|
|
||||||
validateGroupNamesNumbers(re, v)
|
|
||||||
|
|
||||||
m, err := re.FindStringMatch(v.s)
|
|
||||||
if err != nil {
|
|
||||||
fatalf(re, v, "Unexpected error in match: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
fatalf(re, v, "Match is nil")
|
|
||||||
}
|
|
||||||
if want, got := len(v.strs), m.GroupCount(); want != got {
|
|
||||||
fatalf(re, v, "GroupCount() Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
g := m.Groups()
|
|
||||||
if want, got := len(v.strs), len(g); want != got {
|
|
||||||
fatalf(re, v, "len(m.Groups()) Wanted '%v'\nGot '%v'", want, got)
|
|
||||||
}
|
|
||||||
// validate each group's value from the execute
|
|
||||||
for i := range v.name {
|
|
||||||
grp1 := m.GroupByName(v.name[i])
|
|
||||||
grp2 := m.GroupByNumber(v.num[i])
|
|
||||||
// should be identical reference
|
|
||||||
if grp1 != grp2 {
|
|
||||||
fatalf(re, v, "Expected GroupByName and GroupByNumber to return same result for %v, %v", v.name[i], v.num[i])
|
|
||||||
}
|
|
||||||
if want, got := v.strs[i], grp1.String(); want != got {
|
|
||||||
fatalf(re, v, "Value[%v] Wanted '%v'\nGot '%v'", i, want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// validate our group name/num info after execute
|
|
||||||
validateGroupNamesNumbers(re, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErr_GroupName(t *testing.T) {
|
|
||||||
// group 0 is off limits
|
|
||||||
if _, err := Compile("foo(?<0>bar)", 0); err == nil {
|
|
||||||
t.Fatalf("zero group, expected error during compile")
|
|
||||||
} else if want, got := "error parsing regexp: capture number cannot be zero in `foo(?<0>bar)`", err.Error(); want != got {
|
|
||||||
t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
if _, err := Compile("foo(?'0'bar)", 0); err == nil {
|
|
||||||
t.Fatalf("zero group, expected error during compile")
|
|
||||||
} else if want, got := "error parsing regexp: capture number cannot be zero in `foo(?'0'bar)`", err.Error(); want != got {
|
|
||||||
t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// group tag can't start with a num
|
|
||||||
if _, err := Compile("foo(?<1bar>)", 0); err == nil {
|
|
||||||
t.Fatalf("invalid group name, expected error during compile")
|
|
||||||
} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?<1bar>)`", err.Error(); want != got {
|
|
||||||
t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
if _, err := Compile("foo(?'1bar')", 0); err == nil {
|
|
||||||
t.Fatalf("invalid group name, expected error during compile")
|
|
||||||
} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?'1bar')`", err.Error(); want != got {
|
|
||||||
t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// missing closing group tag
|
|
||||||
if _, err := Compile("foo(?<bar)", 0); err == nil {
|
|
||||||
t.Fatalf("invalid group name, expected error during compile")
|
|
||||||
} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?<bar)`", err.Error(); want != got {
|
|
||||||
t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
if _, err := Compile("foo(?'bar)", 0); err == nil {
|
|
||||||
t.Fatalf("invalid group name, expected error during compile")
|
|
||||||
} else if want, got := "error parsing regexp: invalid group name: group names must begin with a word character and have a matching terminator in `foo(?'bar)`", err.Error(); want != got {
|
|
||||||
t.Fatalf("invalid error text, want '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConstantUneffected(t *testing.T) {
|
|
||||||
// had a bug where "constant" sets would get modified with alternations and be broken in memory until restart
|
|
||||||
// this meant that if you used a known-set (like \s) in a larger set it would "poison" \s for the process
|
|
||||||
re := MustCompile(`(\s|\*)test\s`, 0)
|
|
||||||
if want, got := 2, len(re.code.Sets); want != got {
|
|
||||||
t.Fatalf("wanted %v sets, got %v", want, got)
|
|
||||||
}
|
|
||||||
if want, got := "[\\*\\s]", re.code.Sets[0].String(); want != got {
|
|
||||||
t.Fatalf("wanted set 0 %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
if want, got := "[\\s]", re.code.Sets[1].String(); want != got {
|
|
||||||
t.Fatalf("wanted set 1 %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAlternationConstAndEscape(t *testing.T) {
|
|
||||||
re := MustCompile(`\:|\s`, 0)
|
|
||||||
if want, got := 1, len(re.code.Sets); want != got {
|
|
||||||
t.Fatalf("wanted %v sets, got %v", want, got)
|
|
||||||
}
|
|
||||||
if want, got := "[:\\s]", re.code.Sets[0].String(); want != got {
|
|
||||||
t.Fatalf("wanted set 0 %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStartingCharsOptionalNegate(t *testing.T) {
|
|
||||||
// to maintain matching with the corefx we've made the negative char classes be negative and the
|
|
||||||
// categories they contain positive. This means they're not combinable or suitable for prefixes.
|
|
||||||
// In general this could be a fine thing since negatives are extremely wide groups and not
|
|
||||||
// missing much on prefix optimizations.
|
|
||||||
|
|
||||||
// the below expression *could* have a prefix of [\S\d] but
|
|
||||||
// this requires a change in charclass.go when setting
|
|
||||||
// NotSpaceClass = getCharSetFromCategoryString()
|
|
||||||
// to negate the individual categories rather than the CharSet itself
|
|
||||||
// this would deviate from corefx
|
|
||||||
|
|
||||||
re := MustCompile(`(^(\S{2} )?\S{2}(\d+|/) *\S{3}\S{3} ?\d{2,4}[A-Z] ?\d{2}[A-Z]{3}|(\S{2} )?\d{2,4})`, 0)
|
|
||||||
if re.code.FcPrefix != nil {
|
|
||||||
t.Fatalf("FcPrefix wanted nil, got %v", re.code.FcPrefix)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParseNegativeDigit(t *testing.T) {
|
|
||||||
re := MustCompile(`\D`, 0)
|
|
||||||
if want, got := 1, len(re.code.Sets); want != got {
|
|
||||||
t.Fatalf("wanted %v sets, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
if want, got := "[\\P{Nd}]", re.code.Sets[0].String(); want != got {
|
|
||||||
t.Fatalf("wanted set 0 %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRunNegativeDigit(t *testing.T) {
|
|
||||||
re := MustCompile(`\D`, 0)
|
|
||||||
m, err := re.MatchString("this is a test")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCancellingClasses(t *testing.T) {
|
|
||||||
// [\w\W\s] should become "." because it means "anything"
|
|
||||||
re := MustCompile(`[\w\W\s]`, 0)
|
|
||||||
if want, got := 1, len(re.code.Sets); want != got {
|
|
||||||
t.Fatalf("wanted %v sets, got %v", want, got)
|
|
||||||
}
|
|
||||||
if want, got := syntax.AnyClass().String(), re.code.Sets[0].String(); want != got {
|
|
||||||
t.Fatalf("wanted set 0 %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestConcatLoopCaptureSet(t *testing.T) {
|
|
||||||
//(A|B)*?CD different Concat/Loop/Capture/Set (had [A-Z] should be [AB])
|
|
||||||
// we were not copying the Sets in the prefix FC stack, so the underlying sets were unexpectedly mutating
|
|
||||||
// so set [AB] becomes [ABC] when we see the the static C in FC stack generation (which are the valid start chars),
|
|
||||||
// but that was mutating the tree node's original set [AB] because even though we copied the slie header,
|
|
||||||
// the two header's pointed to the same underlying byte array...which was mutated.
|
|
||||||
|
|
||||||
re := MustCompile(`(A|B)*CD`, 0)
|
|
||||||
if want, got := 1, len(re.code.Sets); want != got {
|
|
||||||
t.Fatalf("wanted %v sets, got %v", want, got)
|
|
||||||
}
|
|
||||||
if want, got := "[AB]", re.code.Sets[0].String(); want != got {
|
|
||||||
t.Fatalf("wanted set 0 %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFirstcharsIgnoreCase(t *testing.T) {
|
|
||||||
//((?i)AB(?-i)C|D)E different Firstchars (had [da] should be [ad])
|
|
||||||
// we were not canonicalizing when converting the prefix set to lower case
|
|
||||||
// so our set's were potentially not searching properly
|
|
||||||
re := MustCompile(`((?i)AB(?-i)C|D)E`, 0)
|
|
||||||
|
|
||||||
if re.code.FcPrefix == nil {
|
|
||||||
t.Fatalf("wanted prefix, got nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if want, got := "[ad]", re.code.FcPrefix.PrefixSet.String(); want != got {
|
|
||||||
t.Fatalf("wanted prefix %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRepeatingGroup(t *testing.T) {
|
|
||||||
re := MustCompile(`(data?)+`, 0)
|
|
||||||
|
|
||||||
m, err := re.FindStringMatch("datadat")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
g := m.GroupByNumber(1)
|
|
||||||
if g == nil {
|
|
||||||
t.Fatalf("Expected group")
|
|
||||||
}
|
|
||||||
|
|
||||||
if want, got := 2, len(g.Captures); want != got {
|
|
||||||
t.Fatalf("wanted cap count %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
if want, got := g.Captures[1].String(), g.Capture.String(); want != got {
|
|
||||||
t.Fatalf("expected last capture of the group to be embedded")
|
|
||||||
}
|
|
||||||
|
|
||||||
if want, got := "data", g.Captures[0].String(); want != got {
|
|
||||||
t.Fatalf("expected cap 0 to be %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
if want, got := "dat", g.Captures[1].String(); want != got {
|
|
||||||
t.Fatalf("expected cap 1 to be %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestFindNextMatch_Basic(t *testing.T) {
|
|
||||||
re := MustCompile(`(T|E)(?=h|E|S|$)`, 0)
|
|
||||||
m, err := re.FindStringMatch(`This is a TEST`)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err 0: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Expected match 0")
|
|
||||||
}
|
|
||||||
if want, got := 0, m.Index; want != got {
|
|
||||||
t.Fatalf("expected match 0 to start at %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err = re.FindNextMatch(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err 1: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Expected match 1")
|
|
||||||
}
|
|
||||||
if want, got := 10, m.Index; want != got {
|
|
||||||
t.Fatalf("expected match 1 to start at %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err = re.FindNextMatch(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err 2: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Expected match 2")
|
|
||||||
}
|
|
||||||
if want, got := 11, m.Index; want != got {
|
|
||||||
t.Fatalf("expected match 2 to start at %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err = re.FindNextMatch(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err 3: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Expected match 3")
|
|
||||||
}
|
|
||||||
if want, got := 13, m.Index; want != got {
|
|
||||||
t.Fatalf("expected match 3 to start at %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnicodeSupplementaryCharSetMatch(t *testing.T) {
|
|
||||||
//0x2070E 0x20731 𠜱 0x20779 𠝹
|
|
||||||
re := MustCompile("[𠜎-𠝹]", 0)
|
|
||||||
|
|
||||||
if m, err := re.MatchString("\u2070"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if m {
|
|
||||||
t.Fatalf("Unexpected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m, err := re.MatchString("𠜱"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnicodeSupplementaryCharInRange(t *testing.T) {
|
|
||||||
//0x2070E 0x20731 𠜱 0x20779 𠝹
|
|
||||||
re := MustCompile(".", 0)
|
|
||||||
|
|
||||||
if m, err := re.MatchString("\u2070"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
if m, err := re.MatchString("𠜱"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUnicodeScriptSets(t *testing.T) {
|
|
||||||
re := MustCompile(`\p{Katakana}+`, 0)
|
|
||||||
if m, err := re.MatchString("\u30A0\u30FF"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHexadecimalCurlyBraces(t *testing.T) {
|
|
||||||
re := MustCompile(`\x20`, 0)
|
|
||||||
if m, err := re.MatchString(" "); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
re = MustCompile(`\x{C4}`, 0)
|
|
||||||
if m, err := re.MatchString("Ä"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
re = MustCompile(`\x{0C5}`, 0)
|
|
||||||
if m, err := re.MatchString("Å"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
re = MustCompile(`\x{00C6}`, 0)
|
|
||||||
if m, err := re.MatchString("Æ"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
re = MustCompile(`\x{1FF}`, 0)
|
|
||||||
if m, err := re.MatchString("ǿ"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
re = MustCompile(`\x{02FF}`, 0)
|
|
||||||
if m, err := re.MatchString("˿"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
re = MustCompile(`\x{1392}`, 0)
|
|
||||||
if m, err := re.MatchString("᎒"); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
re = MustCompile(`\x{0010ffff}`, 0)
|
|
||||||
if m, err := re.MatchString(string(0x10ffff)); err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := Compile(`\x2R`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x0`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{2`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{2R`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{2R}`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{}`, 0); err == nil {
|
|
||||||
t.Fatalf("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{10000`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{1234`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
if _, err := Compile(`\x{123456789}`, 0); err == nil {
|
|
||||||
t.Fatal("Expected error")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestEmptyCharClass(t *testing.T) {
|
|
||||||
if _, err := Compile("[]", 0); err == nil {
|
|
||||||
t.Fatal("Empty char class isn't valid outside of ECMAScript mode")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestECMAEmptyCharClass(t *testing.T) {
|
|
||||||
re := MustCompile("[]", ECMAScript)
|
|
||||||
if m, err := re.MatchString("a"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if m {
|
|
||||||
t.Fatal("Expected no match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDot(t *testing.T) {
|
|
||||||
re := MustCompile(".", 0)
|
|
||||||
if m, err := re.MatchString("\r"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatal("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestECMADot(t *testing.T) {
|
|
||||||
re := MustCompile(".", ECMAScript)
|
|
||||||
if m, err := re.MatchString("\r"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if m {
|
|
||||||
t.Fatal("Expected no match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDecimalLookahead(t *testing.T) {
|
|
||||||
re := MustCompile(`\1(A)`, 0)
|
|
||||||
m, err := re.FindStringMatch("AA")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if m != nil {
|
|
||||||
t.Fatal("Expected no match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestECMADecimalLookahead(t *testing.T) {
|
|
||||||
re := MustCompile(`\1(A)`, ECMAScript)
|
|
||||||
m, err := re.FindStringMatch("AA")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if c := m.GroupCount(); c != 2 {
|
|
||||||
t.Fatalf("Group count !=2 (%d)", c)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s := m.GroupByNumber(0).String(); s != "A" {
|
|
||||||
t.Fatalf("Group0 != 'A' ('%s')", s)
|
|
||||||
}
|
|
||||||
|
|
||||||
if s := m.GroupByNumber(1).String(); s != "A" {
|
|
||||||
t.Fatalf("Group1 != 'A' ('%s')", s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestECMAOctal(t *testing.T) {
|
|
||||||
re := MustCompile(`\100`, ECMAScript)
|
|
||||||
if m, err := re.MatchString("@"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatal("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNegateRange(t *testing.T) {
|
|
||||||
re := MustCompile(`[\D]`, 0)
|
|
||||||
if m, err := re.MatchString("A"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatal("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestECMANegateRange(t *testing.T) {
|
|
||||||
re := MustCompile(`[\D]`, ECMAScript)
|
|
||||||
if m, err := re.MatchString("A"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatal("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestThreeByteUnicode_InputOnly(t *testing.T) {
|
|
||||||
// confirm the bmprefix properly ignores 3-byte unicode in the input value
|
|
||||||
// this used to panic
|
|
||||||
re := MustCompile("高", 0)
|
|
||||||
if m, err := re.MatchString("📍Test高"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatal("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultibyteUnicode_MatchPartialPattern(t *testing.T) {
|
|
||||||
re := MustCompile("猟な", 0)
|
|
||||||
if m, err := re.MatchString("なあ🍺な"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if m {
|
|
||||||
t.Fatal("Expected no match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMultibyteUnicode_Match(t *testing.T) {
|
|
||||||
re := MustCompile("猟な", 0)
|
|
||||||
if m, err := re.MatchString("なあ🍺猟な"); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
} else if !m {
|
|
||||||
t.Fatal("Expected match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAlternationNamedOptions_Errors(t *testing.T) {
|
|
||||||
// all of these should give an error "error parsing regexp:"
|
|
||||||
data := []string{
|
|
||||||
"(?(?e))", "(?(?a)", "(?(?", "(?(", "?(a:b)", "?(a)", "?(a|b)", "?((a)", "?((a)a", "?((a)a|", "?((a)a|b",
|
|
||||||
"(?(?i))", "(?(?I))", "(?(?m))", "(?(?M))", "(?(?s))", "(?(?S))", "(?(?x))", "(?(?X))", "(?(?n))", "(?(?N))", " (?(?n))",
|
|
||||||
}
|
|
||||||
for _, p := range data {
|
|
||||||
re, err := Compile(p, 0)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatal("Expected error, got nil")
|
|
||||||
}
|
|
||||||
if re != nil {
|
|
||||||
t.Fatal("Expected unparsed regexp, got non-nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if !strings.HasPrefix(err.Error(), "error parsing regexp: ") {
|
|
||||||
t.Fatalf("Wanted parse error, got '%v'", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAlternationNamedOptions_Success(t *testing.T) {
|
|
||||||
data := []struct {
|
|
||||||
pattern string
|
|
||||||
input string
|
|
||||||
expectSuccess bool
|
|
||||||
matchVal string
|
|
||||||
}{
|
|
||||||
{"(?(cat)|dog)", "cat", true, ""},
|
|
||||||
{"(?(cat)|dog)", "catdog", true, ""},
|
|
||||||
{"(?(cat)dog1|dog2)", "catdog1", false, ""},
|
|
||||||
{"(?(cat)dog1|dog2)", "catdog2", true, "dog2"},
|
|
||||||
{"(?(cat)dog1|dog2)", "catdog1dog2", true, "dog2"},
|
|
||||||
{"(?(dog2))", "dog2", true, ""},
|
|
||||||
{"(?(cat)|dog)", "oof", false, ""},
|
|
||||||
{"(?(a:b))", "a", true, ""},
|
|
||||||
{"(?(a:))", "a", true, ""},
|
|
||||||
}
|
|
||||||
for _, p := range data {
|
|
||||||
re := MustCompile(p.pattern, 0)
|
|
||||||
m, err := re.FindStringMatch(p.input)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error during match: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := p.expectSuccess, m != nil; want != got {
|
|
||||||
t.Fatalf("Success mismatch for %v, wanted %v, got %v", p.pattern, want, got)
|
|
||||||
}
|
|
||||||
if m != nil {
|
|
||||||
if want, got := p.matchVal, m.String(); want != got {
|
|
||||||
t.Fatalf("Match val mismatch for %v, wanted %v, got %v", p.pattern, want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestAlternationConstruct_Matches(t *testing.T) {
|
|
||||||
re := MustCompile("(?(A)A123|C789)", 0)
|
|
||||||
m, err := re.FindStringMatch("A123 B456 C789")
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatal("Expected match, got nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
if want, got := "A123", m.String(); want != got {
|
|
||||||
t.Fatalf("Wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err = re.FindNextMatch(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err in second match: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatal("Expected second match, got nil")
|
|
||||||
}
|
|
||||||
if want, got := "C789", m.String(); want != got {
|
|
||||||
t.Fatalf("Wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err = re.FindNextMatch(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err in third match: %v", err)
|
|
||||||
}
|
|
||||||
if m != nil {
|
|
||||||
t.Fatal("Did not expect third match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserFuzzCrashes(t *testing.T) {
|
|
||||||
var crashes = []string{
|
|
||||||
"(?'-", "(\\c0)", "(\\00(?())", "[\\p{0}", "(\x00?.*.()?(()?)?)*.x\xcb?&(\\s\x80)", "\\p{0}", "[0-[\\p{0}",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range crashes {
|
|
||||||
t.Log(c)
|
|
||||||
Compile(c, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestParserFuzzHangs(t *testing.T) {
|
|
||||||
var hangs = []string{
|
|
||||||
"\r{865720113}z\xd5{\r{861o", "\r{915355}\r{9153}", "\r{525005}", "\x01{19765625}", "(\r{068828256})", "\r{677525005}",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range hangs {
|
|
||||||
t.Log(c)
|
|
||||||
Compile(c, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func BenchmarkParserPrefixLongLen(b *testing.B) {
|
|
||||||
re := MustCompile("\r{100001}T+", 0)
|
|
||||||
inp := strings.Repeat("testing", 10000) + strings.Repeat("\r", 100000) + "TTTT"
|
|
||||||
|
|
||||||
b.ResetTimer()
|
|
||||||
for i := 0; i < b.N; i++ {
|
|
||||||
if m, err := re.MatchString(inp); err != nil {
|
|
||||||
b.Fatalf("Unexpected err: %v", err)
|
|
||||||
} else if m {
|
|
||||||
b.Fatalf("Expected no match")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
func TestPcreStuff(t *testing.T) {
|
|
||||||
re := MustCompile(`(?(?=(a))a)`, Debug)
|
|
||||||
inp := unEscapeToMatch(`a`)
|
|
||||||
fmt.Printf("Inp %q\n", inp)
|
|
||||||
m, err := re.FindStringMatch(inp)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected error: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("Match %s\n", m.dump())
|
|
||||||
fmt.Printf("Text: %v\n", unEscapeGroup(m.String()))
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
//(.*)(\d+) different FirstChars ([\x00-\t\v-\x08] OR [\x00-\t\v-\uffff\p{Nd}]
|
|
|
@ -1,172 +0,0 @@
|
||||||
package regexp2
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strconv"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestReplace_Basic(t *testing.T) {
|
|
||||||
re := MustCompile(`test`, 0)
|
|
||||||
str, err := re.Replace("this is a test", "unit", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "this is a unit", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_NamedGroup(t *testing.T) {
|
|
||||||
re := MustCompile(`[^ ]+\s(?<time>)`, 0)
|
|
||||||
str, err := re.Replace("08/10/99 16:00", "${time}", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "16:00", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_IgnoreCaseUpper(t *testing.T) {
|
|
||||||
re := MustCompile(`dog`, IgnoreCase)
|
|
||||||
str, err := re.Replace("my dog has fleas", "CAT", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "my CAT has fleas", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_IgnoreCaseLower(t *testing.T) {
|
|
||||||
re := MustCompile(`olang`, IgnoreCase)
|
|
||||||
str, err := re.Replace("GoLAnG", "olang", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "Golang", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_NumberGroup(t *testing.T) {
|
|
||||||
re := MustCompile(`D\.(.+)`, None)
|
|
||||||
str, err := re.Replace("D.Bau", "David $1", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "David Bau", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_LimitCount(t *testing.T) {
|
|
||||||
re := MustCompile(`a`, None)
|
|
||||||
str, err := re.Replace("aaaaa", "b", 0, 2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "bbaaa", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_LimitCountSlice(t *testing.T) {
|
|
||||||
re := MustCompile(`a`, None)
|
|
||||||
myStr := "aaaaa"
|
|
||||||
str, err := re.Replace(myStr, "b", 3, 2)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "aaabb", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_BeginBeforeAfterEnd(t *testing.T) {
|
|
||||||
re := MustCompile(`a`, None)
|
|
||||||
myStr := "a test a blah and a"
|
|
||||||
str, err := re.Replace(myStr, "stuff", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "stuff test stuff blstuffh stuffnd stuff", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_BadSyntax(t *testing.T) {
|
|
||||||
re := MustCompile(`a`, None)
|
|
||||||
myStr := "this is a test"
|
|
||||||
_, err := re.Replace(myStr, `$5000000000`, -1, -1)
|
|
||||||
if err == nil {
|
|
||||||
t.Fatalf("Expected err")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplaceFunc_Basic(t *testing.T) {
|
|
||||||
re := MustCompile(`test`, None)
|
|
||||||
str, err := re.ReplaceFunc("this is a test", func(m Match) string { return "unit" }, -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "this is a unit", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplaceFunc_Multiple(t *testing.T) {
|
|
||||||
re := MustCompile(`test`, None)
|
|
||||||
count := 0
|
|
||||||
str, err := re.ReplaceFunc("This test is another test for stuff", func(m Match) string {
|
|
||||||
count++
|
|
||||||
return strconv.Itoa(count)
|
|
||||||
}, -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "This 1 is another 2 for stuff", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplaceFunc_Groups(t *testing.T) {
|
|
||||||
re := MustCompile(`test(?<sub>ing)?`, None)
|
|
||||||
count := 0
|
|
||||||
str, err := re.ReplaceFunc("This testing is another test testingly junk", func(m Match) string {
|
|
||||||
count++
|
|
||||||
if m.GroupByName("sub").Length > 0 {
|
|
||||||
// we have an "ing", make it negative
|
|
||||||
return strconv.Itoa(count * -1)
|
|
||||||
}
|
|
||||||
return strconv.Itoa(count)
|
|
||||||
}, -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "This -1 is another 2 -3ly junk", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_RefNumsDollarAmbiguous(t *testing.T) {
|
|
||||||
re := MustCompile("(123)hello(789)", None)
|
|
||||||
res, err := re.Replace("123hello789", "$1456$2", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if want, got := "$1456789", res; want != got {
|
|
||||||
t.Fatalf("Wrong result: %s", got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestReplace_NestedGroups(t *testing.T) {
|
|
||||||
re := MustCompile(`(\p{Sc}\s?)?(\d+\.?((?<=\.)\d+)?)(?(1)|\s?\p{Sc})?`, None)
|
|
||||||
res, err := re.Replace("$17.43 €2 16.33 £0.98 0.43 £43 12€ 17", "$2", -1, -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if want, got := "17.43 2 16.33 0.98 0.43 43 12 17", res; want != got {
|
|
||||||
t.Fatalf("Wrong result: %s", got)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
package regexp2
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestRightToLeft_Basic(t *testing.T) {
|
|
||||||
re := MustCompile(`foo\d+`, RightToLeft)
|
|
||||||
s := "0123456789foo4567890foo1foo 0987"
|
|
||||||
|
|
||||||
m, err := re.FindStringMatch(s)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "foo1", m.String(); want != got {
|
|
||||||
t.Fatalf("Match 0 failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err = re.FindNextMatch(m)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "foo4567890", m.String(); want != got {
|
|
||||||
t.Fatalf("Match 1 failed, wanted %v, got %v", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRightToLeft_StartAt(t *testing.T) {
|
|
||||||
re := MustCompile(`\d`, RightToLeft)
|
|
||||||
|
|
||||||
m, err := re.FindStringMatchStartingAt("0123", -1)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if m == nil {
|
|
||||||
t.Fatalf("Expected match")
|
|
||||||
}
|
|
||||||
if want, got := "3", m.String(); want != got {
|
|
||||||
t.Fatalf("Find failed, wanted '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestRightToLeft_Replace(t *testing.T) {
|
|
||||||
re := MustCompile(`\d`, RightToLeft)
|
|
||||||
s := "0123456789foo4567890foo "
|
|
||||||
str, err := re.Replace(s, "#", -1, 7)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Unexpected err: %v", err)
|
|
||||||
}
|
|
||||||
if want, got := "0123456789foo#######foo ", str; want != got {
|
|
||||||
t.Fatalf("Replace failed, wanted '%v', got '%v'", want, got)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
[?.5x1fACc8E3'
|
|
|
@ -1 +0,0 @@
|
||||||
\b\b\b\b\b\b\b\b\b
|
|
|
@ -1 +0,0 @@
|
||||||
(?#)(?#)](?#)]?#)
|
|
|
@ -1 +0,0 @@
|
||||||
\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\11(\1
|
|
|
@ -1 +0,0 @@
|
||||||
(?-)(?-)
|
|
|
@ -1 +0,0 @@
|
||||||
(?-----------------
|
|
|
@ -1 +0,0 @@
|
||||||
((?'256'abc)\d+)?(?'16')(.*)
|
|
|
@ -1 +0,0 @@
|
||||||
((((((){2147483647}((){2147483647}(){2147483647})){2147483647}))))
|
|
|
@ -1 +0,0 @@
|
||||||
[\b\b\b\b\b\b
|
|
|
@ -1 +0,0 @@
|
||||||
\D\D\D\D
|
|
|
@ -1 +0,0 @@
|
||||||
(?I)іііііΉііΉіΉ
|
|
|
@ -1 +0,0 @@
|
||||||
(){6,1}
|
|
|
@ -1,2 +0,0 @@
|
||||||
((')'()'()'(')'()
|
|
||||||
)
|
|
|
@ -1 +0,0 @@
|
||||||
((){976})
|
|
|
@ -1 +0,0 @@
|
||||||
[ケ-ケ-[[-ケ-[ケ]]][ケ-ケ-[[-ケ-[ケ]]]
|
|
|
@ -1 +0,0 @@
|
||||||
\8090820312
|
|
|
@ -1 +0,0 @@
|
||||||
(?=)((?=)(?=)(?=)(?=)(?=)(?=)(?=))(?=)(?=)(?=)(?=)(?=)(?=)(?=)(?=)(?=)(?=)
|
|
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
[cA2sx5fl7Uv_10)][cA2sx5fl7Uv_10]
|
|
|
@ -1 +0,0 @@
|
||||||
("?e*"?e*
|
|
|
@ -1 +0,0 @@
|
||||||
((()?)?)?(()?)?(()?)?(((()?)?)?)?(()?)?(((()?)?((()?)?)?(((()?)?)?(()?)?)?)?)?(()?)?((((()?)?)?)?)?
|
|
|
@ -1 +0,0 @@
|
||||||
[\w\W]?]
|
|
|
@ -1 +0,0 @@
|
||||||
(\6
|
|
|
@ -1 +0,0 @@
|
||||||
(?'𠜎𠜎𠹝𠹝
|
|
|
@ -1 +0,0 @@
|
||||||
(A|9)(A|9)(A|A||A|9)(A|9)(A|A||A(A|9)(A|A||A|9)(A|{Î)(A|A||A|9)|9)
|
|
|
@ -1 +0,0 @@
|
||||||
[\t
|
|
|
@ -1 +0,0 @@
|
||||||
((?'256'bc)\d+)?(?'16')(.)
|
|
|
@ -1 +0,0 @@
|
||||||
{'{(
|
|
|
@ -1 +0,0 @@
|
||||||
(?'-U'(?'-U'(?'-U'(?'-U'(?'U
|
|
|
@ -1 +0,0 @@
|
||||||
(?'03
|
|
|
@ -1 +0,0 @@
|
||||||
['-Q'-?'-Q'-?-''-Q'-?-n\n-''-/'-6-''-Q'-?-n\n-''-/'-6
|
|
|
@ -1 +0,0 @@
|
||||||
[\u8333\u8f3a\u8f3a\u833a\u833a\u833a\u833a\u833a\u8f3a\u8333\u833a\u8f33
|
|
|
@ -1 +0,0 @@
|
||||||
(?'U-6'(?'U-6'(?'U-6'(?'U-6'(?'6'(?'U-
|
|
|
@ -1 +0,0 @@
|
||||||
(?n)()()(()(()()))()((())
|
|
|
@ -1 +0,0 @@
|
||||||
(?I)[[-Ƹ][[-Ƹ][[-Ƹ]+[[-Ƹ]+[[-Ƹ][[-Ƹ]+
|
|
|
@ -1 +0,0 @@
|
||||||
(?n)((@$)(@$))
|
|
|
@ -1 +0,0 @@
|
||||||
$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
|
|
|
@ -1 +0,0 @@
|
||||||
(?)(?)(?)
|
|
|
@ -1 +0,0 @@
|
||||||
(?I)(A9A7450580596923828125)
|
|
|
@ -1 +0,0 @@
|
||||||
(?I)(.*\3826658AA)
|
|
Binary file not shown.
|
@ -1 +0,0 @@
|
||||||
((8((((((((((((((9(((((((((((((((((((((?'251(((((((((
|
|
|
@ -1 +0,0 @@
|
||||||
\A\A\A\A\A\A\A(\A\A\A\A
|
|
|
@ -1 +0,0 @@
|
||||||
[<5B>-<2D>-[<5B>-<2D>-[<5B>]]<5D>
|
|
|
@ -1 +0,0 @@
|
||||||
(?#))(?#))(?#)((?#))(?#))(?#
|
|
|
@ -1 +0,0 @@
|
||||||
(?!(?!(?k(?!(?!(?!
|
|
|
@ -1 +0,0 @@
|
||||||
(c?]?]??]??`?]?`?]??]??`?]?`?]?)
|
|
|
@ -1 +0,0 @@
|
||||||
(?=)
|
|
|
@ -1 +0,0 @@
|
||||||
(?(?<=(?(?<=(?(?<=(?(?<=
|
|
|
@ -1 +0,0 @@
|
||||||
(?x
|
|
|
@ -1 +0,0 @@
|
||||||
[+](?#)([+](?#)
|
|
|
@ -1 +0,0 @@
|
||||||
((?'6')+)?(?'6'.)
|
|
|
@ -1 +0,0 @@
|
||||||
[\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\pp\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p}\p\p
|
|
|
@ -1 +0,0 @@
|
||||||
(\16(.)
|
|
|
@ -1 +0,0 @@
|
||||||
(?I)'''''invalid group name: group names must begin with a word character and have a matching terminator'
|
|
|
@ -1 +0,0 @@
|
||||||
(?I)[RLOKQNGAXBWH][RLOKQNGAXBWH][RLOKQNGAXBWH][RLOKQNGAXBWH][RLOKQNGAXBWH][LOKNGH][ROQNGH][ONGAXBWH][RLOKQNGAXBWH][LOKNGAXBWH][LOKNGH][ROQNGH][ONGAXBWH][RLOKQNGAXBWH][LOKNGH][ROQNGAXBWH]
|
|
|
@ -1 +0,0 @@
|
||||||
(?M)(^^^^^^^
|
|
|
@ -1 +0,0 @@
|
||||||
(?n:(?I:(?I:(?I:(?I:(?I:(?I:(?I:(?I:
|
|
|
@ -1 +0,0 @@
|
||||||
(()(())(())(())(()))()(())()((()(())(())(())(()))(()(())()(())(())(()()())(()))()(()())()()()(())
|
|
|
@ -1 +0,0 @@
|
||||||
(?'e69(?'Call'(?'e69(?'Call
|
|
|
@ -1 +0,0 @@
|
||||||
[[::][::][::][::][::][::][::]]
|
|
|
@ -1 +0,0 @@
|
||||||
((|9|A|A|A|A|A|A|A|A|9|A|A|A|9)A|9|A|A|A|9)
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue