summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/error_highlight/formatter.rb43
-rw-r--r--test/error_highlight/test_error_highlight.rb26
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 }