mustermann-visualizer 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|