Coder Social home page Coder Social logo

error_highlight's Introduction

ErrorHighlight

Installation

Ruby 3.1 will ship with this gem and it will automatically be required when a Ruby process starts up. No special setup is required.

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.

Examples

1.time {}
$ ruby test.rb
test.rb:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)

1.time {}
 ^^^^^
Did you mean?  times

More example

def extract_value(data)
  data[:results].first[:value]
end

When data is { :results => [] }, the following error message is shown:

$ ruby test.rb
test.rb:2:in `extract_value': undefined method `[]' for nil:NilClass (NoMethodError)

  data[:results].first[:value]
                      ^^^^^^^^
        from test.rb:5:in `<main>'

When data is nil, it prints:

$ ruby test.rb
test.rb:2:in `extract_value': undefined method `[]' for nil:NilClass (NoMethodError)

  data[:results].first[:value]
      ^^^^^^^^^^
        from test.rb:5:in `<main>'

Using the ErrorHighlight.spot

Note: This API is experimental, may change in future.

You can use the ErrorHighlight.spot method to get the snippet data. 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).

class Dummy
  def test(_dummy_arg)
    node = RubyVM::AbstractSyntaxTree.of(caller_locations.first, keep_script_lines: true)
    ErrorHighlight.spot(node)
  end
end

pp Dummy.new.test(42) # <- Line 8
#           ^^^^^       <- Column 12--17

#=> {:first_lineno=>8,
#    :first_column=>12,
#    :last_lineno=>8,
#    :last_column=>17,
#    :snippet=>"pp Dummy.new.test(42) # <- Line 8\n"}

Custom Formatter

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.

formatter = Object.new
def formatter.message_for(spot)
  marker = " " * spot[:first_column] + "^" + "~" * (spot[:last_column] - spot[:first_column] - 1)

  "\n\n#{ spot[:snippet] }#{ marker }"
end

ErrorHighlight.formatter = formatter

1.time {}

#=>
#
# test.rb:10:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
#
# 1.time {}
#  ^~~~~
# Did you mean?  times

Disabling error_highlight

Occasionally, you may want to disable the error_highlight gem for e.g. debugging issues in the error object itself. You can disable it entirely by specifying --disable-error_highlight option to the ruby command:

$ ruby --disable-error_highlight -e '1.time {}'
-e:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)
Did you mean?  times

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/error_highlight.

License

The gem is available as open source under the terms of the MIT License.

error_highlight's People

Contributors

andyw8 avatar cboos avatar dependabot[bot] avatar eileencodes avatar hsbt avatar kddnewton avatar koic avatar m-nakamura145 avatar mame avatar pocke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

error_highlight's Issues

Handle very long line well

$ ruby t.rb
t.rb:1:in `<main>': undefined method `gsuub' for "fooo
oooooooooooooooooooooooooooooooooooooooooooooooooooooo
ooooooooooooooooooooooooooooooooooooooooooooooooooo":S
tring (NoMethodError)

"foooooooooooooooooooooooooooooooooooooooooooooooooooo
oooooooooooooooooooooooooooooooooooooooooooooooooooooo
oo".gsuub(//, "")


   ^^^^^^
Did you mean?  gsub

Should we truncate such a long code snippet to fit it with the width of terminal?

ArgumentError#message changes since 0.5.0

Rails CI is getting failures since error_highlight 0.5.0. Refer to https://buildkite.com/rails/rails/builds/90833#0184571b-307d-4e94-8a2d-3c8a2669a12c/1052-1078

It is likely because error_highlight 0.5.0 takes care of ArgumentError via defcaf1

I expect error_highlight does not change the original message.

Steps to reproduce

  • create foo.rb file
require 'minitest/autorun'
require 'error_highlight'

def foo(*args)
  raise ArgumentError, 'You have to supply some args' if args.empty?
  args
end

class BugTest < Minitest::Test
  def test_foo
    error = assert_raises(ArgumentError) do
      foo()
    end
    assert_equal 'You have to supply some args', error.message
  end
end

Expected behavior

  • foo.rb works without failures with error_highlight 0.4.0
$ ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
$ gem install error_highlight -v 0.4.0
Fetching error_highlight-0.4.0.gem
Successfully installed error_highlight-0.4.0
Parsing documentation for error_highlight-0.4.0
Installing ri documentation for error_highlight-0.4.0
Done installing documentation for error_highlight after 0 seconds
1 gem installed
$ ruby foo.rb
Run options: --seed 46613

# Running:

.

Finished in 0.005816s, 171.9293 runs/s, 343.8587 assertions/s.
1 runs, 2 assertions, 0 failures, 0 errors, 0 skips
$

Actual behavior

  • foo.rb works without failures with error_highlight 0.4.0
