error_highlight 0.7.0 → 0.7.2

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: c61fd8e89a64b337d10859ca487015ad69c87555057ce7f15b274ce799a5beb3
4
- data.tar.gz: 946058938dc70f583672f9adf1450a1514e4922638e051b2755545017f266963
3
+ metadata.gz: ef4c5d2f4747887533384fb54a39c1bb318199738f342d42cfe34ee43b82aac8
4
+ data.tar.gz: 416e7a3d066e4010a8f62ba638fda7e6513f0ab743d3f7ac1b6502c3cf260187
5
5
  SHA512:
6
- metadata.gz: b5a627e7b0a5710be27e62444a5127353e592cb5dd075ec0a25aa957fdf92e7c3e730b30a0fe5b4d1d35eabd9cde9dd6abafbc22a3f071ad7af8f39f991ee634
7
- data.tar.gz: 6e72a445acf82b23d8a6deb6537504dfe64b50d74a808ff33104da5dccad6970256f115ef1dc786a25136457ce18b801d0d466b7b8c8dac4ba0d70ab3b5732cc
6
+ metadata.gz: 6c6ab47b4e691ef2df5fc72308b26f8458cebf8f3b1b1ccd2273563f4dfea5dabf1a141040f28ce003a276dce8c6e24848b42d561e9cd3b5a7838c872a26a90d
7
+ data.tar.gz: 3b5b1e262f4d2e38a27d3034886344f48bdfa1e7fc76cecf56c277887ede0d6ead14d0ff2138dddcab6849068e503eef64133bbe1bdbba817ff474099fcf9cd9
@@ -13,7 +13,7 @@ jobs:
13
13
  uses: ruby/actions/.github/workflows/ruby_versions.yml@master
14
14
  with:
15
15
  engine: cruby
16
- min_version: 3.1
16
+ min_version: 3.2
17
17
 
18
18
  build:
19
19
  needs: ruby-versions
@@ -22,7 +22,7 @@ jobs:
22
22
  matrix:
23
23
  ruby: ${{ fromJson(needs.ruby-versions.outputs.versions) }}
24
24
  steps:
25
- - uses: actions/checkout@v4
25
+ - uses: actions/checkout@v7
26
26
  - uses: ruby/setup-ruby@v1
27
27
  with:
28
28
  ruby-version: ${{ matrix.ruby }}
@@ -36,7 +36,7 @@ jobs:
36
36
  prism:
37
37
  runs-on: ubuntu-latest
38
38
  steps:
39
- - uses: actions/checkout@v4
39
+ - uses: actions/checkout@v7
40
40
  - uses: ruby/setup-ruby@v1
41
41
  with:
42
42
  ruby-version: head
@@ -0,0 +1,33 @@
1
+ name: Sync ruby
2
+ on:
3
+ push:
4
+ branches: [master]
5
+ jobs:
6
+ sync:
7
+ name: Sync ruby
8
+ runs-on: ubuntu-latest
9
+ if: ${{ github.repository_owner == 'ruby' }}
10
+ steps:
11
+ - uses: actions/checkout@v7
12
+
13
+ - name: Create GitHub App token
14
+ id: app-token
15
+ uses: actions/create-github-app-token@v3
16
+ with:
17
+ app-id: 2060836
18
+ private-key: ${{ secrets.RUBY_SYNC_DEFAULT_GEMS_PRIVATE_KEY }}
19
+ owner: ruby
20
+ repositories: ruby
21
+
22
+ - name: Sync to ruby/ruby
23
+ uses: convictional/trigger-workflow-and-wait@v1.6.5
24
+ with:
25
+ owner: ruby
26
+ repo: ruby
27
+ workflow_file_name: sync_default_gems.yml
28
+ github_token: ${{ steps.app-token.outputs.token }}
29
+ ref: master
30
+ client_payload: |
31
+ {"gem":"${{ github.event.repository.name }}","before":"${{ github.event.before }}","after":"${{ github.event.after }}"}
32
+ propagate_failure: true
33
+ wait_interval: 10
@@ -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.dev")
21
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
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,13 @@
1
1
  require_relative "version"
2
2
 
3
3
  module ErrorHighlight
4
- # Identify the code fragment at that a given exception occurred.
4
+ # Identify the code fragment where a given exception occurred.
5
5
  #
6
6
  # Options:
7
7
  #
8
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.
9
+ # :name (default) points to the method/variable name where the exception occurred.
10
+ # :args points to the arguments of the method call where the exception occurred.
11
11
  #
