Skip to content

Commit

Permalink
Separate prompt and input line in rendering
Browse files Browse the repository at this point in the history
Often, only one of prompt and input changes.
Split prompt+input_line to a separate rendering item will improve differential rendering performance.
  • Loading branch information
tompng committed Mar 19, 2024
1 parent 3fa3762 commit 14c63cc
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 13 deletions.
29 changes: 18 additions & 11 deletions lib/reline/line_editor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@ def multiline_off
end
end

private def split_by_width(str, max_width)
Reline::Unicode.split_by_width(str, max_width, @encoding)
private def split_by_width(str, max_width, offset: 0)
Reline::Unicode.split_by_width(str, max_width, @encoding, offset: offset)
end

def current_byte_pointer_cursor
Expand Down Expand Up @@ -336,7 +336,7 @@ def screen_scroll_top
@scroll_partial_screen
end

def wrapped_lines
def wrapped_prompt_lines
with_cache(__method__, @buffer_of_lines.size, modified_lines, prompt_list, screen_width) do |n, lines, prompts, width, prev_cache_key, cached_value|
prev_n, prev_lines, prev_prompts, prev_width = prev_cache_key
cached_wraps = {}
Expand All @@ -347,9 +347,14 @@ def wrapped_lines
end

n.times.map do |i|
prompt = prompts[i]
line = lines[i]
cached_wraps[[prompt, line]] || split_by_width("#{prompt}#{line}", width).first.compact
prompt = prompts[i] || ''
line = lines[i] || ''
if (cached = cached_wraps[[prompt, line]])
next cached
end
*wrapped_prompts, code_line_prompt = split_by_width(prompt, width).first.compact
wrapped_lines = split_by_width(line, width, offset: calculate_width(code_line_prompt)).first.compact
wrapped_prompts.map { |p| [p, ''] } + [[code_line_prompt, wrapped_lines.first]] + wrapped_lines.drop(1).map { |c| ['', c] }
end
end
end
Expand Down Expand Up @@ -392,7 +397,7 @@ def wrapped_cursor_position
prompt_width = calculate_width(prompt_list[@line_index], true)
line_before_cursor = whole_lines[@line_index].byteslice(0, @byte_pointer)
wrapped_line_before_cursor = split_by_width(' ' * prompt_width + line_before_cursor, screen_width).first.compact
wrapped_cursor_y = wrapped_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
wrapped_cursor_y = wrapped_prompt_lines[0...@line_index].sum(&:size) + wrapped_line_before_cursor.size - 1
wrapped_cursor_x = calculate_width(wrapped_line_before_cursor.last)
[wrapped_cursor_x, wrapped_cursor_y]
end
Expand Down Expand Up @@ -456,8 +461,9 @@ def render_differential
wrapped_cursor_x, wrapped_cursor_y = wrapped_cursor_position

rendered_lines = @rendered_screen.lines || []
new_lines = wrapped_lines.flatten[screen_scroll_top, screen_height].map do |l|
[[0, Reline::Unicode.calculate_width(l, true), l]]
new_lines = wrapped_prompt_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]]
end
if @menu_info
@menu_info.list.sort!.each do |item|
Expand All @@ -473,7 +479,8 @@ def render_differential
y_range.each do |row|
next if row < 0 || row >= screen_height
dialog_rows = new_lines[row] ||= []
dialog_rows[index + 1] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
# index 0 is for prompt, index 1 is for line, index 2.. is for dialog
dialog_rows[index + 2] = [x_range.begin, dialog.width, dialog.contents[row - y_range.begin]]
end
end

Expand Down Expand Up @@ -511,7 +518,7 @@ def render_differential
end

def current_row
wrapped_lines.flatten[wrapped_cursor_y]
wrapped_prompt_lines.flatten(1)[editor_cursor_y].join
end

def upper_space_height(wrapped_cursor_y)
Expand Down
4 changes: 2 additions & 2 deletions lib/reline/unicode.rb
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ def self.calculate_width(str, allow_escape_code = false)
end
end

def self.split_by_width(str, max_width, encoding = str.encoding)
def self.split_by_width(str, max_width, encoding = str.encoding, offset: 0)
lines = [String.new(encoding: encoding)]
height = 1
width = 0
width = offset
rest = str.encode(Encoding::UTF_8)
in_zero_width = false
seq = String.new(encoding: encoding)
Expand Down

0 comments on commit 14c63cc

Please sign in to comment.