andyw8-seeing_is_believing 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/test.yml +60 -0
  3. data/.gitignore +19 -0
  4. data/.rspec +2 -0
  5. data/Gemfile +2 -0
  6. data/README.md +70 -0
  7. data/Rakefile +88 -0
  8. data/appveyor.yml +32 -0
  9. data/bin/seeing_is_believing +7 -0
  10. data/docs/example.gif +0 -0
  11. data/docs/frog-brown.png +0 -0
  12. data/docs/sib-streaming.gif +0 -0
  13. data/features/deprecated-flags.feature +91 -0
  14. data/features/errors.feature +155 -0
  15. data/features/examples.feature +423 -0
  16. data/features/flags.feature +852 -0
  17. data/features/regression.feature +898 -0
  18. data/features/support/env.rb +102 -0
  19. data/features/xmpfilter-style.feature +471 -0
  20. data/lib/seeing_is_believing/binary/align_chunk.rb +47 -0
  21. data/lib/seeing_is_believing/binary/align_file.rb +24 -0
  22. data/lib/seeing_is_believing/binary/align_line.rb +25 -0
  23. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +56 -0
  24. data/lib/seeing_is_believing/binary/annotate_every_line.rb +52 -0
  25. data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +179 -0
  26. data/lib/seeing_is_believing/binary/comment_lines.rb +36 -0
  27. data/lib/seeing_is_believing/binary/commentable_lines.rb +126 -0
  28. data/lib/seeing_is_believing/binary/config.rb +455 -0
  29. data/lib/seeing_is_believing/binary/data_structures.rb +58 -0
  30. data/lib/seeing_is_believing/binary/engine.rb +161 -0
  31. data/lib/seeing_is_believing/binary/format_comment.rb +79 -0
  32. data/lib/seeing_is_believing/binary/interline_align.rb +57 -0
  33. data/lib/seeing_is_believing/binary/remove_annotations.rb +113 -0
  34. data/lib/seeing_is_believing/binary/rewrite_comments.rb +62 -0
  35. data/lib/seeing_is_believing/binary.rb +73 -0
  36. data/lib/seeing_is_believing/code.rb +139 -0
  37. data/lib/seeing_is_believing/compatibility.rb +28 -0
  38. data/lib/seeing_is_believing/debugger.rb +32 -0
  39. data/lib/seeing_is_believing/error.rb +17 -0
  40. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +195 -0
  41. data/lib/seeing_is_believing/event_stream/consumer.rb +221 -0
  42. data/lib/seeing_is_believing/event_stream/events.rb +193 -0
  43. data/lib/seeing_is_believing/event_stream/handlers/debug.rb +61 -0
  44. data/lib/seeing_is_believing/event_stream/handlers/record_exit_events.rb +26 -0
  45. data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +23 -0
  46. data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +41 -0
  47. data/lib/seeing_is_believing/event_stream/producer.rb +178 -0
  48. data/lib/seeing_is_believing/hard_core_ensure.rb +58 -0
  49. data/lib/seeing_is_believing/hash_struct.rb +206 -0
  50. data/lib/seeing_is_believing/result.rb +89 -0
  51. data/lib/seeing_is_believing/safe.rb +112 -0
  52. data/lib/seeing_is_believing/swap_files.rb +90 -0
  53. data/lib/seeing_is_believing/the_matrix.rb +97 -0
  54. data/lib/seeing_is_believing/version.rb +3 -0
  55. data/lib/seeing_is_believing/wrap_expressions.rb +265 -0
  56. data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +19 -0
  57. data/lib/seeing_is_believing.rb +69 -0
  58. data/seeing_is_believing.gemspec +84 -0
  59. data/spec/binary/alignment_specs.rb +27 -0
  60. data/spec/binary/comment_lines_spec.rb +852 -0
  61. data/spec/binary/config_spec.rb +831 -0
  62. data/spec/binary/engine_spec.rb +114 -0
  63. data/spec/binary/format_comment_spec.rb +210 -0
  64. data/spec/binary/marker_spec.rb +71 -0
  65. data/spec/binary/remove_annotations_spec.rb +342 -0
  66. data/spec/binary/rewrite_comments_spec.rb +106 -0
  67. data/spec/code_spec.rb +233 -0
  68. data/spec/debugger_spec.rb +45 -0
  69. data/spec/evaluate_by_moving_files_spec.rb +204 -0
  70. data/spec/event_stream_spec.rb +762 -0
  71. data/spec/hard_core_ensure_spec.rb +120 -0
  72. data/spec/hash_struct_spec.rb +514 -0
  73. data/spec/seeing_is_believing_spec.rb +1094 -0
  74. data/spec/sib_spec_helpers/version.rb +17 -0
  75. data/spec/spec_helper.rb +26 -0
  76. data/spec/spec_helper_spec.rb +16 -0
  77. data/spec/wrap_expressions_spec.rb +1013 -0
  78. metadata +340 -0
@@ -0,0 +1,342 @@
1
+ require 'spec_helper'
2
+ require 'seeing_is_believing/binary/remove_annotations'
3
+ require 'seeing_is_believing/binary/data_structures'
4
+
5
+ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
6
+ def regexes
7
+ SeeingIsBelieving::Binary::Markers.new
8
+ end
9
+
10
+ def call(code, should_clean_values=true)
11
+ indentation = code[/\A */]
12
+ code = code.gsub /^#{indentation}/, ''
13
+ code << "\n" unless code.end_with? "\n"
14
+ described_class.call(code, should_clean_values, regexes).chomp
15
+ end
16
+
17
+ context 'when there are lines that are just normal comments' do
18
+ example { expect(call "1 # hello").to eq "1 # hello" }
19
+ example { expect(call "1 # hello\n"\
20
+ "# world").to eq "1 # hello\n"\
21
+ "# world" }
22
+ example { expect(call "1 # not special\n"\
23
+ "2 # => 3").to eq "1 # not special\n2" }
24
+ end
25
+
26
+ context 'when told to clean out value annotations' do
27
+ example { expect(call "1# => 1", true).to eq "1" }
28
+ example { expect(call "1 # => 1", true).to eq "1" }
29
+ example { expect(call "1 # => 1", true).to eq "1" }
30
+ example { expect(call "1 # => 1", true).to eq "1" }
31
+ example { expect(call "1 # => 1", true).to eq "1" }
32
+ example { expect(call "1 # => 1", true).to eq "1" }
33
+ example { expect(call "1 # => 1", true).to eq "1" }
34
+ example { expect(call "\n1 # => 1", true).to eq "\n1" }
35
+ end
36
+
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 # =>" }
46
+ end
47
+
48
+ context 'cleaning inline exception annotations' do
49
+ example { expect(call "1# ~> 1" ).to eq "1" }
50
+ example { expect(call "1 # ~> 1" ).to eq "1" }
51
+ example { expect(call "1 # ~> 1" ).to eq "1" }
52
+ example { expect(call "1 # ~> 1" ).to eq "1" }
53
+ example { expect(call "1 # ~> 1").to eq "1" }
54
+ example { expect(call "1 # ~> 1").to eq "1" }
55
+ example { expect(call "\n1 # ~> 1").to eq "\n1" }
56
+
57
+ example { expect(call "# >> 1").to eq "" }
58
+ example { expect(call "# !> 1").to eq "" }
59
+ end
60
+
61
+ context 'cleaning multiline results' do
62
+ it 'cleans values whose hash and value locations exactly match the annotation on the line prior' do
63
+ expect(call "1# => 2\n"\
64
+ " # 3").to eq "1"
65
+ end
66
+
67
+ it 'does not clean values where the comment appears at a different position' do
68
+ expect(call "1# => 2\n"\
69
+ "# 3").to eq "1\n"\
70
+ "# 3"
71
+
72
+ expect(call "1# => 2\n"\
73
+ " # 3").to eq "1\n"\
74
+ " # 3"
75
+
76
+ expect(call "1# => 2\n"\
77
+ "# 3").to eq "1\n"\
78
+ "# 3"
79
+ expect(call "1# => 2\n"\
80
+ " # 3").to eq "1\n"\
81
+ " # 3"
82
+
83
+ end
84
+
85
+ it 'does not clean values where the nextline value appears before the initial annotation value' do
86
+ # does clean
87
+ expect(call "1# => 2\n"\
88
+ " # 3").to eq "1"
89
+ expect(call "1# => 2\n"\
90
+ " # 3").to eq "1"
91
+
92
+ # does not clean
93
+ expect(call "1# => 2\n"\
94
+ " # 3 4").to eq "1\n"\
95
+ " # 3 4"
96
+ expect(call "1# => 2\n"\
97
+ " # 3").to eq "1\n"\
98
+ " # 3"
99
+ expect(call "1# => 2\n"\
100
+ " # 3").to eq "1\n"\
101
+ " # 3"
102
+ end
103
+
104
+ it 'does not clean values where there is content before the comment' do
105
+ expect(call "1# => 2\n"\
106
+ "3# 4").to eq "1\n"\
107
+ "3# 4"
108
+ end
109
+
110
+ it 'cleans successive rows of these' do
111
+ expect(call "1# => 2\n"\
112
+ " # 3\n"\
113
+ " # 4" ).to eq "1"
114
+ expect(call "1# => 2\n"\
115
+ " # 3\n"\
116
+ " # 4\n"\
117
+ "5# => 6\n"\
118
+ " # 7\n"\
119
+ " # 8" ).to eq "1\n5"
120
+ end
121
+
122
+ it 'does not clean values where there is non-annotation inbetween' do
123
+ expect(call "1# => 2\n"\
124
+ "# 3\n"\
125
+ " # 4").to eq "1\n"\
126
+ "# 3\n"\
127
+ " # 4"
128
+
129
+ expect(call "1# => 2\n"\
130
+ "3 \n"\
131
+ " # 4").to eq "1\n"\
132
+ "3 \n"\
133
+ " # 4"
134
+ expect(call "1# => 2\n"\
135
+ "# 3\n"\
136
+ " # 4").to eq "1\n"\
137
+ "# 3\n"\
138
+ " # 4"
139
+ end
140
+
141
+ it 'cleans multiline portion, regardless of whether cleaning values (this is soooooo xmpfilter specific)' do
142
+ expect(call "1# => 2\n"\
143
+ " # 3").to eq "1"
144
+
145
+ expect(call "1# => 2\n"\
146
+ " # 3",
147
+ false).to eq "1# =>"
148
+ end
149
+
150
+ it 'works on inline exceptions' do
151
+ expect(call "1# ~> 2\n"\
152
+ " # 3").to eq "1"
153
+ end
154
+ end
155
+
156
+ context 'cleaning stdout annotations' do
157
+ example { expect(call(<<-CODE)).to eq "1" }
158
+ 1
159
+ # >> 2
160
+ CODE
161
+
162
+ example { expect(call(<<-CODE)).to eq "1" }
163
+ 1
164
+
165
+ # >> 2
166
+ CODE
167
+
168
+ example { expect(call(<<-CODE)).to eq "1\n" }
169
+ 1
170
+
171
+
172
+ # >> 2
173
+ CODE
174
+
175
+ example { expect(call(<<-CODE)).to eq "1\n" }
176
+ 1
177
+
178
+
179
+ # >> 2
180
+ # >> 2
181
+ # >> 2
182
+ CODE
183
+
184
+
185
+ example { expect(call(<<-CODE)).to eq "1\n" }
186
+ 1
187
+
188
+
189
+ # >> 2
190
+ # >> 3
191
+ CODE
192
+
193
+ example { expect(call <<-CODE.gsub(/^\s*/, '')).to eq "\n1" }
194
+
195
+ # >> err
196
+ 1
197
+ CODE
198
+ end
199
+
200
+ context 'cleaning stderr annotations' do
201
+ example { expect(call(<<-CODE)).to eq "1" }
202
+ 1
203
+ # !> 2
204
+ CODE
205
+
206
+ example { expect(call(<<-CODE)).to eq "1" }
207
+ 1
208
+
209
+ # !> 2
210
+ CODE
211
+
212
+ example { expect(call(<<-CODE)).to eq "1" }
213
+ 1
214
+
215
+ # !> 2
216
+ # !> 3
217
+ CODE
218
+
219
+ example { expect(call(<<-CODE)).to eq "1\n" }
220
+ 1
221
+
222
+
223
+ # !> 2
224
+ # !> 2
225
+ # !> 2
226
+ CODE
227
+
228
+ example { expect(call <<-CODE.gsub(/^\s*/, '')).to eq "\n1" }
229
+
230
+ # !> err
231
+ 1
232
+ CODE
233
+ end
234
+
235
+
236
+ context 'cleaning end of file exception annotations' do
237
+ example { expect(call(<<-CODE)).to eq "1" }
238
+ 1
239
+ # ~> 2
240
+ CODE
241
+
242
+ example { expect(call(<<-CODE)).to eq "1" }
243
+ 1
244
+
245
+ # ~> 2
246
+ CODE
247
+
248
+ example { expect(call(<<-CODE)).to eq "1" }
249
+ 1
250
+
251
+ # ~> 2
252
+ # ~> 3
253
+ CODE
254
+
255
+ example { expect(call(<<-CODE)).to eq "1\n" }
256
+ 1
257
+
258
+
259
+ # ~> 2
260
+ # ~> 2
261
+ # ~> 2
262
+ CODE
263
+
264
+ example { expect(call(<<-CODE)).to eq "1\n" }
265
+ 1 # ~> error
266
+
267
+
268
+ # ~> error again
269
+ CODE
270
+ end
271
+
272
+ context 'putting it all together' do
273
+ example { expect(call(<<-CODE)).to eq "1" }
274
+ 1
275
+
276
+ # >> 1
277
+ # >> 2
278
+
279
+ # !> 3
280
+ # !> 4
281
+
282
+ # ~> 5
283
+ # ~> 6
284
+ CODE
285
+
286
+ example { expect(call(<<-CODE)).to eq "1" }
287
+ 1
288
+
289
+ # >> 1
290
+
291
+ # >> 2
292
+
293
+ # !> 3
294
+
295
+ # !> 4
296
+
297
+ # ~> 5
298
+
299
+ # ~> 6
300
+ CODE
301
+
302
+ example { expect(call(<<-CODE)).to eq "1" }
303
+ 1
304
+
305
+ # >> 1
306
+ # >> 2
307
+ # !> 3
308
+ # !> 4
309
+ # ~> 5
310
+ # ~> 6
311
+ CODE
312
+
313
+ example { expect(call(<<-CODE)).to eq "1\n3" }
314
+ 1
315
+ # >> 1
316
+ # >> 2
317
+ 3
318
+ # !> 4
319
+ # !> 5
320
+ CODE
321
+
322
+ example { expect(call(<<-CODE)).to eq "1\n3\n6" }
323
+ 1
324
+ # >> 1
325
+ # >> 2
326
+ 3
327
+ # !> 4
328
+ # !> 5
329
+ 6
330
+ # ~> 7
331
+ # ~> 8
332
+ CODE
333
+
334
+ example { expect(call(<<-CODE)).to eq "1\n\nputs \"omg\"" }
335
+ 1 # => 1
336
+
337
+ puts "omg" # ~> RuntimeError: omg
338
+
339
+ # ~> RuntimeError
340
+ CODE
341
+ end
342
+ end
@@ -0,0 +1,106 @@
1
+ require 'spec_helper'
2
+ require 'seeing_is_believing/binary/rewrite_comments'
3
+
4
+ RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
5
+ def call(code, options={}, &block)
6
+ code = code + "\n" unless code.end_with? "\n"
7
+ described_class.call(code, options, &block).chomp
8
+ end
9
+
10
+ it 'ignores multiline comments' do
11
+ seen = []
12
+ call("123\n=begin\n456\n=end\n789") do |*args|
13
+ seen << args
14
+ args[-2..-1]
15
+ end
16
+ expect(seen).to eq []
17
+ end
18
+
19
+ it 'yields the Code::InlineComment' do
20
+ seen = []
21
+ call("# c1\n"\
22
+ "123 # c2 # x\n"\
23
+ "n456\n"\
24
+ " \t # c3\n"\
25
+ "%Q{\n"\
26
+ " 1}#c4\n"\
27
+ "# c5") do |comment|
28
+ seen << comment
29
+ ['', '']
30
+ end
31
+ expect(seen.map(&:text)).to eq [
32
+ "# c1",
33
+ "# c2 # x",
34
+ "# c3",
35
+ "#c4",
36
+ "# c5",
37
+ ]
38
+ end
39
+
40
+ it 'rewrites the whitespace and comment with the whitespace and comment that are returned' do
41
+ rewritten = call("# c1\n"\
42
+ "123 #c2\n"\
43
+ "n456\n"\
44
+ " \t # c3\n"\
45
+ "%Q{\n"\
46
+ " 1}#c4") do |c|
47
+ ["NEW_WHITESPACE#{c.line_number}", "--COMMENT-#{c.line_number}--"]
48
+ end
49
+ expect(rewritten).to eq "NEW_WHITESPACE1--COMMENT-1--\n"\
50
+ "123NEW_WHITESPACE2--COMMENT-2--\n"\
51
+ "n456\n"\
52
+ "NEW_WHITESPACE4--COMMENT-4--\n"\
53
+ "%Q{\n"\
54
+ " 1}NEW_WHITESPACE6--COMMENT-6--"
55
+ end
56
+
57
+ it 'can be given additional lines to make sure are provided, whether they have comments on them or not' do
58
+ rewritten = call("'a'\n"\
59
+ "'b'\n"\
60
+ "'c' # c\n"\
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",
65
+ c.line_number,
66
+ c.whitespace_col,
67
+ c.whitespace,
68
+ c.text_col,
69
+ c.text,
70
+ c.full_range.begin_pos,
71
+ c.full_range.end_pos,
72
+ c.whitespace_range.begin_pos,
73
+ c.whitespace_range.end_pos,
74
+ c.comment_range.begin_pos,
75
+ c.comment_range.end_pos
76
+ ['pre', value]
77
+ end
78
+ expect(rewritten).to eq \
79
+ "'a'\n"\
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"
84
+
85
+ rewritten = call("", include_lines: [1]) { |c| ['a', 'b'] }
86
+ expect(rewritten).to eq "ab"
87
+
88
+ rewritten = call("a", include_lines: [1]) { |c| ['b', 'c'] }
89
+ expect(rewritten).to eq "abc"
90
+
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/
105
+ end
106
+ end
data/spec/code_spec.rb ADDED
@@ -0,0 +1,233 @@
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
+
190
+
191
+
192
+ # describe '#inline_comments' do
193
+ # xit 'finds their line_number, column_number, preceding_whitespace, text, preceding_whitespace_range, and comment_range' do
194
+ # code = code_for <<-CODE.gsub(/^\s*/, '')
195
+ # # c1
196
+ # not c
197
+ # # c2
198
+
199
+ # not c
200
+ # =begin
201
+ # mul
202
+ # ti
203
+ # line
204
+ # =end
205
+ # preceding code \t # c3
206
+ # CODE
207
+ # cs = code.inline_comments
208
+ # expect(cs.map &:line_number ).to eq [1, 3, 4]
209
+ # expect(cs.map &:column_number ).to eq [1, 1, 18]
210
+ # expect(cs.map &:preceding_whitespace).to eq ["", "", " \t "]
211
+ # expect(cs.map &:text ).to eq ['# c1', '# c2', '# c3']
212
+
213
+ # preceding_whitespace_range
214
+ # comment_range
215
+
216
+ # expect(code.inline_comments.map &:ra)
217
+ # multilines = code.multiline_comments
218
+ # end
219
+
220
+ # it 'finds multiline comments'
221
+
222
+ # it 'finds comments in syntactically invalid files'
223
+ # end
224
+
225
+ # it 'knows whether a string is a heredoc'
226
+ # it 'knows if the file is syntactically valid'
227
+ # it 'knows whether there is a data segment'
228
+ # it 'knows what line the data segment starts on'
229
+ # it 'knows how many lines there are'
230
+ # it 'provides the ast'
231
+ # it 'provides the ability to rewrite code'
232
+ # it 'provides access to the source with #[]'
233
+ # end