seeing_is_believing 2.0.0.beta1 → 2.0.0.beta2

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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/Readme.md +8 -20
  3. data/features/examples.feature +1 -3
  4. data/features/flags.feature +4 -12
  5. data/features/regression.feature +95 -1
  6. data/lib/seeing_is_believing.rb +17 -21
  7. data/lib/seeing_is_believing/binary.rb +3 -3
  8. data/lib/seeing_is_believing/binary/add_annotations.rb +86 -118
  9. data/lib/seeing_is_believing/binary/align_chunk.rb +22 -23
  10. data/lib/seeing_is_believing/binary/align_file.rb +10 -13
  11. data/lib/seeing_is_believing/binary/align_line.rb +0 -4
  12. data/lib/seeing_is_believing/binary/arg_parser.rb +11 -11
  13. data/lib/seeing_is_believing/binary/clean_body.rb +96 -0
  14. data/lib/seeing_is_believing/binary/comment_formatter.rb +55 -0
  15. data/lib/seeing_is_believing/binary/comment_lines.rb +37 -0
  16. data/lib/seeing_is_believing/binary/commentable_lines.rb +158 -0
  17. data/lib/seeing_is_believing/binary/rewrite_comments.rb +42 -0
  18. data/lib/seeing_is_believing/result.rb +2 -9
  19. data/lib/seeing_is_believing/the_matrix.rb +8 -8
  20. data/lib/seeing_is_believing/version.rb +1 -1
  21. data/lib/seeing_is_believing/{program_rewriter.rb → wrap_expressions.rb} +30 -22
  22. data/spec/binary/arg_parser_spec.rb +7 -7
  23. data/spec/binary/clean_body_spec.rb +217 -0
  24. data/spec/binary/comment_formatter_spec.rb +54 -0
  25. data/spec/binary/comment_lines_spec.rb +847 -0
  26. data/spec/binary/rewrite_comments_spec.rb +54 -0
  27. data/spec/seeing_is_believing_spec.rb +1 -2
  28. data/spec/{program_rewriter_spec.rb → wrap_expressions_spec.rb} +117 -40
  29. metadata +41 -56
  30. data/lib/seeing_is_believing/binary/line_formatter.rb +0 -47
  31. data/lib/seeing_is_believing/binary/remove_previous_annotations.rb +0 -75
  32. data/lib/seeing_is_believing/queue.rb +0 -55
  33. data/lib/seeing_is_believing/remove_inline_comments.rb +0 -46
  34. data/lib/seeing_is_believing/syntax_analyzer.rb +0 -61
  35. data/lib/seeing_is_believing/tracks_line_numbers_seen.rb +0 -19
  36. data/spec/binary/line_formatter_spec.rb +0 -53
  37. data/spec/binary/remove_previous_annotations_spec.rb +0 -198
  38. data/spec/queue_spec.rb +0 -80
  39. data/spec/syntax_analyzer_spec.rb +0 -56
