error_highlight 0.1.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +6 -0
- data/.github/workflows/ruby.yml +3 -3
- data/README.md +52 -2
- data/error_highlight.gemspec +1 -1
- data/lib/error_highlight/base.rb +107 -46
- data/lib/error_highlight/core_ext.rb +31 -34
- data/lib/error_highlight/formatter.rb +23 -0
- data/lib/error_highlight/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9ae1f66c6b1151cf65329869a64d4ea9ce7732344a6318aa99dc8a16aabeeff
|
4
|
+
data.tar.gz: d2e52f94f74c6569cea530e8a843d99030c0a2860f38b8591952faedba759419
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b6610f3e482792ae7f2eb8a2e8ada6c13f588b8d9fdbf2bac0948d99efb5046d92b9b5c83294661ecc91ffb1239b446b759d206db60da7dedaab5af48c7e3d4c
|
7
|
+
data.tar.gz: 481f0da516eff4f75662bb709f56aeb11b380336f3fbd82465af16c24e48bbaa20d4590e5bdad5373c283f7d8eaba90c458bd807a6f1c1b809aef4710cb5a302
|
data/.github/workflows/ruby.yml
CHANGED
@@ -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@
|
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
|
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
|
-
$
|
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
|
```
|
data/error_highlight.gemspec
CHANGED
@@ -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)/}) }
|
data/lib/error_highlight/base.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
require_relative "version"
|
2
2
|
|
3
3
|
module ErrorHighlight
|
4
|
-
# Identify the code fragment that
|
4
|
+
# Identify the code fragment at that a given exception occurred.
|
5
5
|
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
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
|
-
#
|
22
|
+
# snippet: String,
|
23
|
+
# script_lines: [String],
|
19
24
|
# } | nil
|
20
|
-
def self.spot(
|
21
|
-
|
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
|
-
|
69
|
+
class NonAscii < Exception; end
|
70
|
+
private_constant :NonAscii
|
71
|
+
|
72
|
+
def initialize(node, point_type: :name, name: nil)
|
26
73
|
@node = node
|
27
|
-
@
|
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 =
|
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 @
|
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 @
|
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 @
|
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 @
|
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 @
|
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 @
|
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 @
|
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
|
-
|
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
|
-
@
|
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 && @
|
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
|
-
@
|
216
|
+
@snippet = lines[i + 1..]
|
156
217
|
@beg_column -= i + 1
|
157
218
|
@end_column -= i + 1
|
158
219
|
else
|
159
|
-
@
|
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
|
-
@
|
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 == :[]= && @
|
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 @
|
262
|
+
if @snippet.match(/[\s)]*\]\s*=/, args_last_column)
|
202
263
|
@end_column = $~.end(0)
|
203
264
|
end
|
204
|
-
elsif @
|
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 == :[]= && @
|
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 @
|
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 @
|
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 @
|
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 @
|
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 @
|
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 == :[]= && @
|
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 @
|
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
|
-
@
|
403
|
-
if @
|
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
|
-
@
|
418
|
-
if @
|
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
|
-
@
|
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
|
-
@
|
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
|
-
@
|
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
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
26
|
-
|
27
|
-
|
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
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
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
|
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.
|
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:
|
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.
|
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
|