seeing_is_believing 3.0.0.beta.1 → 3.0.0.beta.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c4baad8edd542826d976fb5439fe4523724e25c2
4
- data.tar.gz: 806d6b1cb7b43dc168c7653ff9995b3dd1b37220
3
+ metadata.gz: 981bfb703701767923dee2010cd2073ec901e20f
4
+ data.tar.gz: 0eaa0290fac0e7b0b312196371507f717b2ad1d7
5
5
  SHA512:
6
- metadata.gz: 74cde1ec9bd35565d7e49110210fadc6e8b9eb01538e78411138babf50abf5bc299a6cb592e3e7cc0d873c97c5549d8ddb5aa0a6a3e2eb07dc76130f433f077c
7
- data.tar.gz: 57811be453dd99be97df3c4ed6a17b7a0329178776c2bce14a4247f13d51acac04f1d9ccfc9930a7ae55983b2f285e0ad6c351c703041cab3dafa071e09f8941
6
+ metadata.gz: 019b0ac6587cb94c98bc9e916daa49f8b21e6eba166a3a6c5e930de573ecce274004f11de8cd47e366af46c4da881376b05c4ef4d3a8a43a5e0c614517a53d75
7
+ data.tar.gz: d515e9959c12741755f0ff298a5f571d8860290a15f3548e3386ae7ef8b4b9c0364ccac1c7a7816a771fc9a100746f813e259dce2cac943d01c6f8d70510dc1d
@@ -3,6 +3,12 @@ Feature: Xmpfilter style
3
3
  Support the same (or highly similar) interface as xmpfilter,
4
4
  so that people who use that lib can easily transition to SiB.
5
5
 
6
+ TODO:
7
+ * multiple values on pp lines
8
+ * show that exceptions respect line-length restriction flags
9
+ * Scenario: pp output on line with exception
10
+ * when input has previously identified exception
11
+
6
12
 
7
13
  Scenario: --xmpfilter-style Generic updating of marked lines
8
14
  Given the file "magic_comments.rb":
@@ -18,6 +24,7 @@ Feature: Xmpfilter style
18
24
  # =>
19
25
  "omg2"
20
26
  # => "not omg2"
27
+ 3+3#=>
21
28
  """
22
29
  When I run "seeing_is_believing --xmpfilter-style magic_comments.rb"
23
30
  Then stderr is empty
@@ -35,6 +42,7 @@ Feature: Xmpfilter style
35
42
  # => "omg"
36
43
  "omg2"
37
44
  # => "omg2"
45
+ 3+3# => 6
38
46
  """
39
47
 
40
48
 
@@ -101,42 +109,44 @@ Feature: Xmpfilter style
101
109
  """
102
110
 
103
111
 
104
- @josh1
105
112
  Scenario: Errors on annotated lines
106
113
  Given the file "xmpfilter_error_on_annotated_line.rb":
107
114
  """
108
- raise "omg" # =>
115
+ raise "ZOMG\n!!!!" # =>
109
116
  """
110
117
  When I run "seeing_is_believing --xmpfilter-style xmpfilter_error_on_annotated_line.rb"
111
118
  Then stderr is empty
112
119
  And the exit status is 1
113
120
  Then stdout is:
114
121
  """
115
- raise "omg" # => # ~> RuntimeError: ZOMG\n!!!!
122
+ raise "ZOMG\n!!!!" # => RuntimeError: ZOMG\n!!!!
116
123
 
117
124
  # ~> RuntimeError
118
- # ~> omg
125
+ # ~> ZOMG
126
+ # ~> !!!!
119
127
  # ~>
120
128
  # ~> xmpfilter_error_on_annotated_line.rb:1:in `<main>'
121
129
  """
122
130
 
123
131
 
124
- @josh2
125
132
  Scenario: Errors on unannotated lines
126
- Given the file "xmpfilter_error_on_annotated_line.rb":
133
+ Given the file "xmpfilter_error_on_unannotated_line.rb":
127
134
  """
128
- raise "omg"
135
+ raise "ZOMG\n!!!!"
129
136
  """
130
- When I run "seeing_is_believing --xmpfilter-style xmpfilter_error_on_annotated_line.rb"
137
+ When I run "seeing_is_believing --xmpfilter-style xmpfilter_error_on_unannotated_line.rb"
131
138
  Then stderr is empty
132
139
  And the exit status is 1
133
140
  Then stdout is:
134
141
  """
135
- raise "omg" # =>
136
- """
142
+ raise "ZOMG\n!!!!" # ~> RuntimeError: ZOMG\n!!!!
137
143
 
138
-
139
- Scenario: pp output on line with exception
144
+ # ~> RuntimeError
145
+ # ~> ZOMG
146
+ # ~> !!!!
147
+ # ~>
148
+ # ~> xmpfilter_error_on_unannotated_line.rb:1:in `<main>'
149
+ """
140
150
 
141
151
 
142
152
  Scenario: Cleaning previous output
@@ -34,7 +34,7 @@ class SeeingIsBelieving
34
34
  end
35
35
 
36
36
  if options.print_cleaned?
37
- stdout.print RemoveAnnotations.call(options.prepared_body, true, options.markers)
37
+ stdout.print RemoveAnnotations.call(options.prepared_body, true, options.marker_regexes)
38
38
  return SUCCESS_STATUS
39
39
  end
40
40
 
@@ -1,12 +1,12 @@
1
1
  class SeeingIsBelieving
2
2
  module Binary
3
3
  class AnnotateEveryLine
4
- def self.prepare_body(uncleaned_body, markers)
4
+ def self.prepare_body(uncleaned_body, marker_regexes)
5
5
  require 'seeing_is_believing/binary/remove_annotations'
6
- RemoveAnnotations.call uncleaned_body, true, markers
6
+ RemoveAnnotations.call uncleaned_body, true, marker_regexes
7
7
  end