@@ -1,47 +0,0 @@
1
- class SeeingIsBelieving
2
- class Binary
3
- class LineFormatter
4
- attr_accessor :line, :separator, :result, :options
5
-
6
- def initialize(line, separator, result, options)
7
- self.line = line
8
- self.separator = separator
9
- self.result = result.gsub "\n", '\n'
10
- self.options = options
11
- end
12
-
13
- def call
14
- return line unless sep_plus_result.start_with? separator
15
- return line unless formatted_line.start_with? "#{line_with_padding}#{separator}"
16
- formatted_line
17
- end
18
-
19
- private
20
-
21
- def line_length
22
- options.fetch :line_length, Float::INFINITY
23
- end
24
-
25
- def result_length
26
- options.fetch :result_length, Float::INFINITY
27
- end
28
-
29
- def sep_plus_result
30
- @sep_plus_result ||= truncate "#{separator}#{result}", result_length
31
- end
32
-
33
- def formatted_line
34
- @formatted_line ||= truncate "#{line_with_padding}#{sep_plus_result}", line_length
35
- end
36
-
37
- def line_with_padding
38
- "%-#{options[:pad_to]}s" % line
39
- end
40
-
41
- def truncate(string, length)
42
- return string if string.size <= length
43
- string.slice(0, length).sub(/.{0,3}$/) { |last_chars| last_chars.gsub /./, '.' }
44
- end
45
- end
46
- end
47
- end
@@ -1,75 +0,0 @@
1
- require 'seeing_is_believing/remove_inline_comments'
2
-
3
- class SeeingIsBelieving
4
- class Binary
5
- class RemovePreviousAnnotations
6
- def self.call(code)
7
- new(code).call
8
- end
9
-
10
- def initialize(code)
11
- self.code = code
12
- self.comments = { result: [],
13
- exception: [],
14
- stdout: [],
15
- stderr: [],
16
- }
17
- end
18
-
19
- def call
20
- RemoveInlineComments.call code, additional_rewrites: remove_whitespace_preceeding_comments do |comment|
21
- if comment.text[/\A#\s*=>/] then comments[:result] << comment; true
22
- elsif comment.text[/\A#\s*~>/] then comments[:exception] << comment; true
23
- elsif comment.text[/\A#\s*>>/] then comments[:stdout] << comment; true
24
- elsif comment.text[/\A#\s*!>/] then comments[:stderr] << comment; true
25
- else false
26
- end
27
- end
28
- end
29
-
30
- private
31
-
32
- attr_accessor :code, :comments
33
-
34
- def remove_whitespace_preceeding_comments
35
- lambda do |buffer, rewriter|
36
- comments[:result].each { |comment| remove_whitespace_before comment.location.expression.begin_pos, buffer, rewriter, false }
37
- comments[:exception].each { |comment| remove_whitespace_before comment.location.expression.begin_pos, buffer, rewriter, true }
38
- comments[:stdout].each { |comment| remove_whitespace_before comment.location.expression.begin_pos, buffer, rewriter, true }
39
- comments[:stderr].each { |comment| remove_whitespace_before comment.location.expression.begin_pos, buffer, rewriter, true }
40
- end
41
- end
42
-
43
- # any whitespace before the index (on the same line) will be removed
44
- # if the preceeding whitespace is at the beginning of the line, the newline will be removed
45
- # if there is a newline before all of that, and remove_preceeding_newline is true, it will be removed as well
46
- def remove_whitespace_before(index, buffer, rewriter, remove_preceeding_newline)
47
- end_pos = index
48
- begin_pos = end_pos - 1
49
- begin_pos -= 1 while code[begin_pos] =~ /\s/ && code[begin_pos] != "\n"
50
- begin_pos -= 1 if code[begin_pos] == "\n"
51
- begin_pos -= 1 if code[begin_pos] == "\n" && remove_preceeding_newline
52
- return if begin_pos.next == end_pos
53
- rewriter.remove Parser::Source::Range.new(buffer, begin_pos.next, end_pos)
54
- end
55
-
56
- # returns comments in groups that are on consecutive lines
57
- def adjacent_comments(comments, buffer)
58
- comments = comments.sort_by { |comment| comment.location.begin_pos }
59
- current_chunk = 0
60
- last_line_seen = -100
61
- chunks_to_comment = comments.chunk do |comment|
62
- line, col = buffer.decompose_position comment.location.begin_pos
63
- if last_line_seen.next == line
64
- last_line_seen = line
65
- current_chunk
66
- else
67
- last_line_seen = line
68
- current_chunk += 1
69
- end
70
- end
71
- chunks_to_comment.map &:last
72
- end
73
- end
74
- end
75
- end
@@ -1,55 +0,0 @@
1
- class SeeingIsBelieving
2
- class Queue
3
- class While
4
- attr_accessor :queue, :conditional
5
-
6
- def initialize(queue, &conditional)
7
- self.queue, self.conditional = queue, conditional
8
- end
9
-
10
- def each(&block)
11
- block.call queue.dequeue while !queue.empty? && conditional.call(queue.peek)
12
- end
13
- end
14
- end
15
-
16
- class Queue
17
- attr_accessor :value_generator
18
-
19
- def initialize(&value_generator)
20
- self.value_generator = value_generator
21
- end
22
-
23
- def dequeue
24
- return if permanently_empty?
25
- peek.tap { @next_value = nil }
26
- end
27
-
28
- def peek
29
- return if permanently_empty?
30
- @next_value ||= value_generator.call.tap { |value| @permanently_empty = value.nil? }
31
- end
32
-
33
- def empty?
34
- permanently_empty? || peek.nil?
35
- end
36
-
37
- def each(&block)
38
- block.call dequeue until empty?
39
- end
40
-
41
- def until(&block)
42
- While.new(self) { |*args| !block.call(*args) }
43
- end
44
-
45
- def while(&block)
46
- While.new self, &block
47
- end
48
-
49
- private
50
-
51
- def permanently_empty?
52
- @permanently_empty
53
- end
54
- end
55
- end
@@ -1,46 +0,0 @@
1
- require 'parser/current'
2
-
3
- class SeeingIsBelieving
4
- module RemoveInlineComments
5
- module NonLeading
6
- def self.call(code)
7
- ranges = []
8
-
9
- nonleading_comments = lambda do |buffer, rewriter|
10
- ranges.sort_by(&:begin_pos)
11
- .drop_while.with_index(1) { |range, index|
12
- line, col = buffer.decompose_position range.begin_pos
13
- index == line && col.zero?
14
- }
15
- .each { |range| rewriter.remove range }
16
- end
17
-
18
- RemoveInlineComments.call code, additional_rewrites: nonleading_comments do |comment|
19
- ranges << comment.location
20
- false
21
- end
22
- end
23
- end
24
-
25
- extend self
26
-
27
- # selector is a block that will receive the comment object
28
- # if it returns true, the comment will be removed
29
- def self.call(code, options={}, &selector)
30
- selector ||= Proc.new { true }
31
- additional_rewrites = options.fetch :additional_rewrites, Proc.new {}
32
- buffer = Parser::Source::Buffer.new "strip_comments"
33
- buffer.source = code
34
- parser = Parser::CurrentRuby.new
35
- rewriter = Parser::Source::Rewriter.new(buffer)
36
- ast, comments = parser.parse_with_comments(buffer)
37
- comments.select { |comment| comment.type == :inline }
38
- .select { |comment| selector.call comment }
39
- .each { |comment| rewriter.remove comment.location.expression }
40
- additional_rewrites.call buffer, rewriter
41
- rewriter.process
42
- rescue Parser::SyntaxError => e
43
- raise SyntaxError, e.message
44
- end
45
- end
46
- end
@@ -1,61 +0,0 @@
1
- require 'ripper'
2
- require 'parser/current'
3
-
4
- class SeeingIsBelieving
5
- class SyntaxAnalyzer < Ripper::SexpBuilder
6
-
7
- # HELPERS
8
-
9
- def self.parsed(code)
10
- instance = new code
11
- instance.parse
12
- instance
13
- end
14
-
15
- # SYNTACTIC VALIDITY
16
-
17
- def self.begins_multiline_comment?(line)
18
- line == '=begin'
19
- end
20
-
21
- def self.ends_multiline_comment?(line)
22
- line == '=end'
23
- end
24
-
25
- def self.begin_and_end_comments_are_complete?(code)
26
- code.scan(/^=(?:begin|end)$/)
27
- .each_slice(2)
28
- .all? { |b, e| b == '=begin' && e == '=end' }
29
- end
30
-
31
- # MISC
32
-
33
- def self.begins_data_segment?(line)
34
- line == '__END__'
35
- end
36
-
37
- # COMMENTS
38
-
39
- def self.line_is_comment?(line)
40
- line =~ /^\s*#/
41
- end
42
-
43
- def self.ends_in_comment?(code)
44
- # must do the newline hack or it totally fucks up on comments like "# Transfer-Encoding: chunked"
45
- code =~ /^=end\Z/ || parsed("\n#{code.lines.to_a.last.to_s}").has_comment?
46
- end
47
-
48
- def self.unclosed_comment?(code)
49
- !begin_and_end_comments_are_complete?(code)
50
- end
51
-
52
- def has_comment?
53
- @has_comment
54
- end
55
-
56
- def on_comment(*)
57
- @has_comment = true
58
- super
59
- end
60
- end
61
- end
@@ -1,19 +0,0 @@
1
- class SeeingIsBelieving
2
- module TracksLineNumbersSeen
3
- INITIAL_LINE_NUMBER = 1 # uhm, should this change to 0?
4
-
5
- def track_line_number(line_number)
6
- @min_line_number = line_number if line_number < min_line_number
7
- @max_line_number = line_number if line_number > max_line_number
8
- self
9
- end
10
-
11
- def min_line_number
12
- @min_line_number || INITIAL_LINE_NUMBER
13
- end
14
-
15
- def max_line_number
16
- @max_line_number || INITIAL_LINE_NUMBER
17
- end
18
- end
19
- end
@@ -1,53 +0,0 @@
1
- require 'seeing_is_believing/binary/line_formatter'
2
-
3
- describe SeeingIsBelieving::Binary::LineFormatter do
4
- def result_for(line, separator, result, options={})
5
- described_class.new(line, separator, result, options).call
6
- end
7
-
8
- specify 'it returns the consolidated result if there are no truncations' do
9
- result_for('1', '=>', '12345').should == '1=>12345'
10
- end
11
-
12
- specify 'result_length truncates a result to the specified length, using elipses up to that length if appropriate' do
13
- line = '1'
14
- separator = '=>'
15
- result = '12345'
16
- result_for(line, separator, result, result_length: Float::INFINITY).should == '1=>12345'
17
- result_for(line, separator, result, result_length: 7).should == '1=>12345'
18
- result_for(line, separator, result, result_length: 6).should == '1=>1...'
19
- result_for(line, separator, result, result_length: 5).should == '1=>...'
20
- result_for(line, separator, result, result_length: 4).should == '1'
21
- result_for(line, separator, result, result_length: 0).should == '1'
22
- end
23
-
24
- specify 'line_length truncates a result to the specified length, minus the length of the line' do
25
- line = '1'
26
- separator = '=>'
27
- result = '12345'
28
- result_for(line, separator, result).should == '1=>12345'
29
- result_for(line, separator, result, line_length: Float::INFINITY).should == '1=>12345'
30
- result_for(line, separator, result, line_length: 8).should == '1=>12345'
31
- result_for(line, separator, result, line_length: 7).should == '1=>1...'
32
- result_for(line, separator, result, line_length: 6).should == '1=>...'
33
- result_for(line, separator, result, line_length: 5).should == '1'
34
- result_for(line, separator, result, line_length: 0).should == '1'
35
- end
36
-
37
- specify 'pad_to will pad the length that the line is displayed in' do
38
- result_for('1', '=>', '2', pad_to: 0).should == '1=>2'
39
- result_for('1', '=>', '2', pad_to: 1).should == '1=>2'
40
- result_for('1', '=>', '2', pad_to: 2).should == '1 =>2'
41
- end
42
-
43
- specify 'pad_to is ignored when separator/result will not be printed' do
44
- result_for('1', '=>', '12345', pad_to: 2, line_length: 2).should == '1'
45
- result_for('1', '=>', '12345', pad_to: 2, result_length: 2).should == '1'
46
- end
47
-
48
- specify 'they can all work together' do
49
- result_for('1', '=>', '12345', line_length: 100, result_length: 100, pad_to: 2).should == '1 =>12345'
50
- result_for('1', '=>', '12345', line_length: 8, result_length: 100, pad_to: 2).should == '1 =>1...'
51
- result_for('1', '=>', '12345', line_length: 100, result_length: 6, pad_to: 2).should == '1 =>1...'
52
- end
53
- end
@@ -1,198 +0,0 @@
1
- require 'seeing_is_believing/binary/remove_previous_annotations'
2
-
3
- describe SeeingIsBelieving::Binary::RemovePreviousAnnotations do
4
- def call(code)
5
- indentation = code[/\A */]
6
- code = code.gsub /^#{indentation}/, ''
7
- described_class.call(code).chomp
8
- end
9
-
10
- # show some examples of what it should not remove
11
-
12
- example { call("1#=>1").should == "1" }
13
- example { call("1 #=>1").should == "1" }
14
- example { call("1 #=>1").should == "1" }
15
- example { call("1 #=> 1").should == "1" }
16
- example { call("1 #=> 1").should == "1" }
17
- example { call("1 #=> 1").should == "1" }
18
- example { call("\n1 # => 1").should == "\n1" }
19
-
20
- example { call("1#~>1").should == "1" }
21
- example { call("1 #~>1").should == "1" }
22
- example { call("1 #~>1").should == "1" }
23
- example { call("1 #~> 1").should == "1" }
24
- example { call("1 #~> 1").should == "1" }
25
- example { call("1 #~> 1").should == "1" }
26
- example { call("\n1 # ~> 1").should == "\n1" }
27
-
28
- example { call("# >> 1").should == "" }
29
- example { call("# !> 1").should == "" }
30
-
31
- example { call(<<-CODE).should == "1" }
32
- 1
33
- # >> 2
34
- CODE
35
-
36
- example { call(<<-CODE).should == "1" }
37
- 1
38
-
39
- # >> 2
40
- CODE
41
-
42
- example { call(<<-CODE).should == "1\n" }
43
- 1
44
-
45
-
46
- # >> 2
47
- CODE
48
-
49
- example { call(<<-CODE).should == "1\n" }
50
- 1
51
-
52
-
53
- # >> 2
54
- # >> 2
55
- # >> 2
56
- CODE
57
-
58
-
59
- example { call(<<-CODE).should == "1\n" }
60
- 1
61
-
62
-
63
- # >> 2
64
- # >> 3
65
- CODE
66
-
67
- example { call(<<-CODE).should == "1" }
68
- 1
69
- # !> 2
70
- CODE
71
-
72
- example { call(<<-CODE).should == "1" }
73
- 1
74
-
75
- # !> 2
76
- CODE
77
-
78
- example { call(<<-CODE).should == "1" }
79
- 1
80
-
81
- # !> 2
82
- # !> 3
83
- CODE
84
-
85
- example { call(<<-CODE).should == "1\n" }
86
- 1
87
-
88
-
89
- # !> 2
90
- # !> 2
91
- # !> 2
92
- CODE
93
-
94
-
95
- example { call(<<-CODE).should == "1" }
96
- 1
97
- # ~>2
98
- CODE
99
-
100
- example { call(<<-CODE).should == "1" }
101
- 1
102
-
103
- # ~> 2
104
- CODE
105
-
106
- example { call(<<-CODE).should == "1" }
107
- 1
108
-
109
- # ~> 2
110
- # ~> 3
111
- CODE
112
-
113
- example { call(<<-CODE).should == "1\n" }
114
- 1
115
-
116
-
117
- # ~> 2
118
- # ~> 2
119
- # ~> 2
120
- CODE
121
-
122
- example { call(<<-CODE).should == "1\n" }
123
- 1 # ~> error
124
-
125
-
126
- # ~> error again
127
- CODE
128
-
129
- example { call(<<-CODE).should == "1" }
130
- 1
131
-
132
- # >> 1
133
- # >> 2
134
-
135
- # !> 3
136
- # !> 4
137
-
138
- # ~> 5
139
- # ~> 6
140
- CODE
141
-
142
- example { call(<<-CODE).should == "1" }
143
- 1
144
-
145
- # >> 1
146
-
147
- # >> 2
148
-
149
- # !> 3
150
-
151
- # !> 4
152
-
153
- # ~> 5
154
-
155
- # ~> 6
156
- CODE
157
-
158
- example { call(<<-CODE).should == "1" }
159
- 1
160
-
161
- # >> 1
162
- # >> 2
163
- # !> 3
164
- # !> 4
165
- # ~> 5
166
- # ~> 6
167
- CODE
168
-
169
- example { call(<<-CODE).should == "1\n3" }
170
- 1
171
- # >> 1
172
- # >> 2
173
- 3
174
- # !> 4
175
- # !> 5
176
- CODE
177
-
178
- example { call(<<-CODE).should == "1\n3\n6" }
179
- 1
180
- # >> 1
181
- # >> 2
182
- 3
183
- # !> 4
184
- # !> 5
185
- 6
186
- # ~> 7
187
- # ~> 8
188
- CODE
189
-
190
- example { call(<<-CODE).should == "1\n\nputs \"omg\"" }
191
- 1 # => 1
192
-
193
- puts "omg" # ~> RuntimeError: omg
194
-
195
- # ~> RuntimeError
196
- CODE
197
-
198
- end