Skip to content
Open
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
24 changes: 13 additions & 11 deletions lib/reline/line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,8 @@ def calculate_overlay_levels(overlay_levels)
end

def render_line_differential(old_items, new_items)
old_levels = calculate_overlay_levels(old_items.zip(new_items).each_with_index.map {|((x, w, c), (nx, _nw, nc)), i| [x, w, c == nc && x == nx ? i : -1] if x }.compact)
new_levels = calculate_overlay_levels(new_items.each_with_index.map { |(x, w), i| [x, w, i] if x }.compact).take(screen_width)
old_levels = calculate_overlay_levels(old_items.map { |k, x, w, c| _, nx, _, nc = new_items.assoc(k); [x, w, c == nc && x == nx ? k : :outdated] }).take(screen_width)
new_levels = calculate_overlay_levels(new_items.map { |k, x, w| [x, w, k] }).take(screen_width)
base_x = 0
new_levels.zip(old_levels).chunk { |n, o| n == o ? :skip : n || :blank }.each do |level, chunk|
width = chunk.size
Expand All @@ -415,7 +415,7 @@ def render_line_differential(old_items, new_items)
Reline::IOGate.move_cursor_column base_x
Reline::IOGate.write "#{Reline::IOGate.reset_color_sequence}#{' ' * width}"
else
x, w, content = new_items[level]
_, x, w, content = new_items.assoc(level)
cover_begin = base_x != 0 && new_levels[base_x - 1] == level
cover_end = new_levels[base_x + width] == level
pos = 0
Expand Down Expand Up @@ -474,39 +474,41 @@ def render
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position
new_lines = wrapped_prompt_and_input_lines.flatten(1)[screen_scroll_top, screen_height].map do |prompt, line|
prompt_width = Reline::Unicode.calculate_width(prompt, true)
[[0, prompt_width, prompt], [prompt_width, Reline::Unicode.calculate_width(line, true), line]]
[
[:builtin_prompt, 0, prompt_width, prompt],
[:builtin_line, prompt_width, Reline::Unicode.calculate_width(line, true), line]
]
end

# Add rprompt to the first visible line if set and there's room
if @rprompt && !@rprompt.empty? && new_lines[0]
rprompt_width = Reline::Unicode.calculate_width(@rprompt, true)
right_col = screen_width - rprompt_width
first_line = new_lines[0]
# Calculate the end of the current content (prompt + input)
content_end = first_line.sum { |_, width, _| width }
content_end = first_line.map { |_, x, width, _| x + width }.max
# Only show rprompt if there's at least 1 char gap between content and rprompt
if right_col > content_end
first_line << [right_col, rprompt_width, @rprompt]
first_line << [:builtin_rprompt, right_col, rprompt_width, @rprompt]
end
end

if @menu_info
@menu_info.lines(screen_width).each do |item|
new_lines << [[0, Reline::Unicode.calculate_width(item), item]]
new_lines << [[:builtin_menu, 0, Reline::Unicode.calculate_width(item), item]]
end
@menu_info = nil # TODO: do not change state here
end

@dialogs.each_with_index do |dialog, index|
@dialogs.each do |dialog|
next unless dialog.contents

identifier = :"dialog_#{dialog.name}"
x_range, y_range = dialog_range dialog, wrapped_cursor_y - screen_scroll_top
y_range.each do |row|
next if row < 0 || row >= screen_height

dialog_rows = new_lines[row] ||= []
# index 0 is for prompt, index 1 is for line, index 2 is for rprompt, index 3.. is for dialog
dialog_rows[index + 3] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
dialog_rows << [identifier, x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
end
end

Expand Down
50 changes: 25 additions & 25 deletions test/reline/test_line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -130,107 +130,107 @@ def teardown

def test_line_increase_decrease
assert_output '[COL_0]bb' do
@line_editor.render_line_differential([[0, 1, 'a']], [[0, 2, 'bb']])
@line_editor.render_line_differential([[:line, 0, 1, 'a']], [[:line, 0, 2, 'bb']])
end

assert_output '[COL_0]b[COL_1][ERASE]' do
@line_editor.render_line_differential([[0, 2, 'aa']], [[0, 1, 'b']])
@line_editor.render_line_differential([[:line, 0, 2, 'aa']], [[:line, 0, 1, 'b']])
end
end

def test_dialog_appear_disappear
assert_output '[COL_3]dialog' do
@line_editor.render_line_differential([[0, 1, 'a']], [[0, 1, 'a'], [3, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 1, 'a']], [[:line, 0, 1, 'a'], [:dialog, 3, 6, 'dialog']])
end

assert_output '[COL_3]dialog' do
@line_editor.render_line_differential([[0, 10, 'a' * 10]], [[0, 10, 'a' * 10], [3, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 10, 'a' * 10]], [[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'dialog']])
end

assert_output '[COL_1][ERASE]' do
@line_editor.render_line_differential([[0, 1, 'a'], [3, 6, 'dialog']], [[0, 1, 'a']])
@line_editor.render_line_differential([[:line, 0, 1, 'a'], [:dialog, 3, 6, 'dialog']], [[:line, 0, 1, 'a']])
end

assert_output '[COL_3]aaaaaa' do
@line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'a' * 10]])
@line_editor.render_line_differential([[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'dialog']], [[:line, 0, 10, 'a' * 10]])
end
end