12
12
  # backtrace_location: Thread::Backtrace::Location
13
13
  # It locates the code fragment of the given backtrace_location.
@@ -28,7 +28,7 @@ module ErrorHighlight
28
28
  # Currently, ErrorHighlight.spot only supports a single-line code fragment.
29
29
  # Therefore, if the return value is not nil, first_lineno and last_lineno will have
30
30
  # the same value. If the relevant code fragment spans multiple lines
31
- # (e.g., Array#[] of +ary[(newline)expr(newline)]+), the method will return nil.
31
+ # (e.g., Array#[] of <tt>ary[(newline)expr(newline)]</tt>), the method will return nil.
32
32
  # This restriction may be removed in the future.
33
33
  def self.spot(obj, **opts)
34
34
  case obj
@@ -113,7 +113,7 @@ module ErrorHighlight
113
113
  snippet = @node.script_lines[lineno - 1 .. last_lineno - 1].join("")
114
114
  snippet += "\n" unless snippet.end_with?("\n")
115
115
 
116
- # It require some work to support Unicode (or multibyte) characters.
116
+ # It requires some work to support Unicode (or multibyte) characters.
117
117
  # Tentatively, we stop highlighting if the code snippet has non-ascii characters.
118
118
  # See https://github.com/ruby/error_highlight/issues/4
119
119
  raise NonAscii unless snippet.ascii_only?
@@ -122,56 +122,51 @@ module ErrorHighlight
122
122
  end
123
123
  end
124
124
 
125
- OPT_GETCONSTANT_PATH = (RUBY_VERSION.split(".").map {|s| s.to_i } <=> [3, 2]) >= 0
126
- private_constant :OPT_GETCONSTANT_PATH
127
-
128
125
  def spot
129
126
  return nil unless @node
130
127
 
131
- if OPT_GETCONSTANT_PATH
132
- # In Ruby 3.2 or later, a nested constant access (like `Foo::Bar::Baz`)
133
- # is compiled to one instruction (opt_getconstant_path).
134
- # @node points to the node of the whole `Foo::Bar::Baz` even if `Foo`
135
- # or `Foo::Bar` causes NameError.
136
- # So we try to spot the sub-node that causes the NameError by using
137
- # `NameError#name`.
138
- case @node.type
139
- when :COLON2
140
- subnodes = []
141
- node = @node
142
- while node.type == :COLON2
143
- node2, const = node.children
144
- subnodes << node if const == @name
145
- node = node2
146
- end
147
- if node.type == :CONST || node.type == :COLON3
148
- if node.children.first == @name
149
- subnodes << node
150
- end
151
-
152
- # If we found only one sub-node whose name is equal to @name, use it
153
- return nil if subnodes.size != 1
154
- @node = subnodes.first
155
- else
156
- # Do nothing; opt_getconstant_path is used only when the const base is
157
- # NODE_CONST (`Foo`) or NODE_COLON3 (`::Foo`)
158
- end
159
- when :constant_path_node
160
- subnodes = []
161
- node = @node
162
-
163
- begin
164
- subnodes << node if node.name == @name
165
- end while (node = node.parent).is_a?(Prism::ConstantPathNode)
166
-
167
- if node.is_a?(Prism::ConstantReadNode) && node.name == @name
128
+ # In Ruby 3.2 or later, a nested constant access (like `Foo::Bar::Baz`)
129
+ # is compiled to one instruction (opt_getconstant_path).
130
+ # @node points to the node of the whole `Foo::Bar::Baz` even if `Foo`
131
+ # or `Foo::Bar` causes NameError.
132
+ # So we try to spot the sub-node that causes the NameError by using
133
+ # `NameError#name`.
134
+ case @node.type
135
+ when :COLON2
136
+ subnodes = []
137
+ node = @node
138
+ while node.type == :COLON2
139
+ node2, const = node.children
140
+ subnodes << node if const == @name
141
+ node = node2
142
+ end
143
+ if node.type == :CONST || node.type == :COLON3
144
+ if node.children.first == @name
168
145
  subnodes << node
169
146
  end
170
147
 
171
148
  # If we found only one sub-node whose name is equal to @name, use it
172
149
  return nil if subnodes.size != 1
173
150
  @node = subnodes.first
151
+ else
152
+ # Do nothing; opt_getconstant_path is used only when the const base is
153
+ # NODE_CONST (`Foo`) or NODE_COLON3 (`::Foo`)
174
154
  end
