seeing_is_believing 3.0.0.beta.4 → 3.0.0.beta.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -8
- data/Rakefile +1 -1
- data/Readme.md +65 -25
- data/bin/seeing_is_believing +1 -0
- data/docs/sib-streaming.gif +0 -0
- data/features/deprecated-flags.feature +62 -2
- data/features/errors.feature +12 -7
- data/features/examples.feature +143 -4
- data/features/flags.feature +89 -29
- data/features/regression.feature +58 -14
- data/features/support/env.rb +4 -0
- data/features/xmpfilter-style.feature +181 -36
- data/lib/seeing_is_believing.rb +44 -33
- data/lib/seeing_is_believing/binary.rb +31 -88
- data/lib/seeing_is_believing/binary/align_chunk.rb +30 -11
- data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +10 -16
- data/lib/seeing_is_believing/binary/annotate_every_line.rb +5 -25
- data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +136 -0
- data/lib/seeing_is_believing/binary/comment_lines.rb +8 -10
- data/lib/seeing_is_believing/binary/commentable_lines.rb +20 -26
- data/lib/seeing_is_believing/binary/config.rb +392 -0
- data/lib/seeing_is_believing/binary/data_structures.rb +57 -0
- data/lib/seeing_is_believing/binary/engine.rb +104 -0
- data/lib/seeing_is_believing/binary/{comment_formatter.rb → format_comment.rb} +6 -6
- data/lib/seeing_is_believing/binary/remove_annotations.rb +29 -28
- data/lib/seeing_is_believing/binary/rewrite_comments.rb +42 -43
- data/lib/seeing_is_believing/code.rb +105 -49
- data/lib/seeing_is_believing/debugger.rb +6 -5
- data/lib/seeing_is_believing/error.rb +6 -17
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +78 -129
- data/lib/seeing_is_believing/event_stream/consumer.rb +114 -64
- data/lib/seeing_is_believing/event_stream/events.rb +169 -11
- data/lib/seeing_is_believing/event_stream/handlers/debug.rb +57 -0
- data/lib/seeing_is_believing/event_stream/handlers/record_exitstatus.rb +18 -0
- data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +45 -0
- data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +39 -0
- data/lib/seeing_is_believing/event_stream/producer.rb +25 -24
- data/lib/seeing_is_believing/hash_struct.rb +206 -0
- data/lib/seeing_is_believing/result.rb +20 -3
- data/lib/seeing_is_believing/the_matrix.rb +20 -12
- data/lib/seeing_is_believing/version.rb +1 -1
- data/lib/seeing_is_believing/wrap_expressions.rb +55 -115
- data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +14 -0
- data/seeing_is_believing.gemspec +1 -1
- data/spec/binary/alignment_specs.rb +27 -0
- data/spec/binary/comment_lines_spec.rb +3 -2
- data/spec/binary/config_spec.rb +657 -0
- data/spec/binary/engine_spec.rb +97 -0
- data/spec/binary/{comment_formatter_spec.rb → format_comment_spec.rb} +2 -2
- data/spec/binary/marker_spec.rb +71 -0
- data/spec/binary/options_spec.rb +0 -0
- data/spec/binary/remove_annotations_spec.rb +31 -18
- data/spec/binary/rewrite_comments_spec.rb +26 -11
- data/spec/code_spec.rb +190 -6
- data/spec/debugger_spec.rb +4 -0
- data/spec/evaluate_by_moving_files_spec.rb +38 -20
- data/spec/event_stream_spec.rb +265 -116
- data/spec/hash_struct_spec.rb +514 -0
- data/spec/seeing_is_believing_spec.rb +108 -46
- data/spec/spec_helper.rb +9 -0
- data/spec/wrap_expressions_spec.rb +207 -172
- metadata +30 -18
- data/docs/for-presentations +0 -33
- data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +0 -128
- data/lib/seeing_is_believing/binary/interpret_flags.rb +0 -156
- data/lib/seeing_is_believing/binary/parse_args.rb +0 -263
- data/lib/seeing_is_believing/event_stream/update_result.rb +0 -24
- data/lib/seeing_is_believing/inspect_expressions.rb +0 -21
- data/lib/seeing_is_believing/parser_helpers.rb +0 -82
- data/spec/binary/interpret_flags_spec.rb +0 -332
- data/spec/binary/parse_args_spec.rb +0 -415
data/lib/seeing_is_believing.rb
CHANGED
@@ -1,58 +1,69 @@
|
|
1
|
-
require 'stringio'
|
2
1
|
require 'tmpdir'
|
3
2
|
|
4
3
|
require 'seeing_is_believing/result'
|
5
4
|
require 'seeing_is_believing/version'
|
6
5
|
require 'seeing_is_believing/debugger'
|
7
|
-
require 'seeing_is_believing/
|
6
|
+
require 'seeing_is_believing/wrap_expressions_with_inspect'
|
7
|
+
require 'seeing_is_believing/hash_struct'
|
8
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'
|
9
11
|
|
10
12
|
class SeeingIsBelieving
|
11
|
-
|
13
|
+
class Options < HashStruct
|
14
|
+
predicate(:event_handler) { EventStream::Handlers::UpdateResult.new Result.new }
|
15
|
+
attribute(:filename) { nil }
|
16
|
+
attribute(:encoding) { nil }
|
17
|
+
attribute(:stdin) { "" }
|
18
|
+
attribute(:require_files) { ['seeing_is_believing/the_matrix'] }
|
19
|
+
attribute(:load_path_dirs) { [File.expand_path('..', __FILE__)] }
|
20
|
+
attribute(:timeout_seconds) { 0 }
|
21
|
+
attribute(:debugger) { Debugger::Null }
|
22
|
+
attribute(:max_line_captures) { Float::INFINITY }
|
23
|
+
attribute(:rewrite_code) { WrapExpressionsWithInspect }
|
24
|
+
end
|
12
25
|
|
13
26
|
def self.call(*args)
|
14
27
|
new(*args).call
|
15
28
|
end
|
16
29
|
|
30
|
+
attr_reader :options
|
17
31
|
def initialize(program, options={})
|
18
|
-
@program
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@require = options.fetch :require, ['seeing_is_believing/the_matrix']
|
22
|
-
@load_path = options.fetch :load_path, []
|
23
|
-
@encoding = options.fetch :encoding, nil
|
24
|
-
@timeout = options[:timeout]
|
25
|
-
@debugger = options.fetch :debugger, Debugger.new(stream: nil)
|
26
|
-
@number_of_captures = options.fetch :number_of_captures, Float::INFINITY
|
27
|
-
@evaluator = options.fetch :evaluator, EvaluateByMovingFiles
|
28
|
-
@record_expressions = options.fetch :record_expressions, InspectExpressions # TODO: rename to wrap_expressions
|
32
|
+
@program = program
|
33
|
+
@program += "\n" unless @program.end_with? "\n"
|
34
|
+
@options = Options.new options
|
29
35
|
end
|
30
36
|
|
31
37
|
def call
|
32
38
|
@memoized_result ||= Dir.mktmpdir("seeing_is_believing_temp_dir") { |dir|
|
33
|
-
filename
|
34
|
-
new_program =
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
39
|
+
filename = options.filename || File.join(dir, 'program.rb')
|
40
|
+
new_program = options.rewrite_code.call @program
|
41
|
+
|
42
|
+
options.debugger.context("REWRITTEN PROGRAM") { new_program }
|
43
|
+
|
44
|
+
EvaluateByMovingFiles.call \
|
45
|
+
new_program,
|
46
|
+
filename,
|
47
|
+
event_handler: debugging_handler,
|
48
|
+
provided_input: options.stdin,
|
49
|
+
require_files: options.require_files,
|
50
|
+
load_path_dirs: options.load_path_dirs,
|
51
|
+
encoding: options.encoding,
|
52
|
+
timeout_seconds: options.timeout_seconds,
|
53
|
+
max_line_captures: options.max_line_captures
|
54
|
+
|
55
|
+
options.event_handler
|
49
56
|
}
|
50
57
|
end
|
51
58
|
|
52
59
|
private
|
53
60
|
|
54
|
-
|
55
|
-
|
56
|
-
|
61
|
+
# Even though the debugger can be disabled,
|
62
|
+
# Handlers::Debug is somewhat expensive, and there could be tens of millions of calls
|
63
|
+
# e.g. https://github.com/JoshCheek/seeing_is_believing/issues/12
|
64
|
+
# so just skip it in this case
|
65
|
+
def debugging_handler
|
66
|
+
return options.event_handler unless options.debugger.enabled?
|
67
|
+
EventStream::Handlers::Debug.new options.debugger, options.event_handler
|
57
68
|
end
|
58
69
|
end
|
@@ -1,125 +1,68 @@
|
|
1
1
|
require 'seeing_is_believing'
|
2
|
-
require 'seeing_is_believing/binary/
|
3
|
-
require 'seeing_is_believing/binary/
|
4
|
-
require 'seeing_is_believing/binary/remove_annotations'
|
2
|
+
require 'seeing_is_believing/binary/config'
|
3
|
+
require 'seeing_is_believing/binary/engine'
|
5
4
|
|
6
5
|
class SeeingIsBelieving
|
7
6
|
module Binary
|
8
7
|
SUCCESS_STATUS = 0
|
9
|
-
DISPLAYABLE_ERROR_STATUS = 1 # e.g.
|
10
|
-
NONDISPLAYABLE_ERROR_STATUS = 2 # e.g.
|
8
|
+
DISPLAYABLE_ERROR_STATUS = 1 # e.g. user code raises an exception (we can display this in the output)
|
9
|
+
NONDISPLAYABLE_ERROR_STATUS = 2 # e.g. SiB was invoked incorrectly
|
11
10
|
|
12
11
|
def self.call(argv, stdin, stdout, stderr)
|
13
|
-
|
14
|
-
|
12
|
+
config = Config.new.parse_args(argv).finalize(stdin, stdout, stderr, File)
|
13
|
+
engine = Engine.new config
|
15
14
|
|
16
|
-
if
|
17
|
-
|
18
|
-
return NONDISPLAYABLE_ERROR_STATUS
|
19
|
-
end
|
20
|
-
|
21
|
-
if options.print_help? # TODO: Should this be first?
|
22
|
-
stdout.puts options.help_screen
|
15
|
+
if config.print_help?
|
16
|
+
stdout.puts config.help_screen
|
23
17
|
return SUCCESS_STATUS
|
24
18
|
end
|
25
19
|
|
26
|
-
if
|
20
|
+
if config.print_version?
|
27
21
|
stdout.puts SeeingIsBelieving::VERSION
|
28
22
|
return SUCCESS_STATUS
|
29
23
|
end
|
30
24
|
|
31
|
-
if
|
32
|
-
stderr.puts
|
25
|
+
if config.errors.any?
|
26
|
+
stderr.puts *config.errors, *config.deprecations
|
33
27
|
return NONDISPLAYABLE_ERROR_STATUS
|
34
28
|
end
|
35
29
|
|
36
|
-
if
|
37
|
-
stdout.print
|
30
|
+
if config.print_cleaned?
|
31
|
+
stdout.print engine.cleaned_body
|
38
32
|
return SUCCESS_STATUS
|
39
33
|
end
|
40
34
|
|
41
|
-
|
42
|
-
|
43
|
-
stderr.puts syntax_error_notice
|
44
|
-
return NONDISPLAYABLE_ERROR_STATUS
|
45
|
-
end
|
46
|
-
|
47
|
-
results, program_timedout, unexpected_exception =
|
48
|
-
evaluate_program(options.prepared_body, options.lib_options)
|
49
|
-
|
50
|
-
if program_timedout
|
51
|
-
stderr.puts "Timeout Error after #{options.timeout} seconds!"
|
35
|
+
if engine.syntax_error?
|
36
|
+
stderr.puts engine.syntax_error
|
52
37
|
return NONDISPLAYABLE_ERROR_STATUS
|
53
38
|
end
|
54
39
|
|
55
|
-
|
56
|
-
stderr.puts unexpected_exception.message
|
57
|
-
return NONDISPLAYABLE_ERROR_STATUS
|
58
|
-
end
|
40
|
+
engine.evaluate!
|
59
41
|
|
60
|
-
if
|
61
|
-
stderr.puts
|
62
|
-
unexpected_exception.message,
|
63
|
-
"",
|
64
|
-
unexpected_exception.backtrace
|
42
|
+
if engine.timed_out?
|
43
|
+
stderr.puts "Timeout Error after #{config.timeout_seconds} seconds!"
|
65
44
|
return NONDISPLAYABLE_ERROR_STATUS
|
66
45
|
end
|
67
46
|
|
68
|
-
if
|
47
|
+
if config.result_as_json?
|
69
48
|
require 'json'
|
70
|
-
stdout.puts JSON.dump(
|
49
|
+
stdout.puts JSON.dump(engine.result.as_json)
|
71
50
|
return SUCCESS_STATUS
|
51
|
+
elsif config.print_event_stream?
|
52
|
+
# no op, the event stream handler has been printing it all along
|
53
|
+
elsif config.debug?
|
54
|
+
config.debugger.context("OUTPUT") { engine.annotated_body }
|
55
|
+
else
|
56
|
+
stdout.print engine.annotated_body
|
72
57
|
end
|
73
58
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
results,
|
78
|
-
options.annotator_options)
|
79
|
-
|
80
|
-
if options.inherit_exit_status?
|
81
|
-
results.exitstatus
|
82
|
-
elsif results.has_exception? && results.exitstatus != 0 # e.g. `exit 0` raises SystemExit but isn't an error
|
83
|
-
DISPLAYABLE_ERROR_STATUS
|
84
|
-
else
|
59
|
+
if config.inherit_exitstatus?
|
60
|
+
engine.exitstatus
|
61
|
+
elsif engine.exitstatus.zero?
|
85
62
|
SUCCESS_STATUS
|
63
|
+
else
|
64
|
+
DISPLAYABLE_ERROR_STATUS
|
86
65
|
end
|
87
66
|
end
|
88
|
-
|
89
|
-
private
|
90
|
-
|
91
|
-
def self.syntax_error_notice_for(body)
|
92
|
-
out, err, syntax_status = Open3.capture3 RbConfig.ruby, '-c', stdin_data: body
|
93
|
-
return err unless syntax_status.success?
|
94
|
-
|
95
|
-
# The stdin_data may still be getting written when the pipe closes
|
96
|
-
# This is because Ruby will stop reading from stdin if everything left is in the DATA segment, and the data segment is not referenced.
|
97
|
-
# In this case, the Syntax is fine
|
98
|
-
# https://bugs.ruby-lang.org/issues/9583
|
99
|
-
rescue Errno::EPIPE
|
100
|
-
return nil
|
101
|
-
end
|
102
|
-
|
103
|
-
def self.evaluate_program(body, options)
|
104
|
-
return SeeingIsBelieving.call(body, options), nil, nil
|
105
|
-
rescue Timeout::Error
|
106
|
-
return nil, true, nil
|
107
|
-
rescue Exception
|
108
|
-
return nil, false, $!
|
109
|
-
end
|
110
|
-
|
111
|
-
def self.result_as_data_structure(results)
|
112
|
-
exception = results.has_exception? && { line_number_in_this_file: results.exception.line_number,
|
113
|
-
class_name: results.exception.class_name,
|
114
|
-
message: results.exception.message,
|
115
|
-
backtrace: results.exception.backtrace
|
116
|
-
}
|
117
|
-
{ stdout: results.stdout,
|
118
|
-
stderr: results.stderr,
|
119
|
-
exit_status: results.exitstatus,
|
120
|
-
exception: exception,
|
121
|
-
lines: results.each.with_object(Hash.new).with_index(1) { |(result, hash), line_number| hash[line_number] = result },
|
122
|
-
}
|
123
|
-
end
|
124
67
|
end
|
125
68
|
end
|
@@ -18,19 +18,38 @@ class SeeingIsBelieving
|
|
18
18
|
|
19
19
|
def line_lengths
|
20
20
|
@line_lengths ||= begin
|
21
|
-
|
22
|
-
Hash[
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
21
|
+
# sheesh, I need like Hash#map_values or something
|
22
|
+
line_nums_to_cols = Hash.[] \
|
23
|
+
CommentableLines.call(body)
|
24
|
+
.map { |line_num, (file_index, col_index)|
|
25
|
+
[line_num, col_index-amount_of_preceding_whitespace(file_index)]
|
26
|
+
}
|
27
|
+
|
28
|
+
Hash.[] \
|
29
|
+
line_nums_to_cols
|
30
|
+
.keys
|
31
|
+
.sort
|
32
|
+
.slice_before { |line_number| line_nums_to_cols[line_number].zero? }
|
33
|
+
.flat_map { |slice|
|
34
|
+
max_chunk_length = 2 + slice.map { |line_num| line_nums_to_cols[line_num] }.max
|
35
|
+
slice.map { |line_number| [line_number, max_chunk_length] }
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def trim_trailing_whitespace(line_nums_to_indexes)
|
41
|
+
line_nums_to_indexes.each do |num, indexes|
|
42
|
+
index_in_file = indexes[0]
|
43
|
+
num_to_trim = amount_of_preceding_whitespace(index_in_file)
|
44
|
+
indexes.map! { |index| index - num_to_trim }
|
32
45
|
end
|
33
46
|
end
|
47
|
+
|
48
|
+
def amount_of_preceding_whitespace(index_of_trailing_newline)
|
49
|
+
index = index_of_trailing_newline - 1
|
50
|
+
index -= 1 while 0 <= index && body[index] !~ /[\S\n]/
|
51
|
+
index_of_trailing_newline - index - 1
|
52
|
+
end
|
34
53
|
end
|
35
54
|
end
|
36
55
|
end
|
@@ -1,32 +1,26 @@
|
|
1
1
|
require 'seeing_is_believing/binary' # defines the markers
|
2
|
-
require 'seeing_is_believing/binary/
|
2
|
+
require 'seeing_is_believing/binary/format_comment'
|
3
3
|
|
4
4
|
class SeeingIsBelieving
|
5
5
|
module Binary
|
6
6
|
module AnnotateEndOfFile
|
7
7
|
extend self
|
8
8
|
|
9
|
-
# TODO: Switch options to markers
|
10
9
|
def add_stdout_stderr_and_exceptions_to(new_body, results, options)
|
11
10
|
output = stdout_ouptut_for(results, options) <<
|
12
11
|
stderr_ouptut_for(results, options) <<
|
13
12
|
exception_output_for(results, options)
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
new_body.sub! "\n__END__", "\n#{output}__END__"
|
19
|
-
else
|
20
|
-
new_body << "\n" unless new_body.end_with? "\n"
|
21
|
-
new_body << output
|
22
|
-
end
|
14
|
+
code = Code.new(new_body)
|
15
|
+
code.rewriter.insert_after code.body_range, output
|
16
|
+
new_body.replace code.rewriter.process
|
23
17
|
end
|
24
18
|
|
25
19
|
def stdout_ouptut_for(results, options)
|
26
20
|
return '' unless results.has_stdout?
|
27
21
|
output = "\n"
|
28
22
|
results.stdout.each_line do |line|
|
29
|
-
output <<
|
23
|
+
output << FormatComment.call(0, options[:markers][:stdout][:prefix], line.chomp, options) << "\n"
|
30
24
|
end
|
31
25
|
output
|
32
26
|
end
|
@@ -35,23 +29,23 @@ class SeeingIsBelieving
|
|
35
29
|
return '' unless results.has_stderr?
|
36
30
|
output = "\n"
|
37
31
|
results.stderr.each_line do |line|
|
38
|
-
output <<
|
32
|
+
output << FormatComment.call(0, options[:markers][:stderr][:prefix], line.chomp, options) << "\n"
|
39
33
|
end
|
40
34
|
output
|
41
35
|
end
|
42
36
|
|
43
37
|
def exception_output_for(results, options)
|
44
38
|
return '' unless results.has_exception?
|
45
|
-
exception_marker = options[:markers][:exception]
|
39
|
+
exception_marker = options[:markers][:exception][:prefix]
|
46
40
|
exception = results.exception
|
47
41
|
output = "\n"
|
48
|
-
output <<
|
42
|
+
output << FormatComment.new(0, exception_marker, exception.class_name, options).call << "\n"
|
49
43
|
exception.message.each_line do |line|
|
50
|
-
output <<
|
44
|
+
output << FormatComment.new(0, exception_marker, line.chomp, options).call << "\n"
|
51
45
|
end
|
52
46
|
output << exception_marker.sub(/\s+$/, '') << "\n"
|
53
47
|
exception.backtrace.each do |line|
|
54
|
-
output <<
|
48
|
+
output << FormatComment.new(0, exception_marker, line.chomp, options).call << "\n"
|
55
49
|
end
|
56
50
|
output
|
57
51
|
end
|
@@ -1,16 +1,6 @@
|
|
1
1
|
class SeeingIsBelieving
|
2
2
|
module Binary
|
3
3
|
class AnnotateEveryLine
|
4
|
-
def self.prepare_body(uncleaned_body, marker_regexes)
|
5
|
-
require 'seeing_is_believing/binary/remove_annotations'
|
6
|
-
RemoveAnnotations.call uncleaned_body, true, marker_regexes
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.expression_wrapper(markers, marker_regexes)
|
10
|
-
require 'seeing_is_believing/inspect_expressions'
|
11
|
-
InspectExpressions
|
12
|
-
end
|
13
|
-
|
14
4
|
def self.call(body, results, options)
|
15
5
|
new(body, results, options).call
|
16
6
|
end
|
@@ -24,7 +14,9 @@ class SeeingIsBelieving
|
|
24
14
|
def call
|
25
15
|
@new_body ||= begin
|
26
16
|
require 'seeing_is_believing/binary/comment_lines'
|
27
|
-
require 'seeing_is_believing/binary/
|
17
|
+
require 'seeing_is_believing/binary/format_comment'
|
18
|
+
exception_text = @options[:markers][:exception][:prefix]
|
19
|
+
value_text = @options[:markers][:value][:prefix]
|
28
20
|
|
29
21
|
alignment_strategy = @options[:alignment_strategy].new(@body)
|
30
22
|
exception_lineno = @results.has_exception? ? @results.exception.line_number : -1
|
@@ -32,10 +24,10 @@ class SeeingIsBelieving
|
|
32
24
|
options = @options.merge pad_to: alignment_strategy.line_length_for(line_number)
|
33
25
|
if exception_lineno == line_number
|
34
26
|
result = sprintf "%s: %s", @results.exception.class_name, @results.exception.message.gsub("\n", '\n')
|
35
|
-
|
27
|
+
FormatComment.call(line.size, exception_text, result, options)
|
36
28
|
elsif @results[line_number].any?
|
37
29
|
result = @results[line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
|
38
|
-
|
30
|
+
FormatComment.call(line.size, value_text, result, options)
|
39
31
|
else
|
40
32
|
''
|
41
33
|
end
|
@@ -44,21 +36,9 @@ class SeeingIsBelieving
|
|
44
36
|
require 'seeing_is_believing/binary/annotate_end_of_file'
|
45
37
|
AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
|
46
38
|
|
47
|
-
# What's w/ this debugger? maybe this should move higher?
|
48
|
-
@options.fetch(:debugger).context "OUTPUT"
|
49
39
|
new_body
|
50
40
|
end
|
51
41
|
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def value_marker
|
56
|
-
@value_marker ||= @options.fetch(:markers).fetch(:value)
|
57
|
-
end
|
58
|
-
|
59
|
-
def exception_marker
|
60
|
-
@xnextline_marker ||= @options.fetch(:markers).fetch(:exception)
|
61
|
-
end
|
62
42
|
end
|
63
43
|
end
|
64
44
|
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'seeing_is_believing/code'
|
3
|
+
|
4
|
+
|
5
|
+
# *sigh* need to find a way to join the annotators.
|
6
|
+
# They are sinful ugly, kinda hard to work with,
|
7
|
+
# and absurdly duplicated.
|
8
|
+
|
9
|
+
class SeeingIsBelieving
|
10
|
+
module Binary
|
11
|
+
# Based on the behaviour of xmpfilger (a binary in the rcodetools gem)
|
12
|
+
# See https://github.com/JoshCheek/seeing_is_believing/issues/44 for more details
|
13
|
+
class AnnotateMarkedLines
|
14
|
+
def self.code_rewriter(markers)
|
15
|
+
lambda do |program|
|
16
|
+
inspect_linenos = []
|
17
|
+
pp_linenos = []
|
18
|
+
value_regex = markers[:value][:regex]
|
19
|
+
Code.new(program).inline_comments.each do |c|
|
20
|
+
next unless c.text[value_regex]
|
21
|
+
c.whitespace_col == 0 ? pp_linenos << c.line_number - 1
|
22
|
+
: inspect_linenos << c.line_number
|
23
|
+
end
|
24
|
+
|
25
|
+
should_inspect = false
|
26
|
+
should_pp = false
|
27
|
+
WrapExpressions.call \
|
28
|
+
program,
|
29
|
+
before_each: -> line_number {
|
30
|
+
should_inspect = inspect_linenos.include? line_number
|
31
|
+
should_pp = pp_linenos.include? line_number
|
32
|
+
should_inspect || should_pp ? '(' : ''
|
33
|
+
},
|
34
|
+
after_each: -> line_number {
|
35
|
+
# 74 b/c pretty print_defaults to 79 (guessing 80 chars with 1 reserved for newline), and
|
36
|
+
# 79 - "# => ".length # => 4
|
37
|
+
inspect = "$SiB.record_result :inspect, #{line_number}, v"
|
38
|
+
pp = "$SiB.record_result(:pp, #{line_number}, v) { PP.pp v, '', 74 }"
|
39
|
+
|
40
|
+
if should_inspect && should_pp then ").tap { |v| #{inspect}; #{pp} }"
|
41
|
+
elsif should_inspect then ").tap { |v| #{inspect} }"
|
42
|
+
elsif should_pp then ").tap { |v| #{pp} }"
|
43
|
+
else ""
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.call(body, results, options)
|
50
|
+
new(body, results, options).call
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(body, results, options={})
|
54
|
+
@options = options
|
55
|
+
@body = body
|
56
|
+
@results = results
|
57
|
+
end
|
58
|
+
|
59
|
+
# seems like maybe this should respect the alignment strategy (not what xmpfilter does, but there are other ways I'd like to deviate anyway)
|
60
|
+
# and we should just add a new alignment strategy for default xmpfilter style
|
61
|
+
def call
|
62
|
+
@new_body ||= begin
|
63
|
+
require 'seeing_is_believing/binary/rewrite_comments'
|
64
|
+
require 'seeing_is_believing/binary/format_comment'
|
65
|
+
include_lines = []
|
66
|
+
|
67
|
+
if @results.has_exception?
|
68
|
+
exception_result = sprintf "%s: %s", @results.exception.class_name, @results.exception.message.gsub("\n", '\n')
|
69
|
+
exception_lineno = @results.exception.line_number
|
70
|
+
include_lines << exception_lineno
|
71
|
+
end
|
72
|
+
|
73
|
+
new_body = RewriteComments.call @body, include_lines: include_lines do |comment|
|
74
|
+
exception_on_line = exception_lineno == comment.line_number
|
75
|
+
annotate_this_line = comment.text[value_regex]
|
76
|
+
pp_annotation = annotate_this_line && comment.whitespace_col.zero?
|
77
|
+
normal_annotation = annotate_this_line && !pp_annotation
|
78
|
+
if exception_on_line && annotate_this_line
|
79
|
+
[comment.whitespace, FormatComment.call(comment.text_col, value_prefix, exception_result, @options)]
|
80
|
+
elsif exception_on_line
|
81
|
+
whitespace = comment.whitespace
|
82
|
+
whitespace = " " if whitespace.empty?
|
83
|
+
[whitespace, FormatComment.call(0, exception_prefix, exception_result, @options)]
|
84
|
+
elsif normal_annotation
|
85
|
+
result = @results[comment.line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
|
86
|
+
[comment.whitespace, FormatComment.call(comment.text_col, value_prefix, result, @options)]
|
87
|
+
elsif pp_annotation
|
88
|
+
result = @results[comment.line_number-1, :pp].map { |result| result.chomp }.join("\n,") # ["1\n2", "1\n2", ...
|
89
|
+
swap_leading_whitespace_in_multiline_comment(result)
|
90
|
+
comment_lines = result.each_line.map.with_index do |comment_line, result_offest|
|
91
|
+
if result_offest == 0
|
92
|
+
FormatComment.call(comment.whitespace_col, value_prefix, comment_line.chomp, @options)
|
93
|
+
else
|
94
|
+
leading_whitespace = " " * comment.text_col
|
95
|
+
leading_whitespace << FormatComment.call(comment.whitespace_col, nextline_prefix, comment_line.chomp, @options)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
comment_lines = [value_prefix.rstrip] if comment_lines.empty?
|
99
|
+
[comment.whitespace, comment_lines.join("\n")]
|
100
|
+
else
|
101
|
+
[comment.whitespace, comment.text]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
require 'seeing_is_believing/binary/annotate_end_of_file'
|
106
|
+
AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
|
107
|
+
|
108
|
+
new_body
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def value_prefix
|
113
|
+
@value_prefix ||= @options[:markers][:value][:prefix]
|
114
|
+
end
|
115
|
+
|
116
|
+
def nextline_prefix
|
117
|
+
@nextline_prefix ||= ('#' + ' '*value_prefix.length.pred)
|
118
|
+
end
|
119
|
+
|
120
|
+
def exception_prefix
|
121
|
+
@exception_prefix ||= @options[:markers][:exception][:prefix]
|
122
|
+
end
|
123
|
+
|
124
|
+
def value_regex
|
125
|
+
@value_regex ||= @options[:markers][:value][:regex]
|
126
|
+
end
|
127
|
+
|
128
|
+
def swap_leading_whitespace_in_multiline_comment(comment)
|
129
|
+
return if comment.scan("\n").size < 2
|
130
|
+
return if comment[0] =~ /\S/
|
131
|
+
nonbreaking_space = " "
|
132
|
+
comment[0] = nonbreaking_space
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|