From 5da6a8d851f58b393d7bd682aa9d8e6e9e86b809 Mon Sep 17 00:00:00 2001 From: aycabta Date: Tue, 31 Aug 2021 15:09:04 +0900 Subject: [PATCH] Fix Reline::Unicode.take_range as it was not fully functional --- lib/reline.rb | 2 +- lib/reline/line_editor.rb | 42 +++++++++++++++++++++++---------------- lib/reline/unicode.rb | 11 +++++----- 3 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/reline.rb b/lib/reline.rb index cf668c79e3..18ccce2058 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -18,7 +18,7 @@ class ConfigEncodingConversionError < StandardError; end Key = Struct.new('Key', :char, :combined_char, :with_meta) CursorPos = Struct.new(:x, :y) - DialogRenderInfo = Struct.new(:pos, :contents, :pointer, :bg_color, :height, keyword_init: true) + DialogRenderInfo = Struct.new(:pos, :contents, :pointer, :bg_color, :width, :height, keyword_init: true) class Core ATTR_READER_NAMES = %i( diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index f5c45d1c7b..a42d9ae81d 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -538,12 +538,13 @@ def call end class Dialog - attr_reader :name, :contents, :contents_width + attr_reader :name, :contents, :width attr_accessor :scroll_top, :column, :vertical_offset, :lines_backup def initialize(name, proc_scope) @name = name @proc_scope = proc_scope + @width = nil @scroll_top = 0 end @@ -551,9 +552,15 @@ def set_cursor_pos(col, row) @proc_scope.set_cursor_pos(col, row) end + def width=(v) + @width = v + end + def contents=(contents) @contents = contents - @contents_width = contents.map{ |line| Reline::Unicode.calculate_width(line, true) }.max if contents + if contents and @width.nil? + @width = contents.map{ |line| Reline::Unicode.calculate_width(line, true) }.max + end end def call @@ -582,6 +589,7 @@ def add_dialog_proc(name, p, context = nil) end dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from) dialog_render_info = dialog.call + dialog.width = dialog_render_info.width if dialog_render_info.width old_dialog = dialog.clone if dialog_render_info and dialog_render_info.contents and not dialog_render_info.contents.empty? height = dialog_render_info.height || DIALOG_HEIGHT @@ -615,7 +623,7 @@ def add_dialog_proc(name, p, context = nil) upper_space = @first_line_started_from - @started_from lower_space = @highest_in_all - @first_line_started_from - @started_from - 1 dialog.column = dialog_render_info.pos.x - diff = (dialog.column + dialog.contents_width) - (@screen_size.last - 1) + diff = (dialog.column + dialog.width) - (@screen_size.last - 1) if diff > 0 dialog.column -= diff end @@ -644,7 +652,7 @@ def add_dialog_proc(name, p, context = nil) bg_color = '46' end end - @output.write "\e[#{bg_color}m%-#{dialog.contents_width}s\e[49m" % item.slice(0, dialog.contents_width) + @output.write "\e[#{bg_color}m%-#{dialog.width}s\e[49m" % item.slice(0, dialog.width) Reline::IOGate.move_cursor_column(dialog.column) move_cursor_down(1) if i < (dialog.contents.size - 1) end @@ -685,11 +693,11 @@ def add_dialog_proc(name, p, context = nil) line_num.times do |i| Reline::IOGate.move_cursor_column(old_dialog.column) if visual_lines[start + i].nil? - s = ' ' * dialog.contents_width + s = ' ' * dialog.width else - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.contents_width) + s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width) end - @output.write "\e[39m\e[49m%-#{dialog.contents_width}s\e[39m\e[49m" % s + @output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % s move_cursor_down(1) if i < (line_num - 1) end move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff) @@ -702,11 +710,11 @@ def add_dialog_proc(name, p, context = nil) line_num.times do |i| Reline::IOGate.move_cursor_column(old_dialog.column) if visual_lines[start + i].nil? - s = ' ' * dialog.contents_width + s = ' ' * dialog.width else - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.contents_width) + s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, dialog.width) end - @output.write "\e[39m\e[49m%-#{dialog.contents_width}s\e[39m\e[49m" % s + @output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % s move_cursor_down(1) if i < (line_num - 1) end move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff) @@ -729,20 +737,20 @@ def add_dialog_proc(name, p, context = nil) end move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff) end - if (old_dialog.column + old_dialog.contents_width) > (dialog.column + dialog.contents_width) + if (old_dialog.column + old_dialog.width) > (dialog.column + dialog.width) # rerender right move_cursor_down(old_dialog.vertical_offset + y_diff) - width = (old_dialog.column + old_dialog.contents_width) - (dialog.column + dialog.contents_width) + width = (old_dialog.column + old_dialog.width) - (dialog.column + dialog.width) start = visual_start + old_dialog.vertical_offset line_num = old_dialog.contents.size line_num.times do |i| - Reline::IOGate.move_cursor_column(old_dialog.column + dialog.contents_width) + Reline::IOGate.move_cursor_column(old_dialog.column + dialog.width) if visual_lines[start + i].nil? s = ' ' * width else - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.contents_width, width) + s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width) end - Reline::IOGate.move_cursor_column(dialog.column + dialog.contents_width) + Reline::IOGate.move_cursor_column(dialog.column + dialog.width) @output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s move_cursor_down(1) if i < (line_num - 1) end @@ -780,10 +788,10 @@ def add_dialog_proc(name, p, context = nil) dialog_vertical_size.times do |i| if i < visual_lines_under_dialog.size Reline::IOGate.move_cursor_column(0) - @output.write "\e[39m\e[49m%-#{dialog.contents_width}s\e[39m\e[49m" % visual_lines_under_dialog[i] + @output.write "\e[39m\e[49m%-#{dialog.width}s\e[39m\e[49m" % visual_lines_under_dialog[i] else Reline::IOGate.move_cursor_column(dialog.column) - @output.write "\e[39m\e[49m#{' ' * dialog.contents_width}\e[39m\e[49m" + @output.write "\e[39m\e[49m#{' ' * dialog.width}\e[39m\e[49m" end Reline::IOGate.erase_after_cursor move_cursor_down(1) if i < (dialog_vertical_size - 1) diff --git a/lib/reline/unicode.rb b/lib/reline/unicode.rb index 3bb08fac48..d111a849c4 100644 --- a/lib/reline/unicode.rb +++ b/lib/reline/unicode.rb @@ -186,9 +186,9 @@ def self.split_by_width(str, max_width, encoding = str.encoding) end # Take a chunk of a String with escape sequences. - def self.take_range(str, col, length, encoding = str.encoding) + def self.take_range(str, start_col, max_width, encoding = str.encoding) chunk = String.new(encoding: encoding) - width = 0 + total_width = 0 rest = str.encode(Encoding::UTF_8) in_zero_width = false rest.scan(WIDTH_SCANNER) do |gc| @@ -206,9 +206,10 @@ def self.take_range(str, col, length, encoding = str.encoding) if in_zero_width chunk << gc else - width = get_mbchar_width(gc) - break if (width + length) <= col - chunk << gc if col <= width + mbchar_width = get_mbchar_width(gc) + total_width += mbchar_width + break if (start_col + max_width) < total_width + chunk << gc if start_col < total_width end end end