[ruby/error_highlight] Adjust truncation, add opt-out mechanism, rename methods, and prepare error highlighting to render on extremely small screens
https://github.com/ruby/error_highlight/commit/c565340958
This commit is contained in:
parent
e7c9dfb3e9
commit
e9ba6c2ea4
@ -1,5 +1,7 @@
|
|||||||
module ErrorHighlight
|
module ErrorHighlight
|
||||||
class DefaultFormatter
|
class DefaultFormatter
|
||||||
|
MIN_SNIPPET_WIDTH = 20
|
||||||
|
|
||||||
def self.message_for(spot)
|
def self.message_for(spot)
|
||||||
# currently only a one-line code snippet is supported
|
# currently only a one-line code snippet is supported
|
||||||
return "" unless spot[:first_lineno] == spot[:last_lineno]
|
return "" unless spot[:first_lineno] == spot[:last_lineno]
|
||||||
@ -7,22 +9,24 @@ module ErrorHighlight
|
|||||||
snippet = spot[:snippet]
|
snippet = spot[:snippet]
|
||||||
first_column = spot[:first_column]
|
first_column = spot[:first_column]
|
||||||
last_column = spot[:last_column]
|
last_column = spot[:last_column]
|
||||||
|
ellipsis = "..."
|
||||||
|
|
||||||
# truncate snippet to fit in the viewport
|
# truncate snippet to fit in the viewport
|
||||||
if snippet.size > viewport_size
|
if snippet_max_width && snippet.size > snippet_max_width
|
||||||
visible_start = [first_column - viewport_size / 2, 0].max
|
available_width = snippet_max_width - ellipsis.size
|
||||||
visible_end = visible_start + viewport_size
|
center = first_column - snippet_max_width / 2
|
||||||
|
|
||||||
# avoid centering the snippet when the error is at the end of the line
|
visible_start = last_column < available_width ? 0 : [center, 0].max
|
||||||
visible_start = snippet.size - viewport_size if visible_end > snippet.size
|
visible_end = visible_start + snippet_max_width
|
||||||
|
visible_start = snippet.size - snippet_max_width if visible_end > snippet.size
|
||||||
|
|
||||||
prefix = visible_start.positive? ? "..." : ""
|
prefix = visible_start.positive? ? ellipsis : ""
|
||||||
suffix = visible_end < snippet.size ? "..." : ""
|
suffix = visible_end < snippet.size ? ellipsis : ""
|
||||||
|
|
||||||
snippet = prefix + snippet[(visible_start + prefix.size)...(visible_end - suffix.size)] + suffix
|
snippet = prefix + snippet[(visible_start + prefix.size)...(visible_end - suffix.size)] + suffix
|
||||||
snippet << "\n" unless snippet.end_with?("\n")
|
snippet << "\n" unless snippet.end_with?("\n")
|
||||||
|
|
||||||
first_column = first_column - visible_start
|
first_column -= visible_start
|
||||||
last_column = [last_column - visible_start, snippet.size - 1].min
|
last_column = [last_column - visible_start, snippet.size - 1].min
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -32,18 +36,31 @@ module ErrorHighlight
|
|||||||
"\n\n#{ snippet }#{ marker }"
|
"\n\n#{ snippet }#{ marker }"
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.viewport_size
|
def self.snippet_max_width
|
||||||
Ractor.current[:__error_highlight_viewport_size__] ||= terminal_columns
|
return if Ractor.current[:__error_highlight_max_snippet_width__] == :disabled
|
||||||
|
|
||||||
|
Ractor.current[:__error_highlight_max_snippet_width__] ||= terminal_width
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.viewport_size=(viewport_size)
|
def self.snippet_max_width=(width)
|
||||||
Ractor.current[:__error_highlight_viewport_size__] = viewport_size
|
return Ractor.current[:__error_highlight_max_snippet_width__] = :disabled if width.nil?
|
||||||
|
|
||||||
|
width = width.to_i
|
||||||
|
|
||||||
|
if width < MIN_SNIPPET_WIDTH
|
||||||
|
warn "'snippet_max_width' adjusted to minimum value of #{MIN_SNIPPET_WIDTH}."
|
||||||
|
width = MIN_SNIPPET_WIDTH
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.terminal_columns
|
Ractor.current[:__error_highlight_max_snippet_width__] = width
|
||||||
# lazy load io/console, so it's not loaded when viewport_size is set
|
end
|
||||||
|
|
||||||
|
def self.terminal_width
|
||||||
|
# lazy load io/console, so it's not loaded when snippet_max_width is set
|
||||||
require "io/console"
|
require "io/console"
|
||||||
IO.console.winsize[1]
|
STDERR.winsize[1] if STDERR.tty?
|
||||||
|
rescue LoadError, NoMethodError, SystemCallError
|
||||||
|
# do not truncate when window size is not available
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ require "did_you_mean"
|
|||||||
require "tempfile"
|
require "tempfile"
|
||||||
|
|
||||||
class ErrorHighlightTest < Test::Unit::TestCase
|
class ErrorHighlightTest < Test::Unit::TestCase
|
||||||
ErrorHighlight::DefaultFormatter.viewport_size = 80
|
ErrorHighlight::DefaultFormatter.snippet_max_width = 80
|
||||||
|
|
||||||
class DummyFormatter
|
class DummyFormatter
|
||||||
def self.message_for(corrections)
|
def self.message_for(corrections)
|
||||||
@ -1287,7 +1287,7 @@ undefined method `time' for #{ ONE_RECV_MESSAGE }
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_errors_on_small_viewports_at_the_end
|
def test_errors_on_small_terminal_window_at_the_end
|
||||||
assert_error_message(NoMethodError, <<~END) do
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
@ -1299,7 +1299,7 @@ undefined method `time' for #{ ONE_RECV_MESSAGE }
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_errors_on_small_viewports_at_the_beginning
|
def test_errors_on_small_terminal_window_at_the_beginning
|
||||||
assert_error_message(NoMethodError, <<~END) do
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
@ -1312,11 +1312,11 @@ undefined method `time' for #{ ONE_RECV_MESSAGE }
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_errors_on_small_viewports_at_the_middle
|
def test_errors_on_small_terminal_window_at_the_middle_near_beginning
|
||||||
assert_error_message(NoMethodError, <<~END) do
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
...000000000000000000000000000000000 + 1.time { 10000000000000000000000000000...
|
100000000000000000000000000000000000000 + 1.time { 1000000000000000000000...
|
||||||
^^^^^
|
^^^^^
|
||||||
END
|
END
|
||||||
|
|
||||||
@ -1324,7 +1324,76 @@ undefined method `time' for #{ ONE_RECV_MESSAGE }
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_errors_on_small_viewports_when_larger_than_viewport
|
def test_errors_on_small_terminal_window_at_the_middle
|
||||||
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
|
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
|
...000000000000000000000000000000000 + 1.time { 10000000000000000000000000000...
|
||||||
|
^^^^^
|
||||||
|
END
|
||||||
|
|
||||||
|
10000000000000000000000000000000000000000000000000000000000000000000000 + 1.time { 1000000000000000000000000000000 }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_errors_on_extremely_small_terminal_window
|
||||||
|
custom_max_width = 30
|
||||||
|
original_max_width = ErrorHighlight::DefaultFormatter.snippet_max_width
|
||||||
|
|
||||||
|
ErrorHighlight::DefaultFormatter.snippet_max_width = custom_max_width
|
||||||
|
|
||||||
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
|
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
|
...00000000 + 1.time { 1000...
|
||||||
|
^^^^^
|
||||||
|
END
|
||||||
|
|
||||||
|
100000000000000 + 1.time { 100000000000000 }
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
ErrorHighlight::DefaultFormatter.snippet_max_width = original_max_width
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_errors_on_terminal_window_smaller_than_min_width
|
||||||
|
custom_max_width = 5
|
||||||
|
original_max_width = ErrorHighlight::DefaultFormatter.snippet_max_width
|
||||||
|
|
||||||
|
ErrorHighlight::DefaultFormatter.snippet_max_width = custom_max_width
|
||||||
|
|
||||||
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
|
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
|
...000 + 1.time {...
|
||||||
|
^^^^^
|
||||||
|
END
|
||||||
|
|
||||||
|
100000000000000 + 1.time { 100000000000000 }
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
ErrorHighlight::DefaultFormatter.snippet_max_width = original_max_width
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_errors_on_terminal_window_when_truncation_is_disabled
|
||||||
|
custom_max_width = nil
|
||||||
|
original_max_width = ErrorHighlight::DefaultFormatter.snippet_max_width
|
||||||
|
|
||||||
|
ErrorHighlight::DefaultFormatter.snippet_max_width = custom_max_width
|
||||||
|
|
||||||
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
|
undefined method `time' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
|
10000000000000000000000000000000000000000000000000000000000000000000000 + 1.time { 1000000000000000000000000000000 }
|
||||||
|
^^^^^
|
||||||
|
END
|
||||||
|
|
||||||
|
10000000000000000000000000000000000000000000000000000000000000000000000 + 1.time { 1000000000000000000000000000000 }
|
||||||
|
end
|
||||||
|
ensure
|
||||||
|
ErrorHighlight::DefaultFormatter.snippet_max_width = original_max_width
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_errors_on_small_terminal_window_when_larger_than_viewport
|
||||||
assert_error_message(NoMethodError, <<~END) do
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
undefined method `timessssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss!' for #{ ONE_RECV_MESSAGE }
|
undefined method `timessssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss!' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
@ -1336,7 +1405,7 @@ undefined method `timessssssssssssssssssssssssssssssssssssssssssssssssssssssssss
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_errors_on_small_viewports_when_exact_size_of_viewport
|
def test_errors_on_small_terminal_window_when_exact_size_of_viewport
|
||||||
assert_error_message(NoMethodError, <<~END) do
|
assert_error_message(NoMethodError, <<~END) do
|
||||||
undefined method `timessssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss!' for #{ ONE_RECV_MESSAGE }
|
undefined method `timessssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss!' for #{ ONE_RECV_MESSAGE }
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user