andyw8-seeing_is_believing 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/test.yml +60 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/Gemfile +2 -0
- data/README.md +70 -0
- data/Rakefile +88 -0
- data/appveyor.yml +32 -0
- data/bin/seeing_is_believing +7 -0
- data/docs/example.gif +0 -0
- data/docs/frog-brown.png +0 -0
- data/docs/sib-streaming.gif +0 -0
- data/features/deprecated-flags.feature +91 -0
- data/features/errors.feature +155 -0
- data/features/examples.feature +423 -0
- data/features/flags.feature +852 -0
- data/features/regression.feature +898 -0
- data/features/support/env.rb +102 -0
- data/features/xmpfilter-style.feature +471 -0
- data/lib/seeing_is_believing/binary/align_chunk.rb +47 -0
- data/lib/seeing_is_believing/binary/align_file.rb +24 -0
- data/lib/seeing_is_believing/binary/align_line.rb +25 -0
- data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +56 -0
- data/lib/seeing_is_believing/binary/annotate_every_line.rb +52 -0
- data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +179 -0
- data/lib/seeing_is_believing/binary/comment_lines.rb +36 -0
- data/lib/seeing_is_believing/binary/commentable_lines.rb +126 -0
- data/lib/seeing_is_believing/binary/config.rb +455 -0
- data/lib/seeing_is_believing/binary/data_structures.rb +58 -0
- data/lib/seeing_is_believing/binary/engine.rb +161 -0
- data/lib/seeing_is_believing/binary/format_comment.rb +79 -0
- data/lib/seeing_is_believing/binary/interline_align.rb +57 -0
- data/lib/seeing_is_believing/binary/remove_annotations.rb +113 -0
- data/lib/seeing_is_believing/binary/rewrite_comments.rb +62 -0
- data/lib/seeing_is_believing/binary.rb +73 -0
- data/lib/seeing_is_believing/code.rb +139 -0
- data/lib/seeing_is_believing/compatibility.rb +28 -0
- data/lib/seeing_is_believing/debugger.rb +32 -0
- data/lib/seeing_is_believing/error.rb +17 -0
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +195 -0
- data/lib/seeing_is_believing/event_stream/consumer.rb +221 -0
- data/lib/seeing_is_believing/event_stream/events.rb +193 -0
- data/lib/seeing_is_believing/event_stream/handlers/debug.rb +61 -0
- data/lib/seeing_is_believing/event_stream/handlers/record_exit_events.rb +26 -0
- data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +23 -0
- data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +41 -0
- data/lib/seeing_is_believing/event_stream/producer.rb +178 -0
- data/lib/seeing_is_believing/hard_core_ensure.rb +58 -0
- data/lib/seeing_is_believing/hash_struct.rb +206 -0
- data/lib/seeing_is_believing/result.rb +89 -0
- data/lib/seeing_is_believing/safe.rb +112 -0
- data/lib/seeing_is_believing/swap_files.rb +90 -0
- data/lib/seeing_is_believing/the_matrix.rb +97 -0
- data/lib/seeing_is_believing/version.rb +3 -0
- data/lib/seeing_is_believing/wrap_expressions.rb +265 -0
- data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +19 -0
- data/lib/seeing_is_believing.rb +69 -0
- data/seeing_is_believing.gemspec +84 -0
- data/spec/binary/alignment_specs.rb +27 -0
- data/spec/binary/comment_lines_spec.rb +852 -0
- data/spec/binary/config_spec.rb +831 -0
- data/spec/binary/engine_spec.rb +114 -0
- data/spec/binary/format_comment_spec.rb +210 -0
- data/spec/binary/marker_spec.rb +71 -0
- data/spec/binary/remove_annotations_spec.rb +342 -0
- data/spec/binary/rewrite_comments_spec.rb +106 -0
- data/spec/code_spec.rb +233 -0
- data/spec/debugger_spec.rb +45 -0
- data/spec/evaluate_by_moving_files_spec.rb +204 -0
- data/spec/event_stream_spec.rb +762 -0
- data/spec/hard_core_ensure_spec.rb +120 -0
- data/spec/hash_struct_spec.rb +514 -0
- data/spec/seeing_is_believing_spec.rb +1094 -0
- data/spec/sib_spec_helpers/version.rb +17 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/spec_helper_spec.rb +16 -0
- data/spec/wrap_expressions_spec.rb +1013 -0
- 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
|