ruby-changes:67316
From: aycabta <ko1@a...>
Date: Sun, 29 Aug 2021 20:30:55 +0900 (JST)
Subject: [ruby-changes:67316] 8d4370b066 (master): [ruby/reline] Support for multiple dialog rendering
https://git.ruby-lang.org/ruby.git/commit/?id=8d4370b066 From 8d4370b066fd9ff7e6f6c9ee0c5035ad5c81050e Mon Sep 17 00:00:00 2001 From: aycabta <aycabta@g...> Date: Fri, 27 Aug 2021 14:58:05 +0900 Subject: [ruby/reline] Support for multiple dialog rendering https://github.com/ruby/reline/commit/f589fab718 --- lib/reline.rb | 18 +++-- lib/reline/line_editor.rb | 172 +++++++++++++++++++++++++++------------------- 2 files changed, 115 insertions(+), 75 deletions(-) diff --git a/lib/reline.rb b/lib/reline.rb index 8c72d69..81c5f9e 100644 --- a/lib/reline.rb +++ b/lib/reline.rb @@ -34,7 +34,7 @@ module Reline https://github.com/ruby/ruby/blob/trunk/lib/reline.rb#L34 auto_indent_proc pre_input_hook dig_perfect_match_proc - dialog_proc + dialog_proc_list ).each(&method(:attr_reader)) attr_accessor :config @@ -48,6 +48,7 @@ module Reline https://github.com/ruby/ruby/blob/trunk/lib/reline.rb#L48 yield self @completion_quote_character = nil @bracketed_paste_finished = false + @dialog_proc_list = [] end def encoding @@ -131,9 +132,10 @@ module Reline https://github.com/ruby/ruby/blob/trunk/lib/reline.rb#L132 @dig_perfect_match_proc = p end - def dialog_proc=(p) + def add_dialog_proc(name_sym, p) raise ArgumentError unless p.respond_to?(:call) or p.nil? - @dialog_proc = p + raise ArgumentError unless name_sym.instance_of?(Symbol) + @dialog_proc_list << [name_sym, p] end def input=(val) @@ -206,14 +208,15 @@ module Reline https://github.com/ruby/ruby/blob/trunk/lib/reline.rb#L208 else y = 0 end - [Reline::CursorPos.new(x, y), result, pointer] + cursor_pos_to_render = Reline::CursorPos.new(x, y) + [cursor_pos_to_render, result, pointer] } def readmultiline(prompt = '', add_hist = false, &confirm_multiline_termination) unless confirm_multiline_termination raise ArgumentError.new('#readmultiline needs block to confirm multiline termination') end - @dialog_proc = DEFAULT_DIALOG_PROC_AUTOCOMPLETE + add_dialog_proc(:autocomplete, DEFAULT_DIALOG_PROC_AUTOCOMPLETE) inner_readline(prompt, add_hist, true, &confirm_multiline_termination) whole_buffer = line_editor.whole_buffer.dup @@ -269,7 +272,10 @@ module Reline https://github.com/ruby/ruby/blob/trunk/lib/reline.rb#L272 line_editor.auto_indent_proc = auto_indent_proc line_editor.dig_perfect_match_proc = dig_perfect_match_proc line_editor.pre_input_hook = pre_input_hook - line_editor.dialog_proc = dialog_proc + @dialog_proc_list.each do |d| + name_sym, dialog_proc = d + line_editor.add_dialog_proc(name_sym, dialog_proc) + end unless config.test_mode config.read diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb index 25d6ba4..c888474 100644 --- a/lib/reline/line_editor.rb +++ b/lib/reline/line_editor.rb @@ -249,10 +249,7 @@ class Reline::LineEditor https://github.com/ruby/ruby/blob/trunk/lib/reline/line_editor.rb#L249 @drop_terminate_spaces = false @in_pasting = false @auto_indent_proc = nil - @dialog_column = nil - @dialog_vertical_offset = nil - @dialog_contents = nil - @dialog_lines_backup = nil + @dialogs = [] reset_line end @@ -521,75 +518,98 @@ class Reline::LineEditor https://github.com/ruby/ruby/blob/trunk/lib/reline/line_editor.rb#L518 end end - def dialog_proc=(p) - @dialog_proc_scope = DialogProcScope.new(self, p) - @dialog_proc = p + class Dialog + attr_reader :name + attr_accessor :column, :vertical_offset, :contents, :lines_backup + + def initialize(name, proc_scope) + @name = name + @proc_scope = proc_scope + end + + def set_cursor_pos(col, row) + @proc_scope.set_cursor_pos(col, row) + end + + def call + @proc_scope.call + end end - DIALOG_HEIGHT = 5 + def add_dialog_proc(name, p) + return if @dialogs.any? { |d| d.name == name } + @dialogs << Dialog.new(name, DialogProcScope.new(self, p)) + end + + DIALOG_HEIGHT = 20 DIALOG_WIDTH = 40 private def render_dialog(cursor_column) - return if @dialog_proc_scope.nil? + @dialogs.each do |dialog| + render_each_dialog(dialog, cursor_column) + end + end + + private def render_each_dialog(dialog, cursor_column) if @in_pasting - @dialog_contents = nil + dialog.contents = nil return end - @dialog_proc_scope.set_cursor_pos(cursor_column, @first_line_started_from + @started_from) - pos, result, pointer = @dialog_proc_scope.call - old_dialog_contents = @dialog_contents - old_dialog_column = @dialog_column - old_dialog_vertical_offset = @dialog_vertical_offset + dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from) + pos, result, pointer = dialog.call + old_dialog_contents = dialog.contents + old_dialog_column = dialog.column + old_dialog_vertical_offset = dialog.vertical_offset if result and not result.empty? - @dialog_contents = result - @dialog_contents = @dialog_contents[0...DIALOG_HEIGHT] if @dialog_contents.size > DIALOG_HEIGHT + dialog.contents = result + dialog.contents = dialog.contents[0...DIALOG_HEIGHT] if dialog.contents.size > DIALOG_HEIGHT else - @dialog_lines_backup = { + dialog.lines_backup = { lines: modify_lines(whole_lines), line_index: @line_index, first_line_started_from: @first_line_started_from, started_from: @started_from, byte_pointer: @byte_pointer } - clear_dialog - @dialog_contents = nil + clear_each_dialog(dialog) + dialog.contents = nil return end upper_space = @first_line_started_from - @started_from lower_space = @highest_in_all - @first_line_started_from - @started_from - 1 - @dialog_column = pos.x - diff = (@dialog_column + DIALOG_WIDTH) - (@screen_size.last - 1) + dialog.column = pos.x + diff = (dialog.column + DIALOG_WIDTH) - (@screen_size.last - 1) if diff > 0 - @dialog_column -= diff + dialog.column -= diff end if (lower_space + @rest_height) >= DIALOG_HEIGHT - @dialog_vertical_offset = pos.y + 1 + dialog.vertical_offset = pos.y + 1 elsif upper_space >= DIALOG_HEIGHT - @dialog_vertical_offset = pos.y + -(DIALOG_HEIGHT + 1) + dialog.vertical_offset = pos.y + -(DIALOG_HEIGHT + 1) else if (lower_space + @rest_height) < DIALOG_HEIGHT scroll_down(DIALOG_HEIGHT) move_cursor_up(DIALOG_HEIGHT) end - @dialog_vertical_offset = pos.y + 1 + dialog.vertical_offset = pos.y + 1 end Reline::IOGate.hide_cursor - reset_dialog(old_dialog_contents, old_dialog_column, old_dialog_vertical_offset) - move_cursor_down(@dialog_vertical_offset) - Reline::IOGate.move_cursor_column(@dialog_column) - @dialog_contents.each_with_index do |item, i| + reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset) + move_cursor_down(dialog.vertical_offset) + Reline::IOGate.move_cursor_column(dialog.column) + dialog.contents.each_with_index do |item, i| if i == pointer bg_color = '45' else bg_color = '46' end @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) + Reline::IOGate.move_cursor_column(dialog.column) + move_cursor_down(1) if i < (dialog.contents.size - 1) end Reline::IOGate.move_cursor_column(cursor_column) - move_cursor_up(@dialog_vertical_offset + @dialog_contents.size - 1) + move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1) Reline::IOGate.show_cursor - @dialog_lines_backup = { + dialog.lines_backup = { lines: modify_lines(whole_lines), line_index: @line_index, first_line_started_from: @first_line_started_from, @@ -598,53 +618,61 @@ class Reline::LineEditor https://github.com/ruby/ruby/blob/trunk/lib/reline/line_editor.rb#L618 } end - private def reset_dialog(old_dialog_contents, old_dialog_column, old_dialog_vertical_offset) - return if @dialog_lines_backup.nil? or old_dialog_contents.nil? - prompt, prompt_width, prompt_list = check_multiline_prompt(@dialog_lines_backup[:lines], prompt) + private def reset_dialog(dialog, old_dialog_contents, old_dialog_column, old_dialog_vertical_offset) + return if dialog.lines_backup.nil? or old_dialog_contents.nil? + prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt) visual_lines = [] visual_start = nil - @dialog_lines_backup[:lines].each_with_index { |l, i| + dialog.lines_backup[:lines].each_with_index { |l, i| pr = prompt_list ? prompt_list[i] : prompt vl, _ = split_by_width(pr + l, @screen_size.last) vl.compact! - if i == @dialog_lines_backup[:line_index] - visual_start = visual_lines.size + @dialog_lines_backup[:started_from] + if i == dialog.lines_backup[:line_index] + visual_start = visual_lines.size + dialog.lines_backup[:started_from] end visual_lines.concat(vl) } - old_y = @dialog_lines_backup[:first_line_started_from] + @dialog_lines_backup[:started_from] + old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from] y = @first_line_started_from + @started_from y_diff = y - old_y - if (old_y + old_dialog_vertical_offset) < (y + @dialog_vertical_offset) + if (old_y + old_dialog_vertical_offset) < (y + dialog.vertical_offset) # rerender top move_cursor_down(old_dialog_vertical_offset - y_diff) start = visual_start + old_dialog_vertical_offset - line_num = @dialog_vertical_offset - old_dialog_ve (... truncated) -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/