155
+ when :constant_path_node
156
+ subnodes = []
157
+ node = @node
158
+
159
+ begin
160
+ subnodes << node if node.name == @name
161
+ end while (node = node.parent).is_a?(Prism::ConstantPathNode)
162
+
163
+ if node.is_a?(Prism::ConstantReadNode) && node.name == @name
164
+ subnodes << node
165
+ end
166
+
167
+ # If we found only one sub-node whose name is equal to @name, use it
168
+ return nil if subnodes.size != 1
169
+ @node = subnodes.first
175
170
  end
176
171
 
177
172
  case @node.type
@@ -239,6 +234,24 @@ module ErrorHighlight
239
234
  when :OP_CDECL
240
235
  spot_op_cdecl
241
236
 
237
+ when :DEFN
238
+ # There is nothing to highlight for the arguments of a method
239
+ # definition, so just return nil instead of raising.
240
+ return nil if @point_type != :name
241
+ spot_defn
242
+
243
+ when :DEFS
244
+ return nil if @point_type != :name
245
+ spot_defs
246
+
247
+ when :LAMBDA
248
+ return nil if @point_type != :name
249
+ spot_lambda
250
+
251
+ when :ITER
252
+ return nil if @point_type != :name
253
+ spot_iter
254
+
242
255
  when :call_node
243
256
  case @point_type
244
257
  when :name
@@ -280,6 +293,37 @@ module ErrorHighlight
280
293
  when :constant_path_operator_write_node
281
294
  prism_spot_constant_path_operator_write
282
295
 
296
+ when :def_node
297
+ case @point_type
298
+ when :name
299
+ prism_spot_def_for_name
300
+ when :args
301
+ # There is nothing to highlight for the arguments of a method
302
+ # definition (e.g. an ArgumentError for a missing required keyword
303
+ # is spotted with point_type: :args). Return nil instead of raising
304
+ # NotImplementedError, which would otherwise escape
305
+ # Exception#detailed_message / #full_message.
306
+ return nil
307
+ end
308
+
309
+ when :lambda_node
310
+ case @point_type
311
+ when :name
312
+ prism_spot_lambda_for_name
313
+ when :args
314
+ # See the comment for :def_node above.
315
+ return nil
316
+ end
317
+
318
+ when :block_node
319
+ case @point_type
320
+ when :name
321
+ prism_spot_block_for_name
322
+ when :args
323
+ # See the comment for :def_node above.
324
+ return nil
325
+ end
326
+
283
327
  end
284
328
 
285
329
  if @snippet && @beg_column && @end_column && @beg_column < @end_column
@@ -344,6 +388,7 @@ module ErrorHighlight
344
388
  end