8
8
 
9
- def self.expression_wrapper(markers)
9
+ def self.expression_wrapper(markers, marker_regexes)
10
10
  require 'seeing_is_believing/inspect_expressions'
11
11
  InspectExpressions
12
12
  end
@@ -45,7 +45,7 @@ class SeeingIsBelieving
45
45
  AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
46
46
 
47
47
  # What's w/ this debugger? maybe this should move higher?
48
- @options[:debugger].context "OUTPUT"
48
+ @options.fetch(:debugger).context "OUTPUT"
49
49
  new_body
50
50
  end
51
51
  end
@@ -53,11 +53,11 @@ class SeeingIsBelieving
53
53
  private
54
54
 
55
55
  def value_marker
56
- @value_marker ||= @options[:markers][:value]
56
+ @value_marker ||= @options.fetch(:markers).fetch(:value)
57
57
  end
58
58
 
59
59
  def exception_marker
60
- @exception_marker ||= @options[:markers][:exception]
60
+ @xnextline_marker ||= @options.fetch(:markers).fetch(:exception)
61
61
  end
62
62
  end
63
63
  end
@@ -3,34 +3,17 @@ require 'seeing_is_believing/code'
3
3
  class SeeingIsBelieving
4
4
  module Binary
5
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
-
6
+ def self.prepare_body(uncleaned_body, marker_regexes)
24
7
  require 'seeing_is_believing/binary/remove_annotations'
25
- RemoveAnnotations.call partially_cleaned_body, false, markers
8
+ RemoveAnnotations.call uncleaned_body, false, marker_regexes
26
9
  end
27
10
 
28
- def self.expression_wrapper(markers)
11
+ def self.expression_wrapper(markers, marker_regexes)
29
12
  -> program, number_of_captures {
30
13
  inspect_linenos = []
31
14
  pp_linenos = []
32
15
  Code.new(program).inline_comments.each do |c|
33
- next unless c.text.start_with? markers[:value].sub(/\s+$/, '')
16
+ next unless c.text[marker_regexes[:value]]
34
17
  c.whitespace_col == 0 ? pp_linenos << c.line_number - 1
35
18
  : inspect_linenos << c.line_number
36
19
  end
@@ -73,14 +56,33 @@ class SeeingIsBelieving
73
56
  def call
74
57
  @new_body ||= begin
75
58
  # TODO: doesn't currently realign output markers, do we want to do that?
76
- require 'seeing_is_believing/binary' # defines the markers
77
59
  require 'seeing_is_believing/binary/rewrite_comments'
78
60
  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
61
+ always_rewrite = []
62
+
63
+ if @results.has_exception?
64
+ exception_result = sprintf "%s: %s", @results.exception.class_name, @results.exception.message.gsub("\n", '\n')
65
+ exception_lineno = @results.exception.line_number
66
+ always_rewrite << exception_lineno
67
+ end
68
+
69
+ new_body = RewriteComments.call @body, always_rewrite: always_rewrite do |comment|
70
+ exception_on_line = exception_lineno == comment.line_number
71
+ annotate_this_line = comment.text[value_regex]
72
+ pp_annotation = annotate_this_line && comment.whitespace_col.zero?
73
+ normal_annotation = annotate_this_line && !pp_annotation
74
+ if exception_on_line && annotate_this_line
75
+ [comment.whitespace, CommentFormatter.call(comment.text_col, value_marker, exception_result, @options)]
76
+ elsif exception_on_line
77
+ whitespace = comment.whitespace
78
+ whitespace = " " if whitespace.empty?
79
+ [whitespace, CommentFormatter.call(comment.line_number, exception_marker, exception_result, @options)]
80
+ elsif normal_annotation
81
+ result = @results[comment.line_number].map { |result| result.gsub "\n", '\n' }.join(', ')
82
+ [comment.whitespace, CommentFormatter.call(comment.text_col, value_marker, result, @options)]
83
+ elsif pp_annotation
84
+ # result = sprintf "%s: %s", @results.exception.class_name, @results.exception.message.gsub("\n", '\n')
85
+ # CommentFormatter.call(line.size, exception_marker, result, options)
84
86
  # TODO: check that having multiple mult-line output values here looks good (e.g. avdi's example in a loop)
85
87
  result = @results[comment.line_number-1, :pp].map { |result| result.chomp }.join(', ')
86
88
  comment_lines = result.each_line.map.with_index do |comment_line, result_offest|
@@ -92,41 +94,33 @@ class SeeingIsBelieving
92
94
  end
93
95
  [comment.whitespace, comment_lines.join("\n")]
94
96
  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
+ [comment.whitespace, comment.text]
97
98
  end
98
99
  end
99
100
 
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
101
  require 'seeing_is_believing/binary/annotate_end_of_file'
112
102
  AnnotateEndOfFile.add_stdout_stderr_and_exceptions_to new_body, @results, @options
113
103
 
114
104
  # What's w/ this debugger? maybe this should move higher?
115
- @options[:debugger].context "OUTPUT"
105
+ @options.fetch(:debugger).context "OUTPUT"
116
106
  new_body
117
107
  end
118
108
  end
119
109
 
120
110
  def value_marker
121
- @value_marker ||= @options[:markers][:value]
111
+ @value_marker ||= @options.fetch(:markers).fetch(:value)
122
112
  end
123
113
 
124
114
  def nextline_marker
125
- @xnextline_marker ||= @options[:markers][:nextline]
115
+ @xnextline_marker ||= ('#' + ' '*value_marker.size.pred)
116
+ end
117
+
118
+ def exception_marker
119
+ @exception_marker ||= @options.fetch(:markers).fetch(:exception)
126
120
  end
127
121
 
128
122
  def value_regex
129
- @value_regex ||= /\A#{value_marker.sub(/\s+$/, '')}/
123
+ @value_regex ||= @options.fetch(:marker_regexes).fetch(:value)
130
124
  end
131
125
  end
132
126
  end
@@ -17,6 +17,14 @@ require 'seeing_is_believing/binary/annotate_xmpfilter_style'
17
17
  class SeeingIsBelieving
18
18
  module Binary
19
19
  class InterpretFlags
20
+ def self.to_regex(string)
21
+ flag_to_bit = {'i' => 0b001, 'x' => 0b010, 'm' => 0b100}
22
+ string =~ %r{\A/(.*)/([mxi]*)\Z}
23
+ Regexp.new ($1||string),
24
+ ($2||"").each_char.inject(0) { |bits, flag| bits|flag_to_bit[flag] }
25
+ end
26
+
27
+
20
28
  def self.attr_predicate(name)
21
29
  define_method("#{name}?") { predicates.fetch name }
22
30
  end
@@ -35,6 +43,7 @@ class SeeingIsBelieving
35
43
  attr_attribute :help_screen
36
44
  attr_attribute :debugger
37
45
  attr_attribute :markers
46
+ attr_attribute :marker_regexes
38
47
  attr_attribute :timeout
39
48
  attr_attribute :shebang
40
49
  attr_attribute :filename
@@ -47,11 +56,12 @@ class SeeingIsBelieving
47
56
  def initialize(flags, stdin, stdout)
48
57
  # Some simple attributes
49
58
  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)
