error_highlight 0.1.0 → 0.4.0

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
  SHA256:
3
- metadata.gz: f6edc97e3eca88e8281056e7a3d9bceb448c0ac614b742e4ed38908a40e51e01
4
- data.tar.gz: 255b537c7d07f8dfd01b747719472e57d24e91703b8df678d655a3fc3f71897d
3
+ metadata.gz: a9ae1f66c6b1151cf65329869a64d4ea9ce7732344a6318aa99dc8a16aabeeff
4
+ data.tar.gz: d2e52f94f74c6569cea530e8a843d99030c0a2860f38b8591952faedba759419
5
5
  SHA512:
6
- metadata.gz: a63da67176a0dabd493fdafa1de60155a26696d22ba633d17765b29a359b0e4ffcc30d4806236bf416c828427d32171aca4210bdb1e018e0347e4cefef3e953a
7
- data.tar.gz: '08febd90564658489923f2ef0d2aab82a3796e52c7aa2a11ddcd6cc0d1f65abacd9b757b61bbb36bb59d7cf0e0c1eea820bce6d3b0bad3f771ead27714bc70ee'
6
+ metadata.gz: b6610f3e482792ae7f2eb8a2e8ada6c13f588b8d9fdbf2bac0948d99efb5046d92b9b5c83294661ecc91ffb1239b446b759d206db60da7dedaab5af48c7e3d4c
7
+ data.tar.gz: 481f0da516eff4f75662bb709f56aeb11b380336f3fbd82465af16c24e48bbaa20d4590e5bdad5373c283f7d8eaba90c458bd807a6f1c1b809aef4710cb5a302
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: 'github-actions'
4
+ directory: '/'
5
+ schedule:
6
+ interval: 'weekly'
@@ -13,9 +13,9 @@ jobs:
13
13
  runs-on: ubuntu-latest
14
14
  strategy:
15
15
  matrix:
16
- ruby: [ 'ruby-head' ]
16
+ ruby: [ 'ruby-head', '3.1' ]
17
17
  steps:
18
- - uses: actions/checkout@v2
18
+ - uses: actions/checkout@v3
19
19
  - uses: ruby/setup-ruby@v1
20
20
  with:
21
21
  ruby-version: ${{ matrix.ruby }}
@@ -24,4 +24,4 @@ jobs:
24
24
  bundle install
25
25
  - name: Run the test suite
26
26
  run: |
27
- bundle exec rake TESTOPT=-v
27
+ RUBYOPT=--disable-error_highlight bundle exec rake TESTOPT=-v
data/README.md CHANGED
@@ -29,7 +29,7 @@ def extract_value(data)
29
29
  end
30
30
  ```
31
31
 
32
- When `data` is `{ :results => [] }`, the following error messsage is shown:
32
+ When `data` is `{ :results => [] }`, the following error message is shown:
33
33
 
34
34
  ```
35
35
  $ ruby test.rb
@@ -51,13 +51,63 @@ test.rb:2:in `extract_value': undefined method `[]' for nil:NilClass (NoMethodEr
51
51
  from test.rb:5:in `<main>'
52
52
  ```
53
53
 
