error_highlight 0.7.0 → 0.7.1

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: e6a847df9db6582ba1912ba3a2a64a76ba18e38d43c38413ac885fb993cbce07
4
+ data.tar.gz: c6e6872fd7276af7fac719d173da9f07af9fed0760d05385d1b6b6217449b9f0
5
5
  SHA512:
6
- metadata.gz: b5a627e7b0a5710be27e62444a5127353e592cb5dd075ec0a25aa957fdf92e7c3e730b30a0fe5b4d1d35eabd9cde9dd6abafbc22a3f071ad7af8f39f991ee634
7
- data.tar.gz: 6e72a445acf82b23d8a6deb6537504dfe64b50d74a808ff33104da5dccad6970256f115ef1dc786a25136457ce18b801d0d466b7b8c8dac4ba0d70ab3b5732cc
6
+ metadata.gz: 9699f20efc393a3fc7c09312a29818416bb255c3078c45a4a4f75deda3f2becefe415bc98565928a001ff88638433868d89656d9382b6b736a781f51d17e1901
7
+ data.tar.gz: 48410be2cb3bbfd0c241ded954d11c960abc551fc896c907ed05d6c49ea37ec116ee92556cd713c9b5fe2f033fe3eb16606e788b97a61196f97a2050f839ded2
@@ -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@v6
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@v6
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@v6
12
+
13
+ - name: Create GitHub App token
14
+ id: app-token
15
+ uses: actions/create-github-app-token@v2
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.
@@ -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,20 @@ module ErrorHighlight
239
234
  when :OP_CDECL
240
235
  spot_op_cdecl
241
236
 
237
+ when :DEFN
238
+ raise NotImplementedError if @point_type != :name
239
+ spot_defn
240
+
241
+ when :DEFS
242
+ raise NotImplementedError if @point_type != :name
243
+ spot_defs
244
+
245
+ when :LAMBDA
246
+ spot_lambda
247
+
248
+ when :ITER
249
+ spot_iter
250
+
242
251
  when :call_node
243
252
  case @point_type
244
253
  when :name
@@ -280,6 +289,30 @@ module ErrorHighlight
280
289
  when :constant_path_operator_write_node
281
290
  prism_spot_constant_path_operator_write
282
291
 
292
+ when :def_node
293
+ case @point_type
294
+ when :name
295
+ prism_spot_def_for_name
296
+ when :args
297
+ raise NotImplementedError
298
+ end
299
+
300
+ when :lambda_node
301
+ case @point_type
302
+ when :name
303
+ prism_spot_lambda_for_name
304
+ when :args
305
+ raise NotImplementedError
306
+ end
307
+
308
+ when :block_node
309
+ case @point_type
310
+ when :name
311
+ prism_spot_block_for_name
312
+ when :args
313
+ raise NotImplementedError
314
+ end
315
+
283
316
  end
284
317
 
285
318
  if @snippet && @beg_column && @end_column && @beg_column < @end_column
@@ -344,6 +377,7 @@ module ErrorHighlight
344
377
  end
