seeing_is_believing 3.0.0.beta.4 → 3.0.0.beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|