54
+ ## Using the `ErrorHighlight.spot`
55
+
56
+ *Note: This API is experimental, may change in future.*
57
+
58
+ You can use the `ErrorHighlight.spot` method to get the snippet data.
59
+ Note that the argument must be a RubyVM::AbstractSyntaxTree::Node object that is created with `keep_script_lines: true` option (which is available since Ruby 3.1).
60
+
61
+ ```ruby
62
+ class Dummy
63
+ def test(_dummy_arg)
64
+ node = RubyVM::AbstractSyntaxTree.of(caller_locations.first, keep_script_lines: true)
65
+ ErrorHighlight.spot(node)
66
+ end
67
+ end
68
+
69
+ pp Dummy.new.test(42) # <- Line 8
70
+ # ^^^^^ <- Column 12--17
71
+
72
+ #=> {:first_lineno=>8,
73
+ # :first_column=>12,
74
+ # :last_lineno=>8,
75
+ # :last_column=>17,
76
+ # :snippet=>"pp Dummy.new.test(42) # <- Line 8\n"}
77
+ ```
78
+
79
+ ## Custom Formatter
80
+
81
+ If you want to customize the message format for code snippet, use `ErrorHighlight.formatter=` to set your custom object that responds to `message_for` method.
82
+
83
+ ```ruby
84
+ formatter = Object.new
85
+ def formatter.message_for(spot)
86
+ marker = " " * spot[:first_column] + "^" + "~" * (spot[:last_column] - spot[:first_column] - 1)
87
+
88
+ "\n\n#{ spot[:snippet] }#{ marker }"
89
+ end
90
+
91
+ ErrorHighlight.formatter = formatter
92
+
93
+ 1.time {}
94
+
95
+ #=>
96
+ #
97
+ # test.rb:10:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
98
+ #
99
+ # 1.time {}
100
+ # ^~~~~
101
+ # Did you mean? times
102
+ ```
103
+
54
104
  ## Disabling `error_highlight`
55
105
 
56
106
  Occasionally, you may want to disable the `error_highlight` gem for e.g. debugging issues in the error object itself. You
57
107
  can disable it entirely by specifying `--disable-error_highlight` option to the `ruby` command:
58
108
 
59
109
  ```bash
60
- $ uby --disable-error_highlight -e '1.time {}'
110
+ $ ruby --disable-error_highlight -e '1.time {}'
61
111
  -e:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
62
112
  Did you mean? times
63
113
  ```
@@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
18
18
  spec.homepage = "https://github.com/ruby/error_highlight"
19
19
 
20
20
  spec.license = "MIT"
21
- spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
21
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0.dev")
22
22
 
23
23
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
24
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
@@ -1,13 +1,17 @@
1
1
  require_relative "version"
2
2
 
3
3
  module ErrorHighlight
4
- # Identify the code fragment that seems associated with a given error
4
+ # Identify the code fragment at that a given exception occurred.
5
5
  #
6
- # Arguments:
7
- # node: RubyVM::AbstractSyntaxTree::Node
8
- # point: :name | :args
9
- # name: The name associated with the NameError/NoMethodError
10
- # fetch: A block to fetch a specified code line (or lines)
6
+ # Options:
7
+ #
8
+ # point_type: :name | :args
9
+ # :name (default) points the method/variable name that the exception occurred.
10
+ # :args points the arguments of the method call that the exception occurred.
11
+ #
12
+ # backtrace_location: Thread::Backtrace::Location
13
+ # It locates the code fragment of the given backtrace_location.
14
+ # By default, it uses the first frame of backtrace_locations of the given exception.
11
15
  #
12
16
  # Returns:
13
17
  # {
@@ -15,23 +19,76 @@ module ErrorHighlight
15
19
  # first_column: Integer,
16
20
  # last_lineno: Integer,
17
21
  # last_column: Integer,
18
- # line: String,
22
+ # snippet: String,
23
+ # script_lines: [String],
19
24
  # } | nil
20
- def self.spot(...)
21
- Spotter.new(...).spot
25
+ def self.spot(obj, **opts)
26
+ case obj
27
+ when Exception
28
+ exc = obj
29
+ loc = opts[:backtrace_location]
30
+ opts = { point_type: opts.fetch(:point_type, :name) }
31
+
32
+ unless loc
33
+ case exc
34
+ when TypeError, ArgumentError
35
+ opts[:point_type] = :args
36
+ end
37
+
38
+ locs = exc.backtrace_locations
39
+ return nil unless locs
40
+
41
+ loc = locs.first
42
+ return nil unless loc
43
+
44
+ opts[:name] = exc.name if NameError === obj
45
+ end
46
+
47
+ return nil unless Thread::Backtrace::Location === loc
48
+
49
+ node = RubyVM::AbstractSyntaxTree.of(loc, keep_script_lines: true)
50
+
51
+ Spotter.new(node, **opts).spot
52
+
53
+ when RubyVM::AbstractSyntaxTree::Node
54
+ # Just for compatibility
55
+ Spotter.new(node, **opts).spot
56
+
57
+ else
58
+ raise TypeError, "Exception is expected"
59
+ end
60
+
61
+ rescue SyntaxError,
62
+ SystemCallError, # file not found or something
63
+ ArgumentError # eval'ed code
64
+
65
+ return nil
22
66
  end
23
67
 
24
68
  class Spotter
25
- def initialize(node, point, name: nil, &fetch)
69
+ class NonAscii < Exception; end
70
+ private_constant :NonAscii
71
+
72
+ def initialize(node, point_type: :name, name: nil)
26
73
  @node = node
27
- @point = point
74
+ @point_type = point_type
28
75
  @name = name
29
76
 
30
77
  # Not-implemented-yet options
31
78
  @arg = nil # Specify the index or keyword at which argument caused the TypeError/ArgumentError
32
79
  @multiline = false # Allow multiline spot
33
80
 
34
- @fetch = fetch
81
+ @fetch = -> (lineno, last_lineno = lineno) do
82
+ snippet = @node.script_lines[lineno - 1 .. last_lineno - 1].join("")
83
+ snippet += "\n" unless snippet.end_with?("\n")
84
+
85
+ # It require some work to support Unicode (or multibyte) characters.
86
+ # Tentatively, we stop highlighting if the code snippet has non-ascii characters.
87
+ # See https://github.com/ruby/error_highlight/issues/4
88
+ raise NonAscii unless snippet.ascii_only?
89
+
90
+ snippet
91
+ end
35
92
  end
36
93
 
37
94
  def spot
@@ -40,7 +97,7 @@ module ErrorHighlight
40
97
  case @node.type
41
98
 
42
99
  when :CALL, :QCALL
43
- case @point
100
+ case @point_type
44
101
  when :name
45
102
  spot_call_for_name
46
103
  when :args
@@ -48,7 +105,7 @@ module ErrorHighlight
48
105
  end
49
106
 
50
107
  when :ATTRASGN
51
- case @point
108
+ case @point_type
52
109
  when :name
53
110
  spot_attrasgn_for_name
54
111
  when :args
@@ -56,7 +113,7 @@ module ErrorHighlight
56
113
  end
57
114
 
58
115
  when :OPCALL
59
- case @point
116
+ case @point_type
60
117
  when :name
61
118
  spot_opcall_for_name
62
119
  when :args
@@ -64,7 +121,7 @@ module ErrorHighlight
64
121
  end
65
122
 
66
123
  when :FCALL
67
- case @point
124
+ case @point_type
68
125
  when :name
69
126
  spot_fcall_for_name
70
127
  when :args
@@ -75,7 +132,7 @@ module ErrorHighlight
75
132
  spot_vcall
76
133
 
77
134
  when :OP_ASGN1
78
- case @point
135
+ case @point_type
79
136
  when :name
80
137
  spot_op_asgn1_for_name
81
138
  when :args
@@ -83,7 +140,7 @@ module ErrorHighlight
83
140
  end
84
141
 
85
142
  when :OP_ASGN2
86
- case @point
143
+ case @point_type
87
144
  when :name
88
145
  spot_op_asgn2_for_name
89
146
  when :args
@@ -103,17 +160,21 @@ module ErrorHighlight
103
160
  spot_op_cdecl
104
161
  end
105
162
 
106
- if @line && @beg_column && @end_column && @beg_column < @end_column
163
+ if @snippet && @beg_column && @end_column && @beg_column < @end_column
107
164
  return {
108
165
  first_lineno: @beg_lineno,
109
166
  first_column: @beg_column,
110
167
  last_lineno: @end_lineno,
111
168
  last_column: @end_column,
112
- line: @line,
169
+ snippet: @snippet,
170
+ script_lines: @node.script_lines,
113
171
  }
114
172
  else
115
173
  return nil
116
174
  end
175
+
176
+ rescue NonAscii
177
+ nil
117
178
  end
118
179
 
119
180
  private
@@ -133,34 +194,34 @@ module ErrorHighlight
133
194
  nd_recv, mid, nd_args = @node.children
134
195
  lineno = nd_recv.last_lineno
135
196
  lines = @fetch[lineno, @node.last_lineno]
