seeing_is_believing 2.2.0 → 3.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Changelog.md +33 -0
  4. data/bin/seeing_is_believing +1 -1
  5. data/features/errors.feature +3 -3
  6. data/features/examples.feature +6 -6
  7. data/features/flags.feature +51 -196
  8. data/features/regression.feature +12 -3
  9. data/features/safe.feature +33 -0
  10. data/features/support/env.rb +20 -0
  11. data/features/xmpfilter-style.feature +156 -0
  12. data/lib/seeing_is_believing.rb +17 -35
  13. data/lib/seeing_is_believing/binary.rb +81 -176
  14. data/lib/seeing_is_believing/binary/align_chunk.rb +5 -7
  15. data/lib/seeing_is_believing/binary/align_file.rb +4 -5
  16. data/lib/seeing_is_believing/binary/align_line.rb +4 -4
  17. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +60 -0
  18. data/lib/seeing_is_believing/binary/annotate_every_line.rb +64 -0
  19. data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +133 -0
  20. data/lib/seeing_is_believing/binary/comment_formatter.rb +19 -5
  21. data/lib/seeing_is_believing/binary/comment_lines.rb +1 -1
  22. data/lib/seeing_is_believing/binary/commentable_lines.rb +1 -1
  23. data/lib/seeing_is_believing/binary/interpret_flags.rb +149 -0
  24. data/lib/seeing_is_believing/binary/parse_args.rb +96 -104
  25. data/lib/seeing_is_believing/binary/remove_annotations.rb +95 -0
  26. data/lib/seeing_is_believing/binary/rewrite_comments.rb +8 -30
  27. data/lib/seeing_is_believing/code.rb +99 -0
  28. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +27 -19
  29. data/lib/seeing_is_believing/evaluate_with_eval_in.rb +27 -0
  30. data/lib/seeing_is_believing/event_stream/consumer.rb +111 -0
  31. data/lib/seeing_is_believing/event_stream/events.rb +16 -0
  32. data/lib/seeing_is_believing/event_stream/producer.rb +106 -0
  33. data/lib/seeing_is_believing/event_stream/update_result.rb +21 -0
  34. data/lib/seeing_is_believing/inspect_expressions.rb +24 -0
  35. data/lib/seeing_is_believing/parser_helpers.rb +1 -11
  36. data/lib/seeing_is_believing/result.rb +14 -56
  37. data/lib/seeing_is_believing/the_matrix.rb +14 -14
  38. data/lib/seeing_is_believing/version.rb +1 -1
  39. data/lib/seeing_is_believing/wrap_expressions.rb +32 -9
  40. data/seeing_is_believing.gemspec +7 -7
  41. data/spec/binary/comment_formatter_spec.rb +169 -18
  42. data/spec/binary/comment_lines_spec.rb +1 -1
  43. data/spec/binary/interpret_flags_spec.rb +307 -0
  44. data/spec/binary/parse_args_spec.rb +93 -91
  45. data/spec/binary/{clean_body_spec.rb → remove_annotations_spec.rb} +29 -22
  46. data/spec/binary/rewrite_comments_spec.rb +13 -13
  47. data/spec/code_spec.rb +49 -0
  48. data/spec/debugger_spec.rb +1 -1
  49. data/spec/evaluate_by_moving_files_spec.rb +7 -3
  50. data/spec/event_stream_spec.rb +390 -0
  51. data/spec/hard_core_ensure_spec.rb +1 -1
  52. data/spec/seeing_is_believing_spec.rb +137 -40
  53. data/spec/spec_helper.rb +3 -3
  54. data/spec/wrap_expressions_spec.rb +48 -35
  55. metadata +58 -35
  56. data/lib/seeing_is_believing/binary/add_annotations.rb +0 -144
  57. data/lib/seeing_is_believing/binary/clean_body.rb +0 -95
  58. data/lib/seeing_is_believing/has_exception.rb +0 -27
  59. data/lib/seeing_is_believing/line.rb +0 -90
  60. data/spec/line_spec.rb +0 -86
@@ -1,10 +1,10 @@
1
1
  require 'seeing_is_believing/binary/commentable_lines'
2
2
 
3
3
  class SeeingIsBelieving
4
- class Binary
4
+ module Binary
5
5
  class AlignChunk
6
- def initialize(body, start_line, end_line)
7
- self.body, self.start_line, self.end_line = body, start_line, end_line
6
+ def initialize(body)
7
+ self.body = body
8
8
  end
9
9
 
10
10
  # max line length of the the chunk (newline separated sections of code exempting comments) + 2 spaces for padding
@@ -14,7 +14,7 @@ class SeeingIsBelieving
14
14
 
15
15
  private
16
16
 
17
- attr_accessor :body, :start_line, :end_line
17
+ attr_accessor :body
18
18
 
19
19
  def line_lengths
20
20
  @line_lengths ||= begin
@@ -24,9 +24,7 @@ class SeeingIsBelieving
24
24
  .sort
25
25
  .slice_before { |line_number| line_num_to_indexes[line_number].last.zero? }
26
26
  .map { |slice|
27
- max_chunk_length = 2 + slice.select { |line_num| start_line <= line_num && line_num <= end_line }
28
- .map { |line_num| line_num_to_indexes[line_num].last }
29
- .max
27
+ max_chunk_length = 2 + slice.map { |line_num| line_num_to_indexes[line_num].last }.max
30
28
  slice.map { |line_number| [line_number, max_chunk_length] }
31
29
  }
32
30
  .flatten(1)
@@ -1,12 +1,12 @@
1
1
  require 'seeing_is_believing/binary/commentable_lines'
2
2
 
3
3
  class SeeingIsBelieving
4
- class Binary
4
+ module Binary
5
5
  class AlignFile
6
- attr_accessor :body, :start_line, :end_line
6
+ attr_accessor :body
7
7
 
8
- def initialize(body, start_line, end_line)
9
- self.body, self.start_line, self.end_line = body, start_line, end_line
8
+ def initialize(body)
9
+ self.body = body
10
10
  end
11
11
 
12
12
  # max line length of the lines to output (exempting comments) + 2 spaces for padding
@@ -14,7 +14,6 @@ class SeeingIsBelieving
14
14
  @max_source_line_length ||= 2 + begin
15
15
  line_num_to_indexes = CommentableLines.new(body).call # {line_number => [index_in_file, index_in_col]}
16
16
  max_value = line_num_to_indexes
17
- .select { |line_num, _| start_line <= line_num && line_num <= end_line }
18
17
  .values
19
18
  .map { |index, col| col }.max
20
19
  max_value || 0
@@ -1,10 +1,10 @@
1
1
  class SeeingIsBelieving
2
- class Binary
2
+ module Binary
3
3
  class AlignLine
4
- attr_accessor :body, :start_line, :end_line
4
+ attr_accessor :body
5
5
 
6
- def initialize(body, start_line, end_line)
7
- self.body, self.start_line, self.end_line = body, start_line, end_line
6
+ def initialize(body)
7
+ self.body = body
8
8
  end
9
9
 
10
10
  # length of the line + 2 spaces for padding
@@ -0,0 +1,60 @@
1
+ require 'seeing_is_believing/binary' # defines the markers
2
+ require 'seeing_is_believing/binary/comment_formatter'
3
+
4
+ class SeeingIsBelieving
5
+ module Binary
6
+ module AnnotateEndOfFile
7
+ extend self
8
+
9
+ # TODO: Switch options to markers
10
+ def add_stdout_stderr_and_exceptions_to(new_body, results, options)
11
+ output = stdout_ouptut_for(results, options) <<
12
+ stderr_ouptut_for(results, options) <<
13
+ exception_output_for(results, options)
14
+
15
+ # this technically could find an __END__ in a string or whatever
16
+ # going to just ignore that, though
17
+ if new_body[/^__END__$/]
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
23
+ end
24
+
25
+ def stdout_ouptut_for(results, options)
26
+ return '' unless results.has_stdout?
27
+ output = "\n"
28
+ results.stdout.each_line do |line|
29
+ output << CommentFormatter.call(0, options[:markers][:stdout], line.chomp, options) << "\n"
30
+ end
31
+ output
32
+ end
33
+
34
+ def stderr_ouptut_for(results, options)
35
+ return '' unless results.has_stderr?
36
+ output = "\n"
37
+ results.stderr.each_line do |line|
38
+ output << CommentFormatter.call(0, options[:markers][:stderr], line.chomp, options) << "\n"
39
+ end
40
+ output
41
+ end
42
+
43
+ def exception_output_for(results, options)
44
+ return '' unless results.has_exception?
45
+ exception_marker = options[:markers][:exception]
46
+ exception = results.exception
47
+ output = "\n"
48
+ output << CommentFormatter.new(0, exception_marker, exception.class_name, options).call << "\n"
49
+ exception.message.each_line do |line|
50
+ output << CommentFormatter.new(0, exception_marker, line.chomp, options).call << "\n"
51
+ end
52
+ output << exception_marker.sub(/\s+$/, '') << "\n"
53
+ exception.backtrace.each do |line|
54
+ output << CommentFormatter.new(0, exception_marker, line.chomp, options).call << "\n"
55
+ end
56
+ output
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,64 @@
1
+ class SeeingIsBelieving
2
+ module Binary
3
+ class AnnotateEveryLine
4
+ def self.prepare_body(uncleaned_body, markers)
5
+ require 'seeing_is_believing/binary/remove_annotations'
6
+ RemoveAnnotations.call uncleaned_body, true, markers
7
+ end
8
+
9
+ def self.expression_wrapper(markers)
10
+ require 'seeing_is_believing/inspect_expressions'
11
+ InspectExpressions
12
+ end
13
+
14
+ def self.call(body, results, options)
15
+ new(body, results, options).call
16
+ end
17
+
18
+ def initialize(body, results, options={})
19
+ @options = options
20
+ @body = body
21
+ @results = results
22
+ end
23
+
24
+ def call
25
+ @new_body ||= begin
26
+ require 'seeing_is_believing/binary/comment_lines'
27
+ require 'seeing_is_believing/binary/comment_formatter'
28
+
29
+ alignment_strategy = @options[:alignment_strategy].new(@body)
30
+ exception_lineno = @results.has_exception? ? @results.exception.line_number : -1
31
+ new_body = CommentLines.call @body do |line, line_number|
32
+ options = @options.merge pad_to: alignment_strategy.line_length_for(line_number)
33
+ if exception_lineno == line_number
34
+ result = sprintf "%s: %s", @results.exception.class_name, @results.exception.message.gsub("\n", '\n')
35
+ CommentFormatter.call(line.size, exception_marker, result, options)
36
+ elsif @results[line_number].any?
37
+ result = @results[line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
38
+ CommentFormatter.call(line.size, value_marker, result, options)
39
+ else
40
+ ''
41
+ end
42
+ end
43
+
44
+ require 'seeing_is_believing/binary/annotate_end_of_file'
45
+ AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
46
+
47
+ # What's w/ this debugger? maybe this should move higher?
48
+ @options[:debugger].context "OUTPUT"
49
+ new_body
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def value_marker
56
+ @value_marker ||= @options[:markers][:value]
57
+ end
58
+
59
+ def exception_marker
60
+ @exception_marker ||= @options[:markers][:exception]
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,133 @@
1
+ require 'seeing_is_believing/code'
2
+
3
+ class SeeingIsBelieving
4
+ module Binary
5
+ class AnnotateXmpfilterStyle
6
+ def self.prepare_body(uncleaned_body, markers)
7
+ # TODO: There's definitely a lot of overlap in responsibilities with invoking of parser
8
+ # and this is a conspicuous hack, since this functionality should really be provided by RemoveAnnotations
9
+ code = Code.new(uncleaned_body)
10
+ code.inline_comments
11
+ .select { |c| c.whitespace_col == 0 } # TODO: Would be nice to support indentation here
12
+ .slice_before { |c| c.text.start_with? markers[:value] }
13
+ .flat_map { |cs|
14
+ consecutives = cs.each_cons(2).take_while { |c1, c2| c1.line_number.next == c2.line_number }
15
+ cs[1, consecutives.size]
16
+ }
17
+ .select { |c| c.text.start_with? markers[:nextline] }
18
+ .each { |c|
19
+ range_with_preceding_newline = code.range_for(c.comment_range.begin_pos.pred, c.comment_range.end_pos)
20
+ code.rewriter.remove range_with_preceding_newline
21
+ }
22
+ partially_cleaned_body = code.rewriter.process
23
+
24
+ require 'seeing_is_believing/binary/remove_annotations'
25
+ RemoveAnnotations.call partially_cleaned_body, false, markers
26
+ end
27
+
28
+ def self.expression_wrapper(markers)
29
+ -> program, number_of_captures {
30
+ inspect_linenos = []
31
+ pp_linenos = []
32
+ Code.new(program).inline_comments.each do |c|
33
+ next unless c.text.start_with? markers[:value].sub(/\s+$/, '')
34
+ c.whitespace_col == 0 ? pp_linenos << c.line_number - 1
35
+ : inspect_linenos << c.line_number
36
+ end
37
+
38
+ InspectExpressions.call program,
39
+ number_of_captures,
40
+ before_all: -> {
41
+ # TODO: this is duplicated with the InspectExpressions class
42
+ number_of_captures_as_str = number_of_captures.inspect
43
+ number_of_captures_as_str = 'Float::INFINITY' if number_of_captures == Float::INFINITY
44
+ "begin; require 'pp'; $SiB.max_line_captures = #{number_of_captures_as_str}; $SiB.num_lines = #{program.lines.count}; "
45
+ },
46
+ after_each: -> line_number {
47
+ should_inspect = inspect_linenos.include?(line_number)
48
+ should_pp = pp_linenos.include?(line_number)
49
+ inspect = "$SiB.record_result(:inspect, #{line_number}, v)"
50
+ pp = "$SiB.record_result(:pp, #{line_number}, v) { PP.pp v, '', 74 }" # TODO: Is 74 the right value? Prob not, I think it's 80(default width) - 1(comment width) - 5(" => {"), but if I allow indented `# => `, then that would need to be less than 74 (idk if I actually do this or not, though :P)
51
+
52
+ if should_inspect && should_pp then ").tap { |v| #{inspect}; #{pp} }"
53
+ elsif should_inspect then ").tap { |v| #{inspect} }"
54
+ elsif should_pp then ").tap { |v| #{pp} }"
55
+ else ")"
56
+ end
57
+ }
58
+ }
59
+ end
60
+
61
+ def self.call(body, results, options)
62
+ new(body, results, options).call
63
+ end
64
+
65
+ def initialize(body, results, options={})
66
+ @options = options
67
+ @body = body
68
+ @results = results
69
+ end
70
+
71
+ # TODO: I think that this should respect the alignment strategy
72
+ # and we should just add a new alignment strategy for default xmpfilter style
73
+ def call
74
+ @new_body ||= begin
75
+ # TODO: doesn't currently realign output markers, do we want to do that?
76
+ require 'seeing_is_believing/binary' # defines the markers
77
+ require 'seeing_is_believing/binary/rewrite_comments'
78
+ require 'seeing_is_believing/binary/comment_formatter'
79
+ exception_lineno = @results.has_exception? ? @results.exception.line_number : -1
80
+ new_body = RewriteComments.call @body do |comment|
81
+ if !comment.text[value_regex]
82
+ [comment.whitespace, comment.text]
83
+ elsif comment.whitespace_col == 0
84
+ # TODO: check that having multiple mult-line output values here looks good (e.g. avdi's example in a loop)
85
+ result = @results[comment.line_number-1, :pp].map { |result| result.chomp }.join(', ')
86
+ comment_lines = result.each_line.map.with_index do |comment_line, result_offest|
87
+ if result_offest == 0
88
+ CommentFormatter.call(comment.whitespace_col, value_marker, comment_line.chomp, @options)
89
+ else
90
+ CommentFormatter.call(comment.whitespace_col, nextline_marker, comment_line.chomp, @options)
91
+ end
92
+ end
93
+ [comment.whitespace, comment_lines.join("\n")]
94
+ else
95
+ result = @results[comment.line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
96
+ [comment.whitespace, CommentFormatter.call(comment.text_col, value_marker, result, @options)]
97
+ end
98
+ end
99
+
100
+ # if exception_lineno == line_number
101
+ # if comment.text[value_regex] # has exception and comment
102
+ # # '# => # ~> exception...'
103
+ # [comment.whitespace, comment.text]
104
+ # else # exception, no comment
105
+ # # NORMAL EXCEPTION
106
+ # # result = @results[line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
107
+ # # CommentFormatter.call(line.size, value_marker, result, options)
108
+ # # [comment.whitespace, comment.text]
109
+ # end
110
+
111
+ require 'seeing_is_believing/binary/annotate_end_of_file'
112
+ AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
113
+
114
+ # What's w/ this debugger? maybe this should move higher?
115
+ @options[:debugger].context "OUTPUT"
116
+ new_body
117
+ end
118
+ end
119
+
120
+ def value_marker
121
+ @value_marker ||= @options[:markers][:value]
122
+ end
123
+
124
+ def nextline_marker
125
+ @xnextline_marker ||= @options[:markers][:nextline]
126
+ end
127
+
128
+ def value_regex
129
+ @value_regex ||= /\A#{value_marker.sub(/\s+$/, '')}/
130
+ end
131
+ end
132
+ end
133
+ end
@@ -1,7 +1,7 @@
1
1
  class SeeingIsBelieving
2
- class Binary
3
- # not sure I like this name, it formats comments that
4
- # show results e.g. "# => [1, 2, 3]"
2
+ module Binary
3
+ # not sure I like this name, it formats comments that show results
4
+ # e.g. "# => [1, 2, 3]"
5
5
  #
6
6
  # line_length is the length of the line this comment is being appended to
7
7
  #
@@ -15,13 +15,14 @@ class SeeingIsBelieving
15
15
  def initialize(line_length, separator, result, options)
16
16
  self.line_length = line_length
17
17
  self.separator = separator
18
- self.result = result.gsub "\n", '\n'
18
+ self.result = result
19
19
  self.options = options
20
20
  end
21
21
 
22
22
  def call
23
23
  @formatted ||= begin
24
- formatted = truncate "#{separator}#{result}", max_result_length
24
+ formatted = escape_non_printable result, chars_not_to_escape
25
+ formatted = truncate "#{separator}#{formatted}", max_result_length
25
26
  formatted = "#{' '*padding_length}#{formatted}"
26
27
  formatted = truncate formatted, max_line_length
27
28
  formatted = '' unless formatted.sub(/^ */, '').start_with? separator
@@ -57,6 +58,19 @@ class SeeingIsBelieving
57
58
  def ellipsify(string)
58
59
  string.sub(/.{0,3}$/) { |last_chars| '.' * last_chars.size }
59
60
  end
61
+
62
+ def chars_not_to_escape
63
+ options.fetch :dont_escape, []
64
+ end
65
+
66
+ def escape_non_printable(str, omissions)
67
+ str.each_char
68
+ .map { |char|
69
+ next char if 0x20 <= char.ord # above this has a printable representation
70
+ next char if omissions.include? char
71
+ char.inspect[1...-1]
72
+ }.join('')
73
+ end
60
74
  end
61
75
  end
62
76
  end
@@ -2,7 +2,7 @@ require 'seeing_is_believing/binary/commentable_lines'
2
2
 
3
3
  class SeeingIsBelieving
4
4
  # spec/binary/comment_lines_spec.rb
5
- class Binary
5
+ module Binary
6
6
 
7
7
  # takes a body and a block
8
8
  # passes the block the line
@@ -1,7 +1,7 @@
1
1
  require 'seeing_is_believing/parser_helpers'
2
2
 
3
3
  class SeeingIsBelieving
4
- class Binary
4
+ module Binary
5
5
 
6
6
  # could possibly be sped up by just reflecting on the tokens instead of the whole ast
7
7
  #
@@ -0,0 +1,149 @@
1
+ # Debugger initialization happens here
2
+ require 'seeing_is_believing/debugger'
3
+
4
+ # Alignment decision happens here
5
+ require 'seeing_is_believing/binary/align_file'
6
+ require 'seeing_is_believing/binary/align_line'
7
+ require 'seeing_is_believing/binary/align_chunk'
8
+
9
+ # Evaluator decision happens here
10
+ require 'seeing_is_believing/evaluate_by_moving_files'
11
+ require 'seeing_is_believing/evaluate_with_eval_in'
12
+
13
+ # Annotator decision happens here
14
+ require 'seeing_is_believing/binary/annotate_every_line'
15
+ require 'seeing_is_believing/binary/annotate_xmpfilter_style'
16
+
17
+ class SeeingIsBelieving
18
+ module Binary
19
+ class InterpretFlags
20
+ def self.attr_predicate(name)
21
+ define_method("#{name}?") { predicates.fetch name }
22
+ end
23
+ attr_predicate :print_version
24
+ attr_predicate :inherit_exit_status
25
+ attr_predicate :result_as_json
26
+ attr_predicate :print_help
27
+ attr_predicate :print_cleaned
28
+ attr_predicate :provided_filename_dne
29
+ attr_predicate :file_is_on_stdin
30
+
31
+ def self.attr_attribute(name)
32
+ define_method(name) { attributes.fetch name }
33
+ end
34
+ attr_attribute :annotator
35
+ attr_attribute :help_screen
36
+ attr_attribute :debugger
37
+ attr_attribute :markers
38
+ attr_attribute :timeout
39
+ attr_attribute :shebang
40
+ attr_attribute :filename
41
+ attr_attribute :body
42
+ attr_attribute :annotator_options
43
+ attr_attribute :prepared_body
44
+ attr_attribute :lib_options
45
+ attr_attribute :errors
46
+
47
+ def initialize(flags, stdin, stdout)
48
+ # Some simple attributes
49
+ self.attributes = {}
50
+ attributes[:errors] = flags.fetch(:errors)
51
+ attributes[:markers] = flags.fetch(:markers) # TODO: Should probably object-ify these
52
+ attributes[:timeout] = flags.fetch(:timeout) # b/c binary prints this out in the error message TODO: rename seconds_until_timeout
53
+ attributes[:shebang] = flags.fetch(:shebang) # b/c binary uses this to validate syntax atm
54
+ attributes[:filename] = flags.fetch(:filename)
55
+
56
+ # All predicates
57
+ self.predicates = {}
58
+ predicates[:print_version] = flags.fetch(:version) # TODO: rename rhs to print_version ?
59
+ predicates[:inherit_exit_status] = flags.fetch(:inherit_exit_status)
60
+ predicates[:result_as_json] = flags.fetch(:result_as_json)
61
+ predicates[:print_help] = !!flags.fetch(:help)
62
+ predicates[:print_cleaned] = flags.fetch(:clean) # TODO: Better name on rhs
63
+ predicates[:provided_filename_dne] = !!(filename && !File.exist?(filename)) # TODO: Should this just be an error in errors table?
64
+ predicates[:file_is_on_stdin] = (!filename && !flags.fetch(:program_from_args))
65
+
66
+ # Polymorphism, y'all!
67
+ attributes[:annotator] = (flags.fetch(:xmpfilter_style) ? AnnotateXmpfilterStyle : AnnotateEveryLine)
68
+ attributes[:help_screen] = flags.fetch(:help) == 'help' ? flags.fetch(:short_help_screen) : flags.fetch(:long_help_screen)
69
+ attributes[:debugger] = flags.fetch(:debug) ? Debugger.new(stream: stdout, colour: true) : Debugger.new(stream: nil)
70
+ attributes[:body] = ((print_version? || print_help?) && String.new) ||
71
+ flags.fetch(:program_from_args) ||
72
+ (file_is_on_stdin? && stdin.read) ||
73
+ (File.read filename unless provided_filename_dne?) ||
74
+ String.new
75
+
76
+ # Attributes that depend on predicates
77
+ attributes[:prepared_body] = body && annotator.prepare_body(body, markers)
78
+
79
+ # The lib's options (passed to SeeingIsBelieving.new)
80
+ attributes[:lib_options] = {
81
+ evaluate_with: (flags.fetch(:safe) ? EvaluateWithEvalIn : EvaluateByMovingFiles),
82
+ filename: (flags.fetch(:as) || filename),
83
+ ruby_executable: shebang,
84
+ stdin: (file_is_on_stdin? ? '' : stdin),
85
+ require: (['seeing_is_believing/the_matrix'] + flags.fetch(:require)), # TODO: rename requires: files_to_require, or :requires or maybe :to_require
86
+ load_path: ([File.expand_path('../../..', __FILE__)] + flags.fetch(:load_path)),
87
+ encoding: flags.fetch(:encoding),
88
+ timeout: timeout,
89
+ debugger: debugger,
90
+ number_of_captures: flags.fetch(:number_of_captures), # TODO: Rename to max_number_of_captures
91
+ record_expressions: annotator.expression_wrapper(markers), # TODO: rename to wrap_expressions
92
+ }
93
+
94
+ # The annotator's options (passed to annotator.call)
95
+ attributes[:annotator_options] = {
96
+ alignment_strategy: extract_alignment_strategy(flags.fetch(:alignment_strategy), errors),
97
+ debugger: debugger,
98
+ markers: markers,
99
+ max_line_length: flags.fetch(:max_line_length),
100
+ max_result_length: flags.fetch(:max_result_length),
101
+ }
102
+
103
+ # Some error checking
104
+ if 1 < flags.fetch(:filenames).size
105
+ errors << "Can only have one filename, but had: #{flags.fetch(:filenames).map(&:inspect).join ', '}"
106
+ elsif filename && flags.fetch(:program_from_args)
107
+ errors << "You passed the program in an argument, but have also specified the filename #{filename.inspect}"
108
+ end
109
+ end
110
+
111
+ def inspect
112
+ inspected = "#<#{self.class.name.inspect}\n"
113
+ inspected << " --PREDICATES--\n"
114
+ predicates.each do |predicate, value|
115
+ inspected << inspect_line(sprintf " %-25s %p", predicate.to_s+"?", value)
116
+ end
117
+ inspected << " --ATTRIBUTES--\n"
118
+ attributes.each do |predicate, value|
119
+ inspected << inspect_line(sprintf " %-20s %p", predicate.to_s, value)
120
+ end
121
+ inspected << ">"
122
+ inspected
123
+ end
124
+
125
+ private
126
+
127
+ attr_accessor :predicates, :attributes
128
+
129
+ def extract_alignment_strategy(strategy_name, errors)
130
+ strategies = {'file' => AlignFile, 'chunk' => AlignChunk, 'line' => AlignLine}
131
+ if strategies[strategy_name]
132
+ strategies[strategy_name]
133
+ elsif strategy_name
134
+ errors << "alignment-strategy does not know #{strategy_name}, only knows: #{strategies.keys.join(', ')}"
135
+ else
136
+ errors << "alignment-strategy expected an alignment strategy as the following argument but did not see one"
137
+ end
138
+ end
139
+
140
+ def inspect_line(line)
141
+ if line.size < 78
142
+ line << "\n"
143
+ else
144
+ line[0, 75] << "...\n"
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end