345
378
  elsif mid.to_s =~ /\A\W+\z/ && lines.match(/\G\s*(#{ Regexp.quote(mid) })=.*\n/, nd_recv.last_column)
346
379
  @snippet = $` + $&
380
+ @beg_lineno = @end_lineno = lineno
347
381
  @beg_column = $~.begin(1)
348
382
  @end_column = $~.end(1)
349
383
  end
@@ -470,7 +504,6 @@ module ErrorHighlight
470
504
  def spot_fcall_for_args
471
505
  _mid, nd_args = @node.children
472
506
  if nd_args && nd_args.first_lineno == nd_args.last_lineno
473
- # binary operator
474
507
  fetch_line(nd_args.first_lineno)
475
508
  @beg_column = nd_args.first_column
476
509
  @end_column = nd_args.last_column
@@ -582,8 +615,9 @@ module ErrorHighlight
582
615
  @beg_column = nd_parent.last_column
583
616
  @end_column = @node.last_column
584
617
  else
585
- @snippet = @fetch[@node.last_lineno]
618
+ fetch_line(@node.last_lineno)
586
619
  if @snippet[...@node.last_column].match(/#{ Regexp.quote(const) }\z/)
620
+ @beg_lineno = @end_lineno = @node.last_lineno
587
621
  @beg_column = $~.begin(0)
588
622
  @end_column = $~.end(0)
589
623
  end
@@ -597,7 +631,7 @@ module ErrorHighlight
597
631
  nd_lhs, op, _nd_rhs = @node.children
598
632
  *nd_parent_lhs, _const = nd_lhs.children
599
633
  if @name == op
600
- @snippet = @fetch[nd_lhs.last_lineno]
634
+ fetch_line(nd_lhs.last_lineno)
601
635
  if @snippet.match(/\G\s*(#{ Regexp.quote(op) })=/, nd_lhs.last_column)
602
636
  @beg_column = $~.begin(1)
603
637
  @end_column = $~.end(1)
@@ -607,18 +641,67 @@ module ErrorHighlight
607
641
  @end_column = nd_lhs.last_column
608
642
  if nd_parent_lhs.empty? # example: ::C += 1
609
643
  if nd_lhs.first_lineno == nd_lhs.last_lineno
610
- @snippet = @fetch[nd_lhs.last_lineno]
644
+ fetch_line(nd_lhs.last_lineno)
611
645
  @beg_column = nd_lhs.first_column
612
646
  end
613
647
  else # example: Foo::Bar::C += 1
614
648
  if nd_parent_lhs.last.last_lineno == nd_lhs.last_lineno
615
- @snippet = @fetch[nd_lhs.last_lineno]
649
+ fetch_line(nd_lhs.last_lineno)
616
650
  @beg_column = nd_parent_lhs.last.last_column
617
651
  end
618
652
  end
619
653
  end
620
654
  end
621
655
 
656
+ # Example:
657
+ # def bar; end
658
+ # ^^^
659
+ def spot_defn
660
+ mid, = @node.children
661
+ fetch_line(@node.first_lineno)
662
+ if @snippet.match(/\Gdef\s+(#{ Regexp.quote(mid) }\b)/, @node.first_column)
663
+ @beg_column = $~.begin(1)
664
+ @end_column = $~.end(1)
665
+ end
666
+ end
667
+
668
+ # Example:
669
+ # def Foo.bar; end
670
+ # ^^^^
671
+ def spot_defs
672
+ nd_recv, mid, = @node.children
673
+ fetch_line(nd_recv.last_lineno)
674
+ if @snippet.match(/\G\s*(\.\s*#{ Regexp.quote(mid) }\b)/, nd_recv.last_column)
675
+ @beg_column = $~.begin(1)
676
+ @end_column = $~.end(1)
677
+ end
678
+ end
679
+
680
+ # Example:
681
+ # -> { ... }
682
+ # ^^
683
+ def spot_lambda
684
+ fetch_line(@node.first_lineno)
685
+ if @snippet.match(/\G->/, @node.first_column)
686
+ @beg_column = $~.begin(0)
687
+ @end_column = $~.end(0)
688
+ end
689
+ end
690
+
691
+ # Example:
692
+ # lambda { ... }
693
+ # ^
694
+ # define_method :foo do
695
+ # ^^
696
+ def spot_iter
697
+ _nd_fcall, nd_scope = @node.children
698
+ fetch_line(nd_scope.first_lineno)
699
+ if @snippet.match(/\G(?:do\b|\{)/, nd_scope.first_column)
700
+ @beg_column = $~.begin(0)
701
+ @end_column = $~.end(0)
702
+ end
703
+ end
704
+
622
705
  def fetch_line(lineno)
623
706
  @beg_lineno = @end_lineno = lineno
624
707
  @snippet = @fetch[lineno]
@@ -824,6 +907,31 @@ module ErrorHighlight
824
907
  prism_location(@node.binary_operator_loc.chop)
825
908
  end
826
909
  end
910
+
911
+ # Example:
912
+ # def foo()
913
+ # ^^^
914
+ def prism_spot_def_for_name
915
+ location = @node.name_loc
916
+ location = @node.operator_loc.join(location) if @node.operator_loc
917
+ prism_location(location)
918
+ end
919
+
920
+ # Example:
921
+ # -> x, y { }
922
+ # ^^
923
+ def prism_spot_lambda_for_name
924
+ prism_location(@node.operator_loc)
925
+ end
926
+
927
+ # Example:
928
+ # lambda { }
929
+ # ^
930
+ # define_method :foo do |x, y|
931
+ # ^
932
+ def prism_spot_block_for_name
933
+ prism_location(@node.opening_loc)
934
+ end
827
935
  end
828
936
 
829
937
  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.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,13 @@
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.1
5
5
  platform: ruby
6
- original_platform: ''
7
6
  authors:
8
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
12
  description: The gem enhances Exception#message by adding a short explanation where
14
13
  the exception is raised
@@ -20,6 +19,7 @@ extra_rdoc_files: []
20
19
  files:
21
20
  - ".github/dependabot.yml"
22
21
  - ".github/workflows/ruby.yml"
22
+ - ".github/workflows/sync-ruby.yml"
23
23
  - ".gitignore"
24
24
  - Gemfile
25
25
  - LICENSE.txt
@@ -42,14 +42,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
42
42
  requirements:
43
43
  - - ">="
44
44
  - !ruby/object:Gem::Version
45
- version: 3.1.0.dev
45
+ version: 3.2.0
46
46
  required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  requirements:
48
48
  - - ">="
49
49
  - !ruby/object:Gem::Version
50
50
  version: '0'
51
51
  requirements: []
52
- rubygems_version: 3.6.0.dev
52
+ rubygems_version: 4.0.1
53
53
  specification_version: 4
54
54
  summary: Shows a one-line code snippet with an underline in the error backtrace
55
55
  test_files: []