seeing_is_believing 3.0.0.beta.4 → 3.0.0.beta.5

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -8
  3. data/Rakefile +1 -1
  4. data/Readme.md +65 -25
  5. data/bin/seeing_is_believing +1 -0
  6. data/docs/sib-streaming.gif +0 -0
  7. data/features/deprecated-flags.feature +62 -2
  8. data/features/errors.feature +12 -7
  9. data/features/examples.feature +143 -4
  10. data/features/flags.feature +89 -29
  11. data/features/regression.feature +58 -14
  12. data/features/support/env.rb +4 -0
  13. data/features/xmpfilter-style.feature +181 -36
  14. data/lib/seeing_is_believing.rb +44 -33
  15. data/lib/seeing_is_believing/binary.rb +31 -88
  16. data/lib/seeing_is_believing/binary/align_chunk.rb +30 -11
  17. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +10 -16
  18. data/lib/seeing_is_believing/binary/annotate_every_line.rb +5 -25
  19. data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +136 -0
  20. data/lib/seeing_is_believing/binary/comment_lines.rb +8 -10
  21. data/lib/seeing_is_believing/binary/commentable_lines.rb +20 -26
  22. data/lib/seeing_is_believing/binary/config.rb +392 -0
  23. data/lib/seeing_is_believing/binary/data_structures.rb +57 -0
  24. data/lib/seeing_is_believing/binary/engine.rb +104 -0
  25. data/lib/seeing_is_believing/binary/{comment_formatter.rb → format_comment.rb} +6 -6
  26. data/lib/seeing_is_believing/binary/remove_annotations.rb +29 -28
  27. data/lib/seeing_is_believing/binary/rewrite_comments.rb +42 -43
  28. data/lib/seeing_is_believing/code.rb +105 -49
  29. data/lib/seeing_is_believing/debugger.rb +6 -5
  30. data/lib/seeing_is_believing/error.rb +6 -17
  31. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +78 -129
  32. data/lib/seeing_is_believing/event_stream/consumer.rb +114 -64
  33. data/lib/seeing_is_believing/event_stream/events.rb +169 -11
  34. data/lib/seeing_is_believing/event_stream/handlers/debug.rb +57 -0
  35. data/lib/seeing_is_believing/event_stream/handlers/record_exitstatus.rb +18 -0
  36. data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +45 -0
  37. data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +39 -0
  38. data/lib/seeing_is_believing/event_stream/producer.rb +25 -24
  39. data/lib/seeing_is_believing/hash_struct.rb +206 -0
  40. data/lib/seeing_is_believing/result.rb +20 -3
  41. data/lib/seeing_is_believing/the_matrix.rb +20 -12
  42. data/lib/seeing_is_believing/version.rb +1 -1
  43. data/lib/seeing_is_believing/wrap_expressions.rb +55 -115
  44. data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +14 -0
  45. data/seeing_is_believing.gemspec +1 -1
  46. data/spec/binary/alignment_specs.rb +27 -0
  47. data/spec/binary/comment_lines_spec.rb +3 -2
  48. data/spec/binary/config_spec.rb +657 -0
  49. data/spec/binary/engine_spec.rb +97 -0
  50. data/spec/binary/{comment_formatter_spec.rb → format_comment_spec.rb} +2 -2
  51. data/spec/binary/marker_spec.rb +71 -0
  52. data/spec/binary/options_spec.rb +0 -0
  53. data/spec/binary/remove_annotations_spec.rb +31 -18
  54. data/spec/binary/rewrite_comments_spec.rb +26 -11
  55. data/spec/code_spec.rb +190 -6
  56. data/spec/debugger_spec.rb +4 -0
  57. data/spec/evaluate_by_moving_files_spec.rb +38 -20
  58. data/spec/event_stream_spec.rb +265 -116
  59. data/spec/hash_struct_spec.rb +514 -0
  60. data/spec/seeing_is_believing_spec.rb +108 -46
  61. data/spec/spec_helper.rb +9 -0
  62. data/spec/wrap_expressions_spec.rb +207 -172
  63. metadata +30 -18
  64. data/docs/for-presentations +0 -33
  65. data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +0 -128
  66. data/lib/seeing_is_believing/binary/interpret_flags.rb +0 -156
  67. data/lib/seeing_is_believing/binary/parse_args.rb +0 -263
  68. data/lib/seeing_is_believing/event_stream/update_result.rb +0 -24
  69. data/lib/seeing_is_believing/inspect_expressions.rb +0 -21
  70. data/lib/seeing_is_believing/parser_helpers.rb +0 -82
  71. data/spec/binary/interpret_flags_spec.rb +0 -332
  72. data/spec/binary/parse_args_spec.rb +0 -415
