wool 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +23 -0
- data/LICENSE +45 -0
- data/README.rdoc +17 -0
- data/Rakefile +77 -0
- data/TODO.md +17 -0
- data/VERSION +1 -0
- data/bin/wool +4 -0
- data/features/step_definitions/wool_steps.rb +39 -0
- data/features/support/env.rb +14 -0
- data/features/support/testdata/1_input +1 -0
- data/features/support/testdata/1_output +1 -0
- data/features/support/testdata/2_input +4 -0
- data/features/support/testdata/2_output +4 -0
- data/features/support/testdata/3_input +8 -0
- data/features/support/testdata/3_output +11 -0
- data/features/support/testdata/4_input +5 -0
- data/features/support/testdata/4_output +5 -0
- data/features/wool.feature +24 -0
- data/lib/wool.rb +40 -0
- data/lib/wool/advice/advice.rb +42 -0
- data/lib/wool/advice/comment_advice.rb +37 -0
- data/lib/wool/analysis/annotations.rb +34 -0
- data/lib/wool/analysis/annotations/next_annotation.rb +26 -0
- data/lib/wool/analysis/annotations/parent_annotation.rb +20 -0
- data/lib/wool/analysis/annotations/scope_annotation.rb +37 -0
- data/lib/wool/analysis/lexical_analysis.rb +165 -0
- data/lib/wool/analysis/protocol_registry.rb +32 -0
- data/lib/wool/analysis/protocols.rb +82 -0
- data/lib/wool/analysis/scope.rb +13 -0
- data/lib/wool/analysis/sexp_analysis.rb +98 -0
- data/lib/wool/analysis/signature.rb +16 -0
- data/lib/wool/analysis/symbol.rb +10 -0
- data/lib/wool/analysis/visitor.rb +36 -0
- data/lib/wool/analysis/wool_class.rb +47 -0
- data/lib/wool/rake/task.rb +42 -0
- data/lib/wool/runner.rb +156 -0
- data/lib/wool/scanner.rb +160 -0
- data/lib/wool/support/module_extensions.rb +84 -0
- data/lib/wool/third_party/trollop.rb +845 -0
- data/lib/wool/warning.rb +145 -0
- data/lib/wool/warnings/comment_spacing.rb +30 -0
- data/lib/wool/warnings/extra_blank_lines.rb +29 -0
- data/lib/wool/warnings/extra_whitespace.rb +15 -0
- data/lib/wool/warnings/line_length.rb +113 -0
- data/lib/wool/warnings/misaligned_unindentation.rb +16 -0
- data/lib/wool/warnings/operator_spacing.rb +63 -0
- data/lib/wool/warnings/rescue_exception.rb +41 -0
- data/lib/wool/warnings/semicolon.rb +24 -0
- data/lib/wool/warnings/useless_double_quotes.rb +37 -0
- data/spec/advice_specs/advice_spec.rb +69 -0
- data/spec/advice_specs/comment_advice_spec.rb +38 -0
- data/spec/advice_specs/spec_helper.rb +1 -0
- data/spec/analysis_specs/annotations_specs/next_prev_annotation_spec.rb +47 -0
- data/spec/analysis_specs/annotations_specs/parent_annotation_spec.rb +41 -0
- data/spec/analysis_specs/annotations_specs/spec_helper.rb +5 -0
- data/spec/analysis_specs/lexical_analysis_spec.rb +179 -0
- data/spec/analysis_specs/protocol_registry_spec.rb +58 -0
- data/spec/analysis_specs/protocols_spec.rb +49 -0
- data/spec/analysis_specs/scope_spec.rb +20 -0
- data/spec/analysis_specs/sexp_analysis_spec.rb +134 -0
- data/spec/analysis_specs/spec_helper.rb +2 -0
- data/spec/analysis_specs/visitor_spec.rb +53 -0
- data/spec/analysis_specs/wool_class_spec.rb +54 -0
- data/spec/rake_specs/spec_helper.rb +1 -0
- data/spec/rake_specs/task_spec.rb +67 -0
- data/spec/runner_spec.rb +171 -0
- data/spec/scanner_spec.rb +75 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +93 -0
- data/spec/support_specs/module_extensions_spec.rb +91 -0
- data/spec/support_specs/spec_helper.rb +1 -0
- data/spec/warning_spec.rb +95 -0
- data/spec/warning_specs/comment_spacing_spec.rb +57 -0
- data/spec/warning_specs/extra_blank_lines_spec.rb +70 -0
- data/spec/warning_specs/extra_whitespace_spec.rb +33 -0
- data/spec/warning_specs/line_length_spec.rb +165 -0
- data/spec/warning_specs/misaligned_unindentation_spec.rb +35 -0
- data/spec/warning_specs/operator_spacing_spec.rb +101 -0
- data/spec/warning_specs/rescue_exception_spec.rb +105 -0
- data/spec/warning_specs/semicolon_spec.rb +58 -0
- data/spec/warning_specs/spec_helper.rb +1 -0
- data/spec/warning_specs/useless_double_quotes_spec.rb +62 -0
- data/spec/wool_spec.rb +8 -0
- data/status_reports/2010/12/2010-12-14.md +163 -0
- data/test/third_party_tests/test_trollop.rb +1181 -0
- data/wool.gemspec +173 -0
- metadata +235 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# Warning for using semicolons outside of class declarations.
|
2
|
+
class Wool::SemicolonWarning < Wool::LineWarning
|
3
|
+
type :style
|
4
|
+
short_desc 'Semicolon for multiple statements'
|
5
|
+
desc 'The line uses a semicolon to separate multiple statements outside of a class declaration.'
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
super
|
9
|
+
self.severity = line =~ /['"]/ ? 2 : 4
|
10
|
+
end
|
11
|
+
|
12
|
+
def match?(line = self.body)
|
13
|
+
!!(find_token(line, :on_semicolon) && !find_keyword(line, :class))
|
14
|
+
end
|
15
|
+
|
16
|
+
def fix(line = self.body)
|
17
|
+
left, right = split_on_token(line, :on_semicolon)
|
18
|
+
return line if right.empty?
|
19
|
+
return right[1..-1] if left.empty?
|
20
|
+
|
21
|
+
right = fix(right[1..-1])
|
22
|
+
"#{indent left}\n#{indent right}"
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Warning for using semicolons outside of class declarations.
|
2
|
+
class Wool::UselessDoubleQuotesWarning < Wool::FileWarning
|
3
|
+
type :style
|
4
|
+
severity 1
|
5
|
+
short_desc 'Useless double quotes'
|
6
|
+
setting_accessor :quoted_string, :uses_q_braces
|
7
|
+
desc do
|
8
|
+
if uses_q_braces
|
9
|
+
then "The string %q{#{quoted_string}} can be written with lowercase q for efficiency."
|
10
|
+
else "The string '#{quoted_string}' can be wrapped in single quotes for efficiency."
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def match?(body = self.body)
|
15
|
+
list = find_sexps(:string_content)
|
16
|
+
list.map do |sym, *parts|
|
17
|
+
next if parts.size != 1 # ignore multiparts as they're fine
|
18
|
+
inner_sym, text, pos = parts.first
|
19
|
+
# skip if the string has a backslash or an apostrophe in it.
|
20
|
+
next unless inner_sym == :@tstring_content && text !~ /(\\)|(')/
|
21
|
+
|
22
|
+
previous_char = body.lines.to_a[pos[0] - 1][pos[1]-1,1]
|
23
|
+
uses_q_braces = (previous_char == '{' && body.lines.to_a[pos[0] - 1][pos[1]-3,2] == '%Q')
|
24
|
+
if previous_char == '"' || uses_q_braces
|
25
|
+
warning = Wool::UselessDoubleQuotesWarning.new(
|
26
|
+
file, body, :quoted_string => text, :uses_q_braces => uses_q_braces)
|
27
|
+
warning.line_number = pos[0]
|
28
|
+
warning
|
29
|
+
end
|
30
|
+
end.compact
|
31
|
+
end
|
32
|
+
|
33
|
+
def fix(body = self.body)
|
34
|
+
body.gsub("\"#{quoted_string}\"", "'#{quoted_string}'").
|
35
|
+
gsub("%Q{#{quoted_string}}", "%q{#{quoted_string}}")
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Advice do
|
4
|
+
context '#before_advice' do
|
5
|
+
before do
|
6
|
+
@class = Class.new do
|
7
|
+
extend Advice
|
8
|
+
attr_accessor :closed_over
|
9
|
+
define_method :silly do
|
10
|
+
self.closed_over ||= 1
|
11
|
+
self.closed_over += 5
|
12
|
+
end
|
13
|
+
define_method :checkin do
|
14
|
+
self.closed_over ||= 1
|
15
|
+
self.closed_over *= 2
|
16
|
+
end
|
17
|
+
before_advice :silly, :checkin
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'causes the advised method to run the suggested advice before running' do
|
22
|
+
@class.new.silly.should == 7
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context '#after_advice' do
|
27
|
+
before do
|
28
|
+
@class = Class.new do
|
29
|
+
extend Advice
|
30
|
+
attr_accessor :closed_over
|
31
|
+
define_method :silly do
|
32
|
+
self.closed_over ||= 1
|
33
|
+
self.closed_over += 5
|
34
|
+
end
|
35
|
+
define_method :checkout do
|
36
|
+
self.closed_over ||= 1
|
37
|
+
self.closed_over *= 2
|
38
|
+
end
|
39
|
+
after_advice :silly, :checkout
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'causes the advised method to run the suggested advice after running' do
|
44
|
+
object = @class.new
|
45
|
+
object.silly
|
46
|
+
object.closed_over.should == 12
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context '#argument_advice' do
|
51
|
+
before do
|
52
|
+
@class = Class.new do
|
53
|
+
extend Advice
|
54
|
+
attr_accessor :closed_over
|
55
|
+
define_method :silly do |arg|
|
56
|
+
arg + 5
|
57
|
+
end
|
58
|
+
define_method :twiddle do |arg|
|
59
|
+
arg * 2
|
60
|
+
end
|
61
|
+
argument_advice :silly, :twiddle
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'causes the advised method to run, rewriting the arguments' do
|
66
|
+
@class.new.silly(1).should == 7
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe Advice::CommentAdvice do
|
4
|
+
before do
|
5
|
+
@class = Class.new(Warning) do
|
6
|
+
include Advice::CommentAdvice
|
7
|
+
|
8
|
+
def match?(body = self.body, settings={})
|
9
|
+
body
|
10
|
+
end
|
11
|
+
remove_comments
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context '#remove_comments' do
|
16
|
+
it 'Returns the empty string unmodified' do
|
17
|
+
@class.new('(stdin)', '').match?('').should == ''
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'Turns a comment into the empty string' do
|
21
|
+
@class.new('(stdin)', '# hello').match?('# hello').should == ''
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'strips the comments from the end of a string of code' do
|
25
|
+
@class.new('(stdin)', 'a + b # adding').match?('a + b # adding').should == 'a + b'
|
26
|
+
end
|
27
|
+
|
28
|
+
SAMPLES = [['', ''], ['#hello', ''], [' # hello', ''],
|
29
|
+
[' a + b # hello', ' a + b'], ['(a + b) #', '(a + b)'],
|
30
|
+
['"#" + number', '"#" + number'],
|
31
|
+
['" hello \\"mam\\"" # comment', '" hello \\"mam\\""']]
|
32
|
+
SAMPLES.each do |input, output|
|
33
|
+
it "should turn #{input} into #{output}" do
|
34
|
+
@class.new('(stdin)', input).match?(input).should == output
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'set'
|
3
|
+
describe NextPrevAnnotation do
|
4
|
+
it 'adds the #next and #prev methods to Sexp' do
|
5
|
+
Sexp.instance_methods.should include(:next)
|
6
|
+
Sexp.instance_methods.should include(:prev)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'adds next and prevs to each node with a toy example' do
|
10
|
+
tree = Sexp.new([:abc, Sexp.new([:def, 1, 2]),
|
11
|
+
Sexp.new([:zzz, Sexp.new([:return]), "hi", Sexp.new([:silly, 4])])])
|
12
|
+
NextPrevAnnotation::Annotator.new.annotate!(tree)
|
13
|
+
tree[1].prev.should == nil
|
14
|
+
tree[1].next.should == tree[2]
|
15
|
+
tree[2].prev.should == tree[1]
|
16
|
+
tree[2].next.should == nil
|
17
|
+
tree[2][1].prev.should == nil
|
18
|
+
tree[2][1].next.should == tree[2][2]
|
19
|
+
tree[2][3].prev.should == tree[2][2]
|
20
|
+
tree[2][3].next.should == nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# This will actually verify that every node in the tree has a
|
24
|
+
# proper parent set. It's a complex, but thorough test.
|
25
|
+
it 'adds next and prevs to each node with a real-world parse result' do
|
26
|
+
tree = Sexp.new(Ripper.sexp('x = proc {|x, *rst, &blk| p x ** rst[0]; blk.call(rst[1..-1])}'))
|
27
|
+
tree.next.should == nil
|
28
|
+
tree.prev.should == nil
|
29
|
+
visited = Set.new
|
30
|
+
to_visit = tree.children
|
31
|
+
while to_visit.any?
|
32
|
+
todo = to_visit.pop
|
33
|
+
next unless is_sexp?(todo)
|
34
|
+
|
35
|
+
todo.prev.next.should == todo if is_sexp?(todo.prev)
|
36
|
+
todo.next.prev.should == todo if is_sexp?(todo.next)
|
37
|
+
|
38
|
+
visited << todo
|
39
|
+
case todo[0]
|
40
|
+
when Array
|
41
|
+
to_visit.concat todo
|
42
|
+
when Symbol
|
43
|
+
to_visit.concat todo.children
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
require 'set'
|
3
|
+
describe ParentAnnotation do
|
4
|
+
it 'adds the #parent method to Sexp' do
|
5
|
+
Sexp.instance_methods.should include(:parent)
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'adds parents to each node with a toy example' do
|
9
|
+
tree = Sexp.new([:abc, Sexp.new([:def, 1, 2]),
|
10
|
+
Sexp.new([:zzz, Sexp.new([:return]), "hi", Sexp.new([:silly, 4])])])
|
11
|
+
ParentAnnotation::Annotator.new.annotate!(tree)
|
12
|
+
tree.parent.should == nil
|
13
|
+
tree[1].parent.should == tree
|
14
|
+
tree[2].parent.should == tree
|
15
|
+
tree[2][1].parent.should == tree[2]
|
16
|
+
tree[2][3].parent.should == tree[2]
|
17
|
+
end
|
18
|
+
|
19
|
+
# This will actually verify that every node in the tree has a
|
20
|
+
# proper parent set. It's a complex, but thorough test.
|
21
|
+
it 'adds parents to each node with a real-world parse result' do
|
22
|
+
tree = Sexp.new(Ripper.sexp('x = proc {|x, *rst, &blk| p x ** rst[0]; blk.call(rst[1..-1])}'))
|
23
|
+
tree.parent.should == nil
|
24
|
+
tree.children[0].parent.should == tree
|
25
|
+
visited = Set.new
|
26
|
+
to_visit = tree.children
|
27
|
+
while to_visit.any?
|
28
|
+
todo = to_visit.pop
|
29
|
+
next unless Sexp === todo
|
30
|
+
todo.parent.children.should include(todo)
|
31
|
+
visited << todo
|
32
|
+
|
33
|
+
case todo[0]
|
34
|
+
when Array
|
35
|
+
to_visit.concat todo
|
36
|
+
when Symbol
|
37
|
+
to_visit.concat todo.children
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe LexicalAnalysis do
|
4
|
+
before do
|
5
|
+
@class = Class.new do
|
6
|
+
include LexicalAnalysis
|
7
|
+
attr_accessor :body
|
8
|
+
def initialize(body)
|
9
|
+
self.body = body
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context '#lex' do
|
15
|
+
it 'lexes its body' do
|
16
|
+
@class.new('a').lex.should == [LexicalAnalysis::Token.new([[1,0], :on_ident, 'a'])]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context '#text_between_token_positions' do
|
21
|
+
it 'finds the exclusive text between two simple tokens from a body text' do
|
22
|
+
body = "def initialize(body)\n self.body = body\nend"
|
23
|
+
left = LexicalAnalysis::Token.new([[2, 4], :on_kw, "self"])
|
24
|
+
right = LexicalAnalysis::Token.new([[2, 20], :on_nl, "\n"])
|
25
|
+
@class.new('').text_between_token_positions(body, left, right).should ==
|
26
|
+
'.body = body'
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'allows including the left token with the inclusive :left hash option' do
|
30
|
+
body = "def initialize(body)\n self.body = body\nend"
|
31
|
+
left = LexicalAnalysis::Token.new([[2, 4], :on_kw, "self"])
|
32
|
+
right = LexicalAnalysis::Token.new([[2, 20], :on_nl, "\n"])
|
33
|
+
@class.new('').text_between_token_positions(body, left, right, :left).should ==
|
34
|
+
'self.body = body'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'allows including the right token with the inclusive :right hash option' do
|
38
|
+
body = "def initialize(body)\n self.body = body\nend"
|
39
|
+
left = LexicalAnalysis::Token.new([[2, 4], :on_kw, "self"])
|
40
|
+
right = LexicalAnalysis::Token.new([[2, 20], :on_nl, "\n"])
|
41
|
+
@class.new('').text_between_token_positions(body, left, right, :right).should ==
|
42
|
+
".body = body\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'allows including both tokens with the inclusive :both hash option' do
|
46
|
+
body = "def initialize(body)\n self.body = body\nend"
|
47
|
+
left = LexicalAnalysis::Token.new([[2, 4], :on_kw, "self"])
|
48
|
+
right = LexicalAnalysis::Token.new([[2, 20], :on_nl, "\n"])
|
49
|
+
@class.new('').text_between_token_positions(body, left, right, :both).should ==
|
50
|
+
"self.body = body\n"
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'allows explicitly to exclude the tokens with the inclusive :none option' do
|
54
|
+
body = "def initialize(body)\n self.body = body\nend"
|
55
|
+
left = LexicalAnalysis::Token.new([[2, 4], :on_kw, "self"])
|
56
|
+
right = LexicalAnalysis::Token.new([[2, 20], :on_nl, "\n"])
|
57
|
+
@class.new('').text_between_token_positions(body, left, right, :none).should ==
|
58
|
+
'.body = body'
|
59
|
+
end
|
60
|
+
|
61
|
+
context 'spanning multiple lines' do
|
62
|
+
it 'allows including the left token with the inclusive :left hash option' do
|
63
|
+
body = "def initialize(body)\n self.body = body\nend # a * b"
|
64
|
+
left = LexicalAnalysis::Token.new([[1, 14], :on_lparen, "("])
|
65
|
+
right = LexicalAnalysis::Token.new([[3, 3], :on_sp, " "])
|
66
|
+
@class.new('').text_between_token_positions(body, left, right, :left).should ==
|
67
|
+
"(body)\n self.body = body\nend"
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'allows including the right token with the inclusive :right hash option' do
|
71
|
+
body = "def initialize(body)\n self.body = body\nend # a * b"
|
72
|
+
left = LexicalAnalysis::Token.new([[1, 14], :on_lparen, "("])
|
73
|
+
right = LexicalAnalysis::Token.new([[3, 3], :on_sp, " "])
|
74
|
+
@class.new('').text_between_token_positions(body, left, right, :right).should ==
|
75
|
+
"body)\n self.body = body\nend "
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'allows including both tokens with the inclusive :both hash option' do
|
79
|
+
body = "def initialize(body)\n self.body = body\nend # a * b"
|
80
|
+
left = LexicalAnalysis::Token.new([[1, 14], :on_lparen, "("])
|
81
|
+
right = LexicalAnalysis::Token.new([[3, 3], :on_sp, " "])
|
82
|
+
@class.new('').text_between_token_positions(body, left, right, :both).should ==
|
83
|
+
"(body)\n self.body = body\nend "
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'allows explicitly to exclude the tokens with the inclusive :none option' do
|
87
|
+
body = "def initialize(body)\n self.body = body\nend # a * b"
|
88
|
+
left = LexicalAnalysis::Token.new([[1, 14], :on_lparen, "("])
|
89
|
+
right = LexicalAnalysis::Token.new([[3, 3], :on_sp, " "])
|
90
|
+
@class.new('').text_between_token_positions(body, left, right, :none).should ==
|
91
|
+
"body)\n self.body = body\nend"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context '#find_token' do
|
97
|
+
it 'lexes its body' do
|
98
|
+
@class.new('a + b').find_token(:on_op).should be_true
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'returns falsy if token not found' do
|
102
|
+
@class.new('a + b').find_token(:on_kw).should be_false
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'works with multiple token options' do
|
106
|
+
result = @class.new('a + b # hello').find_token(:on_op, :on_comment)
|
107
|
+
result.type.should == :on_op
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'is not triggered by symbols' do
|
111
|
+
@class.new(':+').find_token(:on_op).should be_false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context '#find_keyword' do
|
116
|
+
it 'lexes its body' do
|
117
|
+
@class.new('class A < B').find_keyword(:class).should be_true
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'returns falsy if token not found' do
|
121
|
+
@class.new('class A < B').find_keyword(:end).should be_false
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'returns the actual token if it is found' do
|
125
|
+
@class.new('class A < B').find_keyword(:class).should ==
|
126
|
+
LexicalAnalysis::Token.new([[1,0], :on_kw, 'class'])
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'works with multiple keyword options' do
|
130
|
+
result = @class.new('class A < B; end').find_keyword(:class, :end)
|
131
|
+
result.body.should == 'class'
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'is not triggered by symbols' do
|
135
|
+
@class.new(':unless').find_keyword(:unless).should be_false
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context '#split_on_token' do
|
140
|
+
it 'splits the input into two parts based on the token searched' do
|
141
|
+
left, right = @class.new('a + b; c + d').split_on_token(:on_semicolon)
|
142
|
+
left.should == 'a + b'
|
143
|
+
right.should == '; c + d'
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'works with multiple searched tokens' do
|
147
|
+
left, right = @class.new('a + b; c + d').split_on_token(:on_semicolon, :on_op)
|
148
|
+
left.should == 'a '
|
149
|
+
right.should == '+ b; c + d'
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'matches its own documentation' do
|
153
|
+
left, right = @class.new('').split_on_token('x = 5 unless y == 2', :on_kw)
|
154
|
+
left.should == 'x = 5 '
|
155
|
+
right.should == 'unless y == 2'
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
context '#split_on_keyword' do
|
161
|
+
it 'splits the input into two parts based on the token searched' do
|
162
|
+
left, right = @class.new('rescue x if y').split_on_keyword(:if)
|
163
|
+
left.should == 'rescue x '
|
164
|
+
right.should == 'if y'
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'works with multiple searched tokens' do
|
168
|
+
left, right = @class.new('rescue x if y').split_on_keyword(:if, :rescue)
|
169
|
+
left.should == ''
|
170
|
+
right.should == 'rescue x if y'
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'matches its own documentation' do
|
174
|
+
left, right = @class.new('').split_on_keyword('x = 5 unless y == 2', :unless)
|
175
|
+
left.should == 'x = 5 '
|
176
|
+
right.should == 'unless y == 2'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|