diff options
-rw-r--r-- | lib/error_highlight/formatter.rb | 43 | ||||
-rw-r--r-- | test/error_highlight/test_error_highlight.rb | 26 |
2 files changed, 69 insertions, 0 deletions
diff --git a/lib/error_highlight/formatter.rb b/lib/error_highlight/formatter.rb index 20ca78d468..b4fce93e55 100644 --- a/lib/error_highlight/formatter.rb +++ b/lib/error_highlight/formatter.rb @@ -3,6 +3,8 @@ module ErrorHighlight def self.message_for(spot) # currently only a one-line code snippet is supported if spot[:first_lineno] == spot[:last_lineno] + spot = truncate(spot) + indent = spot[:snippet][0...spot[:first_column]].gsub(/[^\t]/, " ") marker = indent + "^" * (spot[:last_column] - spot[:first_column]) @@ -11,6 +13,47 @@ module ErrorHighlight "" end end + + def self.viewport_size + Ractor.current[:__error_highlight_viewport_size__] || terminal_columns + end + + def self.viewport_size=(viewport_size) + Ractor.current[:__error_highlight_viewport_size__] = viewport_size + end + + private + + def self.truncate(spot) + ellipsis = '...' + snippet = spot[:snippet] + diff = snippet.size - (viewport_size - ellipsis.size) + + # snippet fits in the terminal + return spot if diff.negative? + + if spot[:first_column] < diff + snippet = snippet[0...snippet.size - diff] + { + **spot, + snippet: snippet + ellipsis + "\n", + last_column: [spot[:last_column], snippet.size].min + } + else + { + **spot, + snippet: ellipsis + snippet[diff..-1], + first_column: spot[:first_column] - (diff - ellipsis.size), + last_column: spot[:last_column] - (diff - ellipsis.size) + } + end + end + + def self.terminal_columns + # lazy load io/console in case viewport_size is set + require "io/console" + IO.console.winsize[1] + end end def self.formatter diff --git a/test/error_highlight/test_error_highlight.rb b/test/error_highlight/test_error_highlight.rb index f0da5b5e62..fd5e32a549 100644 --- a/test/error_highlight/test_error_highlight.rb +++ b/test/error_highlight/test_error_highlight.rb @@ -12,6 +12,8 @@ class ErrorHighlightTest < Test::Unit::TestCase end def setup + ErrorHighlight::DefaultFormatter.viewport_size = 80 + if defined?(DidYouMean) @did_you_mean_old_formatter = DidYouMean.formatter DidYouMean.formatter = DummyFormatter @@ -1285,6 +1287,30 @@ undefined method `time' for #{ ONE_RECV_MESSAGE } end end + def test_errors_on_small_viewports_when_error_lives_at_the_end + assert_error_message(NoMethodError, <<~END) do +undefined method 'gsuub' for an instance of String + +...ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo".gsuub(//, "") + ^^^^^^ + END + + "fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo".gsuub(//, "") + end + end + + def test_errors_on_small_viewports_when_error_lives_at_the_beginning + assert_error_message(NoMethodError, <<~END) do +undefined method 'gsuub' for an instance of Integer + + 1.gsuub(//, "fooooooooooooooooooooooooooooooooooooooooooooooooooooooooo... + ^^^^^^ + END + + 1.gsuub(//, "fooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo") + end + end + def test_simulate_funcallv_from_embedded_ruby assert_error_message(NoMethodError, <<~END) do undefined method `foo' for #{ NIL_RECV_MESSAGE } |