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 +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
|