def test_dialog_change
assert_output '[COL_3]DIALOG' do
@line_editor.render_line_differential([[0, 2, 'a'], [3, 6, 'dialog']], [[0, 2, 'a'], [3, 6, 'DIALOG']])
@line_editor.render_line_differential([[:line, 0, 2, 'a'], [:dialog, 3, 6, 'dialog']], [[:line, 0, 2, 'a'], [:dialog, 3, 6, 'DIALOG']])
end

assert_output '[COL_3]DIALOG' do
@line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'a' * 10], [3, 6, 'DIALOG']])
@line_editor.render_line_differential([[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'dialog']], [[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'DIALOG']])
end
end

def test_update_under_dialog
assert_output '[COL_0]b[COL_1] ' do
@line_editor.render_line_differential([[0, 2, 'aa'], [4, 6, 'dialog']], [[0, 1, 'b'], [4, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 2, 'aa'], [:dialog, 4, 6, 'dialog']], [[:line, 0, 1, 'b'], [:dialog, 4, 6, 'dialog']])
end

assert_output '[COL_0]bbb[COL_9]b' do
@line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'b' * 10], [3, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'dialog']], [[:line, 0, 10, 'b' * 10], [:dialog, 3, 6, 'dialog']])
end

assert_output '[COL_0]b[COL_1] [COL_9][ERASE]' do
@line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 1, 'b'], [3, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'dialog']], [[:line, 0, 1, 'b'], [:dialog, 3, 6, 'dialog']])
end
end

def test_dialog_move
assert_output '[COL_3]dialog[COL_9][ERASE]' do
@line_editor.render_line_differential([[0, 1, 'a'], [4, 6, 'dialog']], [[0, 1, 'a'], [3, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 1, 'a'], [:dialog, 4, 6, 'dialog']], [[:line, 0, 1, 'a'], [:dialog, 3, 6, 'dialog']])
end

assert_output '[COL_4] [COL_5]dialog' do
@line_editor.render_line_differential([[0, 1, 'a'], [4, 6, 'dialog']], [[0, 1, 'a'], [5, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 1, 'a'], [:dialog, 4, 6, 'dialog']], [[:line, 0, 1, 'a'], [:dialog, 5, 6, 'dialog']])
end

assert_output '[COL_2]dialog[COL_8]a' do
@line_editor.render_line_differential([[0, 10, 'a' * 10], [3, 6, 'dialog']], [[0, 10, 'a' * 10], [2, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'dialog']], [[:line, 0, 10, 'a' * 10], [:dialog, 2, 6, 'dialog']])
end

assert_output '[COL_2]a[COL_3]dialog' do
@line_editor.render_line_differential([[0, 10, 'a' * 10], [2, 6, 'dialog']], [[0, 10, 'a' * 10], [3, 6, 'dialog']])
@line_editor.render_line_differential([[:line, 0, 10, 'a' * 10], [:dialog, 2, 6, 'dialog']], [[:line, 0, 10, 'a' * 10], [:dialog, 3, 6, 'dialog']])
end
end

def test_multibyte
base = [0, 12, '一二三一二三']
left = [0, 3, 'LLL']
right = [9, 3, 'RRR']
front = [3, 6, 'FFFFFF']
base = [:base, 0, 12, '一二三一二三']
left = [:left, 0, 3, 'LLL']
right = [:right, 9, 3, 'RRR']
front = [:front, 3, 6, 'FFFFFF']
# 一 FFFFFF 三
# 一二三一二三
assert_output '[COL_2]二三一二' do
@line_editor.render_line_differential([base, front], [base, nil])
@line_editor.render_line_differential([base, front], [base])
end

# LLLFFFFFF 三
# LLL 三一二三
assert_output '[COL_3] 三一二' do
@line_editor.render_line_differential([base, left, front], [base, left, nil])
@line_editor.render_line_differential([base, left, front], [base, left])
end

# 一 FFFFFFRRR
# 一二三一 RRR
assert_output '[COL_2]二三一 ' do
@line_editor.render_line_differential([base, right, front], [base, right, nil])
@line_editor.render_line_differential([base, right, front], [base, right])
end

# LLLFFFFFFRRR
# LLL 三一 RRR
assert_output '[COL_3] 三一 ' do
@line_editor.render_line_differential([base, left, right, front], [base, left, right, nil])
@line_editor.render_line_differential([base, left, right, front], [base, left, right])
end
end

def test_complicated
state_a = [nil, [19, 7, 'bbbbbbb'], [15, 8, 'cccccccc'], [10, 5, 'ddddd'], [18, 4, 'eeee'], [1, 3, 'fff'], [17, 2, 'gg'], [7, 1, 'h']]
state_b = [[5, 9, 'aaaaaaaaa'], nil, [15, 8, 'cccccccc'], nil, [18, 4, 'EEEE'], [25, 4, 'ffff'], [17, 2, 'gg'], [2, 2, 'hh']]
state_a = [[:b, 19, 7, 'bbbbbbb'], [:c, 15, 8, 'cccccccc'], [:d, 10, 5, 'ddddd'], [:e, 18, 4, 'eeee'], [:f, 1, 3, 'fff'], [:g, 17, 2, 'gg'], [:h, 7, 1, 'h']]
state_b = [[:a, 5, 9, 'aaaaaaaaa'], [:c, 15, 8, 'cccccccc'], [:e, 18, 4, 'EEEE'], [:f, 25, 4, 'ffff'], [:g, 17, 2, 'gg'], [:h, 2, 2, 'hh']]
# state_a: " fff h dddddccggeeecbbb"
# state_b: " hh aaaaaaaaa ccggEEEc ffff"

Expand Down
Loading