$ ruby -v
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
$ gem install error_highlight -v 0.5.0
Fetching error_highlight-0.5.0.gem
Successfully installed error_highlight-0.5.0
Parsing documentation for error_highlight-0.5.0
Installing ri documentation for error_highlight-0.5.0
Done installing documentation for error_highlight after 0 seconds
1 gem installed
yahonda@myryzen:~$ ruby foo.rb
Run options: --seed 48895

# Running:

F

Failure:
BugTest#test_foo [foo.rb:14]:
--- expected
+++ actual
@@ -1 +1,4 @@
-"You have to supply some args"
+"You have to supply some args
+
+  raise ArgumentError, 'You have to supply some args' if args.empty?
+        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^"



rails test foo.rb:10



Finished in 0.008049s, 124.2380 runs/s, 248.4761 assertions/s.
1 runs, 2 assertions, 1 failures, 0 errors, 0 skips
$

Bundle install hangs

I'm not sure what changed in my app but I'm on Rails 6.1.5 and Ruby 3.2.0. When I try to bundle install, the command hangs with no output. I see the following if I run in verbose debugging mode.

 bundle install
:   0: Starting resolution (2022-04-26 14:33:18 -0600)
:   0: User-requested dependencies: [#<Gem::Resolver::DependencyRequest:0x000000010e37a8e8 @dependency=<Gem::Dependency type=:runtime name="error_highlight" requirements="= 0.3.0">, @requester=nil>, #<Gem::Resolver::DependencyRequest:0x000000010e37a898 @dependency=<Gem::Dependency type=:runtime name="did_you_mean" requirements="= 1.6.1">, @requester=nil>, #<Gem::Resolver::DependencyRequest:0x000000010e37a870 @dependency=<Gem::Dependency type=:runtime name="bundler" requirements="= 2.3.9">, @requester=nil>]
Resolving dependencies...:   0: Creating possibility state for error_highlight (= 0.3.0) (1 remaining)
:   1: Attempting to activate [error_highlight-0.3.0]
:   1: Activated error_highlight at [error_highlight-0.3.0]
:   1: Requiring nested dependencies ()
:   1: Creating possibility state for did_you_mean (= 1.6.1) (1 remaining)
:   2: Attempting to activate [did_you_mean-1.6.1]
:   2: Activated did_you_mean at [did_you_mean-1.6.1]
:   2: Requiring nested dependencies ()
:   2: Creating possibility state for bundler (= 2.3.9) (1 remaining)
:   3: Attempting to activate [bundler-2.3.9]
:   3: Activated bundler at [bundler-2.3.9]
:   3: Requiring nested dependencies ()

:   0: Finished resolution (3 steps) (Took 0.006771 seconds) (2022-04-26 14:33:18 -0600)
:   0: Unactivated:
:   0: Activated: error_highlight, did_you_mean, bundler

I'm not completely sure what this means but it seems related to this gem. Any advice would be appreciated. Thanks!

Fails in Github Actions in Ruby 3.1.4

You have already activated error_highlight 0.3.0, but your Gemfile requires error_highlight 0.5.1. Since error_highlight is a default gem, you can either remove your dependency on it or try updating to a newer version of bundler that supports error_highlight as a default gem. (Gem::LoadError).

Running Ruby 3.1.4, Rails 7.1.1

Tweak highlight range

It seems to be clearer if the highlight range will be tweaked to the method name. Currently it contains dot.

Expected

1.time {}
  ^^^^
Did you mean?  times

Actual

1.time {}
 ^^^^^
Did you mean?  times

Does not work in IRB

Due to the design limitation of the ruby internal API (RubyVM::AST.of), error_highlight cannot get the code snippet evaluated by Kernel#eval, etc. This means that error_highlight cannot show highlight lines for IRB.

This is a very tough issue. We need to design how to keep the eval'ed source code.

Slightly related to: https://bugs.ruby-lang.org/issues/16983

NameError column adjustment

If there is no top-level const, the behavior is currently as follows

$ ruby -e 'Foo::Bar::Baz.new'
-e:1:in `<main>': uninitialized constant Foo (NameError)

Foo::Bar::Baz.new
        ^^^^^

Shouldn't it behave as follows?

$ ruby -e 'Foo::Bar::Baz.new'
-e:1:in `<main>': uninitialized constant Foo (NameError)

Foo::Bar::Baz.new
^^^

Doesn't highlight error for missing constant when const_missing is redefined for class

I'm working on new feature for ruby's test/unit tool and I define Test::Unit::TestCase.const_missing. It does some stuff then calls old const_missing like the following (pseudo code):

orig = self.method(:const_missing)
def const_missing(name)
  # do some stuff 
   orig.call(name)
end

This broke error_highlight tests for missing constant, it wasn't highlighting anymore. I'm not sure if this is fixable or not.

Question: understanding strings appended to NoMethodError message upon raise

Hello @mame and friends! Thank you for this gem and for working to improve the error experience in Ruby 3.1

I have a few gems that instantiate or extend built-in error types other than StandardError, often to imitate default behavior when metaprogramming. Under Ruby 3.1, Mocktail's test suite started failing because of changes introduced by the error_highlight gem. Specifically, I'm working on fixing this test at the moment.

To verify this is related to the error_highlight gem, I confirmed that this passes:

$ RUBYLIB='./lib:./test' ruby --disable-error_highlight test/unit/raises_neato_no_method_error_test.rb

It seems that this affects NoMethodError objects I create but not StandardError (I haven't tested other types), and only after they have been raised. Here is a minimal example of my issue:

begin
  e = StandardError.new("pants")
  puts "StandardError message BEFORE raise: #{e.message}"
  raise e
rescue => e
  puts "StandardError message AFTER raise: #{e.message}\n\n"
end

begin
  e = NoMethodError.new("pants")
  puts "NoMethodError message BEFORE raise: #{e.message}"
  raise e
rescue => e
  puts "NoMethodError message AFTER raise: #{e.message}"
end

This will output:

StandardError message BEFORE raise: pants
StandardError message AFTER raise: pants

NoMethodError message BEFORE raise: pants
NoMethodError message AFTER raise: pants

  raise e
  ^^^^^

Because I am instantiating these errors myself in library code, appending their raise line (which is inside my gem) is confusing and not helpful to the user.

As a result, I am wondering:

  • Should I stop raising built-in error types like NoMethodError myself?
  • Is there an API I don't know about that will allow me to prevent these ^^^^^ lines from being appended when the error is raised? Like an argument I can pass to its constructor or a property I can set?
  • Should error classes or this gem expose an API that allows temporary suppression of these changes to error messages, to support cases where library and framework code are creating them artificially?

Handle a backslash well

$ ruby -e '"\\".foo'
-e:1:in `<main>': undefined method `foo' for "\\\\":String (NoMethodError)
"\\\\".foo
    ^^^^

Found by @nobu

The problem is that Exception#message escapes the message. Not only error_highlight but also "\\\\":String (NoMethodError) is wrong. This should be fixed in the side of Exception#message in any way, but I have no good idea how to fix.

Broken link

Line 31 in base.rb has a broken link:

(e.g., Array#[] of +ary[(newline)expr(newline)]+), the method will return nil.

Unicode characters

Currently, error_highlight does not handle Unicode characters well. There are two subissues.

  1. Ruby::AST::Node#first_column and #last_column seem to return the column in bytes, but String#match handles the index in characters. We need to convert the column indexes.
  2. Some Unicode characters are displayed as two (or more?) columns in a terminal with monospace font.

(1) is relatively simple, but (2) is a bit tough. It requires a table telling how many columns each character has. It is known that Reline has such a table. But because error_highlight is a built-in gem that is loaded at Ruby process invocation, it is not good for error_highlight to depend on Reline (unless we make Reline a special built-in gem). We need to discuss how we make the table available to error_highlight.

Deal with hard tabs

$ ruby -e 'puts "\t1.time"' > t.rb
$ ruby t.rb
t.rb:1:in `<main>': undefined method `time' for 1:Integer (NoMethodError)

        1.time
  ^^^^^
Did you mean?  times

Reported by @ioquatix ๐Ÿคช

How about "known issues" document?

This gem has known issues, so I think it is more helpful if a known issue document exists.

I know the following issues:

  • shift ^^^ with multibyte characters
  • comment and backslash ref: #2
  • IRB doesn't highlight

Display a line in application code instead of gems

Currently, error_highlight spots the deepest Ruby frame. This may show code in a gem, which may not be very useful for application writers.

Consider:

# gem code
class SomeGem
  def foo(x)
    raise ArgumentError, "wrong input" if x == nil
  end
end

# app code
SomeGem.new.foo(nil)

Here is the current behavior:

$ ruby test.rb
test.rb:4:in `foo': wrong input (ArgumentError)

    raise ArgumentError, "wrong input" if x == nil
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        from test.rb:9:in `<main>'

However, application writers may want to see the line of SomeGem.new.foo(nil).

How might this be resolved?

  1. Display code snippets for all frames, not just for the deepest one. Python does this, but I am not very keen on it because it makes the error trace very messy.
  2. Add an argument Kernel#raise to tell error_highlight which frame should be displayed, for example, raise ArgumentError, "wrong input", error_highlight_skip_frames: 1.

For 2, there are two ways how to show the error.

2-1. Keep the error trace as is and change only the snippet. This might be confusing because the snippet line does not exist in test.rb:4.

$ ruby test.rb
test.rb:4:in `foo': wrong input (ArgumentError)

SomeGem.new.foo(nil)
                ^^^
        from test.rb:9:in `<main>'

2-2. Skip the error trace. This looks good to me, but I think this is beyond the responsibilities of error_highlight.

$ ruby test.rb
test.rb:9:in `<main>': wrong input (ArgumentError)

SomeGem.new.foo(nil)
                ^^^

I wonder if we should put something like raise skip_frames: n in the Ruby core side? I need to organize my thoughts.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.