error_highlight 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f6edc97e3eca88e8281056e7a3d9bceb448c0ac614b742e4ed38908a40e51e01
4
+ data.tar.gz: 255b537c7d07f8dfd01b747719472e57d24e91703b8df678d655a3fc3f71897d
5
+ SHA512:
6
+ metadata.gz: a63da67176a0dabd493fdafa1de60155a26696d22ba633d17765b29a359b0e4ffcc30d4806236bf416c828427d32171aca4210bdb1e018e0347e4cefef3e953a
7
+ data.tar.gz: '08febd90564658489923f2ef0d2aab82a3796e52c7aa2a11ddcd6cc0d1f65abacd9b757b61bbb36bb59d7cf0e0c1eea820bce6d3b0bad3f771ead27714bc70ee'
@@ -0,0 +1,27 @@
1
+ name: Ruby
2
+
3
+ on:
4
+ pull_request:
5
+ branches:
6
+ - 'master'
7
+ push:
8
+ branches:
9
+ - 'master'
10
+
11
+ jobs:
12
+ build:
13
+ runs-on: ubuntu-latest
14
+ strategy:
15
+ matrix:
16
+ ruby: [ 'ruby-head' ]
17
+ steps:
18
+ - uses: actions/checkout@v2
19
+ - uses: ruby/setup-ruby@v1
20
+ with:
21
+ ruby-version: ${{ matrix.ruby }}
22
+ - name: Bundle install
23
+ run: |
24
+ bundle install
25
+ - name: Run the test suite
26
+ run: |
27
+ bundle exec rake TESTOPT=-v
data/.gitignore ADDED
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "test-unit", "~> 3.0"
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2021 Yusuke Endoh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # ErrorHighlight
2
+
3
+ ## Installation
4
+
5
+ Ruby 3.1 will ship with this gem and it will automatically be `require`d when a Ruby process starts up. No special setup is required.
6
+
7
+ Note: This gem works only on MRI and requires Ruby 3.1 or later because it depends on MRI's internal APIs that are available since 3.1.
8
+
9
+ ## Examples
10
+
11
+ ```ruby
12
+ 1.time {}
13
+ ```
14
+
15
+ ```
16
+ $ ruby test.rb
17
+ test.rb:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
18
+
19
+ 1.time {}
20
+ ^^^^^
21
+ Did you mean? times
22
+ ```
23
+
24
+ ## More example
25
+
26
+ ```ruby
27
+ def extract_value(data)
28
+ data[:results].first[:value]
29
+ end
30
+ ```
31
+
32
+ When `data` is `{ :results => [] }`, the following error messsage is shown:
33
+
34
+ ```
35
+ $ ruby test.rb
36
+ test.rb:2:in `extract_value': undefined method `[]' for nil:NilClass (NoMethodError)
37
+
38
+ data[:results].first[:value]
39
+ ^^^^^^^^
40
+ from test.rb:5:in `<main>'
41
+ ```
42
+
43
+ When `data` is `nil`, it prints:
44
+
45
+ ```
46
+ $ ruby test.rb
47
+ test.rb:2:in `extract_value': undefined method `[]' for nil:NilClass (NoMethodError)
48
+
49
+ data[:results].first[:value]
50
+ ^^^^^^^^^^
51
+ from test.rb:5:in `<main>'
52
+ ```
53
+
54
+ ## Disabling `error_highlight`
55
+
56
+ Occasionally, you may want to disable the `error_highlight` gem for e.g. debugging issues in the error object itself. You
57
+ can disable it entirely by specifying `--disable-error_highlight` option to the `ruby` command:
58
+
59
+ ```bash
60
+ $ uby --disable-error_highlight -e '1.time {}'
61
+ -e:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
62
+ Did you mean? times
63
+ ```
64
+
65
+ ## Contributing
66
+
67
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/error_highlight.
68
+
69
+ ## License
70
+
71
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/test_*.rb"]
10
+ end
11
+
12
+ task default: :test
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ begin
5
+ require_relative "lib/error_highlight/version"
6
+ rescue LoadError # Fallback to load version file in ruby core repository
7
+ require_relative "version"
8
+ end
9
+
10
+ Gem::Specification.new do |spec|
11
+ spec.name = "error_highlight"
12
+ spec.version = ErrorHighlight::VERSION
13
+ spec.authors = ["Yusuke Endoh"]
14
+ spec.email = ["mame@ruby-lang.org"]
15
+
16
+ spec.summary = 'Shows a one-line code snippet with an underline in the error backtrace'
17
+ spec.description = 'The gem enhances Exception#message by adding a short explanation where the exception is raised'
18
+ spec.homepage = "https://github.com/ruby/error_highlight"
19
+
20
+ spec.license = "MIT"
21
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.1.0")
22
+
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
25
+ end
26
+ spec.require_paths = ["lib"]
27
+ end
@@ -0,0 +1,2 @@
1
+ require_relative "error_highlight/base"
2
+ require_relative "error_highlight/core_ext"
@@ -0,0 +1,446 @@
1
+ require_relative "version"
2
+
3
+ module ErrorHighlight
4
+ # Identify the code fragment that seems associated with a given error
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)
11
+ #
12
+ # Returns:
13
+ # {
14
+ # first_lineno: Integer,
15
+ # first_column: Integer,
16
+ # last_lineno: Integer,
17
+ # last_column: Integer,
18
+ # line: String,
19
+ # } | nil
20
+ def self.spot(...)
21
+ Spotter.new(...).spot
22
+ end
23
+
24
+ class Spotter
25
+ def initialize(node, point, name: nil, &fetch)
26
+ @node = node
27
+ @point = point
28
+ @name = name
29
+
30
+ # Not-implemented-yet options
31
+ @arg = nil # Specify the index or keyword at which argument caused the TypeError/ArgumentError
32
+ @multiline = false # Allow multiline spot
33
+
34
+ @fetch = fetch
35
+ end
36
+
37
+ def spot
38
+ return nil unless @node
39
+
40
+ case @node.type
41
+
42
+ when :CALL, :QCALL
43
+ case @point
44
+ when :name
45
+ spot_call_for_name
46
+ when :args
47
+ spot_call_for_args
48
+ end
49
+
50
+ when :ATTRASGN
51
+ case @point
52
+ when :name
53
+ spot_attrasgn_for_name
54
+ when :args
55
+ spot_attrasgn_for_args
56
+ end
57
+
58
+ when :OPCALL
59
+ case @point
60
+ when :name
61
+ spot_opcall_for_name
62
+ when :args
63
+ spot_opcall_for_args
64
+ end
65
+
66
+ when :FCALL
67
+ case @point
68
+ when :name
69
+ spot_fcall_for_name
70
+ when :args
71
+ spot_fcall_for_args
72
+ end
73
+
74
+ when :VCALL
75
+ spot_vcall
76
+
77
+ when :OP_ASGN1
78
+ case @point
79
+ when :name
80
+ spot_op_asgn1_for_name
81
+ when :args
82
+ spot_op_asgn1_for_args
83
+ end
84
+
85
+ when :OP_ASGN2
86
+ case @point
87
+ when :name
88
+ spot_op_asgn2_for_name
89
+ when :args
90
+ spot_op_asgn2_for_args
91
+ end
92
+
93
+ when :CONST
94
+ spot_vcall
95
+
96
+ when :COLON2
97
+ spot_colon2
98
+
99
+ when :COLON3
100
+ spot_vcall
101
+
102
+ when :OP_CDECL
103
+ spot_op_cdecl
104
+ end
105
+
106
+ if @line && @beg_column && @end_column && @beg_column < @end_column
107
+ return {
108
+ first_lineno: @beg_lineno,
109
+ first_column: @beg_column,
110
+ last_lineno: @end_lineno,
111
+ last_column: @end_column,
112
+ line: @line,
113
+ }
114
+ else
115
+ return nil
116
+ end
117
+ end
118
+
119
+ private
120
+
121
+ # Example:
122
+ # x.foo
123
+ # ^^^^
124
+ # x.foo(42)
125
+ # ^^^^
126
+ # x&.foo
127
+ # ^^^^^
128
+ # x[42]
129
+ # ^^^^
130
+ # x += 1
131
+ # ^
132
+ def spot_call_for_name
133
+ nd_recv, mid, nd_args = @node.children
134
+ lineno = nd_recv.last_lineno
135
+ lines = @fetch[lineno, @node.last_lineno]
136
+ if mid == :[] && lines.match(/\G\s*(\[(?:\s*\])?)/, nd_recv.last_column)
137
+ @beg_column = $~.begin(1)
138
+ @line = lines[/.*\n/]
139
+ @beg_lineno = @end_lineno = lineno
140
+ if nd_args
141
+ if nd_recv.last_lineno == nd_args.last_lineno && @line.match(/\s*\]/, nd_args.last_column)
142
+ @end_column = $~.end(0)
143
+ end
144
+ else
145
+ if lines.match(/\G\s*?\[\s*\]/, nd_recv.last_column)
146
+ @end_column = $~.end(0)
147
+ end
148
+ end
149
+ elsif lines.match(/\G\s*?(\&?\.)(\s*?)(#{ Regexp.quote(mid) }).*\n/, nd_recv.last_column)
150
+ lines = $` + $&
151
+ @beg_column = $~.begin($2.include?("\n") ? 3 : 1)
152
+ @end_column = $~.end(3)
153
+ if i = lines[..@beg_column].rindex("\n")
154
+ @beg_lineno = @end_lineno = lineno + lines[..@beg_column].count("\n")
155
+ @line = lines[i + 1..]
156
+ @beg_column -= i + 1
157
+ @end_column -= i + 1
158
+ else
159
+ @line = lines
160
+ @beg_lineno = @end_lineno = lineno
161
+ end
162
+ elsif mid.to_s =~ /\A\W+\z/ && lines.match(/\G\s*(#{ Regexp.quote(mid) })=.*\n/, nd_recv.last_column)
163
+ @line = $` + $&
164
+ @beg_column = $~.begin(1)
165
+ @end_column = $~.end(1)
166
+ end
167
+ end
168
+
169
+ # Example:
170
+ # x.foo(42)
171
+ # ^^
172
+ # x[42]
173
+ # ^^
174
+ # x += 1
175
+ # ^
176
+ def spot_call_for_args
177
+ _nd_recv, _mid, nd_args = @node.children
178
+ if nd_args && nd_args.first_lineno == nd_args.last_lineno
179
+ fetch_line(nd_args.first_lineno)
180
+ @beg_column = nd_args.first_column
181
+ @end_column = nd_args.last_column
182
+ end
183
+ # TODO: support @arg
184
+ end
185
+
186
+ # Example:
187
+ # x.foo = 1
188
+ # ^^^^^^
189
+ # x[42] = 1
190
+ # ^^^^^^
191
+ def spot_attrasgn_for_name
192
+ nd_recv, mid, nd_args = @node.children
193
+ *nd_args, _nd_last_arg, _nil = nd_args.children
194
+ fetch_line(nd_recv.last_lineno)
195
+ if mid == :[]= && @line.match(/\G\s*(\[)/, nd_recv.last_column)
196
+ @beg_column = $~.begin(1)
197
+ args_last_column = $~.end(0)
198
+ if nd_args.last && nd_recv.last_lineno == nd_args.last.last_lineno
199
+ args_last_column = nd_args.last.last_column
200
+ end
201
+ if @line.match(/\s*\]\s*=/, args_last_column)
202
+ @end_column = $~.end(0)
203
+ end
204
+ elsif @line.match(/\G\s*(\.\s*#{ Regexp.quote(mid.to_s.sub(/=\z/, "")) }\s*=)/, nd_recv.last_column)
205
+ @beg_column = $~.begin(1)
206
+ @end_column = $~.end(1)
207
+ end
208
+ end
209
+
210
+ # Example:
211
+ # x.foo = 1
212
+ # ^
213
+ # x[42] = 1
214
+ # ^^^^^^^
215
+ # x[] = 1
216
+ # ^^^^^
217
+ def spot_attrasgn_for_args
218
+ nd_recv, mid, nd_args = @node.children
219
+ fetch_line(nd_recv.last_lineno)
220
+ if mid == :[]= && @line.match(/\G\s*\[/, nd_recv.last_column)
221
+ @beg_column = $~.end(0)
222
+ if nd_recv.last_lineno == nd_args.last_lineno
223
+ @end_column = nd_args.last_column
224
+ end
225
+ elsif nd_args && nd_args.first_lineno == nd_args.last_lineno
226
+ @beg_column = nd_args.first_column
227
+ @end_column = nd_args.last_column
228
+ end
229
+ # TODO: support @arg
230
+ end
231
+
232
+ # Example:
233
+ # x + 1
234
+ # ^
235
+ # +x
236
+ # ^
237
+ def spot_opcall_for_name
238
+ nd_recv, op, nd_arg = @node.children
239
+ fetch_line(nd_recv.last_lineno)
240
+ if nd_arg
241
+ # binary operator
242
+ if @line.match(/\G\s*(#{ Regexp.quote(op) })/, nd_recv.last_column)
243
+ @beg_column = $~.begin(1)
244
+ @end_column = $~.end(1)
245
+ end
246
+ else
247
+ # unary operator
248
+ if @line[...nd_recv.first_column].match(/(#{ Regexp.quote(op.to_s.sub(/@\z/, "")) })\s*\(?\s*\z/)
249
+ @beg_column = $~.begin(1)
250
+ @end_column = $~.end(1)
251
+ end
252
+ end
253
+ end
254
+
255
+ # Example:
256
+ # x + 1
257
+ # ^
258
+ def spot_opcall_for_args
259
+ _nd_recv, _op, nd_arg = @node.children
260
+ if nd_arg && nd_arg.first_lineno == nd_arg.last_lineno
261
+ # binary operator
262
+ fetch_line(nd_arg.first_lineno)
263
+ @beg_column = nd_arg.first_column
264
+ @end_column = nd_arg.last_column
265
+ end
266
+ end
267
+
268
+ # Example:
269
+ # foo(42)
270
+ # ^^^
271
+ # foo 42
272
+ # ^^^
273
+ def spot_fcall_for_name
274
+ mid, _nd_args = @node.children
275
+ fetch_line(@node.first_lineno)
276
+ if @line.match(/(#{ Regexp.quote(mid) })/, @node.first_column)
277
+ @beg_column = $~.begin(1)
278
+ @end_column = $~.end(1)
279
+ end
280
+ end
281
+
282
+ # Example:
283
+ # foo(42)
284
+ # ^^
285
+ # foo 42
286
+ # ^^
287
+ def spot_fcall_for_args
288
+ _mid, nd_args = @node.children
289
+ if nd_args && nd_args.first_lineno == nd_args.last_lineno
290
+ # binary operator
291
+ fetch_line(nd_args.first_lineno)
292
+ @beg_column = nd_args.first_column
293
+ @end_column = nd_args.last_column
294
+ end
295
+ end
296
+
297
+ # Example:
298
+ # foo
299
+ # ^^^
300
+ def spot_vcall
301
+ if @node.first_lineno == @node.last_lineno
302
+ fetch_line(@node.last_lineno)
303
+ @beg_column = @node.first_column
304
+ @end_column = @node.last_column
305
+ end
306
+ end
307
+
308
+ # Example:
309
+ # x[1] += 42
310
+ # ^^^ (for [])
311
+ # x[1] += 42
312
+ # ^ (for +)
313
+ # x[1] += 42
314
+ # ^^^^^^ (for []=)
315
+ def spot_op_asgn1_for_name
316
+ nd_recv, op, nd_args, _nd_rhs = @node.children
317
+ fetch_line(nd_recv.last_lineno)
318
+ if @line.match(/\G\s*(\[)/, nd_recv.last_column)
319
+ bracket_beg_column = $~.begin(1)
320
+ args_last_column = $~.end(0)
321
+ if nd_args && nd_recv.last_lineno == nd_args.last_lineno
322
+ args_last_column = nd_args.last_column
323
+ end
324
+ if @line.match(/\s*\](\s*)(#{ Regexp.quote(op) })=()/, args_last_column)
325
+ case @name
326
+ when :[], :[]=
327
+ @beg_column = bracket_beg_column
328
+ @end_column = $~.begin(@name == :[] ? 1 : 3)
329
+ when op
330
+ @beg_column = $~.begin(2)
331
+ @end_column = $~.end(2)
332
+ end
333
+ end
334
+ end
335
+ end
336
+
337
+ # Example:
338
+ # x[1] += 42
339
+ # ^^^^^^^^
340
+ def spot_op_asgn1_for_args
341
+ nd_recv, mid, nd_args, nd_rhs = @node.children
342
+ fetch_line(nd_recv.last_lineno)
343
+ if mid == :[]= && @line.match(/\G\s*\[/, nd_recv.last_column)
344
+ @beg_column = $~.end(0)
345
+ if nd_recv.last_lineno == nd_rhs.last_lineno
346
+ @end_column = nd_rhs.last_column
347
+ end
348
+ elsif nd_args && nd_args.first_lineno == nd_rhs.last_lineno
349
+ @beg_column = nd_args.first_column
350
+ @end_column = nd_rhs.last_column
351
+ end
352
+ # TODO: support @arg
353
+ end
354
+
355
+ # Example:
356
+ # x.foo += 42
357
+ # ^^^ (for foo)
358
+ # x.foo += 42
359
+ # ^ (for +)
360
+ # x.foo += 42
361
+ # ^^^^^^^ (for foo=)
362
+ def spot_op_asgn2_for_name
363
+ nd_recv, _qcall, attr, op, _nd_rhs = @node.children
364
+ fetch_line(nd_recv.last_lineno)
365
+ if @line.match(/\G\s*(\.)\s*#{ Regexp.quote(attr) }()\s*(#{ Regexp.quote(op) })(=)/, nd_recv.last_column)
366
+ case @name
367
+ when attr
368
+ @beg_column = $~.begin(1)
369
+ @end_column = $~.begin(2)
370
+ when op
371
+ @beg_column = $~.begin(3)
372
+ @end_column = $~.end(3)
373
+ when :"#{ attr }="
374
+ @beg_column = $~.begin(1)
375
+ @end_column = $~.end(4)
376
+ end
377
+ end
378
+ end
379
+
380
+ # Example:
381
+ # x.foo += 42
382
+ # ^^
383
+ def spot_op_asgn2_for_args
384
+ _nd_recv, _qcall, _attr, _op, nd_rhs = @node.children
385
+ if nd_rhs.first_lineno == nd_rhs.last_lineno
386
+ fetch_line(nd_rhs.first_lineno)
387
+ @beg_column = nd_rhs.first_column
388
+ @end_column = nd_rhs.last_column
389
+ end
390
+ end
391
+
392
+ # Example:
393
+ # Foo::Bar
394
+ # ^^^^^
395
+ def spot_colon2
396
+ nd_parent, const = @node.children
397
+ if nd_parent.last_lineno == @node.last_lineno
398
+ fetch_line(nd_parent.last_lineno)
399
+ @beg_column = nd_parent.last_column
400
+ @end_column = @node.last_column
401
+ else
402
+ @line = @fetch[@node.last_lineno]
403
+ if @line[...@node.last_column].match(/#{ Regexp.quote(const) }\z/)
404
+ @beg_column = $~.begin(0)
405
+ @end_column = $~.end(0)
406
+ end
407
+ end
408
+ end
409
+
410
+ # Example:
411
+ # Foo::Bar += 1
412
+ # ^^^^^^^^
413
+ def spot_op_cdecl
414
+ nd_lhs, op, _nd_rhs = @node.children
415
+ *nd_parent_lhs, _const = nd_lhs.children
416
+ if @name == op
417
+ @line = @fetch[nd_lhs.last_lineno]
418
+ if @line.match(/\G\s*(#{ Regexp.quote(op) })=/, nd_lhs.last_column)
419
+ @beg_column = $~.begin(1)
420
+ @end_column = $~.end(1)
421
+ end
422
+ else
423
+ # constant access error
424
+ @end_column = nd_lhs.last_column
425
+ if nd_parent_lhs.empty? # example: ::C += 1
426
+ if nd_lhs.first_lineno == nd_lhs.last_lineno
427
+ @line = @fetch[nd_lhs.last_lineno]
428
+ @beg_column = nd_lhs.first_column
429
+ end
430
+ else # example: Foo::Bar::C += 1
431
+ if nd_parent_lhs.last.last_lineno == nd_lhs.last_lineno
432
+ @line = @fetch[nd_lhs.last_lineno]
433
+ @beg_column = nd_parent_lhs.last.last_column
434
+ end
435
+ end
436
+ end
437
+ end
438
+
439
+ def fetch_line(lineno)
440
+ @beg_lineno = @end_lineno = lineno
441
+ @line = @fetch[lineno]
442
+ end
443
+ end
444
+
445
+ private_constant :Spotter
446
+ end
@@ -0,0 +1,48 @@
1
+ module ErrorHighlight
2
+ 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
24
+
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("")
28
+ end
29
+
30
+ rescue Errno::ENOENT
31
+ 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)
37
+ end
38
+
39
+ msg
40
+ end
41
+ end
42
+
43
+ NameError.prepend(CoreExt)
44
+
45
+ # temporarily disabled
46
+ #TypeError.prepend(CoreExt)
47
+ #ArgumentError.prepend(CoreExt)
48
+ end
@@ -0,0 +1,3 @@
1
+ module ErrorHighlight
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: error_highlight
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Yusuke Endoh
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-06-29 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: The gem enhances Exception#message by adding a short explanation where
14
+ the exception is raised
15
+ email:
16
+ - mame@ruby-lang.org
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - ".github/workflows/ruby.yml"
22
+ - ".gitignore"
23
+ - Gemfile
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - error_highlight.gemspec
28
+ - lib/error_highlight.rb
29
+ - lib/error_highlight/base.rb
30
+ - lib/error_highlight/core_ext.rb
31
+ - lib/error_highlight/version.rb
32
+ homepage: https://github.com/ruby/error_highlight
33
+ licenses:
34
+ - MIT
35
+ metadata: {}
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 3.1.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 3.3.0.dev
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: Shows a one-line code snippet with an underline in the error backtrace
55
+ test_files: []