Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions lib/reline/line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -641,13 +641,6 @@ def add_dialog_proc(name, p, context = nil)
render_dialog_changes(changes, cursor_column)
end

private def padding_space_with_escape_sequences(str, width)
padding_width = width - calculate_width(str, true)
# padding_width should be only positive value. But macOS and Alacritty returns negative value.
padding_width = 0 if padding_width < 0
str + (' ' * padding_width)
end

private def range_subtract(base_ranges, subtract_ranges)
indices = base_ranges.flat_map(&:to_a).uniq.sort - subtract_ranges.flat_map(&:to_a)
chunks = indices.chunk_while { |a, b| a + 1 == b }
Expand Down Expand Up @@ -723,7 +716,14 @@ def add_dialog_proc(name, p, context = nil)
restore_ranges.each do |range|
col = range.begin
width = range.end - range.begin
s = padding_space_with_escape_sequences(Reline::Unicode.take_range(line, col, width), width)
s, col = Reline::Unicode.take_mbchar_range(
line,
col,
width,
cover_begin: !new_x_ranges&.any? { |new_range| new_range.include? range.begin - 1 },
cover_end: !new_x_ranges&.any? { |new_range| new_range.include? range.end },
padding: true
)
Reline::IOGate.move_cursor_column(col)
@output.write "\e[0m#{s}\e[0m"
end
Expand Down Expand Up @@ -820,7 +820,7 @@ def add_dialog_proc(name, p, context = nil)
bg_color = dialog_render_info.bg_color
end
str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width)
str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width)
str, = Reline::Unicode.take_mbchar_range(item, 0, str_width, padding: true)
colored_content = "\e[#{bg_color}m\e[#{fg_color}m#{str}"
if scrollbar_pos
color_seq = "\e[37m"
Expand Down
30 changes: 27 additions & 3 deletions lib/reline/unicode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -193,16 +193,24 @@ def self.split_by_width(str, max_width, encoding = str.encoding)

# Take a chunk of a String cut by width with escape sequences.
def self.take_range(str, start_col, max_width)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is not used now, but I want to leave it as is because this method might be useful to use it from IRB for string truncation in the future.
If we are going to use it from IRB, we should not release Reline that does not have Reline::Unicode.take_range

take_mbchar_range(str, start_col, max_width).first
end

def self.take_mbchar_range(str, start_col, width, cover_begin: false, cover_end: false, padding: false)
chunk = String.new(encoding: str.encoding)
total_width = 0
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
chunk_start_col = nil
chunk_end_col = nil
rest.scan(WIDTH_SCANNER) do |non_printing_start, non_printing_end, csi, osc, gc|
case
when non_printing_start
in_zero_width = true
chunk << NON_PRINTING_START
when non_printing_end
in_zero_width = false
chunk << NON_PRINTING_END
when csi
chunk << csi
when osc
Expand All @@ -212,13 +220,29 @@ def self.take_range(str, start_col, max_width)
chunk << gc
else
mbchar_width = get_mbchar_width(gc)
prev_width = total_width
total_width += mbchar_width
break if (start_col + max_width) < total_width
chunk << gc if start_col < total_width
break if !cover_end && total_width > start_col + width
if cover_begin ? start_col < total_width : start_col <= prev_width
if padding && chunk_start_col.nil? && start_col < prev_width
chunk << ' ' * (prev_width - start_col)
chunk_start_col = start_col
end
chunk << gc
chunk_start_col ||= prev_width
chunk_end_col = total_width
end
break if total_width >= start_col + width
end
end
end
chunk
chunk_start_col ||= start_col
chunk_end_col ||= start_col
if padding && chunk_end_col < start_col + width
chunk << ' ' * (start_col + width - chunk_end_col)
chunk_end_col = start_col + width
end
[chunk, chunk_start_col, chunk_end_col - chunk_start_col]
end

def self.get_next_mbchar_size(line, byte_pointer)
Expand Down
24 changes: 22 additions & 2 deletions test/reline/test_unicode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def test_split_by_width
def test_take_range
assert_equal 'cdef', Reline::Unicode.take_range('abcdefghi', 2, 4)
assert_equal 'あde', Reline::Unicode.take_range('abあdef', 2, 4)
assert_equal 'zerocdef', Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
assert_equal 'bzerocde', Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
assert_equal "\1zero\2cdef", Reline::Unicode.take_range("ab\1zero\2cdef", 2, 4)
assert_equal "b\1zero\2cde", Reline::Unicode.take_range("ab\1zero\2cdef", 1, 4)
assert_equal "\e[31mcd\e[42mef", Reline::Unicode.take_range("\e[31mabcd\e[42mefg", 2, 4)
assert_equal "\e]0;1\acd", Reline::Unicode.take_range("ab\e]0;1\acd", 2, 3)
assert_equal 'いう', Reline::Unicode.take_range('あいうえお', 2, 4)
Expand All @@ -62,4 +62,24 @@ def test_calculate_width
assert_equal 10, Reline::Unicode.calculate_width('あいうえお')
assert_equal 10, Reline::Unicode.calculate_width('あいうえお', true)
end

def test_take_mbchar_range
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4)
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, padding: true)
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_begin: true)
assert_equal ['cdef', 2, 4], Reline::Unicode.take_mbchar_range('abcdefghi', 2, 4, cover_end: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, padding: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_begin: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 2, 4, cover_end: true)
assert_equal ['う', 4, 2], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4)
assert_equal [' う ', 3, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, padding: true)
assert_equal ['いう', 2, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true)
assert_equal ['うえ', 4, 4], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true)
assert_equal ['いう ', 2, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_begin: true, padding: true)
assert_equal [' うえ', 3, 5], Reline::Unicode.take_mbchar_range('あいうえお', 3, 4, cover_end: true, padding: true)
assert_equal [' うえお ', 3, 10], Reline::Unicode.take_mbchar_range('あいうえお', 3, 10, padding: true)
assert_equal ["\e[31mc\1ABC\2d\e[0mef", 2, 4], Reline::Unicode.take_mbchar_range("\e[31mabc\1ABC\2d\e[0mefghi", 2, 4)
assert_equal ["\e[47m い ", 1, 4], Reline::Unicode.take_mbchar_range("\e[47mあいうえお\e[0m", 1, 4, padding: true)
end
end
19 changes: 19 additions & 0 deletions test/reline/yamatanooroti/test_rendering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,25 @@ def test_rerender_multiple_dialog
EOC
end

def test_autocomplete_rerender_fullwidth_under_dialog
start_terminal(20, 40, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete}, startup_message: 'Multiline REPL.')
write("def hoge\n\n あいうえおabかきくけこab\n aあいうえおabかきくけこb\n abあいうえおabかきくけこ\C-p\C-p\C-p ")
write('S')
write('t')
write(' ')
write('S')
write('t')
close
assert_screen(<<~'EOC')
Multiline REPL.
prompt> def hoge
prompt> St St
prompt> あいうえおabStringけこab
prompt> aあいうえおaStruct けこb
prompt> abあいうえおabかきくけこ
EOC
end

def test_autocomplete_long_with_scrollbar
start_terminal(20, 30, %W{ruby -I#{@pwd}/lib #{@pwd}/test/reline/yamatanooroti/multiline_repl --autocomplete-long}, startup_message: 'Multiline REPL.')
write('S')
Expand Down