error_highlight 0.1.0 → 0.2.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/workflows/ruby.yml +1 -1
- data/README.md +51 -1
- data/lib/error_highlight/base.rb +39 -38
- data/lib/error_highlight/core_ext.rb +11 -9
- data/lib/error_highlight/formatter.rb +24 -0
- data/lib/error_highlight/version.rb +1 -1
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5eb06bb7a0af000c582a2ae98fb4401652556c10cd756a1cba8d68951c4b60b9
|
4
|
+
data.tar.gz: 4fd7da0b60979f72f250c5044af20a8d7badd37776d2b6e7ba75b960f72e0a90
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 475126fb1511ea9e9c25d966c778931c4d2b51ae91658703c0f7d625f536788afad4645b1ae4ddecafb5e9a7a4cb99ed84f88a04c58035e9e968db1c2e2ba715
|
7
|
+
data.tar.gz: df639b399fbb96cb0afcb71f78263268558e689eb058f9a1da2f5a7ccb87e682697e1580eadc702c78c801380cf1e7823e0a668b19fd526a9314bc0975e14d9a
|
data/.github/workflows/ruby.yml
CHANGED
data/README.md
CHANGED
@@ -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 spnippet data.
|
59
|
+
Note that the argument must be a RubyVM::AbstractSyntaxTree::Node object that is created with `save_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, save_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/lib/error_highlight/base.rb
CHANGED
@@ -4,10 +4,9 @@ module ErrorHighlight
|
|
4
4
|
# Identify the code fragment that seems associated with a given error
|
5
5
|
#
|
6
6
|
# Arguments:
|
7
|
-
# node: RubyVM::AbstractSyntaxTree::Node
|
8
|
-
#
|
7
|
+
# node: RubyVM::AbstractSyntaxTree::Node (script_lines should be enabled)
|
8
|
+
# point_type: :name | :args
|
9
9
|
# name: The name associated with the NameError/NoMethodError
|
10
|
-
# fetch: A block to fetch a specified code line (or lines)
|
11
10
|
#
|
12
11
|
# Returns:
|
13
12
|
# {
|
@@ -15,23 +14,25 @@ module ErrorHighlight
|
|
15
14
|
# first_column: Integer,
|
16
15
|
# last_lineno: Integer,
|
17
16
|
# last_column: Integer,
|
18
|
-
#
|
17
|
+
# snippet: String,
|
19
18
|
# } | nil
|
20
19
|
def self.spot(...)
|
21
20
|
Spotter.new(...).spot
|
22
21
|
end
|
23
22
|
|
24
23
|
class Spotter
|
25
|
-
def initialize(node,
|
24
|
+
def initialize(node, point_type: :name, name: nil)
|
26
25
|
@node = node
|
27
|
-
@
|
26
|
+
@point_type = point_type
|
28
27
|
@name = name
|
29
28
|
|
30
29
|
# Not-implemented-yet options
|
31
30
|
@arg = nil # Specify the index or keyword at which argument caused the TypeError/ArgumentError
|
32
31
|
@multiline = false # Allow multiline spot
|
33
32
|
|
34
|
-
@fetch =
|
33
|
+
@fetch = -> (lineno, last_lineno = lineno) do
|
34
|
+
@node.script_lines[lineno - 1 .. last_lineno - 1].join("")
|
35
|
+
end
|
35
36
|
end
|
36
37
|
|
37
38
|
def spot
|
@@ -40,7 +41,7 @@ module ErrorHighlight
|
|
40
41
|
case @node.type
|
41
42
|
|
42
43
|
when :CALL, :QCALL
|
43
|
-
case @
|
44
|
+
case @point_type
|
44
45
|
when :name
|
45
46
|
spot_call_for_name
|
46
47
|
when :args
|
@@ -48,7 +49,7 @@ module ErrorHighlight
|
|
48
49
|
end
|
49
50
|
|
50
51
|
when :ATTRASGN
|
51
|
-
case @
|
52
|
+
case @point_type
|
52
53
|
when :name
|
53
54
|
spot_attrasgn_for_name
|
54
55
|
when :args
|
@@ -56,7 +57,7 @@ module ErrorHighlight
|
|
56
57
|
end
|
57
58
|
|
58
59
|
when :OPCALL
|
59
|
-
case @
|
60
|
+
case @point_type
|
60
61
|
when :name
|
61
62
|
spot_opcall_for_name
|
62
63
|
when :args
|
@@ -64,7 +65,7 @@ module ErrorHighlight
|
|
64
65
|
end
|
65
66
|
|
66
67
|
when :FCALL
|
67
|
-
case @
|
68
|
+
case @point_type
|
68
69
|
when :name
|
69
70
|
spot_fcall_for_name
|
70
71
|
when :args
|
@@ -75,7 +76,7 @@ module ErrorHighlight
|
|
75
76
|
spot_vcall
|
76
77
|
|
77
78
|
when :OP_ASGN1
|
78
|
-
case @
|
79
|
+
case @point_type
|
79
80
|
when :name
|
80
81
|
spot_op_asgn1_for_name
|
81
82
|
when :args
|
@@ -83,7 +84,7 @@ module ErrorHighlight
|
|
83
84
|
end
|
84
85
|
|
85
86
|
when :OP_ASGN2
|
86
|
-
case @
|
87
|
+
case @point_type
|
87
88
|
when :name
|
88
89
|
spot_op_asgn2_for_name
|
89
90
|
when :args
|
@@ -103,13 +104,13 @@ module ErrorHighlight
|
|
103
104
|
spot_op_cdecl
|
104
105
|
end
|
105
106
|
|
106
|
-
if @
|
107
|
+
if @snippet && @beg_column && @end_column && @beg_column < @end_column
|
107
108
|
return {
|
108
109
|
first_lineno: @beg_lineno,
|
109
110
|
first_column: @beg_column,
|
110
111
|
last_lineno: @end_lineno,
|
111
112
|
last_column: @end_column,
|
112
|
-
|
113
|
+
snippet: @snippet,
|
113
114
|
}
|
114
115
|
else
|
115
116
|
return nil
|
@@ -135,10 +136,10 @@ module ErrorHighlight
|
|
135
136
|
lines = @fetch[lineno, @node.last_lineno]
|
136
137
|
if mid == :[] && lines.match(/\G\s*(\[(?:\s*\])?)/, nd_recv.last_column)
|
137
138
|
@beg_column = $~.begin(1)
|
138
|
-
@
|
139
|
+
@snippet = lines[/.*\n/]
|
139
140
|
@beg_lineno = @end_lineno = lineno
|
140
141
|
if nd_args
|
141
|
-
if nd_recv.last_lineno == nd_args.last_lineno && @
|
142
|
+
if nd_recv.last_lineno == nd_args.last_lineno && @snippet.match(/\s*\]/, nd_args.last_column)
|
142
143
|
@end_column = $~.end(0)
|
143
144
|
end
|
144
145
|
else
|
@@ -152,15 +153,15 @@ module ErrorHighlight
|
|
152
153
|
@end_column = $~.end(3)
|
153
154
|
if i = lines[..@beg_column].rindex("\n")
|
154
155
|
@beg_lineno = @end_lineno = lineno + lines[..@beg_column].count("\n")
|
155
|
-
@
|
156
|
+
@snippet = lines[i + 1..]
|
156
157
|
@beg_column -= i + 1
|
157
158
|
@end_column -= i + 1
|
158
159
|
else
|
159
|
-
@
|
160
|
+
@snippet = lines
|
160
161
|
@beg_lineno = @end_lineno = lineno
|
161
162
|
end
|
162
163
|
elsif mid.to_s =~ /\A\W+\z/ && lines.match(/\G\s*(#{ Regexp.quote(mid) })=.*\n/, nd_recv.last_column)
|
163
|
-
@
|
164
|
+
@snippet = $` + $&
|
164
165
|
@beg_column = $~.begin(1)
|
165
166
|
@end_column = $~.end(1)
|
166
167
|
end
|
@@ -192,16 +193,16 @@ module ErrorHighlight
|
|
192
193
|
nd_recv, mid, nd_args = @node.children
|
193
194
|
*nd_args, _nd_last_arg, _nil = nd_args.children
|
194
195
|
fetch_line(nd_recv.last_lineno)
|
195
|
-
if mid == :[]= && @
|
196
|
+
if mid == :[]= && @snippet.match(/\G\s*(\[)/, nd_recv.last_column)
|
196
197
|
@beg_column = $~.begin(1)
|
197
198
|
args_last_column = $~.end(0)
|
198
199
|
if nd_args.last && nd_recv.last_lineno == nd_args.last.last_lineno
|
199
200
|
args_last_column = nd_args.last.last_column
|
200
201
|
end
|
201
|
-
if @
|
202
|
+
if @snippet.match(/\s*\]\s*=/, args_last_column)
|
202
203
|
@end_column = $~.end(0)
|
203
204
|
end
|
204
|
-
elsif @
|
205
|
+
elsif @snippet.match(/\G\s*(\.\s*#{ Regexp.quote(mid.to_s.sub(/=\z/, "")) }\s*=)/, nd_recv.last_column)
|
205
206
|
@beg_column = $~.begin(1)
|
206
207
|
@end_column = $~.end(1)
|
207
208
|
end
|
@@ -217,7 +218,7 @@ module ErrorHighlight
|
|
217
218
|
def spot_attrasgn_for_args
|
218
219
|
nd_recv, mid, nd_args = @node.children
|
219
220
|
fetch_line(nd_recv.last_lineno)
|
220
|
-
if mid == :[]= && @
|
221
|
+
if mid == :[]= && @snippet.match(/\G\s*\[/, nd_recv.last_column)
|
221
222
|
@beg_column = $~.end(0)
|
222
223
|
if nd_recv.last_lineno == nd_args.last_lineno
|
223
224
|
@end_column = nd_args.last_column
|
@@ -239,13 +240,13 @@ module ErrorHighlight
|
|
239
240
|
fetch_line(nd_recv.last_lineno)
|
240
241
|
if nd_arg
|
241
242
|
# binary operator
|
242
|
-
if @
|
243
|
+
if @snippet.match(/\G\s*(#{ Regexp.quote(op) })/, nd_recv.last_column)
|
243
244
|
@beg_column = $~.begin(1)
|
244
245
|
@end_column = $~.end(1)
|
245
246
|
end
|
246
247
|
else
|
247
248
|
# unary operator
|
248
|
-
if @
|
249
|
+
if @snippet[...nd_recv.first_column].match(/(#{ Regexp.quote(op.to_s.sub(/@\z/, "")) })\s*\(?\s*\z/)
|
249
250
|
@beg_column = $~.begin(1)
|
250
251
|
@end_column = $~.end(1)
|
251
252
|
end
|
@@ -273,7 +274,7 @@ module ErrorHighlight
|
|
273
274
|
def spot_fcall_for_name
|
274
275
|
mid, _nd_args = @node.children
|
275
276
|
fetch_line(@node.first_lineno)
|
276
|
-
if @
|
277
|
+
if @snippet.match(/(#{ Regexp.quote(mid) })/, @node.first_column)
|
277
278
|
@beg_column = $~.begin(1)
|
278
279
|
@end_column = $~.end(1)
|
279
280
|
end
|
@@ -315,13 +316,13 @@ module ErrorHighlight
|
|
315
316
|
def spot_op_asgn1_for_name
|
316
317
|
nd_recv, op, nd_args, _nd_rhs = @node.children
|
317
318
|
fetch_line(nd_recv.last_lineno)
|
318
|
-
if @
|
319
|
+
if @snippet.match(/\G\s*(\[)/, nd_recv.last_column)
|
319
320
|
bracket_beg_column = $~.begin(1)
|
320
321
|
args_last_column = $~.end(0)
|
321
322
|
if nd_args && nd_recv.last_lineno == nd_args.last_lineno
|
322
323
|
args_last_column = nd_args.last_column
|
323
324
|
end
|
324
|
-
if @
|
325
|
+
if @snippet.match(/\s*\](\s*)(#{ Regexp.quote(op) })=()/, args_last_column)
|
325
326
|
case @name
|
326
327
|
when :[], :[]=
|
327
328
|
@beg_column = bracket_beg_column
|
@@ -340,7 +341,7 @@ module ErrorHighlight
|
|
340
341
|
def spot_op_asgn1_for_args
|
341
342
|
nd_recv, mid, nd_args, nd_rhs = @node.children
|
342
343
|
fetch_line(nd_recv.last_lineno)
|
343
|
-
if mid == :[]= && @
|
344
|
+
if mid == :[]= && @snippet.match(/\G\s*\[/, nd_recv.last_column)
|
344
345
|
@beg_column = $~.end(0)
|
345
346
|
if nd_recv.last_lineno == nd_rhs.last_lineno
|
346
347
|
@end_column = nd_rhs.last_column
|
@@ -362,7 +363,7 @@ module ErrorHighlight
|
|
362
363
|
def spot_op_asgn2_for_name
|
363
364
|
nd_recv, _qcall, attr, op, _nd_rhs = @node.children
|
364
365
|
fetch_line(nd_recv.last_lineno)
|
365
|
-
if @
|
366
|
+
if @snippet.match(/\G\s*(\.)\s*#{ Regexp.quote(attr) }()\s*(#{ Regexp.quote(op) })(=)/, nd_recv.last_column)
|
366
367
|
case @name
|
367
368
|
when attr
|
368
369
|
@beg_column = $~.begin(1)
|
@@ -399,8 +400,8 @@ module ErrorHighlight
|
|
399
400
|
@beg_column = nd_parent.last_column
|
400
401
|
@end_column = @node.last_column
|
401
402
|
else
|
402
|
-
@
|
403
|
-
if @
|
403
|
+
@snippet = @fetch[@node.last_lineno]
|
404
|
+
if @snippet[...@node.last_column].match(/#{ Regexp.quote(const) }\z/)
|
404
405
|
@beg_column = $~.begin(0)
|
405
406
|
@end_column = $~.end(0)
|
406
407
|
end
|
@@ -414,8 +415,8 @@ module ErrorHighlight
|
|
414
415
|
nd_lhs, op, _nd_rhs = @node.children
|
415
416
|
*nd_parent_lhs, _const = nd_lhs.children
|
416
417
|
if @name == op
|
417
|
-
@
|
418
|
-
if @
|
418
|
+
@snippet = @fetch[nd_lhs.last_lineno]
|
419
|
+
if @snippet.match(/\G\s*(#{ Regexp.quote(op) })=/, nd_lhs.last_column)
|
419
420
|
@beg_column = $~.begin(1)
|
420
421
|
@end_column = $~.end(1)
|
421
422
|
end
|
@@ -424,12 +425,12 @@ module ErrorHighlight
|
|
424
425
|
@end_column = nd_lhs.last_column
|
425
426
|
if nd_parent_lhs.empty? # example: ::C += 1
|
426
427
|
if nd_lhs.first_lineno == nd_lhs.last_lineno
|
427
|
-
@
|
428
|
+
@snippet = @fetch[nd_lhs.last_lineno]
|
428
429
|
@beg_column = nd_lhs.first_column
|
429
430
|
end
|
430
431
|
else # example: Foo::Bar::C += 1
|
431
432
|
if nd_parent_lhs.last.last_lineno == nd_lhs.last_lineno
|
432
|
-
@
|
433
|
+
@snippet = @fetch[nd_lhs.last_lineno]
|
433
434
|
@beg_column = nd_parent_lhs.last.last_column
|
434
435
|
end
|
435
436
|
end
|
@@ -438,7 +439,7 @@ module ErrorHighlight
|
|
438
439
|
|
439
440
|
def fetch_line(lineno)
|
440
441
|
@beg_lineno = @end_lineno = lineno
|
441
|
-
@
|
442
|
+
@snippet = @fetch[lineno]
|
442
443
|
end
|
443
444
|
end
|
444
445
|
|
@@ -1,5 +1,10 @@
|
|
1
|
+
require_relative "formatter"
|
2
|
+
|
1
3
|
module ErrorHighlight
|
2
4
|
module CoreExt
|
5
|
+
# This is a marker to let `DidYouMean::Correctable#original_message` skip
|
6
|
+
# the following method definition of `to_s`.
|
7
|
+
# See https://github.com/ruby/did_you_mean/pull/152
|
3
8
|
SKIP_TO_S_FOR_SUPER_LOOKUP = true
|
4
9
|
private_constant :SKIP_TO_S_FOR_SUPER_LOOKUP
|
5
10
|
|
@@ -16,23 +21,19 @@ module ErrorHighlight
|
|
16
21
|
|
17
22
|
case self
|
18
23
|
when NoMethodError, NameError
|
19
|
-
|
24
|
+
opts[:point_type] = :name
|
20
25
|
opts[:name] = name
|
21
26
|
when TypeError, ArgumentError
|
22
|
-
|
27
|
+
opts[:point_type] = :args
|
23
28
|
end
|
24
29
|
|
25
|
-
spot = ErrorHighlight.spot(node,
|
26
|
-
last_lineno ||= lineno
|
27
|
-
node.script_lines[lineno - 1 .. last_lineno - 1].join("")
|
28
|
-
end
|
30
|
+
spot = ErrorHighlight.spot(node, **opts)
|
29
31
|
|
30
32
|
rescue Errno::ENOENT
|
31
33
|
end
|
32
34
|
|
33
35
|
if spot
|
34
|
-
|
35
|
-
points = "\n\n#{ spot[:line] }#{ marker }"
|
36
|
+
points = ErrorHighlight.formatter.message_for(spot)
|
36
37
|
msg << points if !msg.include?(points)
|
37
38
|
end
|
38
39
|
|
@@ -42,7 +43,8 @@ module ErrorHighlight
|
|
42
43
|
|
43
44
|
NameError.prepend(CoreExt)
|
44
45
|
|
45
|
-
# temporarily disabled
|
46
|
+
# The extension for TypeError/ArgumentError is temporarily disabled due to many test failures
|
47
|
+
|
46
48
|
#TypeError.prepend(CoreExt)
|
47
49
|
#ArgumentError.prepend(CoreExt)
|
48
50
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ErrorHighlight
|
2
|
+
class DefaultFormatter
|
3
|
+
def message_for(spot)
|
4
|
+
# currently only a one-line code snippet is supported
|
5
|
+
if spot[:first_lineno] == spot[:last_lineno]
|
6
|
+
marker = " " * spot[:first_column] + "^" * (spot[:last_column] - spot[:first_column])
|
7
|
+
|
8
|
+
"\n\n#{ spot[:snippet] }#{ marker }"
|
9
|
+
else
|
10
|
+
""
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.formatter
|
16
|
+
@@formatter
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.formatter=(formatter)
|
20
|
+
@@formatter = formatter
|
21
|
+
end
|
22
|
+
|
23
|
+
self.formatter = DefaultFormatter.new
|
24
|
+
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.2.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-
|
11
|
+
date: 2021-06-30 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
|
@@ -28,6 +28,7 @@ files:
|
|
28
28
|
- lib/error_highlight.rb
|
29
29
|
- lib/error_highlight/base.rb
|
30
30
|
- lib/error_highlight/core_ext.rb
|
31
|
+
- lib/error_highlight/formatter.rb
|
31
32
|
- lib/error_highlight/version.rb
|
32
33
|
homepage: https://github.com/ruby/error_highlight
|
33
34
|
licenses:
|
@@ -48,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
48
49
|
- !ruby/object:Gem::Version
|
49
50
|
version: '0'
|
50
51
|
requirements: []
|
51
|
-
rubygems_version: 3.
|
52
|
+
rubygems_version: 3.2.15
|
52
53
|
signing_key:
|
53
54
|
specification_version: 4
|
54
55
|
summary: Shows a one-line code snippet with an underline in the error backtrace
|