mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[ruby/reline] Support for multiple dialog rendering
https://github.com/ruby/reline/commit/f589fab718
This commit is contained in:
parent
acffb0b068
commit
8d4370b066
2 changed files with 115 additions and 75 deletions
|
@ -34,7 +34,7 @@ module Reline
|
|||
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
|
|||
yield self
|
||||
@completion_quote_character = nil
|
||||
@bracketed_paste_finished = false
|
||||
@dialog_proc_list = []
|
||||
end
|
||||
|
||||
def encoding
|
||||
|
@ -131,9 +132,10 @@ module Reline
|
|||
@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
|
|||
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
|
|||
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
|
||||
|
|
|
@ -249,10 +249,7 @@ class Reline::LineEditor
|
|||
@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
|
|||
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
|
||||
|
||||
DIALOG_HEIGHT = 5
|
||||
def set_cursor_pos(col, row)
|
||||
@proc_scope.set_cursor_pos(col, row)
|
||||
end
|
||||
|
||||
def call
|
||||
@proc_scope.call
|
||||
end
|
||||
end
|
||||
|
||||
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
|
|||
}
|
||||
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_vertical_offset
|
||||
line_num = dialog.vertical_offset - old_dialog_vertical_offset
|
||||
line_num.times do |i|
|
||||
Reline::IOGate.move_cursor_column(0)
|
||||
@output.write "\e[39m\e[49m#{visual_lines[start + i]}\e[39m\e[49m"
|
||||
Reline::IOGate.erase_after_cursor
|
||||
Reline::IOGate.move_cursor_column(old_dialog_column)
|
||||
if visual_lines[start + i].nil?
|
||||
s = ' ' * DIALOG_WIDTH
|
||||
else
|
||||
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
|
||||
end
|
||||
@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)
|
||||
end
|
||||
if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + @dialog_vertical_offset + @dialog_contents.size)
|
||||
if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
|
||||
# rerender bottom
|
||||
move_cursor_down(@dialog_vertical_offset + @dialog_contents.size - y_diff)
|
||||
start = visual_start + @dialog_vertical_offset + @dialog_contents.size
|
||||
line_num = (old_dialog_vertical_offset + old_dialog_contents.size) - (@dialog_vertical_offset + @dialog_contents.size)
|
||||
move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff)
|
||||
start = visual_start + dialog.vertical_offset + dialog.contents.size
|
||||
line_num = (old_dialog_vertical_offset + old_dialog_contents.size) - (dialog.vertical_offset + dialog.contents.size)
|
||||
line_num.times do |i|
|
||||
Reline::IOGate.move_cursor_column(0)
|
||||
@output.write "\e[39m\e[49m#{visual_lines[start + i]}\e[39m\e[49m"
|
||||
Reline::IOGate.erase_after_cursor
|
||||
Reline::IOGate.move_cursor_column(old_dialog_column)
|
||||
if visual_lines[start + i].nil?
|
||||
s = ' ' * DIALOG_WIDTH
|
||||
else
|
||||
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, DIALOG_WIDTH)
|
||||
end
|
||||
@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)
|
||||
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
|
||||
end
|
||||
if old_dialog_column < @dialog_column
|
||||
if old_dialog_column < dialog.column
|
||||
# rerender left
|
||||
move_cursor_down(old_dialog_vertical_offset - y_diff)
|
||||
width = @dialog_column - old_dialog_column
|
||||
width = dialog.column - old_dialog_column
|
||||
start = visual_start + old_dialog_vertical_offset
|
||||
line_num = old_dialog_contents.size
|
||||
line_num.times do |i|
|
||||
|
@ -659,10 +687,10 @@ class Reline::LineEditor
|
|||
end
|
||||
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
|
||||
end
|
||||
if (old_dialog_column + DIALOG_WIDTH) > (@dialog_column + DIALOG_WIDTH)
|
||||
if (old_dialog_column + DIALOG_WIDTH) > (dialog.column + DIALOG_WIDTH)
|
||||
# rerender right
|
||||
move_cursor_down(old_dialog_vertical_offset + y_diff)
|
||||
width = (old_dialog_column + DIALOG_WIDTH) - (@dialog_column + DIALOG_WIDTH)
|
||||
width = (old_dialog_column + DIALOG_WIDTH) - (dialog.column + DIALOG_WIDTH)
|
||||
start = visual_start + old_dialog_vertical_offset
|
||||
line_num = old_dialog_contents.size
|
||||
line_num.times do |i|
|
||||
|
@ -672,7 +700,7 @@ class Reline::LineEditor
|
|||
else
|
||||
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column + DIALOG_WIDTH, width)
|
||||
end
|
||||
Reline::IOGate.move_cursor_column(@dialog_column + DIALOG_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
|
||||
|
@ -682,36 +710,42 @@ class Reline::LineEditor
|
|||
end
|
||||
|
||||
private def clear_dialog
|
||||
return unless @dialog_contents
|
||||
prompt, prompt_width, prompt_list = check_multiline_prompt(@dialog_lines_backup[:lines], prompt)
|
||||
@dialogs.each do |dialog|
|
||||
clear_each_dialog(dialog)
|
||||
end
|
||||
end
|
||||
|
||||
private def clear_each_dialog(dialog)
|
||||
return unless dialog.contents
|
||||
prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:lines], prompt)
|
||||
visual_lines = []
|
||||
visual_lines_under_dialog = []
|
||||
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] + @dialog_vertical_offset
|
||||
if i == dialog.lines_backup[:line_index]
|
||||
visual_start = visual_lines.size + dialog.lines_backup[:started_from] + dialog.vertical_offset
|
||||
end
|
||||
visual_lines.concat(vl)
|
||||
}
|
||||
visual_lines_under_dialog = visual_lines[visual_start, @dialog_contents.size]
|
||||
visual_lines_under_dialog = visual_lines[visual_start, dialog.contents.size]
|
||||
Reline::IOGate.hide_cursor
|
||||
move_cursor_down(@dialog_vertical_offset)
|
||||
dialog_vertical_size = @dialog_contents.size
|
||||
move_cursor_down(dialog.vertical_offset)
|
||||
dialog_vertical_size = dialog.contents.size
|
||||
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_WIDTH}s\e[39m\e[49m" % visual_lines_under_dialog[i]
|
||||
else
|
||||
Reline::IOGate.move_cursor_column(@dialog_column)
|
||||
Reline::IOGate.move_cursor_column(dialog.column)
|
||||
@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)
|
||||
end
|
||||
move_cursor_up(dialog_vertical_size - 1 + @dialog_vertical_offset)
|
||||
move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
|
||||
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
||||
Reline::IOGate.show_cursor
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue