2021-06-30 06:28:22 +03:00
|
|
|
module ErrorHighlight
|
|
|
|
class DefaultFormatter
|
2024-10-18 02:12:21 +03:00
|
|
|
MIN_SNIPPET_WIDTH = 20
|
|
|
|
|
2021-10-27 05:25:58 +03:00
|
|
|
def self.message_for(spot)
|
2021-06-30 06:28:22 +03:00
|
|
|
# currently only a one-line code snippet is supported
|
2024-10-13 21:40:45 +03:00
|
|
|
return "" unless spot[:first_lineno] == spot[:last_lineno]
|
2024-10-11 01:52:54 +03:00
|
|
|
|
2024-10-13 21:40:45 +03:00
|
|
|
snippet = spot[:snippet]
|
|
|
|
first_column = spot[:first_column]
|
|
|
|
last_column = spot[:last_column]
|
2024-10-18 02:12:21 +03:00
|
|
|
ellipsis = "..."
|
2021-06-30 06:28:22 +03:00
|
|
|
|
2024-10-13 21:40:45 +03:00
|
|
|
# truncate snippet to fit in the viewport
|
2024-10-23 23:55:09 +03:00
|
|
|
if max_snippet_width && snippet.size > max_snippet_width
|
|
|
|
available_width = max_snippet_width - ellipsis.size
|
|
|
|
center = first_column - max_snippet_width / 2
|
2024-10-13 21:40:45 +03:00
|
|
|
|
2024-10-18 02:12:21 +03:00
|
|
|
visible_start = last_column < available_width ? 0 : [center, 0].max
|
2024-10-23 23:55:09 +03:00
|
|
|
visible_end = visible_start + max_snippet_width
|
|
|
|
visible_start = snippet.size - max_snippet_width if visible_end > snippet.size
|
2024-10-13 21:40:45 +03:00
|
|
|
|
2024-10-18 02:12:21 +03:00
|
|
|
prefix = visible_start.positive? ? ellipsis : ""
|
|
|
|
suffix = visible_end < snippet.size ? ellipsis : ""
|
2024-10-13 21:40:45 +03:00
|
|
|
|
|
|
|
snippet = prefix + snippet[(visible_start + prefix.size)...(visible_end - suffix.size)] + suffix
|
|
|
|
snippet << "\n" unless snippet.end_with?("\n")
|
|
|
|
|
2024-10-18 02:12:21 +03:00
|
|
|
first_column -= visible_start
|
2024-10-13 21:40:45 +03:00
|
|
|
last_column = [last_column - visible_start, snippet.size - 1].min
|
2021-06-30 06:28:22 +03:00
|
|
|
end
|
2024-10-13 21:40:45 +03:00
|
|
|
|
|
|
|
indent = snippet[0...first_column].gsub(/[^\t]/, " ")
|
|
|
|
marker = indent + "^" * (last_column - first_column)
|
|
|
|
|
|
|
|
"\n\n#{ snippet }#{ marker }"
|
2021-06-30 06:28:22 +03:00
|
|
|
end
|
2024-10-11 01:52:54 +03:00
|
|
|
|
2024-10-23 23:55:09 +03:00
|
|
|
def self.max_snippet_width
|
2024-10-18 02:12:21 +03:00
|
|
|
return if Ractor.current[:__error_highlight_max_snippet_width__] == :disabled
|
|
|
|
|
|
|
|
Ractor.current[:__error_highlight_max_snippet_width__] ||= terminal_width
|
2024-10-11 01:52:54 +03:00
|
|
|
end
|
|
|
|
|
2024-10-23 23:55:09 +03:00
|
|
|
def self.max_snippet_width=(width)
|
2024-10-18 02:12:21 +03:00
|
|
|
return Ractor.current[:__error_highlight_max_snippet_width__] = :disabled if width.nil?
|
|
|
|
|
|
|
|
width = width.to_i
|
|
|
|
|
|
|
|
if width < MIN_SNIPPET_WIDTH
|
2024-10-23 23:55:09 +03:00
|
|
|
warn "'max_snippet_width' adjusted to minimum value of #{MIN_SNIPPET_WIDTH}."
|
2024-10-18 02:12:21 +03:00
|
|
|
width = MIN_SNIPPET_WIDTH
|
|
|
|
end
|
|
|
|
|
|
|
|
Ractor.current[:__error_highlight_max_snippet_width__] = width
|
2024-10-11 01:52:54 +03:00
|
|
|
end
|
|
|
|
|
2024-10-18 02:12:21 +03:00
|
|
|
def self.terminal_width
|
2024-10-23 23:55:09 +03:00
|
|
|
# lazy load io/console, so it's not loaded when 'max_snippet_width' is set
|
2024-10-11 01:52:54 +03:00
|
|
|
require "io/console"
|
2024-10-18 02:12:21 +03:00
|
|
|
STDERR.winsize[1] if STDERR.tty?
|
|
|
|
rescue LoadError, NoMethodError, SystemCallError
|
|
|
|
# do not truncate when window size is not available
|
2024-10-11 01:52:54 +03:00
|
|
|
end
|
2021-06-30 06:28:22 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.formatter
|
2021-10-27 05:25:58 +03:00
|
|
|
Ractor.current[:__error_highlight_formatter__] || DefaultFormatter
|
2021-06-30 06:28:22 +03:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.formatter=(formatter)
|
2021-10-27 05:25:58 +03:00
|
|
|
Ractor.current[:__error_highlight_formatter__] = formatter
|
2021-06-30 06:28:22 +03:00
|
|
|
end
|
|
|
|
end
|