59
+ attributes[:errors] = flags.fetch(:errors)
60
+ attributes[:markers] = flags.fetch(:markers) # TODO: Should probably object-ify these
61
+ attributes[:marker_regexes] = flags.fetch(:marker_regexes).each_with_object({}) { |(k, v), rs| rs[k] = self.class.to_regex v }
62
+ attributes[:timeout] = flags.fetch(:timeout) # b/c binary prints this out in the error message TODO: rename seconds_until_timeout
63
+ attributes[:shebang] = flags.fetch(:shebang) # b/c binary uses this to validate syntax atm
64
+ attributes[:filename] = flags.fetch(:filename)
55
65
 
56
66
  # All predicates
57
67
  self.predicates = {}
@@ -74,7 +84,7 @@ class SeeingIsBelieving
74
84
  String.new
75
85
 
76
86
  # Attributes that depend on predicates
77
- attributes[:prepared_body] = body && annotator.prepare_body(body, markers)
87
+ attributes[:prepared_body] = body && annotator.prepare_body(body, marker_regexes)
78
88
 
79
89
  # The lib's options (passed to SeeingIsBelieving.new)
80
90
  attributes[:lib_options] = {
@@ -88,7 +98,7 @@ class SeeingIsBelieving
88
98
  timeout: timeout,
89
99
  debugger: debugger,
90
100
  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
101
+ record_expressions: annotator.expression_wrapper(markers, marker_regexes), # TODO: rename to wrap_expressions
92
102
  }
93
103
 
94
104
  # The annotator's options (passed to annotator.call)
@@ -96,6 +106,7 @@ class SeeingIsBelieving
96
106
  alignment_strategy: extract_alignment_strategy(flags.fetch(:alignment_strategy), errors),
97
107
  debugger: debugger,
98
108
  markers: markers,
109
+ marker_regexes: marker_regexes,
99
110
  max_line_length: flags.fetch(:max_line_length),
100
111
  max_result_length: flags.fetch(:max_result_length),
101
112
  }
@@ -10,7 +10,15 @@ class SeeingIsBelieving
10
10
  exception: '# ~> ',
11
11
  stdout: '# >> ',
12
12
  stderr: '# !> ',
13
- nextline: '# ',
13
+ }
14
+ end
15
+
16
+ # TODO: rename to default_marker_regexes ...or turn into fkn objects
17
+ def self.marker_regexes
18
+ { value: '#\s*=>\s*',
19
+ exception: '#\s*~>\s*',
20
+ stdout: '#\s*>>\s*',
21
+ stderr: '#\s*!>\s*',
14
22
  }
15
23
  end
16
24
 
@@ -88,6 +96,7 @@ class SeeingIsBelieving
88
96
  shebang: 'ruby',
89
97
  result_as_json: false,
90
98
  markers: self.class.default_markers,
99
+ marker_regexes: self.class.marker_regexes,
91
100
  short_help_screen: self.class.help_screen(false),
92
101
  long_help_screen: self.class.help_screen(true),
93
102
  safe: false,
@@ -133,7 +142,6 @@ class SeeingIsBelieving
133
142
  exception_marker = markers.fetch(:exception)
134
143
  stdout_marker = markers.fetch(:stdout)
135
144
  stderr_marker = markers.fetch(:stderr)
136
- nextline_marker = markers.fetch(:nextline)
137
145
 
138
146
  <<FLAGS + if include_examples then <<EXAMPLES else '' end
139
147
  Usage: seeing_is_believing [options] [filename]
@@ -3,60 +3,53 @@ require 'seeing_is_believing/parser_helpers' # We have to parse the file to find
3
3
 
4
4
  class SeeingIsBelieving
5
5
  module Binary
6
+ # TODO: might be here that we hit the issue where
7
+ # you sometimes have to run it 2x to get it to correctly reset whitespace
8
+ # should wipe out the full_range rather than just the comment_range
6
9
  class RemoveAnnotations
7
- def self.call(code, should_clean_values, markers)
8
- new(code, should_clean_values, markers).call
10
+ def self.call(raw_code, should_clean_values, markers)
11
+ new(raw_code, should_clean_values, markers).call
9
12
  end
10
13
 
11
- def initialize(code, should_clean_values, markers)
14
+ def initialize(raw_code, should_clean_values, markers)
12
15
  self.should_clean_values = should_clean_values