345
389
  elsif mid.to_s =~ /\A\W+\z/ && lines.match(/\G\s*(#{ Regexp.quote(mid) })=.*\n/, nd_recv.last_column)
346
390
  @snippet = $` + $&
391
+ @beg_lineno = @end_lineno = lineno
347
392
  @beg_column = $~.begin(1)
348
393
  @end_column = $~.end(1)
349
394
  end
@@ -470,7 +515,6 @@ module ErrorHighlight
470
515
  def spot_fcall_for_args
471
516
  _mid, nd_args = @node.children
472
517
  if nd_args && nd_args.first_lineno == nd_args.last_lineno
473
- # binary operator
474
518
  fetch_line(nd_args.first_lineno)
475
519
  @beg_column = nd_args.first_column
476
520
  @end_column = nd_args.last_column
@@ -582,8 +626,9 @@ module ErrorHighlight
582
626
  @beg_column = nd_parent.last_column
583
627
  @end_column = @node.last_column
584
628
  else
585
- @snippet = @fetch[@node.last_lineno]
629
+ fetch_line(@node.last_lineno)
586
630
  if @snippet[...@node.last_column].match(/#{ Regexp.quote(const) }\z/)
631
+ @beg_lineno = @end_lineno = @node.last_lineno
587
632
  @beg_column = $~.begin(0)
588
633
  @end_column = $~.end(0)
589
634
  end
@@ -597,7 +642,7 @@ module ErrorHighlight
597
642
  nd_lhs, op, _nd_rhs = @node.children
598
643
  *nd_parent_lhs, _const = nd_lhs.children
599
644
  if @name == op
600
- @snippet = @fetch[nd_lhs.last_lineno]
645
+ fetch_line(nd_lhs.last_lineno)
601
646
  if @snippet.match(/\G\s*(#{ Regexp.quote(op) })=/, nd_lhs.last_column)
602
647
  @beg_column = $~.begin(1)
603
648
  @end_column = $~.end(1)
@@ -607,18 +652,67 @@ module ErrorHighlight
607
652
  @end_column = nd_lhs.last_column
608
653
  if nd_parent_lhs.empty? # example: ::C += 1
609
654
  if nd_lhs.first_lineno == nd_lhs.last_lineno
610
- @snippet = @fetch[nd_lhs.last_lineno]
655
+ fetch_line(nd_lhs.last_lineno)
611
656
  @beg_column = nd_lhs.first_column
612
657
  end
613
658
  else # example: Foo::Bar::C += 1
614
659
  if nd_parent_lhs.last.last_lineno == nd_lhs.last_lineno
615
- @snippet = @fetch[nd_lhs.last_lineno]
660
+ fetch_line(nd_lhs.last_lineno)
616
661
  @beg_column = nd_parent_lhs.last.last_column
617
662
  end
618
663
  end
619
664
  end
620
665
  end
621
666
 
667
+ # Example:
668
+ # def bar; end
669
+ # ^^^
670
+ def spot_defn
671
+ mid, = @node.children
672
+ fetch_line(@node.first_lineno)
673
+ if @snippet.match(/\Gdef\s+(#{ Regexp.quote(mid) }\b)/, @node.first_column)
674
+ @beg_column = $~.begin(1)
675
+ @end_column = $~.end(1)
676
+ end
677
+ end
678
+
679
+ # Example:
680
+ # def Foo.bar; end
681
+ # ^^^^
682
+ def spot_defs
683
+ nd_recv, mid, = @node.children
684
+ fetch_line(nd_recv.last_lineno)
685
+ if @snippet.match(/\G\s*(\.\s*#{ Regexp.quote(mid) }\b)/, nd_recv.last_column)
686
+ @beg_column = $~.begin(1)
687
+ @end_column = $~.end(1)
688
+ end
689
+ end
690
+
691
+ # Example:
692
+ # -> { ... }
693
+ # ^^
694
+ def spot_lambda
695
+ fetch_line(@node.first_lineno)
696
+ if @snippet.match(/\G->/, @node.first_column)
697
+ @beg_column = $~.begin(0)
698
+ @end_column = $~.end(0)
699
+ end
700
+ end
701
+
702
+ # Example:
703
+ # lambda { ... }
704
+ # ^
705
+ # define_method :foo do
706
+ # ^^
707
+ def spot_iter
708
+ _nd_fcall, nd_scope = @node.children
709
+ fetch_line(nd_scope.first_lineno)
710
+ if @snippet.match(/\G(?:do\b|\{)/, nd_scope.first_column)
711
+ @beg_column = $~.begin(0)
712
+ @end_column = $~.end(0)
713
+ end
714
+ end
715
+
622
716
  def fetch_line(lineno)
623
717
  @beg_lineno = @end_lineno = lineno
624
718
  @snippet = @fetch[lineno]
@@ -824,6 +918,31 @@ module ErrorHighlight
824
918
  prism_location(@node.binary_operator_loc.chop)
825
919
  end
826
920
  end
921
+
922
+ # Example:
923
+ # def foo()
924
+ # ^^^
925
+ def prism_spot_def_for_name
926
+ location = @node.name_loc
927
+ location = @node.operator_loc.join(location) if @node.operator_loc
928
+ prism_location(location)
929
+ end
930
+
931
+ # Example:
932
+ # -> x, y { }
933
+ # ^^
934
+ def prism_spot_lambda_for_name
935
+ prism_location(@node.operator_loc)
936
+ end
937
+
938
+ # Example:
939
+ # lambda { }
940
+ # ^
941
+ # define_method :foo do |x, y|
942
+ # ^
943
+ def prism_spot_block_for_name
944
+ prism_location(@node.opening_loc)
945
+ end
827
946
  end
828
947
 
829
948
  private_constant :Spotter
@@ -3,9 +3,38 @@ require_relative "formatter"
3
3
  module ErrorHighlight
4
4
  module CoreExt
5
5
  private def generate_snippet
6
- spot = ErrorHighlight.spot(self)
7
- return "" unless spot
8
- return ErrorHighlight.formatter.message_for(spot)
6
+ if ArgumentError === self && message =~ /\A(?:wrong number of arguments|missing keyword[s]?|unknown keyword[s]?|no keywords accepted)\b/
7
+ locs = self.backtrace_locations
8
+ return "" if locs.size < 2
9
+ callee_loc, caller_loc = locs
10
+ callee_spot = ErrorHighlight.spot(self, backtrace_location: callee_loc, point_type: :name)
11
+ caller_spot = ErrorHighlight.spot(self, backtrace_location: caller_loc, point_type: :name)
12
+ if caller_spot && callee_spot &&
13
+ caller_loc.path == callee_loc.path &&
14
+ caller_loc.lineno == callee_loc.lineno &&
15
+ caller_spot == callee_spot
16
+ callee_loc = callee_spot = nil
17
+ end
18
+ ret = +"\n"
19
+ [["caller", caller_loc, caller_spot], ["callee", callee_loc, callee_spot]].each do |header, loc, spot|
20
+ out = nil
21
+ if loc
22
+ out = " #{ header }: #{ loc.path }:#{ loc.lineno }"
23
+ if spot
24
+ _, _, snippet, highlight = ErrorHighlight.formatter.message_for(spot).lines
25
+ out += "\n | #{ snippet } #{ highlight }"
26
+ else
27
+ # do nothing
28
+ end
29
+ end
30
+ ret << "\n" + out if out
31
+ end
32
+ ret
33
+ else
34
+ spot = ErrorHighlight.spot(self)
35
+ return "" unless spot
36
+ return ErrorHighlight.formatter.message_for(spot)
37
+ end
9
38
  end
10
39
 
11
40
  if Exception.method_defined?(:detailed_message)
@@ -56,11 +56,11 @@ module ErrorHighlight
56
56
  end
57
57
 
58
58
  def self.terminal_width
59
- # lazy load io/console, so it's not loaded when 'max_snippet_width' is set
59
+ # lazy load io/console to avoid loading it when 'max_snippet_width' is manually set
60
60
  require "io/console"
61
- STDERR.winsize[1] if STDERR.tty?
61
+ $stderr.winsize[1] if $stderr.tty?
62
62
  rescue LoadError, NoMethodError, SystemCallError
63
- # do not truncate when window size is not available
63
+ # skip truncation when terminal window size is unavailable
64
64
  end
65
65
  end
66
66
 
@@ -1,3 +1,3 @@
1
1
  module ErrorHighlight
2
- VERSION = "0.7.0"
2
+ VERSION = "0.7.2"
3
3
  end
metadata CHANGED
@@ -1,55 +1,56 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: error_highlight
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  platform: ruby
6
- original_platform: ''
7
6
  authors:
8
- - Yusuke Endoh
7
+ - Yusuke Endoh
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-12-03 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies: []
13
- description: The gem enhances Exception#message by adding a short explanation where
14
- the exception is raised
12
+ description: "The gem enhances Exception#message by adding a short explanation where the exception is raised"
15
13
  email:
16
- - mame@ruby-lang.org
14
+ - mame@ruby-lang.org
17
15
  executables: []
18
16
  extensions: []
19
17
  extra_rdoc_files: []
20
18
  files:
21
- - ".github/dependabot.yml"
22
- - ".github/workflows/ruby.yml"
23
- - ".gitignore"
24
- - Gemfile
25
- - LICENSE.txt
26
- - README.md
27
- - Rakefile
28
- - error_highlight.gemspec
29
- - lib/error_highlight.rb
30
- - lib/error_highlight/base.rb
31
- - lib/error_highlight/core_ext.rb
32
- - lib/error_highlight/formatter.rb
33
- - lib/error_highlight/version.rb
34
- homepage: https://github.com/ruby/error_highlight
19
+ - .github/dependabot.yml
20
+ - .github/workflows/ruby.yml
21
+ - .github/workflows/sync-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/formatter.rb
32
+ - lib/error_highlight/version.rb
33
+ homepage: "https://github.com/ruby/error_highlight"
35
34
  licenses:
36
- - MIT
35
+ - MIT
37
36
  metadata: {}
38
37
  rdoc_options: []
39
38
  require_paths:
40
- - lib
39
+ - lib
41
40
  required_ruby_version: !ruby/object:Gem::Requirement
42
41
  requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- version: 3.1.0.dev
42
+ -
43
+ - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: 3.2.0
46
46
  required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  requirements:
48
- - - ">="
49
- - !ruby/object:Gem::Version
50
- version: '0'
48
+ -
49
+ - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
51
52
  requirements: []
52
- rubygems_version: 3.6.0.dev
53
+ rubygems_version: 4.1.0.dev
53
54
  specification_version: 4
54
55
  summary: Shows a one-line code snippet with an underline in the error backtrace
55
56
  test_files: []