@@ -0,0 +1,97 @@
1
+ require 'seeing_is_believing/binary/engine'
2
+ require 'seeing_is_believing/binary/config'
3
+
4
+ class SeeingIsBelieving
5
+ module Binary
6
+ RSpec.describe Engine do
7
+ def call(body, options={})
8
+ timeout = options.fetch :timeout, 0
9
+ filename = options.fetch :filename, "program.rb"
10
+ config = Config.new body: body, timeout_seconds: timeout
11
+ config.lib_options.timeout_seconds = timeout
12
+ config.lib_options.filename = filename
13
+ Engine.new config
14
+ end
15
+
16
+ def assert_must_evaluate(message)
17
+ engine = call ''
18
+ expect { engine.__send__ message }.to raise_error MustEvaluateFirst, /#{message}/
19
+ engine.evaluate!
20
+ engine.__send__ message
21
+ end
22
+
23
+ context 'syntax' do
24
+ let(:valid_engine) { call "1+1", filename: "filename.rb" }
25
+ let(:invalid_engine) { call "1+", filename: "filename.rb" }
26
+
27
+ specify 'syntax_error? is true when the body was syntactically invalid' do
28
+ expect( valid_engine.syntax_error?).to eq false
29
+ expect(invalid_engine.syntax_error?).to eq true
30
+ end
31
+
32
+ specify 'syntax_error contains the syntax\'s error message with file and line information' do
33
+ expect(valid_engine.syntax_error).to eq nil
34
+
35
+ allow_any_instance_of(Code::Syntax).to receive(:error_message).and_return "ERR!!"
36
+ allow_any_instance_of(Code::Syntax).to receive(:line_number).and_return 123
37
+ expect(invalid_engine.syntax_error)
38
+ .to eq SyntaxErrorMessage.new(line_number: 123, filename: "filename.rb", explanation: "ERR!!")
39
+ end
40
+ end
41
+
42
+ context 'cleaned_body' do
43
+ it 'has the annotations removed' do
44
+ expect(call("1 # =>").cleaned_body).to eq "1"
45
+ end
46
+ it 'ends in a newline if the body ended in a newline' do
47
+ expect(call("1").cleaned_body).to eq "1"
48
+ expect(call("1\n").cleaned_body).to eq "1\n"
49
+ end
50
+ end
51
+
52
+ context 'before evaluating it raises if asked for' do
53
+ specify('result') { assert_must_evaluate :result }
54
+ specify('exitstatus') { assert_must_evaluate :exitstatus }
55
+ specify('timed_out?') { assert_must_evaluate :timed_out? }
56
+ specify('annotated_body') { assert_must_evaluate :annotated_body }
57
+ end
58
+
59
+ context 'after evaluating' do
60
+ specify 'result is the result of the evaluation' do
61
+ status = call('exit 55').evaluate!.result.exitstatus
62
+ expect(status).to eq 55
63
+ end
64
+
65
+ it 'has recorded the exitstatus' do
66
+ engine = call('exit 88')
67
+ engine.evaluate!
68
+ expect(engine.exitstatus).to eq 88
69
+ end
70
+
71
+ specify 'timed_out? is true if the program raised a Timeout::Error' do
72
+ expect(call('', timeout: 1).evaluate!.timed_out?).to eq false
73
+ expect(call('sleep 1', timeout: 0.01).evaluate!.timed_out?).to eq true
74
+ end
75
+
76
+ context 'annotated_body' do
77
+ it 'is the body after being run through the annotator' do
78
+ expect(call("1").evaluate!.annotated_body).to eq "1 # => 1"
79
+ end
80
+ it 'ends in a newline if the body ended in a newline' do
81
+ expect(call("1").evaluate!.annotated_body).to eq "1 # => 1"
82
+ expect(call("1\n").evaluate!.annotated_body).to eq "1 # => 1\n"
83
+ end
84
+ end
85
+
86
+ it 'evaluation errors are raised up, and it behaves as if it was not evaluated' do
87
+ evaluated = call('1').evaluate!
88
+ unevaluated = call '1'
89
+ expect(SeeingIsBelieving).to receive(:call).exactly(2).times.and_raise(ArgumentError)
90
+ evaluated.evaluate!
91
+ expect { unevaluated.evaluate! }.to raise_error ArgumentError
92
+ expect { unevaluated.evaluate! }.to raise_error ArgumentError
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -1,9 +1,9 @@
1
1
  # encoding: utf-8
2
2
 
3
3
  require 'spec_helper'
4
- require 'seeing_is_believing/binary/comment_formatter'
4
+ require 'seeing_is_believing/binary/format_comment'
5
5
 
6
- RSpec.describe SeeingIsBelieving::Binary::CommentFormatter do
6
+ RSpec.describe SeeingIsBelieving::Binary::FormatComment do
7
7
  def result_for(line_length, separator, result, options={})
8
8
  described_class.new(line_length, separator, result, options).call
9
9
  end
@@ -0,0 +1,71 @@
1
+ require 'seeing_is_believing/binary/data_structures'
2
+
3
+ RSpec.describe SeeingIsBelieving::Binary::Marker do
4
+
5
+ describe 'to_regex' do
6
+ def assert_parses(input, regex)
7
+ expect(described_class.to_regex input).to eq regex
8
+ end
9
+
10
+ it 'doesn\'t change existing regexes' do
11
+ assert_parses /^.$/ix, /^.$/ix
12
+ end
13
+
14
+ it 'converts strings into regexes' do
15
+ assert_parses '', %r()
16
+ assert_parses 'a', %r(a)
17
+ end
18
+
19
+ it 'ignores surrounding slashes' do
20
+ assert_parses '//', %r()
21
+ assert_parses '/a/', %r(a)
22
+ end
23
+
24
+ it 'respects flags after the trailing slash in surrounding slashes' do
25
+ assert_parses '/a/', %r(a)
26
+ assert_parses '/a//', %r(a/)
27
+ assert_parses '//a/', %r(/a)
28
+ assert_parses '/a/i', %r(a)i
29
+ assert_parses '/a/im', %r(a)im
30
+ assert_parses '/a/xim', %r(a)xim
31
+ assert_parses '/a/mix', %r(a)mix
32
+ assert_parses '/a/mixi', %r(a)mixi
33
+ end
34
+
35
+ it 'isn\'t fooled by strings that kinda look regexy' do
36
+ assert_parses '/a', %r(/a)
37
+ assert_parses 'a/', %r(a/)
38
+ assert_parses '/', %r(/)
39
+ assert_parses '/i', %r(/i)
40
+ end
41
+
42
+ it 'does not escape the content' do
43
+ assert_parses 'a\\s+', %r(a\s+)
44
+ assert_parses '/a\\s+/', %r(a\s+)
45
+ end
46
+ end
47
+
48
+ it 'requires prefix and a regex' do
49
+ described_class.new prefix: '', regex: //
50
+ expect { described_class.new }.to raise_error
51
+ expect { described_class.new prefix: '' }.to raise_error
52
+ expect { described_class.new regex: // }.to raise_error
53
+ end
54
+
55
+ it 'stores the prefix and a regex' do
56
+ marker = described_class.new(prefix: 't', regex: /r/)
57
+ expect(marker.prefix).to eq 't'
58
+ expect(marker.regex).to eq /r/
59
+ end
60
+
61
+ it 'converts strings to rgexes when they are set' do
62
+ marker = described_class.new prefix: 't', regex: 'r1'
63
+ expect(marker[:regex]).to eq /r1/
64
+
65
+ marker.regex = '/r2/i'
66
+ expect(marker.regex).to eq /r2/i
67
+
68
+ marker[:regex] = 'r3'
69
+ expect(marker.regex).to eq /r3/
70
+ end
71
+ end
File without changes
@@ -1,20 +1,19 @@
1
1
  require 'spec_helper'
2
2
  require 'seeing_is_believing/binary/remove_annotations'
3
- require 'seeing_is_believing/binary/parse_args' # for marker info
3
+ require 'seeing_is_believing/binary/data_structures'
4
4
 
5
5
  RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
6
+ def regexes
7
+ SeeingIsBelieving::Binary::Markers.new
8
+ end
9
+
6
10
  def call(code, should_clean_values=true)
7
11
  indentation = code[/\A */]
8
12
  code = code.gsub /^#{indentation}/, ''
13
+ code << "\n" unless code.end_with? "\n"
9
14
  described_class.call(code, should_clean_values, regexes).chomp
10
15
  end
11
16
 
12
- def regexes
13
- SeeingIsBelieving::Binary::ParseArgs
14
- .marker_regexes
15
- .each_with_object({}) { |(name, str), rs| rs[name] = Regexp.new str }
16
- end
17
-
18
17
  context 'when there are lines that are just normal comments' do
19
18
  example { expect(call "1 # hello").to eq "1 # hello" }
20
19
  example { expect(call "1 # hello\n"\
@@ -31,17 +30,19 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
31
30
  example { expect(call "1 # => 1", true).to eq "1" }
32
31
  example { expect(call "1 # => 1", true).to eq "1" }
33
32
  example { expect(call "1 # => 1", true).to eq "1" }
33
+ example { expect(call "1 # => 1", true).to eq "1" }
34
34
  example { expect(call "\n1 # => 1", true).to eq "\n1" }
35
35
  end
36
36
 
37
- context 'when told not to clean out value annotations' do
38
- example { expect(call "1# => 1", false).to eq "1# => 1" }
39
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
40
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
41
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
42
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
43
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
44
- example { expect(call "\n1 # => 1", false).to eq "\n1 # => 1" }
37
+ context 'when told not to clean out value markers, it leaves the marker, but removes the annotation and trailing whitespace' do
38
+ example { expect(call "1# => 1", false).to eq "1# =>" }
39
+ example { expect(call "1 # => 1", false).to eq "1 # =>" }
40
+ example { expect(call "1 # => 1", false).to eq "1 # =>" }
41
+ example { expect(call "1 # => 1", false).to eq "1 # =>" }
42
+ example { expect(call "1 # => 1", false).to eq "1 # =>" }
43
+ example { expect(call "1 # => 1", false).to eq "1 # =>" }
44
+ example { expect(call "1 # => 1", false).to eq "1 # =>" }
45
+ example { expect(call "\n1 # => 1", false).to eq "\n1 # =>" }
45
46
  end
46
47
 
47
48
  context 'cleaning inline exception annotations' do
@@ -53,8 +54,8 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
53
54
  example { expect(call "1 # ~> 1").to eq "1" }
54
55
  example { expect(call "\n1 # ~> 1").to eq "\n1" }
55
56
 
56
- example { expect(call "# >> 1").to eq "" }
57
- example { expect(call "# !> 1").to eq "" }
57
+ example { expect(call "# >> 1").to eq "" }
58
+ example { expect(call "# !> 1").to eq "" }
58
59
  end
59
60
 
60
61
  context 'cleaning multiline results' do
@@ -143,7 +144,7 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
143
144
 
144
145
  expect(call "1# => 2\n"\
145
146
  " # 3",
146
- false).to eq "1# => 2"
147
+ false).to eq "1# =>"
147
148
  end
148
149
 
149
150
  it 'works on inline exceptions' do
@@ -188,6 +189,12 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
188
189
  # >> 2
189
190
  # >> 3
190
191
  CODE
192
+
193
+ example { expect(call <<-CODE.gsub(/^\s*/, '')).to eq "\n1" }
194
+
195
+ # >> err
196
+ 1
197
+ CODE
191
198
  end
192
199
 
193
200
  context 'cleaning stderr annotations' do
@@ -217,6 +224,12 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
217
224
  # !> 2
218
225
  # !> 2
219
226
  CODE
227
+
228
+ example { expect(call <<-CODE.gsub(/^\s*/, '')).to eq "\n1" }
229
+
230
+ # !> err
231
+ 1
232
+ CODE
220
233
  end
221
234
 
222
235
 
@@ -3,7 +3,8 @@ require 'seeing_is_believing/binary/rewrite_comments'
3
3
 
4
4
  RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
5
5
  def call(code, options={}, &block)
6
- described_class.call code, options, &block
6
+ code = code + "\n" unless code.end_with? "\n"
7
+ described_class.call(code, options, &block).chomp
7
8
  end
8
9
 
9
10
  it 'ignores multiline comments' do
@@ -57,9 +58,10 @@ RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
57
58
  rewritten = call("'a'\n"\
58
59
  "'b'\n"\
59
60
  "'c' # c\n"\
60
- "'d'",
61
- always_rewrite: [2, 3]) do |c|
62
- value = sprintf "%d|%d|%p|%d|%p|%d..%d|%d..%d|%d..%d",
61
+ "'d' \n"\
62
+ "'e'",
63
+ include_lines: [2, 3, 4, 5]) do |c|
64
+ value = sprintf "%d|%d|%p|%d|%p|%d...%d|%d...%d|%d...%d",
63
65
  c.line_number,
64
66
  c.whitespace_col,
65
67
  c.whitespace,
@@ -75,17 +77,30 @@ RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
75
77
  end
76
78
  expect(rewritten).to eq \
77
79
  "'a'\n"\
78
- "'b'pre2|3|\"\"|3|\"\"|7..7|7..7|7..7\n"\
79
- "'c'pre3|3|\" \"|4|\"# c\"|11..15|11..12|12..15\n"\
80
- "'d'"
80
+ "'b'pre2|3|\"\"|3|\"\"|7...7|7...7|7...7\n"\
81
+ "'c'pre3|3|\" \"|4|\"# c\"|11...15|11...12|12...15\n"\
82
+ "'d'pre4|3|\" \"|4|\"\"|19...20|19...20|20...20\n"\
83
+ "'e'pre5|3|\"\"|3|\"\"|24...24|24...24|24...24"
81
84
 
82
- rewritten = call("", always_rewrite: [1]) { |c| ['a', 'b'] }
85
+ rewritten = call("", include_lines: [1]) { |c| ['a', 'b'] }
83
86
  expect(rewritten).to eq "ab"
84
87
 
85
- rewritten = call("a", always_rewrite: [1]) { |c| ['b', 'c'] }
88
+ rewritten = call("a", include_lines: [1]) { |c| ['b', 'c'] }
86
89
  expect(rewritten).to eq "abc"
87
90
 
88
- rewritten = call("a\n", always_rewrite: [1]) { |c| ['b', 'c'] }
89
- expect(rewritten).to eq "abc\n"
91
+ rewritten = call("a ", include_lines: [1]) { |c| ['b', 'c'] }
92
+ expect(rewritten).to eq "abc"
93
+ end
94
+
95
+ it 'does not include lines that are uncommentable' do
96
+ rewritten = call("'a\n"\
97
+ "b'",
98
+ include_lines: [1, 2]) { ["whitespace", "comment"] }
99
+ expect(rewritten).to eq "'a\n"\
100
+ "b'whitespacecomment"
101
+ end
102
+
103
+ it 'blows up if given unknown options' do
104
+ expect { call '1', not_an_option: nil }.to raise_error /not_an_option/
90
105
  end
91
106
  end
@@ -1,12 +1,196 @@
1
- # require 'seeing_is_believing/code'
1
+ require 'seeing_is_believing/code'
2
+
3
+ RSpec.describe SeeingIsBelieving::Code do
4
+ def code_for(raw_code, options={})
5
+ manage_newline = options.fetch :nl, true
6
+ raw_code += "\n" if manage_newline && !raw_code.end_with?("\n")
7
+ described_class.new(raw_code)
8
+ end
9
+
10
+ def assert_range(range, begin_pos, end_pos)
11
+ expect(range.begin_pos).to eq begin_pos
12
+ expect(range.end_pos).to eq end_pos
13
+ end
14
+
15
+ it 'raises a SyntaxError if given a file that does not end in a newline' do
16
+ code_for "\n", nl: false
17
+ expect { code_for "", nl: false }.to raise_error SyntaxError, /newline/i
18
+
19
+ code_for "1\n", nl: false
20
+ expect { code_for "1", nl: false }.to raise_error SyntaxError, /newline/i
21
+ end
22
+
23
+ describe '#range_for' do
24
+ it 'returns a range object with the specified start and ends' do
25
+ code = code_for "abcd"
26
+ range = code.range_for(1, 2)
27
+ expect(range.begin_pos).to eq 1
28
+ expect(range.end_pos).to eq 2
29
+ expect(range.source).to eq 'b'
30
+ end
31
+ end
32
+
33
+ describe '#root' do
34
+ it 'returns the root for valid code' do
35
+ expect(code_for('1').root.type).to eq :int
36
+ end
37
+ it 'returns a null root for invalid code' do
38
+ root = code_for('"').root
39
+ expect(root.type).to eq :null_node
40
+ assert_range root.location.expression, 0, 0
41
+ end
42
+ it 'returns a null root for empty code' do
43
+ root = code_for('').root
44
+ expect(root.type).to eq :null_node
45
+ assert_range root.location.expression, 0, 0
46
+ end
47
+ end
48
+
49
+ describe '#index_to_linenum' do
50
+ it 'treats indexes as 0based and lines as 1based' do
51
+ code = code_for "xx\nyyy\n\nzz"
52
+ [[1,0], [1,1], [1,2],
53
+ [2,3], [2,4], [2,5], [2,6],
54
+ [3,7],
55
+ [4,8], [4,9],
56
+ ].each do |expected_lineno, index|
57
+ actual_lineno = code.index_to_linenum index
58
+ expect(expected_lineno).to eq(actual_lineno),
59
+ "index: #{index}\n"\
60
+ "expected lineno: #{expected_lineno}\n"\
61
+ "actual lineno: #{actual_lineno.inspect}"
62
+ end
63
+ end
64
+
65
+ it 'considers any indexes after the end to be on the last line' do
66
+ expect(code_for("a\nb\nc\n").index_to_linenum(1000)).to eq 4
67
+ end
68
+ end
69
+
70
+ describe '#linenum_to_index' do
71
+ it 'treats line numebrs as 1based and indexes as 0based' do
72
+ code = code_for "xx\nyyy\n\nzz\n"
73
+ expect(code.linenum_to_index 1).to eq 0
74
+ expect(code.linenum_to_index 2).to eq 3
75
+ expect(code.linenum_to_index 3).to eq 7
76
+ expect(code.linenum_to_index 4).to eq 8
77
+ expect(code.linenum_to_index 5).to eq 11
78
+ end
79
+
80
+ it 'considers any lines past the end to be at 1 character after the last index' do
81
+ expect(code_for("abc\n").linenum_to_index(100)).to eq 4
82
+ end
83
+ end
84
+
85
+ # should it begin after magic comments and stuff?
86
+ describe '#body_range' do
87
+ it 'returns a range for the whole body' do
88
+ assert_range code_for("\n").body_range, 0, 1
89
+ assert_range code_for("1\n").body_range, 0, 2
90
+ assert_range code_for("1111\n").body_range, 0, 5
91
+ end
92
+
93
+ it 'ends after the last token prior to __END__ statements' do
94
+ assert_range code_for("__END__\n").body_range, 0, 0
95
+ assert_range code_for("\n__END__\n").body_range, 0, 1
96
+ assert_range code_for("a\n__END__\n").body_range, 0, 2
97
+ assert_range code_for("a\n\n\n__END__\n").body_range, 0, 2
98
+ end
99
+
100
+ it 'ends after trailing comments' do
101
+ assert_range code_for("1#a\n").body_range, 0, 4
102
+ assert_range code_for("1#a\n#b\n#c\n").body_range, 0, 10
103
+ assert_range code_for("1#a\n#b\n#c\n\n").body_range, 0, 10
104
+ assert_range code_for("a\n#c\n\n__END__\n").body_range, 0, 5
105
+ assert_range code_for("1#a\n#b\n#c\n__END__\n").body_range, 0, 10
106
+ assert_range code_for("1#a\n#b\n#c\n\n__END__\n").body_range, 0, 10
107
+ end
108
+
109
+ it 'ends after heredocs' do
110
+ assert_range code_for("<<a\nb\na\n").body_range, 0, 8
111
+ assert_range code_for("<<a\nb\na\n1\n").body_range, 0, 10
112
+ assert_range code_for("<<a\nb\na\n#c\n").body_range, 0, 11
113
+ end
114
+ end
115
+
116
+ describe 'void value expressions' do
117
+ def void_value?(ast)
118
+ code_for("\n").void_value?(ast)
119
+ end
120
+
121
+ def ast_for(code)
122
+ Parser::CurrentRuby.parse code
123
+ end
124
+
125
+ it 'knows a `return`, `next`, `redo`, `retry`, and `break` are void values' do
126
+ expect(void_value?(ast_for("def a; return; end").children.last)).to be true
127
+ expect(void_value?(ast_for("loop { next }" ).children.last)).to be true
128
+ expect(void_value?(ast_for("loop { redo }" ).children.last)).to be true
129
+ expect(void_value?(ast_for("loop { break }" ).children.last)).to be true
130
+
131
+ the_retry = ast_for("begin; rescue; retry; end").children.first.children[1].children.last
132
+ expect(the_retry.type).to eq :retry
133
+ expect(void_value? the_retry).to eq true
134
+ end
135
+ it 'knows an `if` is a void value if either side is a void value' do
136
+ the_if = ast_for("def a; if 1; return 2; else; 3; end; end").children.last
137
+ expect(the_if.type).to eq :if
138
+ expect(void_value?(the_if)).to be true
139
+
140
+ the_if = ast_for("def a; if 1; 2; else; return 3; end; end").children.last
141
+ expect(the_if.type).to eq :if
142
+ expect(void_value?(the_if)).to be true
143
+
144
+ the_if = ast_for("def a; if 1; 2; else; 3; end; end").children.last
145
+ expect(the_if.type).to eq :if
146
+ expect(void_value?(the_if)).to be false
147
+ end
148
+ it 'knows a begin is a void value if its last element is a void value' do
149
+ the_begin = ast_for("loop { begin; break; end }").children.last
150
+ expect([:begin, :kwbegin]).to include the_begin.type
151
+ expect(void_value?(the_begin)).to be true
152
+
153
+ the_begin = ast_for("loop { begin; 1; end }").children.last
154
+ expect([:begin, :kwbegin]).to include the_begin.type
155
+ expect(void_value?(the_begin)).to be false
156
+ end
157
+ it 'knows a rescue is a void value if its last child or its else is a void value' do
158
+ the_rescue = ast_for("begin; rescue; retry; end").children.first
159
+ expect(the_rescue.type).to eq :rescue
160
+ expect(void_value?(the_rescue)).to be true
161
+
162
+ the_rescue = ast_for("begin; rescue; 1; else; retry; end").children.first
163
+ expect(the_rescue.type).to eq :rescue
164
+ expect(void_value?(the_rescue)).to be true
165
+
166
+ the_rescue = ast_for("begin; rescue; 1; else; 2; end").children.first
167
+ expect(the_rescue.type).to eq :rescue
168
+ expect(void_value?(the_rescue)).to be false
169
+ end
170
+ it 'knows an ensure is a void value if its body or ensure portion are void values' do
171
+ the_ensure = ast_for("loop { begin; break; ensure; 1; end }").children.last.children.last
172
+ expect(the_ensure.type).to eq :ensure
173
+ expect(void_value?(the_ensure)).to be true
174
+
175
+ the_ensure = ast_for("loop { begin; 1; ensure; break; end }").children.last.children.last
176
+ expect(the_ensure.type).to eq :ensure
177
+ expect(void_value?(the_ensure)).to be true
178
+
179
+ the_ensure = ast_for("loop { begin; 1; ensure; 2; end }").children.last.children.last
180
+ expect(the_ensure.type).to eq :ensure
181
+ expect(void_value?(the_ensure)).to be false
182
+ end
183
+ it 'knows other things are not void values' do
184
+ expect(void_value?(ast_for "123")).to be false
185
+ end
186
+ end
187
+
188
+ end
189
+
2
190
 
3
- # RSpec.describe 'SeeingIsBelieving::Code' do
4
- # describe '#range' do
5
- # it 'returns a range object with the specified start and ends'
6
- # end
7
191
 
8
192
  # describe '#inline_comments' do
9
- # it 'finds their line_number, column_number, preceding_whitespace, text, preceding_whitespace_range, and comment_range' do
193
+ # xit 'finds their line_number, column_number, preceding_whitespace, text, preceding_whitespace_range, and comment_range' do
10
194
  # code = code_for <<-CODE.gsub(/^\s*/, '')
11
195
  # # c1
12
196
  # not c