13
- self.code = code
14
- self.markers = markers
16
+ self.raw_code = raw_code
17
+ self.markers = markers # TECHNICALLY THESE ARE REGEXES RIGHT NOW
18
+ self.code = Code.new(raw_code, 'strip_comments')
15
19
  end
16
20
 
17
21
  def call
18
- code_obj = Code.new(code, 'strip_comments')
19
- removed_comments = { result: [], exception: [], stdout: [], stderr: [] }
22
+ annotation_chunks_in(code).each do |comment, rest|
23
+ rest.each { |comment|
24
+ code.rewriter.remove comment.comment_range
25
+ remove_whitespace_before comment.comment_range.begin_pos, code.buffer, code.rewriter, false
26
+ }
20
27
 
21
- # TODO: This is why you sometimes have to run it 2x to get it to correctly reset whitespace
22
- # should wipe out the full_range rather than just the comment_range
23
- code_obj.inline_comments.each do |comment|
24
28
  case comment.text
25
29
  when value_regex
26
- if should_clean_values
27
- # puts "REMOVING VALUE: #{comment.text}"
28
- removed_comments[:result] << comment
29
- code_obj.rewriter.remove comment.comment_range
30
- end
30
+ next unless should_clean_values
31
+ code.rewriter.remove comment.comment_range
32
+ remove_whitespace_before comment.comment_range.begin_pos, code.buffer, code.rewriter, false
31
33
  when exception_regex
32
- # puts "REMOVING EXCEPTION: #{comment.text}"
33
- removed_comments[:exception] << comment
34
- code_obj.rewriter.remove comment.comment_range
34
+ code.rewriter.remove comment.comment_range
35
+ remove_whitespace_before comment.comment_range.begin_pos, code.buffer, code.rewriter, true
35
36
  when stdout_regex
36
- # puts "REMOVING STDOUT: #{comment.text}"
37
- removed_comments[:stdout] << comment
38
- code_obj.rewriter.remove comment.comment_range
37
+ code.rewriter.remove comment.comment_range
38
+ remove_whitespace_before comment.comment_range.begin_pos, code.buffer, code.rewriter, true
39
39
  when stderr_regex
40
- # puts "REMOVING STDERR: #{comment.text}"
41
- removed_comments[:stderr] << comment
42
- code_obj.rewriter.remove comment.comment_range
40
+ code.rewriter.remove comment.comment_range
41
+ remove_whitespace_before comment.comment_range.begin_pos, code.buffer, code.rewriter, true
42
+ else
43
+ raise "This should be impossible! Something must be broken in the comment section above"
43
44
  end
44
45
  end
45
46
 
46
- remove_whitespace_preceding_comments(code_obj.buffer, code_obj.rewriter, removed_comments)
47
- code_obj.rewriter.process
47
+ code.rewriter.process
48
48
  end
49
49
 
50
50
  private
51
51
 
52
- attr_accessor :code, :should_clean_values, :buffer, :markers
53
-
54
- def remove_whitespace_preceding_comments(buffer, rewriter, removed_comments)
55
- removed_comments[:result].each { |comment| remove_whitespace_before comment.comment_range.begin_pos, buffer, rewriter, false }
56
- removed_comments[:exception].each { |comment| remove_whitespace_before comment.comment_range.begin_pos, buffer, rewriter, true }
57
- removed_comments[:stdout].each { |comment| remove_whitespace_before comment.comment_range.begin_pos, buffer, rewriter, true }
58
- removed_comments[:stderr].each { |comment| remove_whitespace_before comment.comment_range.begin_pos, buffer, rewriter, true }
59
- end
52
+ attr_accessor :raw_code, :should_clean_values, :markers, :code
60
53
 
61
54
  # any whitespace before the index (on the same line) will be removed
62
55
  # if the preceding whitespace is at the beginning of the line, the newline will be removed
@@ -64,31 +57,57 @@ class SeeingIsBelieving
64
57
  def remove_whitespace_before(index, buffer, rewriter, remove_preceding_newline)
65
58
  end_pos = index
66
59
  begin_pos = end_pos - 1
67
- begin_pos -= 1 while code[begin_pos] =~ /\s/ && code[begin_pos] != "\n"
68
- begin_pos -= 1 if code[begin_pos] == "\n"
69
- begin_pos -= 1 if code[begin_pos] == "\n" && remove_preceding_newline
60
+ begin_pos -= 1 while raw_code[begin_pos] =~ /\s/ && raw_code[begin_pos] != "\n"
61
+ begin_pos -= 1 if raw_code[begin_pos] == "\n"
62
+ begin_pos -= 1 if raw_code[begin_pos] == "\n" && remove_preceding_newline
70
63
  return if begin_pos.next == end_pos
71
64
  rewriter.remove Parser::Source::Range.new(buffer, begin_pos.next, end_pos)
72
65
  end
73
66
 