136
- if mid == :[] && lines.match(/\G\s*(\[(?:\s*\])?)/, nd_recv.last_column)
197
+ if mid == :[] && lines.match(/\G[\s)]*(\[(?:\s*\])?)/, nd_recv.last_column)
137
198
  @beg_column = $~.begin(1)
138
- @line = lines[/.*\n/]
199
+ @snippet = lines[/.*\n/]
139
200
  @beg_lineno = @end_lineno = lineno
140
201
  if nd_args
141
- if nd_recv.last_lineno == nd_args.last_lineno && @line.match(/\s*\]/, nd_args.last_column)
202
+ if nd_recv.last_lineno == nd_args.last_lineno && @snippet.match(/\s*\]/, nd_args.last_column)
142
203
  @end_column = $~.end(0)
143
204
  end
144
205
  else
145
- if lines.match(/\G\s*?\[\s*\]/, nd_recv.last_column)
206
+ if lines.match(/\G[\s)]*?\[\s*\]/, nd_recv.last_column)
146
207
  @end_column = $~.end(0)
147
208
  end
148
209
  end
149
- elsif lines.match(/\G\s*?(\&?\.)(\s*?)(#{ Regexp.quote(mid) }).*\n/, nd_recv.last_column)
210
+ elsif lines.match(/\G[\s)]*?(\&?\.)(\s*?)(#{ Regexp.quote(mid) }).*\n/, nd_recv.last_column)
150
211
  lines = $` + $&
151
212
  @beg_column = $~.begin($2.include?("\n") ? 3 : 1)
152
213
  @end_column = $~.end(3)
153
214
  if i = lines[..@beg_column].rindex("\n")
154
215
  @beg_lineno = @end_lineno = lineno + lines[..@beg_column].count("\n")
155
- @line = lines[i + 1..]
216
+ @snippet = lines[i + 1..]
156
217
  @beg_column -= i + 1
157
218
  @end_column -= i + 1
158
219
  else
159
- @line = lines
220
+ @snippet = lines
160
221
  @beg_lineno = @end_lineno = lineno
161
222
  end
162
223
  elsif mid.to_s =~ /\A\W+\z/ && lines.match(/\G\s*(#{ Regexp.quote(mid) })=.*\n/, nd_recv.last_column)
163
- @line = $` + $&
224
+ @snippet = $` + $&
164
225
  @beg_column = $~.begin(1)
165
226
  @end_column = $~.end(1)
166
227
  end
@@ -192,16 +253,16 @@ module ErrorHighlight
192
253
  nd_recv, mid, nd_args = @node.children
193
254
  *nd_args, _nd_last_arg, _nil = nd_args.children
194
255
  fetch_line(nd_recv.last_lineno)
195
- if mid == :[]= && @line.match(/\G\s*(\[)/, nd_recv.last_column)
256
+ if mid == :[]= && @snippet.match(/\G[\s)]*(\[)/, nd_recv.last_column)
196
257
  @beg_column = $~.begin(1)
197
258
  args_last_column = $~.end(0)
198
259
  if nd_args.last && nd_recv.last_lineno == nd_args.last.last_lineno
199
260
  args_last_column = nd_args.last.last_column
200
261
  end
201
- if @line.match(/\s*\]\s*=/, args_last_column)
262
+ if @snippet.match(/[\s)]*\]\s*=/, args_last_column)
202
263
  @end_column = $~.end(0)
203
264
  end
204
- elsif @line.match(/\G\s*(\.\s*#{ Regexp.quote(mid.to_s.sub(/=\z/, "")) }\s*=)/, nd_recv.last_column)
265
+ elsif @snippet.match(/\G[\s)]*(\.\s*#{ Regexp.quote(mid.to_s.sub(/=\z/, "")) }\s*=)/, nd_recv.last_column)
205
266
  @beg_column = $~.begin(1)
206
267
  @end_column = $~.end(1)
207
268
  end
@@ -217,7 +278,7 @@ module ErrorHighlight
217
278
  def spot_attrasgn_for_args
218
279
  nd_recv, mid, nd_args = @node.children
219
280
  fetch_line(nd_recv.last_lineno)
220
- if mid == :[]= && @line.match(/\G\s*\[/, nd_recv.last_column)
281
+ if mid == :[]= && @snippet.match(/\G[\s)]*\[/, nd_recv.last_column)
221
282
  @beg_column = $~.end(0)
222
283
  if nd_recv.last_lineno == nd_args.last_lineno
223
284
  @end_column = nd_args.last_column
@@ -239,13 +300,13 @@ module ErrorHighlight
239
300
  fetch_line(nd_recv.last_lineno)
240
301
  if nd_arg
241
302
  # binary operator
242
- if @line.match(/\G\s*(#{ Regexp.quote(op) })/, nd_recv.last_column)
303
+ if @snippet.match(/\G[\s)]*(#{ Regexp.quote(op) })/, nd_recv.last_column)
243
304
  @beg_column = $~.begin(1)
244
305
  @end_column = $~.end(1)
245
306
  end
246
307
  else
247
308
  # unary operator
248
- if @line[...nd_recv.first_column].match(/(#{ Regexp.quote(op.to_s.sub(/@\z/, "")) })\s*\(?\s*\z/)
309
+ if @snippet[...nd_recv.first_column].match(/(#{ Regexp.quote(op.to_s.sub(/@\z/, "")) })\s*\(?\s*\z/)
249
310
  @beg_column = $~.begin(1)
250
311
  @end_column = $~.end(1)
251
312
  end
@@ -273,7 +334,7 @@ module ErrorHighlight
273
334
  def spot_fcall_for_name
274
335
  mid, _nd_args = @node.children
275
336
  fetch_line(@node.first_lineno)
276
- if @line.match(/(#{ Regexp.quote(mid) })/, @node.first_column)
337
+ if @snippet.match(/(#{ Regexp.quote(mid) })/, @node.first_column)
277
338
  @beg_column = $~.begin(1)
278
339
  @end_column = $~.end(1)
279
340
  end
@@ -315,13 +376,13 @@ module ErrorHighlight
315
376
  def spot_op_asgn1_for_name
316
377
  nd_recv, op, nd_args, _nd_rhs = @node.children
317
378
  fetch_line(nd_recv.last_lineno)
318
- if @line.match(/\G\s*(\[)/, nd_recv.last_column)
379
+ if @snippet.match(/\G[\s)]*(\[)/, nd_recv.last_column)
319
380
  bracket_beg_column = $~.begin(1)
320
381
  args_last_column = $~.end(0)
321
382
  if nd_args && nd_recv.last_lineno == nd_args.last_lineno
322
383
  args_last_column = nd_args.last_column
323
384
  end
324
- if @line.match(/\s*\](\s*)(#{ Regexp.quote(op) })=()/, args_last_column)
385
+ if @snippet.match(/\s*\](\s*)(#{ Regexp.quote(op) })=()/, args_last_column)
325
386
  case @name
326
387
  when :[], :[]=
327
388
  @beg_column = bracket_beg_column
@@ -340,7 +401,7 @@ module ErrorHighlight
340
401
  def spot_op_asgn1_for_args
341
402
  nd_recv, mid, nd_args, nd_rhs = @node.children
342
403
  fetch_line(nd_recv.last_lineno)
343
- if mid == :[]= && @line.match(/\G\s*\[/, nd_recv.last_column)
404
+ if mid == :[]= && @snippet.match(/\G\s*\[/, nd_recv.last_column)
344
405
  @beg_column = $~.end(0)
345
406
  if nd_recv.last_lineno == nd_rhs.last_lineno
346
407
  @end_column = nd_rhs.last_column
@@ -362,7 +423,7 @@ module ErrorHighlight
362
423
  def spot_op_asgn2_for_name
363
424
  nd_recv, _qcall, attr, op, _nd_rhs = @node.children
364
425
  fetch_line(nd_recv.last_lineno)
365
- if @line.match(/\G\s*(\.)\s*#{ Regexp.quote(attr) }()\s*(#{ Regexp.quote(op) })(=)/, nd_recv.last_column)
426
+ if @snippet.match(/\G[\s)]*(\.)\s*#{ Regexp.quote(attr) }()\s*(#{ Regexp.quote(op) })(=)/, nd_recv.last_column)
366
427
  case @name
367
428
  when attr
368
429
  @beg_column = $~.begin(1)
@@ -399,8 +460,8 @@ module ErrorHighlight
399
460
  @beg_column = nd_parent.last_column
400
461
  @end_column = @node.last_column
401
462
  else
402
- @line = @fetch[@node.last_lineno]
403
- if @line[...@node.last_column].match(/#{ Regexp.quote(const) }\z/)
463
+ @snippet = @fetch[@node.last_lineno]
464
+ if @snippet[...@node.last_column].match(/#{ Regexp.quote(const) }\z/)
404
465
  @beg_column = $~.begin(0)
405
466
  @end_column = $~.end(0)
406
467
  end
@@ -414,8 +475,8 @@ module ErrorHighlight
414
475
  nd_lhs, op, _nd_rhs = @node.children
415
476
  *nd_parent_lhs, _const = nd_lhs.children
416
477
  if @name == op
417
- @line = @fetch[nd_lhs.last_lineno]
418
- if @line.match(/\G\s*(#{ Regexp.quote(op) })=/, nd_lhs.last_column)
478
+ @snippet = @fetch[nd_lhs.last_lineno]
479
+ if @snippet.match(/\G\s*(#{ Regexp.quote(op) })=/, nd_lhs.last_column)
419
480
  @beg_column = $~.begin(1)
420
481
  @end_column = $~.end(1)
421
482
  end
@@ -424,12 +485,12 @@ module ErrorHighlight
424
485
  @end_column = nd_lhs.last_column
425
486
  if nd_parent_lhs.empty? # example: ::C += 1
426
487
  if nd_lhs.first_lineno == nd_lhs.last_lineno
427
- @line = @fetch[nd_lhs.last_lineno]
488
+ @snippet = @fetch[nd_lhs.last_lineno]
428
489
  @beg_column = nd_lhs.first_column
429
490
  end
430
491
  else # example: Foo::Bar::C += 1
431
492
  if nd_parent_lhs.last.last_lineno == nd_lhs.last_lineno
432
- @line = @fetch[nd_lhs.last_lineno]
493
+ @snippet = @fetch[nd_lhs.last_lineno]
433
494
  @beg_column = nd_parent_lhs.last.last_column
434
495
  end
435
496
  end
@@ -438,7 +499,7 @@ module ErrorHighlight
438
499
 
439
500
  def fetch_line(lineno)
440
501
  @beg_lineno = @end_lineno = lineno
441
- @line = @fetch[lineno]
502
+ @snippet = @fetch[lineno]
442
503
  end
443
504
  end
444
505
 
@@ -1,48 +1,45 @@
1
+ require_relative "formatter"
2
+
1
3
  module ErrorHighlight
2
4
  module CoreExt
3
- SKIP_TO_S_FOR_SUPER_LOOKUP = true
4
- private_constant :SKIP_TO_S_FOR_SUPER_LOOKUP
5
-
6
- def to_s
7
- msg = super.dup
8
-
9
- locs = backtrace_locations
10
- return msg unless locs
11
-
12
- loc = locs.first
13
- begin
14
- node = RubyVM::AbstractSyntaxTree.of(loc, save_script_lines: true)
15
- opts = {}
16
-
17
- case self
18
- when NoMethodError, NameError
19
- point = :name
20
- opts[:name] = name
21
- when TypeError, ArgumentError
22
- point = :args
23
- end
5
+ private def generate_snippet
6
+ spot = ErrorHighlight.spot(self)
7
+ return "" unless spot
8
+ return ErrorHighlight.formatter.message_for(spot)
9
+ end
24
10
 
25
- spot = ErrorHighlight.spot(node, point, **opts) do |lineno, last_lineno|
26
- last_lineno ||= lineno
27
- node.script_lines[lineno - 1 .. last_lineno - 1].join("")
11
+ if Exception.method_defined?(:detailed_message)
12
+ def detailed_message(highlight: false, error_highlight: true, **)
13
+ return super unless error_highlight
14
+ snippet = generate_snippet
15
+ if highlight
16
+ snippet = snippet.gsub(/.+/) { "\e[1m" + $& + "\e[m" }
28
17
  end
29
-
30
- rescue Errno::ENOENT
18
+ super + snippet
31
19
  end
32
-
33
- if spot
34
- marker = " " * spot[:first_column] + "^" * (spot[:last_column] - spot[:first_column])
35
- points = "\n\n#{ spot[:line] }#{ marker }"
36
- msg << points if !msg.include?(points)
20
+ else
21
+ # This is a marker to let `DidYouMean::Correctable#original_message` skip
22
+ # the following method definition of `to_s`.
23
+ # See https://github.com/ruby/did_you_mean/pull/152
24
+ SKIP_TO_S_FOR_SUPER_LOOKUP = true
25
+ private_constant :SKIP_TO_S_FOR_SUPER_LOOKUP
26
+
27
+ def to_s
28
+ msg = super
29
+ snippet = generate_snippet
30
+ if snippet != "" && !msg.include?(snippet)
31
+ msg + snippet
32
+ else
33
+ msg
34
+ end
37
35
  end
38
-
39
- msg
40
36
  end
41
37
  end
42
38
 
43
39
  NameError.prepend(CoreExt)
44
40
 
45
- # temporarily disabled
41
+ # The extension for TypeError/ArgumentError is temporarily disabled due to many test failures
42
+
46
43
  #TypeError.prepend(CoreExt)
47
44
  #ArgumentError.prepend(CoreExt)
48
45
  end
@@ -0,0 +1,23 @@
1
+ module ErrorHighlight
2
+ class DefaultFormatter
3
+ def self.message_for(spot)
4
+ # currently only a one-line code snippet is supported
5
+ if spot[:first_lineno] == spot[:last_lineno]
6
+ indent = spot[:snippet][0...spot[:first_column]].gsub(/[^\t]/, " ")
7
+ marker = indent + "^" * (spot[:last_column] - spot[:first_column])
8
+
9
+ "\n\n#{ spot[:snippet] }#{ marker }"
10
+ else
11
+ ""
12
+ end
13
+ end
14
+ end
15
+
16
+ def self.formatter
17
+ Ractor.current[:__error_highlight_formatter__] || DefaultFormatter
18
+ end
19
+
20
+ def self.formatter=(formatter)
21
+ Ractor.current[:__error_highlight_formatter__] = formatter
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module ErrorHighlight
2
- VERSION = "0.1.0"
2
+ VERSION = "0.4.0"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: error_highlight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yusuke Endoh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-29 00:00:00.000000000 Z
11
+ date: 2022-08-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: The gem enhances Exception#message by adding a short explanation where
14
14
  the exception is raised
@@ -18,6 +18,7 @@ executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - ".github/dependabot.yml"
21
22
  - ".github/workflows/ruby.yml"
22
23
  - ".gitignore"
23
24
  - Gemfile
@@ -28,6 +29,7 @@ files:
28
29
  - lib/error_highlight.rb
29
30
  - lib/error_highlight/base.rb
30
31
  - lib/error_highlight/core_ext.rb
32
+ - lib/error_highlight/formatter.rb
31
33
  - lib/error_highlight/version.rb
32
34
  homepage: https://github.com/ruby/error_highlight
33
35
  licenses:
@@ -41,14 +43,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
41
43
  requirements:
42
44
  - - ">="
43
45
  - !ruby/object:Gem::Version
44
- version: 3.1.0
46
+ version: 3.1.0.dev
45
47
  required_rubygems_version: !ruby/object:Gem::Requirement
46
48
  requirements:
47
49
  - - ">="
48
50
  - !ruby/object:Gem::Version
49
51
  version: '0'
50
52
  requirements: []
51
- rubygems_version: 3.3.0.dev
53
+ rubygems_version: 3.3.7
52
54
  signing_key:
53
55
  specification_version: 4
54
56
  summary: Shows a one-line code snippet with an underline in the error backtrace