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,90 @@
|
|
1
|
+
require 'seeing_is_believing/error'
|
2
|
+
require 'seeing_is_believing/hard_core_ensure'
|
3
|
+
|
4
|
+
class SeeingIsBelieving
|
5
|
+
class SwapFiles
|
6
|
+
# Might honeslty make more sense to break this out into 2 different classes.
|
7
|
+
# We've got to do some confusing state accounting since there are really 2
|
8
|
+
# algorithms here:
|
9
|
+
#
|
10
|
+
# if the file exists:
|
11
|
+
# make sure there isn't a backup (could cause the user to lose their file)
|
12
|
+
# back the file up
|
13
|
+
# write the rewritten code to the file
|
14
|
+
# if we are told to show the user program
|
15
|
+
# move the backup over the top of the rewritten file
|
16
|
+
# do nothing in the ensure block
|
17
|
+
# else
|
18
|
+
# in the ensure block: move the backup over the top of the rewritten file
|
19
|
+
# if the file DNE:
|
20
|
+
# write the rewritten code to the file
|
21
|
+
# if we are told to show the user program
|
22
|
+
# write the user program to the file
|
23
|
+
# delete the file in the ensure block
|
24
|
+
def self.call(*args, &block)
|
25
|
+
new(*args, &block).call
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(file_path, backup_path, user_program, rewritten_program, &block)
|
29
|
+
self.file_path = file_path
|
30
|
+
self.block = block
|
31
|
+
self.backup_path = backup_path
|
32
|
+
self.user_program = user_program
|
33
|
+
self.rewritten_program = rewritten_program
|
34
|
+
end
|
35
|
+
|
36
|
+
def call
|
37
|
+
HardCoreEnsure.call \
|
38
|
+
code: -> {
|
39
|
+
File.exist? backup_path and
|
40
|
+
raise TempFileAlreadyExists.new(file_path, backup_path)
|
41
|
+
|
42
|
+
@has_file = File.exist? file_path
|
43
|
+
|
44
|
+
if @has_file
|
45
|
+
File.rename file_path, backup_path
|
46
|
+
@needs_restore = true
|
47
|
+
end
|
48
|
+
|
49
|
+
save_file rewritten_program
|
50
|
+
|
51
|
+
block.call self
|
52
|
+
},
|
53
|
+
ensure: -> {
|
54
|
+
set_back_to_initial_conditions
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
def show_user_program
|
59
|
+
if @needs_restore
|
60
|
+
restore
|
61
|
+
else
|
62
|
+
save_file user_program
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
attr_accessor :block, :file_path, :backup_path, :rewritten_program, :user_program
|
69
|
+
|
70
|
+
def restore
|
71
|
+
File.rename(backup_path, file_path)
|
72
|
+
@needs_restore = false
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def save_file(program)
|
77
|
+
File.open file_path, 'w', external_encoding: "utf-8" do |f|
|
78
|
+
f.write program.to_s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def set_back_to_initial_conditions
|
83
|
+
if @needs_restore
|
84
|
+
restore
|
85
|
+
elsif !@has_file
|
86
|
+
File.delete(file_path)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require_relative 'safe'
|
2
|
+
require_relative 'version'
|
3
|
+
require_relative 'event_stream/producer'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
|
7
|
+
using SeeingIsBelieving::Safe
|
8
|
+
|
9
|
+
sib_vars = Marshal.load ENV["SIB_VARIABLES.MARSHAL.B64"].unpack('m0').first
|
10
|
+
event_stream = Timeout.timeout(1) do
|
11
|
+
begin
|
12
|
+
Socket.tcp("localhost", sib_vars.fetch(:event_stream_port))
|
13
|
+
rescue Errno::ECONNREFUSED
|
14
|
+
sleep 0.1
|
15
|
+
retry
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
$SiB = SeeingIsBelieving::EventStream::Producer.new(event_stream)
|
20
|
+
$SiB.record_ruby_version RUBY_VERSION
|
21
|
+
$SiB.record_sib_version SeeingIsBelieving::VERSION
|
22
|
+
$SiB.record_filename sib_vars.fetch(:filename)
|
23
|
+
$SiB.record_num_lines sib_vars.fetch(:num_lines)
|
24
|
+
$SiB.record_max_line_captures sib_vars.fetch(:max_line_captures)
|
25
|
+
|
26
|
+
STDOUT.sync = true
|
27
|
+
STDOUT.binmode
|
28
|
+
STDERR.binmode
|
29
|
+
STDIN.set_encoding "utf-8"
|
30
|
+
stdout, stderr = STDOUT, STDERR
|
31
|
+
|
32
|
+
finish = lambda do
|
33
|
+
$SiB.finish!
|
34
|
+
event_stream.close
|
35
|
+
stdout.flush unless stdout.closed?
|
36
|
+
stderr.flush unless stderr.closed?
|
37
|
+
end
|
38
|
+
|
39
|
+
real_exec = method :exec
|
40
|
+
real_fork = method :fork
|
41
|
+
real_exit_bang = method :exit!
|
42
|
+
fork_defn = lambda do |*args|
|
43
|
+
result = real_fork.call(*args)
|
44
|
+
$SiB.send :forking_occurred_and_you_are_the_child, event_stream unless result
|
45
|
+
result
|
46
|
+
end
|
47
|
+
Kernel.module_eval do
|
48
|
+
private
|
49
|
+
|
50
|
+
define_method :warn do |*args, &block|
|
51
|
+
$stderr.puts(*args)
|
52
|
+
end
|
53
|
+
|
54
|
+
alias :exec :exec # disable warning
|
55
|
+
define_method :exec do |*args, &block|
|
56
|
+
$SiB.record_exec(args)
|
57
|
+
finish.call
|
58
|
+
real_exec.call(*args, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
alias :exit! :exit! # disable warning
|
62
|
+
define_method :exit! do |status=false|
|
63
|
+
finish.call
|
64
|
+
real_exit_bang.call(status)
|
65
|
+
end
|
66
|
+
|
67
|
+
alias :fork :fork
|
68
|
+
define_method :fork, &fork_defn
|
69
|
+
end
|
70
|
+
|
71
|
+
module Process
|
72
|
+
class << self
|
73
|
+
alias :fork :fork
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Kernel.define_singleton_method :fork, &fork_defn
|
78
|
+
Process.define_singleton_method :fork, &fork_defn
|
79
|
+
|
80
|
+
|
81
|
+
# Some things need to be recorded and readded as they are called from Ruby C code and it blows up in really difficult to dianose ways -.-
|
82
|
+
symbol_to_s = Symbol.instance_method(:to_s)
|
83
|
+
exception_message = Exception.instance_method(:message)
|
84
|
+
exception_backtrace = Exception.instance_method(:backtrace)
|
85
|
+
|
86
|
+
# Guarding against hostile users (e.g. me) that do ridiculous things like blowing away these constants
|
87
|
+
exception_class = Exception
|
88
|
+
symbol_class = Symbol
|
89
|
+
|
90
|
+
at_exit do
|
91
|
+
exception_class.class_eval { define_method :message, exception_message }
|
92
|
+
exception_class.class_eval { define_method :backtrace, exception_backtrace }
|
93
|
+
symbol_class.class_eval { define_method :to_s, symbol_to_s }
|
94
|
+
exitstatus = ($! ? $SiB.record_exception(nil, $!) : 0)
|
95
|
+
finish.call
|
96
|
+
real_exit_bang.call(exitstatus) # clears exceptions so they don't print to stderr and change the processes actual exit status (we recorded what it should be)
|
97
|
+
end
|
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'seeing_is_believing/code'
|
2
|
+
|
3
|
+
# comprehensive list of syntaxes that can come up
|
4
|
+
# https://github.com/whitequark/parser/blob/master/doc/AST_FORMAT.md
|
5
|
+
class SeeingIsBelieving
|
6
|
+
class WrapExpressions
|
7
|
+
|
8
|
+
def self.call(program, wrappings)
|
9
|
+
new(program, wrappings).call
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(program, wrappings)
|
13
|
+
self.before_all = wrappings.fetch :before_all, -> { '' }
|
14
|
+
self.after_all = wrappings.fetch :after_all, -> { '' }
|
15
|
+
self.before_each = wrappings.fetch :before_each, -> * { '' }
|
16
|
+
self.after_each = wrappings.fetch :after_each, -> * { '' }
|
17
|
+
self.wrappings = {}
|
18
|
+
self.code = Code.new(program, 'program-without-annotations')
|
19
|
+
code.syntax.valid? || raise(::SyntaxError, code.syntax.error_message)
|
20
|
+
end
|
21
|
+
|
22
|
+
def call
|
23
|
+
@called ||= begin
|
24
|
+
wrap_recursive code.root
|
25
|
+
|
26
|
+
wrappings = wrappings().sort_by(&:first)
|
27
|
+
|
28
|
+
wrappings.each do |line_num, (range, _last_col, meta)|
|
29
|
+
case meta
|
30
|
+
when :total_fucking_failure
|
31
|
+
rewriter.replace range, '.....TOTAL FUCKING FAILURE!.....'
|
32
|
+
when :match_current_line
|
33
|
+
rewriter.insert_before range, '~' # Regexp#~
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
wrappings.each do |line_num, (range, _last_col, _meta)|
|
38
|
+
rewriter.insert_before range, before_each.call(line_num)
|
39
|
+
end
|
40
|
+
|
41
|
+
wrappings.each do |line_num, (range, _last_col, _meta)|
|
42
|
+
rewriter.insert_after range, after_each.call(line_num)
|
43
|
+
end
|
44
|
+
|
45
|
+
rewriter.insert_before root_range, before_all.call
|
46
|
+
rewriter.insert_after root_range, after_all_text
|
47
|
+
rewriter.process
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
attr_accessor :before_all, :after_all, :before_each, :after_each
|
54
|
+
attr_accessor :code, :wrappings
|
55
|
+
|
56
|
+
def buffer
|
57
|
+
code.buffer
|
58
|
+
end
|
59
|
+
|
60
|
+
def rewriter
|
61
|
+
code.rewriter
|
62
|
+
end
|
63
|
+
|
64
|
+
def root_range
|
65
|
+
code.root.location.expression
|
66
|
+
end
|
67
|
+
|
68
|
+
def after_all_text
|
69
|
+
after_all_text = after_all.call
|
70
|
+
data_segment_code = "__END__\n"
|
71
|
+
code_after_end_of_file = buffer.source[root_range.end_pos, data_segment_code.size]
|
72
|
+
ends_in_data_segment = code_after_end_of_file.chomp == data_segment_code.chomp
|
73
|
+
if ends_in_data_segment
|
74
|
+
"#{after_all_text}\n"
|
75
|
+
else
|
76
|
+
after_all_text
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_to_wrappings(range_or_ast, meta=nil)
|
81
|
+
range = range_or_ast
|
82
|
+
if range.kind_of? ::AST::Node
|
83
|
+
location = range_or_ast.location
|
84
|
+
# __ENCODING__ becomes: (const (const nil :Encoding) :UTF_8)
|
85
|
+
# Where the inner const doesn't have a location because it doesn't correspond to a real token.
|
86
|
+
# There is not currently a way to turn this off, but it would be nice to have one like __LINE__ does
|
87
|
+
# https://github.com/whitequark/parser/blob/e2249d7051b1adb6979139928e14a81bc62f566e/lib/parser/builders/default.rb#L333-343
|
88
|
+
return unless location.respond_to? :expression
|
89
|
+
range = location.expression
|
90
|
+
end
|
91
|
+
line, col = buffer.decompose_position range.end_pos
|
92
|
+
_, prev_col, _ = wrappings[line]
|
93
|
+
wrappings[line] = (!wrappings[line] || prev_col < col ? [range, col, meta] : wrappings[line] )
|
94
|
+
end
|
95
|
+
|
96
|
+
def add_children(ast, omit_first = false)
|
97
|
+
(omit_first ? ast.children.drop(1) : ast.children)
|
98
|
+
.each { |child| wrap_recursive child }
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_interpolations_in(ast)
|
102
|
+
ast.children
|
103
|
+
.select { |child| child.type == :begin }
|
104
|
+
.each { |child| add_children child }
|
105
|
+
end
|
106
|
+
|
107
|
+
def wrap_recursive(ast)
|
108
|
+
return wrappings unless ast.kind_of? ::AST::Node
|
109
|
+
case ast.type
|
110
|
+
when :args, :redo, :retry, :alias, :undef, :null_node, :iflipflop, :eflipflop
|
111
|
+
# no op
|
112
|
+
when :defs, :module
|
113
|
+
add_to_wrappings ast
|
114
|
+
add_children ast, true
|
115
|
+
when :ensure, :return, :break, :next, :splat, :kwsplat
|
116
|
+
add_children ast
|
117
|
+
when :rescue
|
118
|
+
add_to_wrappings ast if inline_rescue? ast
|
119
|
+
add_children ast
|
120
|
+
when :class
|
121
|
+
name, * = ast.children
|
122
|
+
namespace, * = name.children
|
123
|
+
add_to_wrappings ast
|
124
|
+
wrap_recursive namespace
|
125
|
+
add_children ast, true
|
126
|
+
when :if
|
127
|
+
if ast.location.kind_of? Parser::Source::Map::Ternary
|
128
|
+
add_to_wrappings ast unless ast.children.any? { |child| code.void_value? child }
|
129
|
+
add_children ast
|
130
|
+
else
|
131
|
+
keyword = ast.location.keyword.source # if, elsif, unless, else, ....
|
132
|
+
if (keyword == 'if' || keyword == 'unless') && ast.children.none? { |child| code.void_value? child }
|
133
|
+
add_to_wrappings ast
|
134
|
+
end
|
135
|
+
add_children ast
|
136
|
+
end
|
137
|
+
when :when, :pair # pair is 1=>2
|
138
|
+
wrap_recursive ast.children.last
|
139
|
+
when :resbody
|
140
|
+
_exception_type, _variable_name, body = ast.children
|
141
|
+
wrap_recursive body
|
142
|
+
when :array
|
143
|
+
add_to_wrappings ast
|
144
|
+
the_begin = ast.location.begin
|
145
|
+
if !the_begin
|
146
|
+
# a = 1,2,3
|
147
|
+
add_children ast
|
148
|
+
elsif the_begin.source !~ /\A%/
|
149
|
+
# normal array
|
150
|
+
add_children ast
|
151
|
+
elsif the_begin.source =~ /\A%/
|
152
|
+
# array of literals
|
153
|
+
|
154
|
+
ast.children.each do |child|
|
155
|
+
t = child.type
|
156
|
+
if t == :dsym || t == :dstr
|
157
|
+
add_interpolations_in child
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
when :block
|
162
|
+
add_to_wrappings ast
|
163
|
+
|
164
|
+
# a {} comes in as
|
165
|
+
# (block
|
166
|
+
# (send nil :a)
|
167
|
+
# (args) nil)
|
168
|
+
#
|
169
|
+
# a.b {} comes in as
|
170
|
+
# (block
|
171
|
+
# (send
|
172
|
+
# (send nil :a) :b)
|
173
|
+
# (args) nil)
|
174
|
+
#
|
175
|
+
# we don't want to wrap the send itself, otherwise could come in as <a>{}
|
176
|
+
# but we do want ot wrap its first child so that we can get <<a>\n.b{}>
|
177
|
+
#
|
178
|
+
# I can't think of anything other than a :send that could be the first child
|
179
|
+
# but I'll check for it anyway.
|
180
|
+
the_send = ast.children[0]
|
181
|
+
wrap_recursive the_send.children.first if the_send.type == :send
|
182
|
+
add_children ast, true
|
183
|
+
when :masgn
|
184
|
+
# we must look at RHS because [1,<<A] and 1,<<A are both allowed
|
185
|
+
#
|
186
|
+
# in the first case, we must take the end_pos of the array,
|
187
|
+
# or we'll insert the after_each in the wrong location
|
188
|
+
#
|
189
|
+
# in the second, there is an implicit Array wrapped around it, with the wrong end_pos,
|
190
|
+
# so we must take the end_pos of the last arg
|
191
|
+
array = ast.children.last
|
192
|
+
if array.type != :array # e.g. `a, b = c`
|
193
|
+
add_to_wrappings ast
|
194
|
+
add_children ast, true
|
195
|
+
elsif array.location.expression.source.start_with? '['
|
196
|
+
add_to_wrappings ast
|
197
|
+
add_children ast, true
|
198
|
+
else
|
199
|
+
begin_pos = ast.location.expression.begin_pos
|
200
|
+
end_pos = array.children.last.location.expression.end_pos
|
201
|
+
range = code.range_for(begin_pos, end_pos)
|
202
|
+
add_to_wrappings range
|
203
|
+
add_children ast.children.last
|
204
|
+
end
|
205
|
+
when :lvasgn, # a = 1
|
206
|
+
:ivasgn, # @a = 1
|
207
|
+
:gvasgn, # $a = 1
|
208
|
+
:cvasgn, # @@a = 1
|
209
|
+
:casgn, # A = 1
|
210
|
+
:or_asgn, # a ||= b
|
211
|
+
:and_asgn, # a &&= b
|
212
|
+
:op_asgn # a += b, a -= b, a *= b, etc
|
213
|
+
|
214
|
+
# a=b gets wrapped <a=b>
|
215
|
+
# but we don't wrap the lvar in `for a in range`
|
216
|
+
if ast.children.last.kind_of? ::AST::Node
|
217
|
+
begin_pos = ast.location.expression.begin_pos
|
218
|
+
end_pos = ast.children.last.location.expression.end_pos
|
219
|
+
range = code.range_for(begin_pos, end_pos)
|
220
|
+
add_to_wrappings range
|
221
|
+
add_children ast, true
|
222
|
+
end
|
223
|
+
when :send
|
224
|
+
_target, message, * = ast.children
|
225
|
+
meta = (:total_fucking_failure if message == :__TOTAL_FUCKING_FAILURE__)
|
226
|
+
add_to_wrappings ast, meta
|
227
|
+
add_children ast
|
228
|
+
when :begin
|
229
|
+
if ast.location.expression.source.start_with?("(") && # e.g. `(1)` we want `<(1)>`
|
230
|
+
!code.void_value?(ast) # e.g. `(return 1)` we want `(return <1>)`
|
231
|
+
add_to_wrappings ast
|
232
|
+
end
|
233
|
+
add_children ast
|
234
|
+
when :str
|
235
|
+
add_to_wrappings ast
|
236
|
+
|
237
|
+
when :regexp, :dstr, :xstr, :dsym
|
238
|
+
add_to_wrappings ast
|
239
|
+
add_interpolations_in ast
|
240
|
+
|
241
|
+
when :hash
|
242
|
+
# method arguments might not have braces around them
|
243
|
+
# in these cases, we want to record the value, not the hash
|
244
|
+
add_to_wrappings ast, meta if ast.location.begin
|
245
|
+
add_children ast
|
246
|
+
|
247
|
+
when :block_pass, :preexe, :postexe
|
248
|
+
add_children ast # strange, I'm not too sure about this :/
|
249
|
+
|
250
|
+
when :match_current_line # ie `if /abc/; ...; end`
|
251
|
+
add_to_wrappings ast, :match_current_line
|
252
|
+
|
253
|
+
else
|
254
|
+
add_to_wrappings ast
|
255
|
+
add_children ast
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def inline_rescue?(ast)
|
260
|
+
primary_code, rescue_body, _else_body = ast.children
|
261
|
+
return false unless primary_code
|
262
|
+
primary_code.loc.expression.last_line == rescue_body.loc.expression.first_line
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'seeing_is_believing/wrap_expressions'
|
2
|
+
class SeeingIsBelieving
|
3
|
+
module WrapExpressionsWithInspect
|
4
|
+
def self.call(program)
|
5
|
+
# NOTE: if it received the AST, it could figure out if it needs
|
6
|
+
# to always wrap the expression in parentheses
|
7
|
+
WrapExpressions.call program,
|
8
|
+
before_all: -> {
|
9
|
+
"BEGIN { $SiB.file_loaded };"
|
10
|
+
},
|
11
|
+
before_each: -> line_number {
|
12
|
+
"$SiB.record_result(:inspect, #{line_number}, ("
|
13
|
+
},
|
14
|
+
after_each: -> line_number {
|
15
|
+
")) { |v| v.inspect }"
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
require 'seeing_is_believing/result'
|
4
|
+
require 'seeing_is_believing/version'
|
5
|
+
require 'seeing_is_believing/debugger'
|
6
|
+
require 'seeing_is_believing/wrap_expressions_with_inspect'
|
7
|
+
require 'seeing_is_believing/hash_struct'
|
8
|
+
require 'seeing_is_believing/evaluate_by_moving_files'
|
9
|
+
require 'seeing_is_believing/event_stream/handlers/debug'
|
10
|
+
require 'seeing_is_believing/event_stream/handlers/update_result'
|
11
|
+
|
12
|
+
class SeeingIsBelieving
|
13
|
+
class Options < HashStruct
|
14
|
+
predicate(:event_handler) { EventStream::Handlers::UpdateResult.new Result.new }
|
15
|
+
predicate(:local_cwd) { false }
|
16
|
+
attribute(:filename) { nil }
|
17
|
+
attribute(:encoding) { nil }
|
18
|
+
attribute(:stdin) { "" }
|
19
|
+
attribute(:require_files) { ['seeing_is_believing/the_matrix'] }
|
20
|
+
attribute(:load_path_dirs) { [File.realpath(__dir__)] }
|
21
|
+
attribute(:timeout_seconds) { 0 }
|
22
|
+
attribute(:debugger) { Debugger::Null }
|
23
|
+
attribute(:max_line_captures) { Float::INFINITY }
|
24
|
+
attribute(:rewrite_code) { WrapExpressionsWithInspect }
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.call(*args)
|
28
|
+
new(*args).call
|
29
|
+
end
|
30
|
+
|
31
|
+
attr_reader :options
|
32
|
+
def initialize(program, options={})
|
33
|
+
@program = program
|
34
|
+
@program += "\n" unless @program.end_with? "\n"
|
35
|
+
@options = Options.new options
|
36
|
+
end
|
37
|
+
|
38
|
+
def call
|
39
|
+
@memoized_result ||= Dir.mktmpdir("seeing_is_believing_temp_dir") { |dir|
|
40
|
+
filename = options.filename || File.join(dir, 'program.rb')
|
41
|
+
new_program = options.rewrite_code.call @program
|
42
|
+
|
43
|
+
options.debugger.context("REWRITTEN PROGRAM") { new_program }
|
44
|
+
|
45
|
+
EvaluateByMovingFiles.call \
|
46
|
+
filename,
|
47
|
+
@program,
|
48
|
+
new_program,
|
49
|
+
event_handler: event_handler(options.debugger, options.event_handler),
|
50
|
+
provided_input: options.stdin,
|
51
|
+
require_files: options.require_files,
|
52
|
+
load_path_dirs: options.load_path_dirs,
|
53
|
+
encoding: options.encoding,
|
54
|
+
timeout_seconds: options.timeout_seconds,
|
55
|
+
max_line_captures: options.max_line_captures,
|
56
|
+
local_cwd: options.local_cwd?
|
57
|
+
|
58
|
+
options.event_handler
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
# If we need debugging, wrap a debugging handler around the current handler
|
65
|
+
def event_handler(debugger, current_handler)
|
66
|
+
return current_handler unless debugger.enabled?
|
67
|
+
EventStream::Handlers::Debug.new debugger, current_handler
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.realpath("lib", __dir__)
|
3
|
+
require "seeing_is_believing/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "andyw8-seeing_is_believing"
|
7
|
+
s.version = SeeingIsBelieving::VERSION
|
8
|
+
s.authors = ["Josh Cheek"]
|
9
|
+
s.email = ["josh.cheek@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/JoshCheek/seeing_is_believing"
|
11
|
+
s.summary = %q{Records results of every line of code in your file}
|
12
|
+
s.description = %q{Records the results of every line of code in your file (intended to be like xmpfilter), inspired by Bret Victor's JavaScript example in his talk "Inventing on Principle"}
|
13
|
+
s.license = "WTFPL"
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n") - ['docs/seeing is believing.psd'] # remove psd b/c it boosts the gem size from 50kb to 20mb O.o
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_dependency "parser", ">= 2.7", "< 4"
|
21
|
+
s.add_dependency "childprocess","~> 4.1"
|
22
|
+
s.add_dependency "ffi", "~> 1.15"
|
23
|
+
|
24
|
+
s.add_development_dependency "pry"
|
25
|
+
s.add_development_dependency "haiti", ">= 0.1", "< 0.3"
|
26
|
+
s.add_development_dependency "rake", "~> 13.0"
|
27
|
+
s.add_development_dependency "rspec", "~> 3.6"
|
28
|
+
s.add_development_dependency "bundler", "~> 2.0"
|
29
|
+
s.add_development_dependency "cucumber", "~> 2.4"
|
30
|
+
s.add_development_dependency "ripper-tags", "~> 0.3"
|
31
|
+
|
32
|
+
s.post_install_message = <<'Omg, frogs <3'.gsub(/(gg+)/) { |capture| "\e[32m#{capture.gsub 'g', '.'}\e[0m" }.gsub("brown", "\e[33m").gsub("off", "\e[0m")
|
33
|
+
.7
|
34
|
+
. .M
|
35
|
+
. . .N
|
36
|
+
M. ..O
|
37
|
+
=. .. .M
|
38
|
+
N . . .N
|
39
|
+
O.. . . O
|
40
|
+
MN:. . M
|
41
|
+
OM...N
|
42
|
+
N .OM brown.NM8MMMMMMoff
|
43
|
+
O ..N brown...MM=:Z7?Z7??MMMMIMMOM8Noff
|
44
|
+
M...O brown,MMMMMMMMMM$$:=ZZ$~:$?7ZMMMMM8?Moff
|
45
|
+
.N . M brownMMI$7:==?:77MMMM7$O~+~ZO~~I=7ZMMMMoff
|
46
|
+
O$...N brownM=$ZZI=MMM7ZZI=?MMZ+:$I?8Z~?ZO~=ZIMMoff
|
47
|
+
.MN ...O brownM~?Z==ZZZ=MM$$=$ZZMMO=~~?$=$Z~~OO+=MMoff
|
48
|
+
OM ...,M DAAAAAAAAAAAAAAAAAAAAAAAMbrown=Z=+OOI=+ZO$O+MMoff
|
49
|
+
NOM.. . N DAAAAAAAAAAAAM?:DAAAAAAAAAAAAMbrown$ZZ?$+IZ7+8?M8off
|
50
|
+
NOM.....O DAAAAAAAAM:ggggDAAAAAAAAAAAAAAMbrown78OI+D=78=$MMoff
|
51
|
+
NO.. ...MN DAAAAAAAMgggggggDAAAAAAAAAAAAAM,MMbrownMO7?I8Z7OMoff
|
52
|
+
MN.... ..O DAAAAAMgggggggggDAAAAAAAAAAAAMgg~MMbrown?NMM7O8MMoff
|
53
|
+
NOM.. ..MN DAAMggggggggggggDAAAAAAAAAAMgggggggMMbrown7.MMMoff
|
54
|
+
NOM.. .. .OM MggggggggggggggggDAAAAAAAMgggggggggggMM:M
|
55
|
+
NOM. .. ..NO Mggggggggggggggggggg~DAAMggggggggggggggI,,M
|
56
|
+
NOM.. .. .MN MMgggbrownZMMMMMMoffggggggggggggggggggggggggggggg=MM
|
57
|
+
NOM. . ... O MggbrownMMMNMMMMMMMMZoffgggggggggggggggggggggggggg$MI
|
58
|
+
NOM8.. .. .MM MZbrownMMMMMMMMMMMMMMMoffgggggggggM~gggggggggggggggMM
|
59
|
+
NOM .. . . NOM Mgggggggggggggggggggggggg8M$ggggggggggggggggMM
|
60
|
+
NOMO. . . . . M.MMMMMMMMMMMMNMMMMNMMMMMMggggggggggggggggggMM,
|
61
|
+
NOMM8. . M:MM MMggggggggggggggggggggggggggggggggggggggMM
|
62
|
+
. .MMMM brownMMMMgggggggggggggbrown7ZMMggggggggggggggggggMM
|
63
|
+
.MNOM brownMMMMMMMMMMMMMMMMMMMggggggggggggggggggggOMM
|
64
|
+
H brownMMMMMMMMMMMMMMMMMggggggg,ggggggggggggggMM
|
65
|
+
brownMMMMMMMMMMMM=gggggggggMMggggggggggggggMMN
|
66
|
+
brown:MMMMggggggggggggggggMgggggggMggggggMMM
|
67
|
+
MMMggggggggggggggMNggggg:7gggggggMMM .MMMMMM
|
68
|
+
Seeing MMMMgggggggggMgggMgggggMgggggggggMMMMggggggMM
|
69
|
+
MggMMMgggggggMgggMgggg=IggggggggMMgggggggggM
|
70
|
+
is MggMMMMgggggMggggMggggMgggggMMM:ggggggggggMM
|
71
|
+
MMggM MMMgggMgggMMgggMggggMMMgggggggggggggM,
|
72
|
+
Believing .MggMM MMMMMMgggMggggMgggggggggggggggggggMM
|
73
|
+
ggMMMMMMMMggM MggMMMgggMMgggOMggggggggggggggMNggMM
|
74
|
+
MgggggggggggggM MMMggMMgggMMMMMMMgggggggggggggMgg+MM
|
75
|
+
:MM7ggggMggIMM D ggM?MgggMM MgggggggggggMMggMM,
|
76
|
+
.MggMgg ggg gM+gggM NMggggggggMMMggMM
|
77
|
+
.MM+gZMMgggggM MMMMMMMMMMggggM
|
78
|
+
MIgg8MM,ggggMM .MMMggMggM
|
79
|
+
MMMgggMMMMggMI ggOMMMggOMMggMM
|
80
|
+
MgggMDggggM M MgggggNMMggMMggM
|
81
|
+
MMMMgggggM ggM .M:gggMggggMggggM
|
82
|
+
MM MMMMggggggMMMI
|
83
|
+
Omg, frogs <3
|
84
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# This is mostly tested in the cukes, but some are hard to hit
|
2
|
+
|
3
|
+
require 'seeing_is_believing/binary/align_chunk'
|
4
|
+
|
5
|
+
RSpec.describe '1-off alignment specs' do
|
6
|
+
def chunk(code)
|
7
|
+
code << "\n"
|
8
|
+
SeeingIsBelieving::Binary::AlignChunk.new code
|
9
|
+
end
|
10
|
+
|
11
|
+
describe 'AlignChunk' do
|
12
|
+
it 'considers entirely whitespace lines to be a chunk separator' do
|
13
|
+
empty_line = chunk "aaaaa\n\n1"
|
14
|
+
whitespace_line = chunk "aaaaa\n \t \n1"
|
15
|
+
expect(whitespace_line.line_length_for 1).to eq empty_line.line_length_for(1)
|
16
|
+
expect(whitespace_line.line_length_for 3).to eq empty_line.line_length_for(3)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'is not fooled by whitespace on the first/last line' do
|
20
|
+
expect(chunk(" \na\n ").line_length_for(2)).to eq 3
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'is not fooled by trailing whitespace in general' do
|
24
|
+
expect(chunk("a ").line_length_for(1)).to eq 3
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|