67
+ def annotation_chunks_in(code)
68
+ code
69
+ .inline_comments
70
+ .map { |comment| [ (comment.text[value_regex] || # associates each comment to its annotation
71
+ comment.text[exception_regex] ||
72
+ comment.text[stdout_regex] ||
73
+ comment.text[stderr_regex]
74
+ ),
75
+ comment]}
76
+ .slice_before { |annotation, comment| annotation } # annotatios begin chunks
77
+ .select { |(annotation, start), *| annotation } # discard chunks not beginning with an annotation (probably can only happens on first comment)
78
+ .map { |(annotation, start), *rest| # end the chunk if the comment doesn't meet nextline criteria
79
+ nextline_comments = []
80
+ prev = start
81
+ rest.each { |_, potential_nextline|
82
+ break unless prev.line_number.next == potential_nextline.line_number &&
83
+ start.text_col == potential_nextline.text_col &&
84
+ potential_nextline.whitespace_col.zero? &&
85
+ annotation.length <= potential_nextline.text[/#\s*/].length
86
+ prev = potential_nextline
87
+ nextline_comments << potential_nextline
88
+ }
89
+ [start, nextline_comments]
90
+ }
91
+ end
92
+
74
93
  def value_regex
75
- @value_regex ||= marker_to_regex markers[:value]
94
+ markers.fetch(:value)
76
95
  end
77
96
 
78
97
  def exception_regex
79
- @exception_regex ||= marker_to_regex markers[:exception]
98
+ markers.fetch(:exception)
80
99
  end
81
100
 
82
101
  def stdout_regex
83
- @stdout_regex ||= marker_to_regex markers[:stdout]
102
+ markers.fetch(:stdout)
84
103
  end
85
104
 
86
105
  def stderr_regex
87
- @stderr_regex ||= marker_to_regex markers[:stderr]
106
+ markers.fetch(:stderr)
88
107
  end
89
108
 
90
- def marker_to_regex(marker)
91
- /\A#{Regexp.escape marker.sub(/\s+$/, '')}/
109
+ def nextline_regex
110
+ markers.fetch(:nextline)
92
111
  end
93
112
  end
94
113
  end
@@ -3,15 +3,61 @@ require 'seeing_is_believing/code'
3
3
  class SeeingIsBelieving
4
4
  module Binary
5
5
  module RewriteComments
6
- def self.call(code, &mapping)
7
- code = Code.new(code)
8
- code.inline_comments.each do |comment|
6
+ def self.call(code, options={}, &mapping)
7
+ code = Code.new(code)
8
+ comments = code.inline_comments
9
+ buffer = code.buffer
10
+
11
+ comments.each do |comment|
9
12
  new_whitespace, new_comment = mapping.call comment
10
13
  code.rewriter.replace comment.whitespace_range, new_whitespace
11
14
  code.rewriter.replace comment.comment_range, new_comment
12
15
  end
16
+
17
+ line_begins = line_begins_for(buffer.source)
18
+ options.fetch(:always_rewrite, []).each { |line_number|
19
+ next if comments.any? { |c| c.line_number == line_number }
20
+
21
+ # TODO: can this move down into Code?
22
+ _, next_line_index = (line_begins.find { |ln, index| ln == line_number } || [nil, buffer.source.size.next])
23
+ col = 0
24
+ col += 1 until col == next_line_index || buffer.source[next_line_index-2-col] == "\n"
25
+
26
+ index = next_line_index - 1
27
+ range = Parser::Source::Range.new buffer, index, index
28
+
29
+ comment = Code::InlineComment.new line_number, # line_number,
30
+ col, # preceding_whitespace_range.column,
31
+ "", # preceding_whitespace,
32
+ col, # comment.location.column,
33
+ "", # comment.text,
34
+ range, # range_for(first_char, comment.location.expression.end_pos),
35
+ range, # preceding_whitespace_range,
36
+ range # comment.location.expression
37
+
38
+ whitespace, body = mapping.call comment
39
+ code.rewriter.insert_before range, "#{whitespace}#{body}"
40
+ }
41
+
13
42
  code.rewriter.process
14
43
  end
44
+
45
+ # TODO: Move down into the Code obj?
46
+ # returns: [[lineno, index], ...]
47
+ def self.line_begins_for(raw_code)
48
+ # Copied from here https://github.com/whitequark/parser/blob/34c40479293bb9b5ba217039cf349111466d1f9a/lib/parser/source/buffer.rb#L213-227
49
+ # I figured it's better to copy it than to violate encapsulation since this is private
50
+ line_begins, index = [ [ 0, 0 ] ], 1
51
+
52
+ raw_code.each_char do |char|
53
+ if char == "\n"
54
+ line_begins.unshift [ line_begins.length, index ]
55
+ end
56
+
57
+ index += 1
58
+ end
59
+ line_begins
60
+ end
15
61
  end
16
62
  end
17
63
  end
@@ -1,3 +1,3 @@
1
1
  class SeeingIsBelieving
2
- VERSION = '3.0.0.beta.1'
2
+ VERSION = '3.0.0.beta.2'
3
3
  end
@@ -27,7 +27,7 @@ class SeeingIsBelieving
27
27
 
28
28
  def call
29
29
  @called ||= begin
30
- find_wrappings
30
+ wrap_recursive
31
31
 
32
32
  rewriter.insert_before root_range, before_all.call
33
33
 
@@ -81,19 +81,19 @@ class SeeingIsBelieving
81
81
 
82
82
  def add_children(ast, omit_first = false)
83
83
  (omit_first ? ast.children.drop(1) : ast.children)
84
- .each { |child| find_wrappings child }
84
+ .each { |child| wrap_recursive child }
85
85
  end
86
86
 
87
- def find_wrappings(ast=root)
87
+ # todo: is this actually add_wrappings
88
+ # and add_wrappings is actually add_wrapping?
89
+ def wrap_recursive(ast=root)
88
90
  return wrappings unless ast.kind_of? ::AST::Node
89
-
90
91
  case ast.type
91
92
  when :args, :redo, :retry, :alias, :undef, :splat, :match_current_line
92
93
  # no op
93
94
  when :defs
94
95
  add_to_wrappings ast
95
- child = ast.children.last
96
- add_to_wrappings child if child
96
+ add_children ast, true
97
97
  when :rescue, :ensure, :return, :break, :next
98
98
  add_children ast
99
99
  when :if
@@ -108,10 +108,10 @@ class SeeingIsBelieving
108
108
  add_children ast
109
109
  end
110
110
  when :when, :pair, :class, :module, :sclass
111
- find_wrappings ast.children.last
111
+ wrap_recursive ast.children.last
112
112
  when :resbody
113
113
  exception_type, variable_name, body = ast.children
114
- find_wrappings body
114
+ wrap_recursive body
115
115
  when :array
116
116
  add_to_wrappings ast
117
117
  the_begin = ast.location.begin
@@ -136,7 +136,7 @@ class SeeingIsBelieving
136
136
  # I can't think of anything other than a :send that could be the first child
137
137
  # but I'll check for it anyway.
138
138
  the_send = ast.children[0]
139
- find_wrappings the_send.children.first if the_send.type == :send
139
+ wrap_recursive the_send.children.first if the_send.type == :send
140
140
  add_children ast, true
141
141
  when :masgn
142
142
  # we must look at RHS because [1,<<A] and 1,<<A are both allowed
@@ -24,6 +24,45 @@ class SeeingIsBelieving
24
24
  InterpretFlags.new(flags.merge(overrides), stdin, stdout)
25
25
  end
26
26
 
27
+ describe '.to_regex' do
28
+ def call(input, regex)
29
+ expect(InterpretFlags.to_regex input).to eq regex
30
+ end
31
+
32
+ it 'converts strings into regexes' do
33
+ call '', %r()
34
+ call 'a', %r(a)
35
+ end
36
+
37
+ it 'ignores surrounding slashes' do
38
+ call '//', %r()
39
+ call '/a/', %r(a)
40
+ end
41
+
42
+ it 'respects flags after the trailing slash in surrounding slashes' do
43
+ call '/a/', %r(a)
44
+ call '/a//', %r(a/)
45
+ call '//a/', %r(/a)
46
+ call '/a/i', %r(a)i
47
+ call '/a/im', %r(a)im
48
+ call '/a/xim', %r(a)xim
49
+ call '/a/mix', %r(a)mix
50
+ call '/a/mixi', %r(a)mixi
51
+ end
52
+
53
+ it 'isn\'t fooled by strings that kinda look regexy' do
54
+ call '/a', %r(/a)
55
+ call 'a/', %r(a/)
56
+ call '/', %r(/)
57
+ call '/i', %r(/i)
58
+ end
59
+
60
+ it 'does not escape the content' do
61
+ call 'a\\s+', %r(a\s+)
62
+ call '/a\\s+/', %r(a\s+)
63
+ end
64
+ end
65
+
27
66
  describe 'annotator' do
28
67
  it 'annotates every line by default' do
29
68
  expect(call.annotator).to eq AnnotateEveryLine
@@ -380,8 +380,8 @@ RSpec.describe SeeingIsBelieving::Binary::ParseArgs do
380
380
  end
381
381
 
382
382
  describe ':markers' do
383
- it 'defaults to a hash with :value, :exception, :stdout, :stderr, and :nextline' do
384
- expect(parse([])[:markers].keys).to eq [:value, :exception, :stdout, :stderr, :nextline]
383
+ it 'defaults to a hash with :value, :exception, :stdout, and :stderr' do
384
+ expect(parse([])[:markers].keys).to eq [:value, :exception, :stdout, :stderr]
385
385
  end
386
386
 
387
387
  def assert_default(marker_name, value)
@@ -392,14 +392,25 @@ RSpec.describe SeeingIsBelieving::Binary::ParseArgs do
392
392
  it('defaults :exception to "# ~> "') { assert_default :exception , "# ~> " }
393
393
  it('defaults :stdout to "# >> "') { assert_default :stdout , "# >> " }
394
394
  it('defaults :stderr to "# !> "') { assert_default :stderr , "# !> " }
395
- it('defaults :nextline to "# "') { assert_default :nextline , "# " }
396
395
 
397
396
  # TODO: When things get a little more stable, don't feel like adding all the cukes to play with this right now
398
397
  it 'overrides :value with --value-marker'
399
398
  it 'overrides :exception with --exception-marker'
400
399
  it 'overrides :stdout with --stdout-marker'
401
400
  it 'overrides :stderr with --stderr-marker'
402
- it 'overrides :nextline with --xmpfilter-nextline-marker'
401
+ end
402
+
403
+ describe ':marker_regexes' do
404
+ it 'is a hash with the same keys as the markers' do
405
+ marker_keys = parse([])[:markers].keys
406
+ marker_regexes_keys = parse([])[:marker_regexes].keys
407
+ expect(marker_regexes_keys).to eq marker_keys
408
+ end
409
+
410
+ it 'overrides :value with --value-regex'
411
+ it 'overrides :exception with --exception-regex'
412
+ it 'overrides :stdout with --stdout-regex'
413
+ it 'overrides :stderr with --stderr-regex'
403
414
  end
404
415
  end
405
416
 
@@ -1,51 +1,155 @@
1
1
  require 'spec_helper'
2
2
  require 'seeing_is_believing/binary/remove_annotations'
3
+ require 'seeing_is_believing/binary/parse_args' # for marker info
3
4
 
4
5
  RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
5
6
  def call(code, should_clean_values=true)
6
7
  indentation = code[/\A */]
7
8
  code = code.gsub /^#{indentation}/, ''
8
- described_class.call(code,
9
- should_clean_values,
10
- value: '# => ',
11
- exception: '# ~> ',
12
- stdout: '# >> ',
13
- stderr: '# !> ',
14
- nextline: '# ',
15
- ).chomp
9
+ described_class.call(code, should_clean_values, regexes).chomp
10
+ end
11
+
12
+ def regexes
13
+ SeeingIsBelieving::Binary::ParseArgs
14
+ .marker_regexes
15
+ .each_with_object({}) { |(name, str), rs| rs[name] = Regexp.new str }
16
+ end
17
+
18
+ context 'when there are lines that are just normal comments' do
19
+ example { expect(call "1 # hello").to eq "1 # hello" }
20
+ example { expect(call "1 # hello\n"\
21
+ "# world").to eq "1 # hello\n"\
22
+ "# world" }
23
+ example { expect(call "1 # not special\n"\
24
+ "2 # => 3").to eq "1 # not special\n2" }
16
25
  end
17
26
 
18
27
  context 'when told to clean out value annotations' do
19
- example { expect(call "1# => 1", true).to eq "1" }
20
- example { expect(call "1 # => 1", true).to eq "1" }
21
- example { expect(call "1 # => 1", true).to eq "1" }
22
- example { expect(call "1 # => 1", true).to eq "1" }
23
- example { expect(call "1 # => 1", true).to eq "1" }
24
- example { expect(call "1 # => 1", true).to eq "1" }
28
+ example { expect(call "1# => 1", true).to eq "1" }
29
+ example { expect(call "1 # => 1", true).to eq "1" }
30
+ example { expect(call "1 # => 1", true).to eq "1" }
31
+ example { expect(call "1 # => 1", true).to eq "1" }
32
+ example { expect(call "1 # => 1", true).to eq "1" }
33
+ example { expect(call "1 # => 1", true).to eq "1" }
25
34
  example { expect(call "\n1 # => 1", true).to eq "\n1" }
26
35
  end
27
36
 
28
37
  context 'when told not to clean out value annotations' do
29
- example { expect(call "1# => 1", false).to eq "1# => 1" }
30
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
31
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
32
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
33
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
34
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
38
+ example { expect(call "1# => 1", false).to eq "1# => 1" }
39
+ example { expect(call "1 # => 1", false).to eq "1 # => 1" }
40
+ example { expect(call "1 # => 1", false).to eq "1 # => 1" }
41
+ example { expect(call "1 # => 1", false).to eq "1 # => 1" }
42
+ example { expect(call "1 # => 1", false).to eq "1 # => 1" }
43
+ example { expect(call "1 # => 1", false).to eq "1 # => 1" }
35
44
  example { expect(call "\n1 # => 1", false).to eq "\n1 # => 1" }
36
45
  end
37
46
 
38
47
  context 'cleaning inline exception annotations' do
39
- example { expect(call "1# ~> 1" ).to eq "1" }
40
- example { expect(call "1 # ~> 1" ).to eq "1" }
41
- example { expect(call "1 # ~> 1" ).to eq "1" }
42
- example { expect(call "1 # ~> 1" ).to eq "1" }
43
- example { expect(call "1 # ~> 1" ).to eq "1" }
44
- example { expect(call "1 # ~> 1" ).to eq "1" }
48
+ example { expect(call "1# ~> 1" ).to eq "1" }
49
+ example { expect(call "1 # ~> 1" ).to eq "1" }
50
+ example { expect(call "1 # ~> 1" ).to eq "1" }
51
+ example { expect(call "1 # ~> 1" ).to eq "1" }
52
+ example { expect(call "1 # ~> 1").to eq "1" }
53
+ example { expect(call "1 # ~> 1").to eq "1" }
45
54
  example { expect(call "\n1 # ~> 1").to eq "\n1" }
46
55
 
47
- example { expect(call "# >> 1").to eq "" }
48
- example { expect(call "# !> 1").to eq "" }
56
+ example { expect(call "# >> 1").to eq "" }
57
+ example { expect(call "# !> 1").to eq "" }
58
+ end
59
+
60
+ context 'cleaning multiline results' do
61
+ it 'cleans values whose hash and value locations exactly match the annotation on the line prior' do
62
+ expect(call "1# => 2\n"\
63
+ " # 3").to eq "1"
64
+ end
65
+
66
+ it 'does not clean values where the comment appears at a different position' do
67
+ expect(call "1# => 2\n"\
68
+ "# 3").to eq "1\n"\
69
+ "# 3"
70
+
71
+ expect(call "1# => 2\n"\
72
+ " # 3").to eq "1\n"\
73
+ " # 3"
74
+
75
+ expect(call "1# => 2\n"\
76
+ "# 3").to eq "1\n"\
77
+ "# 3"
78
+ expect(call "1# => 2\n"\
79
+ " # 3").to eq "1\n"\
80
+ " # 3"
81
+
82
+ end
83
+
84
+ it 'does not clean values where the nextline value appears before the initial annotation value' do
85
+ # does clean
86
+ expect(call "1# => 2\n"\
87
+ " # 3").to eq "1"
88
+ expect(call "1# => 2\n"\
89
+ " # 3").to eq "1"
90
+
91
+ # does not clean
92
+ expect(call "1# => 2\n"\
93
+ " # 3 4").to eq "1\n"\
94
+ " # 3 4"
95
+ expect(call "1# => 2\n"\
96
+ " # 3").to eq "1\n"\
97
+ " # 3"
98
+ expect(call "1# => 2\n"\
99
+ " # 3").to eq "1\n"\
100
+ " # 3"
101
+ end
102
+
103
+ it 'does not clean values where there is content before the comment' do
104
+ expect(call "1# => 2\n"\
105
+ "3# 4").to eq "1\n"\
106
+ "3# 4"
107
+ end
108
+
109
+ it 'cleans successive rows of these' do
110
+ expect(call "1# => 2\n"\
111
+ " # 3\n"\
112
+ " # 4" ).to eq "1"
113
+ expect(call "1# => 2\n"\
114
+ " # 3\n"\
115
+ " # 4\n"\
116
+ "5# => 6\n"\
117
+ " # 7\n"\
118
+ " # 8" ).to eq "1\n5"
119
+ end
120
+
121
+ it 'does not clean values where there is non-annotation inbetween' do
122
+ expect(call "1# => 2\n"\
123
+ "# 3\n"\
124
+ " # 4").to eq "1\n"\
125
+ "# 3\n"\
126
+ " # 4"
127
+
128
+ expect(call "1# => 2\n"\
129
+ "3 \n"\
130
+ " # 4").to eq "1\n"\
131
+ "3 \n"\
132
+ " # 4"
133
+ expect(call "1# => 2\n"\
134
+ "# 3\n"\
135
+ " # 4").to eq "1\n"\
136
+ "# 3\n"\
137
+ " # 4"
138
+ end
139
+
140
+ it 'cleans multiline portion, regardless of whether cleaning values (this is soooooo xmpfilter specific)' do
141
+ expect(call "1# => 2\n"\
142
+ " # 3").to eq "1"
143
+
144
+ expect(call "1# => 2\n"\
145
+ " # 3",
146
+ false).to eq "1# => 2"
147
+ end
148
+
149
+ it 'works on inline exceptions' do
150
+ expect(call "1# ~> 2\n"\
151
+ " # 3").to eq "1"
152
+ end
49
153
  end
50
154
 
51
155
  context 'cleaning stdout annotations' do
@@ -2,8 +2,8 @@ require 'spec_helper'
2
2
  require 'seeing_is_believing/binary/rewrite_comments'
3
3
 
4
4
  RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
5
- def call(code, &block)
6
- described_class.call code, &block
5
+ def call(code, options={}, &block)
6
+ described_class.call code, options, &block
7
7
  end
8
8
 
9
9
  it 'ignores multiline comments' do
@@ -52,4 +52,40 @@ RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
52
52
  "%Q{\n"\
53
53
  " 1}NEW_WHITESPACE6--COMMENT-6--"
54
54
  end
55
+
56
+ it 'can be given additional lines to make sure are provided, whether they have comments on them or not' do
57
+ rewritten = call("'a'\n"\
58
+ "'b'\n"\
59
+ "'c' # c\n"\
60
+ "'d'",
61
+ always_rewrite: [2, 3]) do |c|
62
+ value = sprintf "%d|%d|%p|%d|%p|%d..%d|%d..%d|%d..%d",
63
+ c.line_number,
64
+ c.whitespace_col,
65
+ c.whitespace,
66
+ c.text_col,
67
+ c.text,
68
+ c.full_range.begin_pos,
69
+ c.full_range.end_pos,
70
+ c.whitespace_range.begin_pos,
71
+ c.whitespace_range.end_pos,
72
+ c.comment_range.begin_pos,
73
+ c.comment_range.end_pos
74
+ ['pre', value]
75
+ end
76
+ expect(rewritten).to eq \
77
+ "'a'\n"\
78
+ "'b'pre2|3|\"\"|3|\"\"|7..7|7..7|7..7\n"\
79
+ "'c'pre3|3|\" \"|4|\"# c\"|11..15|11..12|12..15\n"\
80
+ "'d'"
81
+
82
+ rewritten = call("", always_rewrite: [1]) { |c| ['a', 'b'] }
83
+ expect(rewritten).to eq "ab"
84
+
85
+ rewritten = call("a", always_rewrite: [1]) { |c| ['b', 'c'] }
86
+ expect(rewritten).to eq "abc"
87
+
88
+ rewritten = call("a\n", always_rewrite: [1]) { |c| ['b', 'c'] }
89
+ expect(rewritten).to eq "abc\n"
90
+ end
55
91
  end
@@ -17,8 +17,8 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
17
17
  end
18
18
 
19
19
  describe 'wrapping the body' do
20
- let(:options) { { before_all: -> { "[".freeze },
21
- after_all: -> { "]".freeze },
20
+ let(:options) { { before_all: -> { "[".freeze },
21
+ after_all: -> { "]".freeze },
22
22
  before_each: -> * { '<'.freeze },
23
23
  after_each: -> * { '>'.freeze } } }
24
24
 
@@ -798,14 +798,16 @@ RSpec.describe SeeingIsBelieving::WrapExpressions do
798
798
  expect(wrap("def a(b,c=1,*d,&e)\nend")).to eq "<def a(b,c=1,*d,&e)\nend>"
799
799
  end
800
800
 
801
- it 'wraps the body' do
801
+ it 'wraps the the body' do
802
802
  expect(wrap("def a\n1\nend")).to eq "<def a\n<1>\nend>"
803
803
  expect(wrap("def a()\n1\nend")).to eq "<def a()\n<1>\nend>"
804
+ expect(wrap("def a\n1\n2\nend")).to eq "<def a\n<1>\n<2>\nend>"
804
805
  end
805
806
 
806
- it 'wrap singleton method definitions' do
807
+ it 'wraps singleton method definitions' do
807
808
  expect(wrap("def a.b\n1\nend")).to eq "<def a.b\n<1>\nend>"
808
- # expect(wrap("def a.b()\n1\nend")).to eq "<def a.b()\n<1>\nend>"
809
+ expect(wrap("def a.b()\n1\nend")).to eq "<def a.b()\n<1>\nend>"
810
+ expect(wrap("def a.b\n1\n2\nend")).to eq "<def a.b\n<1>\n<2>\nend>" # <-- seems redundant, but this was a regression
809
811
  end
810
812
 
811
813
  it 'wraps calls to yield' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: seeing_is_believing
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.beta.1
4
+ version: 3.0.0.beta.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Josh Cheek
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-23 00:00:00.000000000 Z
11
+ date: 2014-09-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: eval_in
@@ -267,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
267
267
  version: 1.3.1
268
268
  requirements: []
269
269
  rubyforge_project: seeing_is_believing
270
- rubygems_version: 2.4.1
270
+ rubygems_version: 2.0.14
271
271
  signing_key:
272
272
  specification_version: 4
273
273
  summary: Records results of every line of code in your file
@@ -293,4 +293,3 @@ test_files:
293
293
  - spec/seeing_is_believing_spec.rb
294
294
  - spec/spec_helper.rb
295
295
  - spec/wrap_expressions_spec.rb
296
- has_rdoc: