mustermann-visualizer 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +196 -0
- data/examples/highlighting.rb +27 -0
- data/highlighting.png +0 -0
- data/irb.png +0 -0
- data/lib/mustermann/visualizer.rb +38 -0
- data/lib/mustermann/visualizer/highlight.rb +136 -0
- data/lib/mustermann/visualizer/highlighter.rb +36 -0
- data/lib/mustermann/visualizer/highlighter/ad_hoc.rb +94 -0
- data/lib/mustermann/visualizer/highlighter/ast.rb +102 -0
- data/lib/mustermann/visualizer/highlighter/dummy.rb +18 -0
- data/lib/mustermann/visualizer/highlighter/regular.rb +104 -0
- data/lib/mustermann/visualizer/pattern_extension.rb +66 -0
- data/lib/mustermann/visualizer/renderer/ansi.rb +23 -0
- data/lib/mustermann/visualizer/renderer/generic.rb +49 -0
- data/lib/mustermann/visualizer/renderer/hansi_template.rb +34 -0
- data/lib/mustermann/visualizer/renderer/html.rb +50 -0
- data/lib/mustermann/visualizer/renderer/sexp.rb +37 -0
- data/lib/mustermann/visualizer/tree.rb +63 -0
- data/lib/mustermann/visualizer/tree_renderer.rb +78 -0
- data/mustermann-visualizer.gemspec +19 -0
- data/spec/pattern_extension_spec.rb +48 -0
- data/spec/visualizer_spec.rb +179 -0
- data/theme.png +0 -0
- data/tree.png +0 -0
- metadata +98 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'mustermann/visualizer/renderer/generic'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
module Visualizer
|
5
|
+
# @!visibility private
|
6
|
+
module Renderer
|
7
|
+
# Generates Hansi template string.
|
8
|
+
# @see Mustermann::Visualizer::Renderer::ANSI
|
9
|
+
# @!visibility private
|
10
|
+
class HansiTemplate < Generic
|
11
|
+
# @!visibility private
|
12
|
+
def initialize(*)
|
13
|
+
@hansi = Hansi::StringRenderer.new(tags: true)
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
# @!visibility private
|
18
|
+
def escape_string(string)
|
19
|
+
@hansi.escape(string)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
def pre(type)
|
24
|
+
"<#{type}>"
|
25
|
+
end
|
26
|
+
|
27
|
+
# @!visibility private
|
28
|
+
def post(type)
|
29
|
+
"</#{type}>"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'mustermann/visualizer/renderer/generic'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module Mustermann
|
5
|
+
module Visualizer
|
6
|
+
# @!visibility private
|
7
|
+
module Renderer
|
8
|
+
# Generates HTML output.
|
9
|
+
# @!visibility private
|
10
|
+
class HTML < Generic
|
11
|
+
# @!visibility private
|
12
|
+
def initialize(target, tag: :span, class_prefix: "mustermann_", css: :inline, **options)
|
13
|
+
raise ArgumentError, 'css option %p not supported, should be true, false or inline' if css != true and css != false and css != :inline
|
14
|
+
super(target, **options)
|
15
|
+
@css, @tag, @class_prefix = css, tag, class_prefix
|
16
|
+
end
|
17
|
+
|
18
|
+
# @!visibility private
|
19
|
+
def preamble
|
20
|
+
"<style type=\"text/css\">\n%s</style>" % stylesheet if @css == true
|
21
|
+
end
|
22
|
+
|
23
|
+
# @!visibility private
|
24
|
+
def stylesheet
|
25
|
+
@target.theme.to_css { |name| ".#{@class_prefix}pattern .#{@class_prefix}#{name}" }
|
26
|
+
end
|
27
|
+
|
28
|
+
# @!visibility private
|
29
|
+
def escape_string(string)
|
30
|
+
CGI.escape_html(string)
|
31
|
+
end
|
32
|
+
|
33
|
+
# @!visibility private
|
34
|
+
def pre(type)
|
35
|
+
if @css == :inline
|
36
|
+
return "" unless rule = @target.theme[type]
|
37
|
+
"<#{@tag} style=\"#{rule.to_css_rule}\">"
|
38
|
+
else
|
39
|
+
"<#{@tag} class=\"#{@class_prefix}#{type}\">"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!visibility private
|
44
|
+
def post(type)
|
45
|
+
"</#{@tag}>"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'mustermann/visualizer/renderer/generic'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
module Visualizer
|
5
|
+
# @!visibility private
|
6
|
+
module Renderer
|
7
|
+
# Generates a s-expression like string.
|
8
|
+
# @!visibility private
|
9
|
+
class Sexp < Generic
|
10
|
+
# @!visibility private
|
11
|
+
def render
|
12
|
+
@inspect = false
|
13
|
+
super.gsub(/ ?\)( \))*/) { |s| s.gsub(' ', '') }.strip
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# @!visibility private
|
18
|
+
def pre(type)
|
19
|
+
"(#{type} " if type != :pattern
|
20
|
+
end
|
21
|
+
|
22
|
+
# @!visibility private
|
23
|
+
def escape_string(input)
|
24
|
+
inspect = input.inspect
|
25
|
+
input = inspect if inspect != "\"#{input}\""
|
26
|
+
input = inspect if input =~ /[\s\"\'\(\)]/
|
27
|
+
input + " "
|
28
|
+
end
|
29
|
+
|
30
|
+
# @!visibility private
|
31
|
+
def post(type)
|
32
|
+
") " if type != :pattern
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'hansi'
|
2
|
+
|
3
|
+
module Mustermann
|
4
|
+
module Visualizer
|
5
|
+
# Represents a (sub)tree and at the same time a node in the tree.
|
6
|
+
class Tree
|
7
|
+
# @!visibility private
|
8
|
+
attr_reader :line, :children, :prefix_color, :before, :after
|
9
|
+
|
10
|
+
# @!visibility private
|
11
|
+
def initialize(line, *children, prefix_color: :default, before: "", after: "")
|
12
|
+
@line = line
|
13
|
+
@children = children
|
14
|
+
@prefix_color = prefix_color
|
15
|
+
@before = before
|
16
|
+
@after = after
|
17
|
+
end
|
18
|
+
|
19
|
+
# used for positioning {#after}
|
20
|
+
# @!visibility private
|
21
|
+
def line_widths(offset = 0)
|
22
|
+
child_widths = children.flat_map { |c| c.line_widths(offset + 2) }
|
23
|
+
width = length(line + before) + offset
|
24
|
+
[width, *child_widths]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Renders the tree.
|
28
|
+
# @return [String] rendered version of the tree
|
29
|
+
def to_s
|
30
|
+
render("", "", line_widths.max)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Renders tree, including nesting.
|
34
|
+
# @!visibility private
|
35
|
+
def render(first_prefix, prefix, width)
|
36
|
+
output = before + Hansi.render(prefix_color, first_prefix) + line
|
37
|
+
output = ljust(output, width) + " " + after + "\n"
|
38
|
+
children[0..-2].each { |child| output += child.render(prefix + "├ ", prefix + "│ ", width) }
|
39
|
+
output += children.last.render(prefix + "└ ", prefix + " ", width) if children.last
|
40
|
+
output
|
41
|
+
end
|
42
|
+
|
43
|
+
# @!visibility private
|
44
|
+
def length(string)
|
45
|
+
deansi(string).length
|
46
|
+
end
|
47
|
+
|
48
|
+
# @!visibility private
|
49
|
+
def deansi(string)
|
50
|
+
string.gsub(/\e\[[^m]+m/, '')
|
51
|
+
end
|
52
|
+
|
53
|
+
# @!visibility private
|
54
|
+
def ljust(string, width)
|
55
|
+
missing = width - length(string)
|
56
|
+
append = missing > 0 ? " " * missing : ""
|
57
|
+
string + append
|
58
|
+
end
|
59
|
+
|
60
|
+
private :ljust, :deansi, :length
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'mustermann/visualizer/tree'
|
2
|
+
require 'mustermann/ast/translator'
|
3
|
+
require 'hansi'
|
4
|
+
|
5
|
+
module Mustermann
|
6
|
+
module Visualizer
|
7
|
+
# Turns an AST into a Tree
|
8
|
+
# @!visibility private
|
9
|
+
class TreeRenderer < AST::Translator
|
10
|
+
TEMPLATE = '"<base01>%s</base01><underline><green>%s</green></underline><base01>%s</base01>" '
|
11
|
+
THEME = Hansi::Theme[:solarized]
|
12
|
+
PREFIX_COLOR = THEME[:violet]
|
13
|
+
FakeNode = Struct.new(:type, :start, :stop, :length)
|
14
|
+
private_constant(:TEMPLATE, :THEME, :PREFIX_COLOR, :FakeNode)
|
15
|
+
|
16
|
+
# Takes a pattern (or pattern string and option) and turns it into a tree.
|
17
|
+
# Runs translation if pattern implements to_ast, otherwise returns single
|
18
|
+
# node tree.
|
19
|
+
#
|
20
|
+
# @!visibility private
|
21
|
+
def self.render(pattern, **options)
|
22
|
+
pattern &&= Mustermann.new(pattern, **options)
|
23
|
+
renderer = new(pattern.to_s)
|
24
|
+
if pattern.respond_to? :to_ast
|
25
|
+
renderer.translate(pattern.to_ast)
|
26
|
+
else
|
27
|
+
length = renderer.string.length
|
28
|
+
node = FakeNode.new("pattern (not AST based)", 0, length, length)
|
29
|
+
renderer.tree(node)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @!visibility private
|
34
|
+
attr_reader :string
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
def initialize(string)
|
38
|
+
@string = string
|
39
|
+
end
|
40
|
+
|
41
|
+
# access a substring of the pattern, in inspect mode
|
42
|
+
# @!visibility private
|
43
|
+
def sub(*args)
|
44
|
+
string[*args].inspect[1..-2]
|
45
|
+
end
|
46
|
+
|
47
|
+
# creates a tree node
|
48
|
+
# @!visibility private
|
49
|
+
def tree(node, *children, **typed_children)
|
50
|
+
children += children_for(typed_children)
|
51
|
+
children = children.flatten.grep(Tree)
|
52
|
+
infos = sub(0, node.start), sub(node.start, node.length), sub(node.stop..-1)
|
53
|
+
description = Hansi.render(THEME[:green], node.type.to_s.tr("_", " "))
|
54
|
+
after = Hansi.render(TEMPLATE, *infos, theme: THEME, tags: true)
|
55
|
+
Tree.new(description, *children, after: after, prefix_color: PREFIX_COLOR)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Take a hash with trees as values and turn the keys into trees, too.
|
59
|
+
# Read again if that didn't make sense.
|
60
|
+
# @!visibility private
|
61
|
+
def children_for(list)
|
62
|
+
list.map do |key, value|
|
63
|
+
value = Array(value).flatten
|
64
|
+
if value.any?
|
65
|
+
after = " " * string.inspect.length + " "
|
66
|
+
description = Hansi.render(THEME[:orange], key.to_s)
|
67
|
+
Tree.new(description, *value, after: after, prefix_color: PREFIX_COLOR)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
translate(:node) { t.tree(node, payload: t(payload)) }
|
73
|
+
translate(:with_look_ahead) { t.tree(node, head: t(head), payload: t(payload)) }
|
74
|
+
translate(Array) { map { |e| t(e) }}
|
75
|
+
translate(Object) { }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift File.expand_path("../../mustermann/lib", __FILE__)
|
2
|
+
require "mustermann/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "mustermann-visualizer"
|
6
|
+
s.version = Mustermann::VERSION
|
7
|
+
s.author = "Konstantin Haase"
|
8
|
+
s.email = "konstantin.mailinglists@googlemail.com"
|
9
|
+
s.homepage = "https://github.com/rkh/mustermann"
|
10
|
+
s.summary = %q{Visualize Mustermann patterns}
|
11
|
+
s.description = %q{Provides syntax highlighting and other visualizations for Mustermman}
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.required_ruby_version = '>= 2.1.0'
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.add_dependency 'mustermann', Mustermann::VERSION
|
18
|
+
s.add_dependency 'hansi', '~> 0.1.1'
|
19
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/visualizer'
|
3
|
+
require 'pp'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
describe Mustermann::Visualizer::PatternExtension do
|
7
|
+
subject(:pattern) { Mustermann.new("/:name") }
|
8
|
+
before { Hansi.mode = 16 }
|
9
|
+
after { Hansi.mode = nil }
|
10
|
+
|
11
|
+
specify :to_ansi do
|
12
|
+
pattern.to_ansi(inspect: true, capture: :red, default: nil).should be == "\e[0m\"\e[0m/\e[0m\e[91m:\e[0m\e[91mname\e[0m\"\e[0m"
|
13
|
+
pattern.to_ansi(inspect: false, capture: :green, default: nil).should be == "\e[0m/\e[0m\e[32m:\e[0m\e[32mname\e[0m"
|
14
|
+
end
|
15
|
+
|
16
|
+
specify :to_html do
|
17
|
+
pattern.to_html(css: false, class_prefix: "", tag: :tt).should be == '<tt class="pattern"><tt class="root"><tt class="separator">/</tt><tt class="capture">:<tt class="name">name</tt></tt></tt></tt>'
|
18
|
+
end
|
19
|
+
|
20
|
+
specify :to_tree do
|
21
|
+
pattern.to_tree.should be == Mustermann::Visualizer.tree(pattern).to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
specify :color_inspect do
|
25
|
+
pattern.color_inspect.should include(pattern.to_ansi(inspect: true))
|
26
|
+
pattern.color_inspect.should include("#<Mustermann::Sinatra:")
|
27
|
+
end
|
28
|
+
|
29
|
+
specify :to_s do
|
30
|
+
object = Class.new { def puts(arg) arg.to_s end }.new
|
31
|
+
object.puts(pattern).should be == pattern.to_ansi
|
32
|
+
end
|
33
|
+
|
34
|
+
context :pretty_print do
|
35
|
+
before(:all) { ColorPrinter = Class.new(::PP) }
|
36
|
+
let(:output) { StringIO.new }
|
37
|
+
|
38
|
+
specify 'with color printer' do
|
39
|
+
ColorPrinter.new(output, 79).pp(pattern)
|
40
|
+
output.string.should be == pattern.color_inspect
|
41
|
+
end
|
42
|
+
|
43
|
+
specify 'without color printer' do
|
44
|
+
::PP.new(output, 79).pp(pattern)
|
45
|
+
output.string.should be == pattern.inspect
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/visualizer'
|
3
|
+
|
4
|
+
describe Mustermann::Visualizer do
|
5
|
+
subject(:highlight) { Mustermann::Visualizer.highlight(pattern) }
|
6
|
+
before { Hansi.mode = 256 }
|
7
|
+
after { Hansi.mode = nil }
|
8
|
+
|
9
|
+
describe :highlight do
|
10
|
+
context :sinatra do
|
11
|
+
context "/a" do
|
12
|
+
let(:pattern) { Mustermann.new("/a") }
|
13
|
+
its(:to_ansi) { should be == "\e[0m\e[38;5;246m\e[38;5;246m\e[38;5;247m/\e[0m\e[38;5;246m\e[38;5;246m\e[38;5;246ma\e[0m" }
|
14
|
+
its(:to_html) { should be == '<span style="color: #839496;"><span style="color: #93a1a1;">/</span><span style="color: #839496;">a</span></span></span>' }
|
15
|
+
its(:to_sexp) { should be == '(root (separator /) (char a))' }
|
16
|
+
its(:to_pattern) { should be == pattern }
|
17
|
+
its(:to_s) { should be == "/a" }
|
18
|
+
its(:stylesheet) { should include(".mustermann_pattern .mustermann_illegal {\n color: #8b0000;") }
|
19
|
+
|
20
|
+
example do
|
21
|
+
highlight.to_html(css: false).should be ==
|
22
|
+
'<span class="mustermann_pattern"><span class="mustermann_root"><span class="mustermann_separator">/</span><span class="mustermann_char">a</span></span></span>'
|
23
|
+
end
|
24
|
+
|
25
|
+
example do
|
26
|
+
renderer = Mustermann::Visualizer::Renderer::Generic
|
27
|
+
result = highlight.render_with(renderer)
|
28
|
+
result.should be == pattern.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context '/:name' do
|
33
|
+
let(:pattern) { Mustermann.new("/:name") }
|
34
|
+
its(:to_sexp) { should be == "(root (separator /) (capture : (name name)))" }
|
35
|
+
end
|
36
|
+
|
37
|
+
context '/{name}' do
|
38
|
+
let(:pattern) { Mustermann.new("/{name}") }
|
39
|
+
its(:to_sexp) { should be == "(root (separator /) (capture { (name name) }))" }
|
40
|
+
end
|
41
|
+
|
42
|
+
context '/{+name}' do
|
43
|
+
let(:pattern) { Mustermann.new("/{+name}") }
|
44
|
+
its(:to_sexp) { should be == "(root (separator /) (named_splat {+ (name name) }))" }
|
45
|
+
end
|
46
|
+
|
47
|
+
context ':user(@:host)?' do
|
48
|
+
let(:pattern) { Mustermann.new(':user(@:host)?') }
|
49
|
+
its(:to_sexp) { should be == '(root (capture : (name user)) (optional (group "(" (char @) (capture : (name host)) ")") ?))' }
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'a b' do
|
53
|
+
let(:pattern) { Mustermann.new('a b') }
|
54
|
+
its(:to_sexp) { should be == '(root (char a) (char " ") (char b))' }
|
55
|
+
end
|
56
|
+
|
57
|
+
context '\:a' do
|
58
|
+
let(:pattern) { Mustermann.new('\:a') }
|
59
|
+
its(:to_sexp) { should be == '(root (escaped "\\\\" (escaped_char :)) (char a))' }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context :regexp do
|
64
|
+
context 'a' do
|
65
|
+
let(:pattern) { Mustermann.new('a', type: :regexp) }
|
66
|
+
its(:to_sexp) { should be == '(root (char a))' }
|
67
|
+
end
|
68
|
+
|
69
|
+
context '/(\d+)' do
|
70
|
+
let(:pattern) { Mustermann.new('/(\d+)', type: :regexp) }
|
71
|
+
its(:to_sexp) { should be == '(root (separator /) (capture "(" (special "\\\\d") (special +))))' }
|
72
|
+
end
|
73
|
+
|
74
|
+
context '\A' do
|
75
|
+
let(:pattern) { Mustermann.new('\A', type: :regexp) }
|
76
|
+
its(:to_sexp) { should be == '(root (illegal "\\\\A"))' }
|
77
|
+
end
|
78
|
+
|
79
|
+
context '(?<name>.)\g<name>' do
|
80
|
+
let(:pattern) { Mustermann.new('(?<name>.)\g<name>', type: :regexp) }
|
81
|
+
its(:to_sexp) { should be == '(root (capture "(?<" (name) >(special .))) (special "\\\\g<name>"))' }
|
82
|
+
end
|
83
|
+
|
84
|
+
context '\p{Ll}' do
|
85
|
+
let(:pattern) { Mustermann.new('\p{Ll}', type: :regexp) }
|
86
|
+
its(:to_sexp) { should be == '(root (special "\\\\p{Ll}"))' }
|
87
|
+
end
|
88
|
+
|
89
|
+
context '\/' do
|
90
|
+
let(:pattern) { Mustermann.new('\/', type: :regexp) }
|
91
|
+
its(:to_sexp) { should be == '(root (separator /))' }
|
92
|
+
end
|
93
|
+
|
94
|
+
context '\[' do
|
95
|
+
let(:pattern) { Mustermann.new('\[', type: :regexp) }
|
96
|
+
its(:to_sexp) { should be == '(root (escaped "\\\\" (escaped_char [)))' }
|
97
|
+
end
|
98
|
+
|
99
|
+
context '^' do
|
100
|
+
let(:pattern) { Mustermann.new('^', type: :regexp) }
|
101
|
+
its(:to_sexp) { should be == '(root (illegal ^))' }
|
102
|
+
end
|
103
|
+
|
104
|
+
context '(?-mix:.)' do
|
105
|
+
let(:pattern) { Mustermann.new('(?-mix:.)', type: :regexp) }
|
106
|
+
its(:to_sexp) { should be == '(root (special "(") (special .) (special ")"))' }
|
107
|
+
end
|
108
|
+
|
109
|
+
context '[a\d]' do
|
110
|
+
let(:pattern) { Mustermann.new('[a\d]', type: :regexp) }
|
111
|
+
its(:to_sexp) { should be == '(root (special [) (char a) (special "\\\\d") (special ]))' }
|
112
|
+
end
|
113
|
+
|
114
|
+
context '[^a-z]' do
|
115
|
+
let(:pattern) { Mustermann.new('[^a-z]', type: :regexp) }
|
116
|
+
its(:to_sexp) { should be == '(root (special [) (special ^) (char a) (special -) (char z) (special ]))' }
|
117
|
+
end
|
118
|
+
|
119
|
+
context '[[:digit:]]' do
|
120
|
+
let(:pattern) { Mustermann.new('[[:digit:]]', type: :regexp) }
|
121
|
+
its(:to_sexp) { should be == '(root (special [[:digit:]]))' }
|
122
|
+
end
|
123
|
+
|
124
|
+
context 'a{1,}' do
|
125
|
+
let(:pattern) { Mustermann.new('a{1,}', type: :regexp) }
|
126
|
+
its(:to_sexp) { should be == "(root (char a) (special {1,}))" }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context :template do
|
131
|
+
context '/{name}' do
|
132
|
+
let(:pattern) { Mustermann.new("/{+foo,bar*}", type: :template) }
|
133
|
+
its(:to_sexp) { should be == "(root (separator /) (expression {+ (variable (name foo)) , (variable (name bar) *) }))" }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "custom AST based pattern" do
|
138
|
+
let(:my_type) { Class.new(Mustermann::AST::Pattern) { on('x') { |*| node(:char, "o") } }}
|
139
|
+
let(:pattern) { Mustermann.new("fxx", type: my_type) }
|
140
|
+
its(:to_sexp) { should be == "(root (char f) (escaped x) (escaped x))" }
|
141
|
+
end
|
142
|
+
|
143
|
+
context "without known highlighter" do
|
144
|
+
let(:pattern) { Mustermann::Pattern.new("foo") }
|
145
|
+
its(:to_sexp) { should be == "(root (unknown foo))" }
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe :tree do
|
150
|
+
subject(:tree) { Mustermann::Visualizer.tree(pattern) }
|
151
|
+
|
152
|
+
context :sinatra do
|
153
|
+
context "/:a(@:b)" do
|
154
|
+
let(:pattern) { Mustermann.new("/:a(@:b)") }
|
155
|
+
let(:tree_data) do
|
156
|
+
<<-TREE.gsub(/^\s+/, '')
|
157
|
+
\e[38;5;61m\e[0m\e[38;5;100mroot\e[0m \e[0m\e[38;5;66m\"\e[0m\e[38;5;66m\e[38;5;242m\e[0m\e[38;5;66m\e[4m\e[38;5;100m/:a(@:b)\e[0m\e[38;5;66m\e[38;5;242m\e[0m\e[38;5;66m\" \e[0m
|
158
|
+
\e[38;5;61m└ \e[0m\e[38;5;166mpayload\e[0m
|
159
|
+
\e[38;5;61m ├ \e[0m\e[38;5;100mseparator\e[0m \e[0m\e[38;5;66m\"\e[0m\e[38;5;66m\e[38;5;242m\e[0m\e[38;5;66m\e[4m\e[38;5;100m/\e[0m\e[38;5;66m\e[38;5;242m:a(@:b)\e[0m\e[38;5;66m\" \e[0m
|
160
|
+
\e[38;5;61m ├ \e[0m\e[38;5;100mcapture\e[0m \e[0m\e[38;5;66m\"\e[0m\e[38;5;66m\e[38;5;242m/\e[0m\e[38;5;66m\e[4m\e[38;5;100m:a\e[0m\e[38;5;66m\e[38;5;242m(@:b)\e[0m\e[38;5;66m\" \e[0m
|
161
|
+
\e[38;5;61m └ \e[0m\e[38;5;100mgroup\e[0m \e[0m\e[38;5;66m\"\e[0m\e[38;5;66m\e[38;5;242m/:a\e[0m\e[38;5;66m\e[4m\e[38;5;100m(@:b)\e[0m\e[38;5;66m\e[38;5;242m\e[0m\e[38;5;66m\" \e[0m
|
162
|
+
\e[38;5;61m └ \e[0m\e[38;5;166mpayload\e[0m
|
163
|
+
\e[38;5;61m ├ \e[0m\e[38;5;100mchar\e[0m \e[0m\e[38;5;66m\"\e[0m\e[38;5;66m\e[38;5;242m/:a(\e[0m\e[38;5;66m\e[4m\e[38;5;100m@\e[0m\e[38;5;66m\e[38;5;242m:b)\e[0m\e[38;5;66m\" \e[0m
|
164
|
+
\e[38;5;61m └ \e[0m\e[38;5;100mcapture\e[0m \e[0m\e[38;5;66m\"\e[0m\e[38;5;66m\e[38;5;242m/:a(@\e[0m\e[38;5;66m\e[4m\e[38;5;100m:b\e[0m\e[38;5;66m\e[38;5;242m)\e[0m\e[38;5;66m\" \e[0m
|
165
|
+
TREE
|
166
|
+
end
|
167
|
+
its(:to_s) { should be == tree_data }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
context :shell do
|
172
|
+
context "/**/*" do
|
173
|
+
let(:pattern) { Mustermann.new("/**/*", type: :shell) }
|
174
|
+
let(:tree_data) { "\e[38;5;61m\e[0m\e[38;5;100mpattern (not AST based)\e[0m \e[0m\e[38;5;66m\"\e[0m\e[38;5;66m\e[38;5;242m\e[0m\e[38;5;66m\e[4m\e[38;5;100m/**/*\e[0m\e[38;5;66m\e[38;5;242m\e[0m\e[38;5;66m\" \e[0m\n" }
|
175
|
+
its(:to_s) { should be == tree_data }
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|