2019-04-27 01:53:09 -04:00
|
|
|
require 'reline/kill_ring'
|
|
|
|
require 'reline/unicode'
|
|
|
|
|
|
|
|
require 'tempfile'
|
|
|
|
|
|
|
|
class Reline::LineEditor
|
|
|
|
# TODO: undo
|
|
|
|
attr_reader :line
|
2019-05-12 13:26:31 -04:00
|
|
|
attr_reader :byte_pointer
|
2019-04-27 01:53:09 -04:00
|
|
|
attr_accessor :confirm_multiline_termination_proc
|
|
|
|
attr_accessor :completion_proc
|
2019-10-04 04:03:32 -04:00
|
|
|
attr_accessor :completion_append_character
|
2019-05-25 00:21:22 -04:00
|
|
|
attr_accessor :output_modifier_proc
|
2019-06-13 18:42:53 -04:00
|
|
|
attr_accessor :prompt_proc
|
2019-06-18 07:57:58 -04:00
|
|
|
attr_accessor :auto_indent_proc
|
2019-05-12 13:20:20 -04:00
|
|
|
attr_accessor :pre_input_hook
|
2019-04-27 01:53:09 -04:00
|
|
|
attr_accessor :dig_perfect_match_proc
|
2019-05-12 13:14:48 -04:00
|
|
|
attr_writer :output
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
VI_MOTIONS = %i{
|
|
|
|
ed_prev_char
|
|
|
|
ed_next_char
|
|
|
|
vi_zero
|
|
|
|
ed_move_to_beg
|
|
|
|
ed_move_to_end
|
|
|
|
vi_to_column
|
|
|
|
vi_next_char
|
|
|
|
vi_prev_char
|
|
|
|
vi_next_word
|
|
|
|
vi_prev_word
|
|
|
|
vi_to_next_char
|
|
|
|
vi_to_prev_char
|
|
|
|
vi_end_word
|
|
|
|
vi_next_big_word
|
|
|
|
vi_prev_big_word
|
|
|
|
vi_end_big_word
|
|
|
|
vi_repeat_next_char
|
|
|
|
vi_repeat_prev_char
|
|
|
|
}
|
|
|
|
|
|
|
|
module CompletionState
|
|
|
|
NORMAL = :normal
|
|
|
|
COMPLETION = :completion
|
|
|
|
MENU = :menu
|
|
|
|
JOURNEY = :journey
|
2019-12-12 13:57:32 -05:00
|
|
|
MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
|
2019-04-27 01:53:09 -04:00
|
|
|
PERFECT_MATCH = :perfect_match
|
|
|
|
end
|
|
|
|
|
|
|
|
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
|
|
|
|
MenuInfo = Struct.new('MenuInfo', :target, :list)
|
|
|
|
|
2020-11-20 12:59:38 -05:00
|
|
|
PROMPT_LIST_CACHE_TIMEOUT = 0.5
|
|
|
|
|
2020-01-12 08:24:17 -05:00
|
|
|
def initialize(config, encoding)
|
2019-04-27 01:53:09 -04:00
|
|
|
@config = config
|
2019-10-04 04:03:32 -04:00
|
|
|
@completion_append_character = ''
|
2020-01-12 08:24:17 -05:00
|
|
|
reset_variables(encoding: encoding)
|
2019-05-12 11:22:27 -04:00
|
|
|
end
|
|
|
|
|
2021-01-25 23:18:05 -05:00
|
|
|
def set_pasting_state(in_pasting)
|
|
|
|
@in_pasting = in_pasting
|
|
|
|
end
|
|
|
|
|
2020-10-19 19:39:12 -04:00
|
|
|
def simplified_rendering?
|
|
|
|
if finished?
|
|
|
|
false
|
2020-11-20 03:13:15 -05:00
|
|
|
elsif @just_cursor_moving and not @rerender_all
|
|
|
|
true
|
2020-10-19 19:39:12 -04:00
|
|
|
else
|
2021-01-25 23:18:05 -05:00
|
|
|
not @rerender_all and not finished? and @in_pasting
|
2020-10-19 19:39:12 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-12-28 23:28:14 -05:00
|
|
|
private def check_mode_string
|
|
|
|
mode_string = nil
|
2020-12-23 17:15:54 -05:00
|
|
|
if @config.show_mode_in_prompt
|
|
|
|
if @config.editing_mode_is?(:vi_command)
|
2020-12-28 23:28:14 -05:00
|
|
|
mode_string = @config.vi_cmd_mode_string
|
2020-12-23 17:15:54 -05:00
|
|
|
elsif @config.editing_mode_is?(:vi_insert)
|
2020-12-28 23:28:14 -05:00
|
|
|
mode_string = @config.vi_ins_mode_string
|
2020-12-23 17:15:54 -05:00
|
|
|
elsif @config.editing_mode_is?(:emacs)
|
2020-12-28 23:28:14 -05:00
|
|
|
mode_string = @config.emacs_mode_string
|
2020-12-23 17:15:54 -05:00
|
|
|
else
|
2020-12-28 23:28:14 -05:00
|
|
|
mode_string = '?'
|
2020-12-23 17:15:54 -05:00
|
|
|
end
|
|
|
|
end
|
2020-12-28 23:28:14 -05:00
|
|
|
if mode_string != @prev_mode_string
|
2020-12-23 17:15:54 -05:00
|
|
|
@rerender_all = true
|
|
|
|
end
|
2020-12-28 23:28:14 -05:00
|
|
|
@prev_mode_string = mode_string
|
|
|
|
mode_string
|
2020-12-23 17:15:54 -05:00
|
|
|
end
|
|
|
|
|
2019-10-17 03:35:11 -04:00
|
|
|
private def check_multiline_prompt(buffer, prompt)
|
|
|
|
if @vi_arg
|
|
|
|
prompt = "(arg: #{@vi_arg}) "
|
|
|
|
@rerender_all = true
|
|
|
|
elsif @searching_prompt
|
|
|
|
prompt = @searching_prompt
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
prompt = @prompt
|
|
|
|
end
|
2020-12-23 17:15:54 -05:00
|
|
|
if simplified_rendering?
|
2020-12-28 23:28:14 -05:00
|
|
|
mode_string = check_mode_string
|
|
|
|
prompt = mode_string + prompt if mode_string
|
2020-12-23 17:15:54 -05:00
|
|
|
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size]
|
|
|
|
end
|
2019-10-16 12:51:29 -04:00
|
|
|
if @prompt_proc
|
2020-12-18 10:41:00 -05:00
|
|
|
use_cached_prompt_list = false
|
|
|
|
if @cached_prompt_list
|
|
|
|
if @just_cursor_moving
|
|
|
|
use_cached_prompt_list = true
|
|
|
|
elsif Time.now.to_f < (@prompt_cache_time + PROMPT_LIST_CACHE_TIMEOUT) and buffer.size == @cached_prompt_list.size
|
|
|
|
use_cached_prompt_list = true
|
|
|
|
end
|
|
|
|
end
|
2021-01-05 04:29:09 -05:00
|
|
|
use_cached_prompt_list = false if @rerender_all
|
2020-12-18 10:41:00 -05:00
|
|
|
if use_cached_prompt_list
|
2020-11-20 10:30:11 -05:00
|
|
|
prompt_list = @cached_prompt_list
|
|
|
|
else
|
|
|
|
prompt_list = @cached_prompt_list = @prompt_proc.(buffer)
|
|
|
|
@prompt_cache_time = Time.now.to_f
|
|
|
|
end
|
2019-10-17 03:35:11 -04:00
|
|
|
prompt_list.map!{ prompt } if @vi_arg or @searching_prompt
|
2021-02-14 14:24:03 -05:00
|
|
|
prompt_list = [prompt] if prompt_list.empty?
|
2020-12-28 23:28:14 -05:00
|
|
|
mode_string = check_mode_string
|
|
|
|
prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string
|
2019-10-16 12:51:29 -04:00
|
|
|
prompt = prompt_list[@line_index]
|
2020-12-24 06:28:26 -05:00
|
|
|
prompt = prompt_list[0] if prompt.nil?
|
2021-01-05 04:29:09 -05:00
|
|
|
prompt = prompt_list.last if prompt.nil?
|
|
|
|
if buffer.size > prompt_list.size
|
|
|
|
(buffer.size - prompt_list.size).times do
|
|
|
|
prompt_list << prompt_list.last
|
|
|
|
end
|
|
|
|
end
|
2019-10-16 12:51:29 -04:00
|
|
|
prompt_width = calculate_width(prompt, true)
|
2019-10-16 13:19:01 -04:00
|
|
|
[prompt, prompt_width, prompt_list]
|
2019-10-16 12:51:29 -04:00
|
|
|
else
|
2020-12-28 23:28:14 -05:00
|
|
|
mode_string = check_mode_string
|
|
|
|
prompt = mode_string + prompt if mode_string
|
2019-10-16 13:19:01 -04:00
|
|
|
prompt_width = calculate_width(prompt, true)
|
|
|
|
[prompt, prompt_width, nil]
|
2019-10-16 12:51:29 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-01-12 08:24:17 -05:00
|
|
|
def reset(prompt = '', encoding:)
|
2019-05-21 13:52:29 -04:00
|
|
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
|
|
|
@screen_size = Reline::IOGate.get_screen_size
|
2020-12-12 21:30:05 -05:00
|
|
|
@screen_height = @screen_size.first
|
2020-01-12 08:24:17 -05:00
|
|
|
reset_variables(prompt, encoding: encoding)
|
2021-07-31 08:39:04 -04:00
|
|
|
@old_trap = Signal.trap(:INT) {
|
2021-01-29 04:27:54 -05:00
|
|
|
if @scroll_partial_screen
|
|
|
|
move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1)
|
|
|
|
else
|
|
|
|
move_cursor_down(@highest_in_all - @line_index - 1)
|
|
|
|
end
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
scroll_down(1)
|
2021-08-01 04:41:55 -04:00
|
|
|
case @old_trap
|
|
|
|
when 'DEFAULT', 'SYSTEM_DEFAULT'
|
|
|
|
raise Interrupt
|
|
|
|
when 'IGNORE'
|
|
|
|
# Do nothing
|
|
|
|
when 'EXIT'
|
|
|
|
exit
|
|
|
|
else
|
|
|
|
@old_trap.call
|
|
|
|
end
|
2019-05-26 18:35:35 -04:00
|
|
|
}
|
2019-08-29 01:36:45 -04:00
|
|
|
Reline::IOGate.set_winch_handler do
|
|
|
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
2019-08-31 22:02:14 -04:00
|
|
|
old_screen_size = @screen_size
|
2019-08-29 01:36:45 -04:00
|
|
|
@screen_size = Reline::IOGate.get_screen_size
|
2020-12-12 21:30:05 -05:00
|
|
|
@screen_height = @screen_size.first
|
2019-08-31 22:02:14 -04:00
|
|
|
if old_screen_size.last < @screen_size.last # columns increase
|
|
|
|
@rerender_all = true
|
|
|
|
rerender
|
|
|
|
else
|
|
|
|
back = 0
|
|
|
|
new_buffer = whole_lines
|
2019-10-17 03:35:11 -04:00
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
2019-08-31 22:02:14 -04:00
|
|
|
new_buffer.each_with_index do |line, index|
|
|
|
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
|
|
|
width = prompt_width + calculate_width(line)
|
|
|
|
height = calculate_height_by_width(width)
|
|
|
|
back += height
|
|
|
|
end
|
|
|
|
@highest_in_all = back
|
|
|
|
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
|
|
|
@first_line_started_from =
|
|
|
|
if @line_index.zero?
|
|
|
|
0
|
|
|
|
else
|
2020-09-11 12:51:26 -04:00
|
|
|
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
2019-08-31 22:02:14 -04:00
|
|
|
end
|
|
|
|
if @prompt_proc
|
|
|
|
prompt = prompt_list[@line_index]
|
|
|
|
prompt_width = calculate_width(prompt, true)
|
|
|
|
end
|
|
|
|
calculate_nearest_cursor
|
|
|
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
|
|
|
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
|
|
|
@rerender_all = true
|
|
|
|
end
|
2019-08-29 01:36:45 -04:00
|
|
|
end
|
2019-05-26 18:35:35 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def finalize
|
|
|
|
Signal.trap('SIGINT', @old_trap)
|
2019-05-21 13:52:29 -04:00
|
|
|
end
|
|
|
|
|
2019-05-26 21:09:21 -04:00
|
|
|
def eof?
|
|
|
|
@eof
|
|
|
|
end
|
|
|
|
|
2020-01-12 08:24:17 -05:00
|
|
|
def reset_variables(prompt = '', encoding:)
|
2019-04-27 01:53:09 -04:00
|
|
|
@prompt = prompt
|
2019-11-15 02:50:11 -05:00
|
|
|
@mark_pointer = nil
|
2019-04-27 01:53:09 -04:00
|
|
|
@encoding = encoding
|
|
|
|
@is_multiline = false
|
|
|
|
@finished = false
|
|
|
|
@cleared = false
|
|
|
|
@rerender_all = false
|
|
|
|
@history_pointer = nil
|
2020-11-23 04:17:06 -05:00
|
|
|
@kill_ring ||= Reline::KillRing.new
|
2019-04-27 01:53:09 -04:00
|
|
|
@vi_clipboard = ''
|
|
|
|
@vi_arg = nil
|
|
|
|
@waiting_proc = nil
|
|
|
|
@waiting_operator_proc = nil
|
2020-11-14 08:52:38 -05:00
|
|
|
@waiting_operator_vi_arg = nil
|
2019-04-27 01:53:09 -04:00
|
|
|
@completion_journey_data = nil
|
|
|
|
@completion_state = CompletionState::NORMAL
|
|
|
|
@perfect_matched = nil
|
2019-05-12 11:22:27 -04:00
|
|
|
@menu_info = nil
|
|
|
|
@first_prompt = true
|
|
|
|
@searching_prompt = nil
|
|
|
|
@first_char = true
|
2020-11-19 08:59:16 -05:00
|
|
|
@add_newline_to_end_of_buffer = false
|
2020-11-20 23:08:53 -05:00
|
|
|
@just_cursor_moving = nil
|
2020-11-20 10:30:11 -05:00
|
|
|
@cached_prompt_list = nil
|
|
|
|
@prompt_cache_time = nil
|
2019-05-26 21:09:21 -04:00
|
|
|
@eof = false
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
@continuous_insertion_buffer = String.new(encoding: @encoding)
|
2020-12-12 21:30:05 -05:00
|
|
|
@scroll_partial_screen = nil
|
2020-12-28 23:28:14 -05:00
|
|
|
@prev_mode_string = nil
|
2020-12-24 08:53:24 -05:00
|
|
|
@drop_terminate_spaces = false
|
2021-01-28 02:25:27 -05:00
|
|
|
@in_pasting = false
|
|
|
|
@auto_indent_proc = nil
|
2021-08-27 01:58:05 -04:00
|
|
|
@dialogs = []
|
2019-05-12 13:26:31 -04:00
|
|
|
reset_line
|
|
|
|
end
|
|
|
|
|
|
|
|
def reset_line
|
2019-05-12 11:22:27 -04:00
|
|
|
@cursor = 0
|
|
|
|
@cursor_max = 0
|
|
|
|
@byte_pointer = 0
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)]
|
|
|
|
@line_index = 0
|
|
|
|
@previous_line_index = nil
|
|
|
|
@line = @buffer_of_lines[0]
|
2019-04-27 01:53:09 -04:00
|
|
|
@first_line_started_from = 0
|
|
|
|
@move_up = 0
|
|
|
|
@started_from = 0
|
|
|
|
@highest_in_this = 1
|
|
|
|
@highest_in_all = 1
|
2019-05-12 11:22:27 -04:00
|
|
|
@line_backup_in_history = nil
|
|
|
|
@multibyte_buffer = String.new(encoding: 'ASCII-8BIT')
|
2019-06-18 07:57:58 -04:00
|
|
|
@check_new_auto_indent = false
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
def multiline_on
|
|
|
|
@is_multiline = true
|
|
|
|
end
|
|
|
|
|
|
|
|
def multiline_off
|
|
|
|
@is_multiline = false
|
|
|
|
end
|
|
|
|
|
2020-09-11 12:51:26 -04:00
|
|
|
private def calculate_height_by_lines(lines, prompt)
|
2019-11-01 10:45:07 -04:00
|
|
|
result = 0
|
2020-09-11 12:51:26 -04:00
|
|
|
prompt_list = prompt.is_a?(Array) ? prompt : nil
|
2019-11-01 10:45:07 -04:00
|
|
|
lines.each_with_index { |line, i|
|
|
|
|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
|
2019-11-20 17:00:01 -05:00
|
|
|
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
|
2019-11-01 10:45:07 -04:00
|
|
|
}
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2019-04-27 01:53:09 -04:00
|
|
|
private def insert_new_line(cursor_line, next_line)
|
|
|
|
@line = cursor_line
|
|
|
|
@buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding))
|
|
|
|
@previous_line_index = @line_index
|
|
|
|
@line_index += 1
|
2020-11-20 23:08:53 -05:00
|
|
|
@just_cursor_moving = false
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private def calculate_height_by_width(width)
|
2019-07-05 23:11:12 -04:00
|
|
|
width.div(@screen_size.last) + 1
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2020-08-27 13:09:34 -04:00
|
|
|
private def split_by_width(str, max_width)
|
|
|
|
Reline::Unicode.split_by_width(str, max_width, @encoding)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private def scroll_down(val)
|
|
|
|
if val <= @rest_height
|
2019-05-15 02:52:12 -04:00
|
|
|
Reline::IOGate.move_cursor_down(val)
|
2019-04-27 01:53:09 -04:00
|
|
|
@rest_height -= val
|
|
|
|
else
|
2019-05-15 02:52:12 -04:00
|
|
|
Reline::IOGate.move_cursor_down(@rest_height)
|
|
|
|
Reline::IOGate.scroll_down(val - @rest_height)
|
2019-04-27 01:53:09 -04:00
|
|
|
@rest_height = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def move_cursor_up(val)
|
|
|
|
if val > 0
|
2019-05-15 02:52:12 -04:00
|
|
|
Reline::IOGate.move_cursor_up(val)
|
2019-04-27 01:53:09 -04:00
|
|
|
@rest_height += val
|
|
|
|
elsif val < 0
|
|
|
|
move_cursor_down(-val)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def move_cursor_down(val)
|
|
|
|
if val > 0
|
2019-05-15 02:52:12 -04:00
|
|
|
Reline::IOGate.move_cursor_down(val)
|
2019-04-27 01:53:09 -04:00
|
|
|
@rest_height -= val
|
|
|
|
@rest_height = 0 if @rest_height < 0
|
|
|
|
elsif val < 0
|
|
|
|
move_cursor_up(-val)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-12-12 21:30:05 -05:00
|
|
|
private def calculate_nearest_cursor(line_to_calc = @line, cursor = @cursor, started_from = @started_from, byte_pointer = @byte_pointer, update = true)
|
|
|
|
new_cursor_max = calculate_width(line_to_calc)
|
2019-04-27 01:53:09 -04:00
|
|
|
new_cursor = 0
|
|
|
|
new_byte_pointer = 0
|
|
|
|
height = 1
|
|
|
|
max_width = @screen_size.last
|
2019-05-19 18:45:36 -04:00
|
|
|
if @config.editing_mode_is?(:vi_command)
|
2020-12-12 21:30:05 -05:00
|
|
|
last_byte_size = Reline::Unicode.get_prev_mbchar_size(line_to_calc, line_to_calc.bytesize)
|
2019-05-20 11:59:43 -04:00
|
|
|
if last_byte_size > 0
|
2020-12-12 21:30:05 -05:00
|
|
|
last_mbchar = line_to_calc.byteslice(line_to_calc.bytesize - last_byte_size, last_byte_size)
|
2019-05-20 11:59:43 -04:00
|
|
|
last_width = Reline::Unicode.get_mbchar_width(last_mbchar)
|
2020-12-12 21:30:05 -05:00
|
|
|
end_of_line_cursor = new_cursor_max - last_width
|
2019-05-20 11:59:43 -04:00
|
|
|
else
|
2020-12-12 21:30:05 -05:00
|
|
|
end_of_line_cursor = new_cursor_max
|
2019-05-20 11:59:43 -04:00
|
|
|
end
|
2019-05-19 18:45:36 -04:00
|
|
|
else
|
2020-12-12 21:30:05 -05:00
|
|
|
end_of_line_cursor = new_cursor_max
|
2019-05-19 18:45:36 -04:00
|
|
|
end
|
2021-02-17 13:41:47 -05:00
|
|
|
line_to_calc.grapheme_clusters.each do |gc|
|
|
|
|
mbchar = gc.encode(Encoding::UTF_8)
|
|
|
|
mbchar_width = Reline::Unicode.get_mbchar_width(mbchar)
|
2019-04-27 01:53:09 -04:00
|
|
|
now = new_cursor + mbchar_width
|
2020-12-12 21:30:05 -05:00
|
|
|
if now > end_of_line_cursor or now > cursor
|
2019-04-27 01:53:09 -04:00
|
|
|
break
|
|
|
|
end
|
|
|
|
new_cursor += mbchar_width
|
|
|
|
if new_cursor > max_width * height
|
|
|
|
height += 1
|
|
|
|
end
|
|
|
|
new_byte_pointer += gc.bytesize
|
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
new_started_from = height - 1
|
|
|
|
if update
|
|
|
|
@cursor = new_cursor
|
|
|
|
@cursor_max = new_cursor_max
|
|
|
|
@started_from = new_started_from
|
|
|
|
@byte_pointer = new_byte_pointer
|
|
|
|
else
|
|
|
|
[new_cursor, new_cursor_max, new_started_from, new_byte_pointer]
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2020-10-19 19:39:12 -04:00
|
|
|
def rerender_all
|
|
|
|
@rerender_all = true
|
2020-11-27 09:11:46 -05:00
|
|
|
process_insert(force: true)
|
2020-10-19 19:39:12 -04:00
|
|
|
rerender
|
|
|
|
end
|
|
|
|
|
2019-07-30 13:13:21 -04:00
|
|
|
def rerender
|
2019-05-26 19:39:39 -04:00
|
|
|
return if @line.nil?
|
2019-04-27 01:53:09 -04:00
|
|
|
if @menu_info
|
2019-05-25 12:32:12 -04:00
|
|
|
scroll_down(@highest_in_all - @first_line_started_from)
|
|
|
|
@rerender_all = true
|
2020-11-19 16:00:25 -05:00
|
|
|
end
|
|
|
|
if @menu_info
|
|
|
|
show_menu
|
2019-04-27 01:53:09 -04:00
|
|
|
@menu_info = nil
|
|
|
|
end
|
2019-10-17 03:35:11 -04:00
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
2019-04-27 01:53:09 -04:00
|
|
|
if @cleared
|
2020-11-19 16:00:25 -05:00
|
|
|
clear_screen_buffer(prompt, prompt_list, prompt_width)
|
2019-04-27 01:53:09 -04:00
|
|
|
@cleared = false
|
|
|
|
return
|
|
|
|
end
|
2021-01-30 23:45:21 -05:00
|
|
|
if @is_multiline and finished? and @scroll_partial_screen
|
|
|
|
# Re-output all code higher than the screen when finished.
|
|
|
|
Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen)
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
@scroll_partial_screen = nil
|
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
2021-02-05 07:39:29 -05:00
|
|
|
if @previous_line_index
|
|
|
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
|
|
|
else
|
|
|
|
new_lines = whole_lines
|
|
|
|
end
|
|
|
|
modify_lines(new_lines).each_with_index do |line, index|
|
2021-01-30 23:45:21 -05:00
|
|
|
@output.write "#{prompt_list ? prompt_list[index] : prompt}#{line}\n"
|
|
|
|
Reline::IOGate.erase_after_cursor
|
|
|
|
end
|
|
|
|
@output.flush
|
2021-08-17 06:21:27 -04:00
|
|
|
clear_dialog
|
2021-01-30 23:45:21 -05:00
|
|
|
return
|
|
|
|
end
|
2019-06-13 18:42:53 -04:00
|
|
|
new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line))
|
2021-01-13 16:21:54 -05:00
|
|
|
rendered = false
|
2020-11-19 08:59:16 -05:00
|
|
|
if @add_newline_to_end_of_buffer
|
2021-01-25 23:18:05 -05:00
|
|
|
rerender_added_newline(prompt, prompt_width)
|
2020-11-19 08:59:16 -05:00
|
|
|
@add_newline_to_end_of_buffer = false
|
2020-12-12 21:30:05 -05:00
|
|
|
else
|
|
|
|
if @just_cursor_moving and not @rerender_all
|
|
|
|
rendered = just_move_cursor
|
2021-08-17 06:21:27 -04:00
|
|
|
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
2020-12-12 21:30:05 -05:00
|
|
|
@just_cursor_moving = false
|
|
|
|
return
|
|
|
|
elsif @previous_line_index or new_highest_in_this != @highest_in_this
|
|
|
|
rerender_changed_current_line
|
|
|
|
@previous_line_index = nil
|
|
|
|
rendered = true
|
|
|
|
elsif @rerender_all
|
|
|
|
rerender_all_lines
|
|
|
|
@rerender_all = false
|
|
|
|
rendered = true
|
|
|
|
else
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2019-05-26 21:09:21 -04:00
|
|
|
if @is_multiline
|
|
|
|
if finished?
|
2019-05-30 17:03:18 -04:00
|
|
|
# Always rerender on finish because output_modifier_proc may return a different output.
|
2021-02-05 07:39:29 -05:00
|
|
|
if @previous_line_index
|
|
|
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
|
|
|
else
|
|
|
|
new_lines = whole_lines
|
|
|
|
end
|
|
|
|
line = modify_lines(new_lines)[@line_index]
|
2021-08-17 06:21:27 -04:00
|
|
|
clear_dialog
|
2021-02-05 07:39:29 -05:00
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
2020-12-12 21:30:05 -05:00
|
|
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
2021-02-02 07:28:59 -05:00
|
|
|
move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1)
|
2019-05-26 21:09:21 -04:00
|
|
|
scroll_down(1)
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
Reline::IOGate.erase_after_cursor
|
2021-08-17 06:21:27 -04:00
|
|
|
else
|
|
|
|
if not rendered and not @in_pasting
|
2021-01-25 23:18:05 -05:00
|
|
|
line = modify_lines(whole_lines)[@line_index]
|
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
|
|
|
|
render_partial(prompt, prompt_width, line, @first_line_started_from)
|
|
|
|
end
|
2021-08-17 06:21:27 -04:00
|
|
|
render_dialog((prompt_width + @cursor) % @screen_size.last)
|
2019-05-26 21:09:21 -04:00
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
@buffer_of_lines[@line_index] = @line
|
2021-01-29 04:27:54 -05:00
|
|
|
@rest_height = 0 if @scroll_partial_screen
|
2019-05-22 19:23:22 -04:00
|
|
|
else
|
2021-01-25 23:18:05 -05:00
|
|
|
line = modify_lines(whole_lines)[@line_index]
|
2020-12-12 21:30:05 -05:00
|
|
|
render_partial(prompt, prompt_width, line, 0)
|
2019-05-26 21:09:21 -04:00
|
|
|
if finished?
|
|
|
|
scroll_down(1)
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
Reline::IOGate.erase_after_cursor
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-08-17 06:21:27 -04:00
|
|
|
class DialogProcScope
|
2021-08-27 08:33:22 -04:00
|
|
|
def initialize(line_editor, proc_to_exec, context)
|
2021-08-17 06:21:27 -04:00
|
|
|
@line_editor = line_editor
|
|
|
|
@proc_to_exec = proc_to_exec
|
2021-08-27 08:33:22 -04:00
|
|
|
@context = context
|
2021-08-17 06:21:27 -04:00
|
|
|
@cursor_pos = Reline::CursorPos.new
|
|
|
|
end
|
|
|
|
|
2021-08-27 08:33:22 -04:00
|
|
|
def context
|
|
|
|
@context
|
|
|
|
end
|
|
|
|
|
2021-08-17 06:21:27 -04:00
|
|
|
def retrieve_completion_block(set_completion_quote_character = false)
|
|
|
|
@line_editor.retrieve_completion_block(set_completion_quote_character)
|
|
|
|
end
|
|
|
|
|
|
|
|
def call_completion_proc_with_checking_args(pre, target, post)
|
|
|
|
@line_editor.call_completion_proc_with_checking_args(pre, target, post)
|
|
|
|
end
|
|
|
|
|
|
|
|
def set_cursor_pos(col, row)
|
|
|
|
@cursor_pos.x = col
|
|
|
|
@cursor_pos.y = row
|
|
|
|
end
|
|
|
|
|
|
|
|
def cursor_pos
|
|
|
|
@cursor_pos
|
|
|
|
end
|
|
|
|
|
|
|
|
def just_cursor_moving
|
|
|
|
@line_editor.instance_variable_get(:@just_cursor_moving)
|
|
|
|
end
|
|
|
|
|
2021-08-24 03:15:04 -04:00
|
|
|
def screen_width
|
|
|
|
@line_editor.instance_variable_get(:@screen_size).last
|
|
|
|
end
|
|
|
|
|
2021-08-26 12:51:44 -04:00
|
|
|
def completion_journey_data
|
|
|
|
@line_editor.instance_variable_get(:@completion_journey_data)
|
|
|
|
end
|
|
|
|
|
2021-08-17 06:21:27 -04:00
|
|
|
def call
|
|
|
|
instance_exec(&@proc_to_exec)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-08-27 01:58:05 -04:00
|
|
|
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
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
|
|
|
|
2021-08-27 08:33:22 -04:00
|
|
|
def add_dialog_proc(name, p, context = nil)
|
2021-08-27 01:58:05 -04:00
|
|
|
return if @dialogs.any? { |d| d.name == name }
|
2021-08-27 08:33:22 -04:00
|
|
|
@dialogs << Dialog.new(name, DialogProcScope.new(self, p, context))
|
2021-08-27 01:58:05 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
DIALOG_HEIGHT = 20
|
2021-08-17 06:21:27 -04:00
|
|
|
DIALOG_WIDTH = 40
|
|
|
|
private def render_dialog(cursor_column)
|
2021-08-27 01:58:05 -04:00
|
|
|
@dialogs.each do |dialog|
|
|
|
|
render_each_dialog(dialog, cursor_column)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def render_each_dialog(dialog, cursor_column)
|
2021-08-17 06:21:27 -04:00
|
|
|
if @in_pasting
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.contents = nil
|
2021-08-17 06:21:27 -04:00
|
|
|
return
|
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from)
|
2021-08-27 13:10:51 -04:00
|
|
|
pos, result, pointer, bg = dialog.call
|
2021-08-27 01:58:05 -04:00
|
|
|
old_dialog_contents = dialog.contents
|
|
|
|
old_dialog_column = dialog.column
|
|
|
|
old_dialog_vertical_offset = dialog.vertical_offset
|
2021-08-17 06:21:27 -04:00
|
|
|
if result and not result.empty?
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.contents = result
|
|
|
|
dialog.contents = dialog.contents[0...DIALOG_HEIGHT] if dialog.contents.size > DIALOG_HEIGHT
|
2021-08-17 06:21:27 -04:00
|
|
|
else
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.lines_backup = {
|
2021-08-25 04:22:23 -04:00
|
|
|
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
|
|
|
|
}
|
2021-08-27 01:58:05 -04:00
|
|
|
clear_each_dialog(dialog)
|
|
|
|
dialog.contents = nil
|
2021-08-17 06:21:27 -04:00
|
|
|
return
|
|
|
|
end
|
|
|
|
upper_space = @first_line_started_from - @started_from
|
|
|
|
lower_space = @highest_in_all - @first_line_started_from - @started_from - 1
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.column = pos.x
|
|
|
|
diff = (dialog.column + DIALOG_WIDTH) - (@screen_size.last - 1)
|
2021-08-23 08:57:48 -04:00
|
|
|
if diff > 0
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.column -= diff
|
2021-08-23 08:57:48 -04:00
|
|
|
end
|
2021-08-17 06:21:27 -04:00
|
|
|
if (lower_space + @rest_height) >= DIALOG_HEIGHT
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.vertical_offset = pos.y + 1
|
2021-08-17 06:21:27 -04:00
|
|
|
elsif upper_space >= DIALOG_HEIGHT
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.vertical_offset = pos.y + -(DIALOG_HEIGHT + 1)
|
2021-08-17 06:21:27 -04:00
|
|
|
else
|
|
|
|
if (lower_space + @rest_height) < DIALOG_HEIGHT
|
2021-08-23 09:02:25 -04:00
|
|
|
scroll_down(DIALOG_HEIGHT)
|
|
|
|
move_cursor_up(DIALOG_HEIGHT)
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.vertical_offset = pos.y + 1
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
2021-08-21 12:31:21 -04:00
|
|
|
Reline::IOGate.hide_cursor
|
2021-08-27 01:58:05 -04:00
|
|
|
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|
|
2021-08-26 12:51:44 -04:00
|
|
|
if i == pointer
|
|
|
|
bg_color = '45'
|
|
|
|
else
|
2021-08-27 13:10:51 -04:00
|
|
|
if bg
|
|
|
|
bg_color = bg
|
|
|
|
else
|
|
|
|
bg_color = '46'
|
|
|
|
end
|
2021-08-26 12:51:44 -04:00
|
|
|
end
|
|
|
|
@output.write "\e[#{bg_color}m%-#{DIALOG_WIDTH}s\e[49m" % item.slice(0, DIALOG_WIDTH)
|
2021-08-27 01:58:05 -04:00
|
|
|
Reline::IOGate.move_cursor_column(dialog.column)
|
|
|
|
move_cursor_down(1) if i < (dialog.contents.size - 1)
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
|
|
|
Reline::IOGate.move_cursor_column(cursor_column)
|
2021-08-27 01:58:05 -04:00
|
|
|
move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1)
|
2021-08-21 12:31:21 -04:00
|
|
|
Reline::IOGate.show_cursor
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.lines_backup = {
|
2021-08-17 06:21:27 -04:00
|
|
|
lines: modify_lines(whole_lines),
|
|
|
|
line_index: @line_index,
|
2021-08-25 04:21:05 -04:00
|
|
|
first_line_started_from: @first_line_started_from,
|
2021-08-17 06:21:27 -04:00
|
|
|
started_from: @started_from,
|
|
|
|
byte_pointer: @byte_pointer
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2021-08-27 01:58:05 -04:00
|
|
|
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)
|
2021-08-17 06:21:27 -04:00
|
|
|
visual_lines = []
|
|
|
|
visual_start = nil
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.lines_backup[:lines].each_with_index { |l, i|
|
2021-08-17 06:21:27 -04:00
|
|
|
pr = prompt_list ? prompt_list[i] : prompt
|
2021-08-18 06:46:01 -04:00
|
|
|
vl, _ = split_by_width(pr + l, @screen_size.last)
|
2021-08-25 03:27:24 -04:00
|
|
|
vl.compact!
|
2021-08-27 01:58:05 -04:00
|
|
|
if i == dialog.lines_backup[:line_index]
|
|
|
|
visual_start = visual_lines.size + dialog.lines_backup[:started_from]
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
|
|
|
visual_lines.concat(vl)
|
|
|
|
}
|
2021-08-27 01:58:05 -04:00
|
|
|
old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from]
|
2021-08-25 04:21:05 -04:00
|
|
|
y = @first_line_started_from + @started_from
|
|
|
|
y_diff = y - old_y
|
2021-08-27 01:58:05 -04:00
|
|
|
if (old_y + old_dialog_vertical_offset) < (y + dialog.vertical_offset)
|
2021-08-18 14:49:38 -04:00
|
|
|
# rerender top
|
2021-08-25 04:21:05 -04:00
|
|
|
move_cursor_down(old_dialog_vertical_offset - y_diff)
|
2021-08-17 06:21:27 -04:00
|
|
|
start = visual_start + old_dialog_vertical_offset
|
2021-08-27 01:58:05 -04:00
|
|
|
line_num = dialog.vertical_offset - old_dialog_vertical_offset
|
2021-08-17 06:21:27 -04:00
|
|
|
line_num.times do |i|
|
2021-08-27 01:58:05 -04:00
|
|
|
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
|
2021-08-17 06:21:27 -04:00
|
|
|
move_cursor_down(1) if i < (line_num - 1)
|
|
|
|
end
|
2021-08-25 04:21:05 -04:00
|
|
|
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
if (old_y + old_dialog_vertical_offset + old_dialog_contents.size) > (y + dialog.vertical_offset + dialog.contents.size)
|
2021-08-18 14:49:38 -04:00
|
|
|
# rerender bottom
|
2021-08-27 01:58:05 -04:00
|
|
|
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)
|
2021-08-17 06:21:27 -04:00
|
|
|
line_num.times do |i|
|
2021-08-27 01:58:05 -04:00
|
|
|
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
|
2021-08-17 06:21:27 -04:00
|
|
|
move_cursor_down(1) if i < (line_num - 1)
|
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff)
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
if old_dialog_column < dialog.column
|
2021-08-18 14:49:38 -04:00
|
|
|
# rerender left
|
2021-08-25 04:21:05 -04:00
|
|
|
move_cursor_down(old_dialog_vertical_offset - y_diff)
|
2021-08-27 01:58:05 -04:00
|
|
|
width = dialog.column - old_dialog_column
|
2021-08-17 06:21:27 -04:00
|
|
|
start = visual_start + old_dialog_vertical_offset
|
2021-08-18 11:10:48 -04:00
|
|
|
line_num = old_dialog_contents.size
|
2021-08-17 06:21:27 -04:00
|
|
|
line_num.times do |i|
|
|
|
|
Reline::IOGate.move_cursor_column(old_dialog_column)
|
|
|
|
if visual_lines[start + i].nil?
|
|
|
|
s = ' ' * width
|
|
|
|
else
|
|
|
|
s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog_column, width)
|
|
|
|
end
|
2021-08-18 11:10:48 -04:00
|
|
|
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
|
2021-08-17 06:21:27 -04:00
|
|
|
move_cursor_down(1) if i < (line_num - 1)
|
|
|
|
end
|
2021-08-25 04:21:05 -04:00
|
|
|
move_cursor_up(old_dialog_vertical_offset + line_num - 1 - y_diff)
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
if (old_dialog_column + DIALOG_WIDTH) > (dialog.column + DIALOG_WIDTH)
|
2021-08-18 14:49:38 -04:00
|
|
|
# rerender right
|
2021-08-25 04:21:05 -04:00
|
|
|
move_cursor_down(old_dialog_vertical_offset + y_diff)
|
2021-08-27 01:58:05 -04:00
|
|
|
width = (old_dialog_column + DIALOG_WIDTH) - (dialog.column + DIALOG_WIDTH)
|
2021-08-17 06:21:27 -04:00
|
|
|
start = visual_start + old_dialog_vertical_offset
|
2021-08-18 11:10:48 -04:00
|
|
|
line_num = old_dialog_contents.size
|
2021-08-17 06:21:27 -04:00
|
|
|
line_num.times do |i|
|
|
|
|
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_WIDTH, width)
|
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
Reline::IOGate.move_cursor_column(dialog.column + DIALOG_WIDTH)
|
2021-08-18 11:10:48 -04:00
|
|
|
@output.write "\e[39m\e[49m%-#{width}s\e[39m\e[49m" % s
|
2021-08-17 06:21:27 -04:00
|
|
|
move_cursor_down(1) if i < (line_num - 1)
|
|
|
|
end
|
2021-08-25 04:21:05 -04:00
|
|
|
move_cursor_up(old_dialog_vertical_offset + line_num - 1 + y_diff)
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
2021-08-25 04:22:54 -04:00
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private def clear_dialog
|
2021-08-27 01:58:05 -04:00
|
|
|
@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)
|
2021-08-17 06:21:27 -04:00
|
|
|
visual_lines = []
|
|
|
|
visual_lines_under_dialog = []
|
|
|
|
visual_start = nil
|
2021-08-27 01:58:05 -04:00
|
|
|
dialog.lines_backup[:lines].each_with_index { |l, i|
|
2021-08-17 06:21:27 -04:00
|
|
|
pr = prompt_list ? prompt_list[i] : prompt
|
2021-08-18 06:46:01 -04:00
|
|
|
vl, _ = split_by_width(pr + l, @screen_size.last)
|
2021-08-25 03:27:24 -04:00
|
|
|
vl.compact!
|
2021-08-27 01:58:05 -04:00
|
|
|
if i == dialog.lines_backup[:line_index]
|
|
|
|
visual_start = visual_lines.size + dialog.lines_backup[:started_from] + dialog.vertical_offset
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
|
|
|
visual_lines.concat(vl)
|
|
|
|
}
|
2021-08-27 01:58:05 -04:00
|
|
|
visual_lines_under_dialog = visual_lines[visual_start, dialog.contents.size]
|
2021-08-27 13:15:27 -04:00
|
|
|
visual_lines_under_dialog = [] if visual_lines_under_dialog.nil?
|
2021-08-21 12:31:21 -04:00
|
|
|
Reline::IOGate.hide_cursor
|
2021-08-27 01:58:05 -04:00
|
|
|
move_cursor_down(dialog.vertical_offset)
|
|
|
|
dialog_vertical_size = dialog.contents.size
|
2021-08-17 06:21:27 -04:00
|
|
|
dialog_vertical_size.times do |i|
|
|
|
|
if i < visual_lines_under_dialog.size
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
2021-08-18 11:10:48 -04:00
|
|
|
@output.write "\e[39m\e[49m%-#{DIALOG_WIDTH}s\e[39m\e[49m" % visual_lines_under_dialog[i]
|
2021-08-17 06:21:27 -04:00
|
|
|
else
|
2021-08-27 01:58:05 -04:00
|
|
|
Reline::IOGate.move_cursor_column(dialog.column)
|
2021-08-18 11:10:48 -04:00
|
|
|
@output.write "\e[39m\e[49m#{' ' * DIALOG_WIDTH}\e[39m\e[49m"
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
2021-08-21 11:58:38 -04:00
|
|
|
Reline::IOGate.erase_after_cursor
|
2021-08-17 06:21:27 -04:00
|
|
|
move_cursor_down(1) if i < (dialog_vertical_size - 1)
|
|
|
|
end
|
2021-08-27 01:58:05 -04:00
|
|
|
move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset)
|
2021-08-25 04:22:54 -04:00
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
2021-08-21 12:31:21 -04:00
|
|
|
Reline::IOGate.show_cursor
|
2021-08-17 06:21:27 -04:00
|
|
|
end
|
|
|
|
|
2020-12-12 21:30:05 -05:00
|
|
|
private def calculate_scroll_partial_screen(highest_in_all, cursor_y)
|
|
|
|
if @screen_height < highest_in_all
|
|
|
|
old_scroll_partial_screen = @scroll_partial_screen
|
|
|
|
if cursor_y == 0
|
|
|
|
@scroll_partial_screen = 0
|
|
|
|
elsif cursor_y == (highest_in_all - 1)
|
|
|
|
@scroll_partial_screen = highest_in_all - @screen_height
|
|
|
|
else
|
|
|
|
if @scroll_partial_screen
|
|
|
|
if cursor_y <= @scroll_partial_screen
|
|
|
|
@scroll_partial_screen = cursor_y
|
|
|
|
elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y
|
|
|
|
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if cursor_y > (@screen_height - 1)
|
|
|
|
@scroll_partial_screen = cursor_y - (@screen_height - 1)
|
|
|
|
else
|
|
|
|
@scroll_partial_screen = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if @scroll_partial_screen != old_scroll_partial_screen
|
|
|
|
@rerender_all = true
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if @scroll_partial_screen
|
|
|
|
@rerender_all = true
|
|
|
|
end
|
|
|
|
@scroll_partial_screen = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-01-25 23:18:05 -05:00
|
|
|
private def rerender_added_newline(prompt, prompt_width)
|
2020-11-19 16:00:25 -05:00
|
|
|
scroll_down(1)
|
|
|
|
@buffer_of_lines[@previous_line_index] = @line
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
2021-01-25 23:18:05 -05:00
|
|
|
unless @in_pasting
|
|
|
|
render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false)
|
|
|
|
end
|
2021-02-02 09:58:20 -05:00
|
|
|
@cursor = @cursor_max = calculate_width(@line)
|
2020-11-19 16:00:25 -05:00
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
@highest_in_all += @highest_in_this
|
|
|
|
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
|
|
|
@first_line_started_from += @started_from + 1
|
|
|
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
|
|
|
@previous_line_index = nil
|
|
|
|
end
|
|
|
|
|
2020-11-20 03:13:15 -05:00
|
|
|
def just_move_cursor
|
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines, prompt)
|
|
|
|
move_cursor_up(@started_from)
|
|
|
|
new_first_line_started_from =
|
|
|
|
if @line_index.zero?
|
|
|
|
0
|
|
|
|
else
|
|
|
|
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
first_line_diff = new_first_line_started_from - @first_line_started_from
|
2020-12-27 04:37:16 -05:00
|
|
|
new_cursor, new_cursor_max, new_started_from, new_byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false)
|
2020-12-12 21:30:05 -05:00
|
|
|
new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1
|
|
|
|
calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from)
|
2020-11-20 03:13:15 -05:00
|
|
|
@previous_line_index = nil
|
2020-12-12 21:30:05 -05:00
|
|
|
if @rerender_all
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
rerender_all_lines
|
|
|
|
@rerender_all = false
|
|
|
|
true
|
|
|
|
else
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
@first_line_started_from = new_first_line_started_from
|
|
|
|
@started_from = new_started_from
|
|
|
|
@cursor = new_cursor
|
2020-12-27 04:37:16 -05:00
|
|
|
@cursor_max = new_cursor_max
|
|
|
|
@byte_pointer = new_byte_pointer
|
2020-12-12 21:30:05 -05:00
|
|
|
move_cursor_down(first_line_diff + @started_from)
|
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
|
|
|
false
|
|
|
|
end
|
2020-11-20 03:13:15 -05:00
|
|
|
end
|
|
|
|
|
2020-11-19 16:00:25 -05:00
|
|
|
private def rerender_changed_current_line
|
|
|
|
if @previous_line_index
|
|
|
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
|
|
|
else
|
|
|
|
new_lines = whole_lines
|
|
|
|
end
|
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines, prompt)
|
|
|
|
all_height = calculate_height_by_lines(new_lines, prompt_list || prompt)
|
|
|
|
diff = all_height - @highest_in_all
|
|
|
|
move_cursor_down(@highest_in_all - @first_line_started_from - @started_from - 1)
|
|
|
|
if diff > 0
|
|
|
|
scroll_down(diff)
|
|
|
|
move_cursor_up(all_height - 1)
|
|
|
|
elsif diff < 0
|
|
|
|
(-diff).times do
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
Reline::IOGate.erase_after_cursor
|
|
|
|
move_cursor_up(1)
|
|
|
|
end
|
|
|
|
move_cursor_up(all_height - 1)
|
|
|
|
else
|
|
|
|
move_cursor_up(all_height - 1)
|
|
|
|
end
|
|
|
|
@highest_in_all = all_height
|
|
|
|
back = render_whole_lines(new_lines, prompt_list || prompt, prompt_width)
|
|
|
|
move_cursor_up(back)
|
|
|
|
if @previous_line_index
|
|
|
|
@buffer_of_lines[@previous_line_index] = @line
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
end
|
|
|
|
@first_line_started_from =
|
|
|
|
if @line_index.zero?
|
|
|
|
0
|
|
|
|
else
|
|
|
|
calculate_height_by_lines(@buffer_of_lines[0..(@line_index - 1)], prompt_list || prompt)
|
|
|
|
end
|
|
|
|
if @prompt_proc
|
|
|
|
prompt = prompt_list[@line_index]
|
|
|
|
prompt_width = calculate_width(prompt, true)
|
|
|
|
end
|
|
|
|
move_cursor_down(@first_line_started_from)
|
|
|
|
calculate_nearest_cursor
|
|
|
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
|
|
|
move_cursor_down(@started_from)
|
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
|
|
|
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
|
|
|
end
|
|
|
|
|
|
|
|
private def rerender_all_lines
|
|
|
|
move_cursor_up(@first_line_started_from + @started_from)
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
back = 0
|
|
|
|
new_buffer = whole_lines
|
|
|
|
prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer, prompt)
|
|
|
|
new_buffer.each_with_index do |line, index|
|
|
|
|
prompt_width = calculate_width(prompt_list[index], true) if @prompt_proc
|
|
|
|
width = prompt_width + calculate_width(line)
|
|
|
|
height = calculate_height_by_width(width)
|
|
|
|
back += height
|
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
old_highest_in_all = @highest_in_all
|
|
|
|
if @line_index.zero?
|
|
|
|
new_first_line_started_from = 0
|
|
|
|
else
|
|
|
|
new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
|
|
|
|
end
|
|
|
|
new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
2021-01-31 09:44:13 -05:00
|
|
|
calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from)
|
|
|
|
if @scroll_partial_screen
|
|
|
|
move_cursor_up(@first_line_started_from + @started_from)
|
|
|
|
scroll_down(@screen_height - 1)
|
|
|
|
move_cursor_up(@screen_height)
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
elsif back > old_highest_in_all
|
2020-11-19 16:00:25 -05:00
|
|
|
scroll_down(back - 1)
|
|
|
|
move_cursor_up(back - 1)
|
2020-12-12 21:30:05 -05:00
|
|
|
elsif back < old_highest_in_all
|
2020-11-19 16:00:25 -05:00
|
|
|
scroll_down(back)
|
|
|
|
Reline::IOGate.erase_after_cursor
|
2020-12-12 21:30:05 -05:00
|
|
|
(old_highest_in_all - back - 1).times do
|
2020-11-19 16:00:25 -05:00
|
|
|
scroll_down(1)
|
|
|
|
Reline::IOGate.erase_after_cursor
|
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
move_cursor_up(old_highest_in_all - 1)
|
2020-11-19 16:00:25 -05:00
|
|
|
end
|
|
|
|
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
|
|
|
|
if @prompt_proc
|
|
|
|
prompt = prompt_list[@line_index]
|
|
|
|
prompt_width = calculate_width(prompt, true)
|
|
|
|
end
|
|
|
|
@highest_in_this = calculate_height_by_width(prompt_width + @cursor_max)
|
2020-12-12 21:30:05 -05:00
|
|
|
@highest_in_all = back
|
|
|
|
@first_line_started_from = new_first_line_started_from
|
|
|
|
@started_from = new_started_from
|
|
|
|
if @scroll_partial_screen
|
|
|
|
Reline::IOGate.move_cursor_up(@screen_height - (@first_line_started_from + @started_from - @scroll_partial_screen) - 1)
|
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
|
|
|
else
|
|
|
|
move_cursor_down(@first_line_started_from + @started_from - back + 1)
|
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
|
|
|
end
|
2020-11-19 16:00:25 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
private def render_whole_lines(lines, prompt, prompt_width)
|
|
|
|
rendered_height = 0
|
|
|
|
modify_lines(lines).each_with_index do |line, index|
|
|
|
|
if prompt.is_a?(Array)
|
|
|
|
line_prompt = prompt[index]
|
|
|
|
prompt_width = calculate_width(line_prompt, true)
|
|
|
|
else
|
|
|
|
line_prompt = prompt
|
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
height = render_partial(line_prompt, prompt_width, line, rendered_height, with_control: false)
|
2020-11-19 16:00:25 -05:00
|
|
|
if index < (lines.size - 1)
|
2020-12-12 21:30:05 -05:00
|
|
|
if @scroll_partial_screen
|
|
|
|
if (@scroll_partial_screen - height) < rendered_height and (@scroll_partial_screen + @screen_height - 1) >= (rendered_height + height)
|
|
|
|
move_cursor_down(1)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
scroll_down(1)
|
|
|
|
end
|
2020-11-19 16:00:25 -05:00
|
|
|
rendered_height += height
|
|
|
|
else
|
|
|
|
rendered_height += height - 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rendered_height
|
|
|
|
end
|
|
|
|
|
2020-12-12 21:30:05 -05:00
|
|
|
private def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true)
|
2020-08-27 13:09:34 -04:00
|
|
|
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
|
2020-12-12 21:30:05 -05:00
|
|
|
cursor_up_from_last_line = 0
|
|
|
|
if @scroll_partial_screen
|
|
|
|
last_visual_line = this_started_from + (height - 1)
|
|
|
|
last_screen_line = @scroll_partial_screen + (@screen_height - 1)
|
|
|
|
if (@scroll_partial_screen - this_started_from) >= height
|
|
|
|
# Render nothing because this line is before the screen.
|
|
|
|
visual_lines = []
|
|
|
|
elsif this_started_from > last_screen_line
|
|
|
|
# Render nothing because this line is after the screen.
|
|
|
|
visual_lines = []
|
|
|
|
else
|
|
|
|
deleted_lines_before_screen = []
|
|
|
|
if @scroll_partial_screen > this_started_from and last_visual_line >= @scroll_partial_screen
|
|
|
|
# A part of visual lines are before the screen.
|
|
|
|
deleted_lines_before_screen = visual_lines.shift((@scroll_partial_screen - this_started_from) * 2)
|
|
|
|
deleted_lines_before_screen.compact!
|
|
|
|
end
|
|
|
|
if this_started_from <= last_screen_line and last_screen_line < last_visual_line
|
|
|
|
# A part of visual lines are after the screen.
|
|
|
|
visual_lines.pop((last_visual_line - last_screen_line) * 2)
|
|
|
|
end
|
|
|
|
move_cursor_up(deleted_lines_before_screen.size - @started_from)
|
|
|
|
cursor_up_from_last_line = @started_from - deleted_lines_before_screen.size
|
|
|
|
end
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
if with_control
|
2019-05-21 17:48:27 -04:00
|
|
|
if height > @highest_in_this
|
|
|
|
diff = height - @highest_in_this
|
2019-04-27 01:53:09 -04:00
|
|
|
scroll_down(diff)
|
|
|
|
@highest_in_all += diff
|
2019-05-21 17:48:27 -04:00
|
|
|
@highest_in_this = height
|
2019-05-29 23:29:38 -04:00
|
|
|
move_cursor_up(diff)
|
|
|
|
elsif height < @highest_in_this
|
|
|
|
diff = @highest_in_this - height
|
|
|
|
@highest_in_all -= diff
|
|
|
|
@highest_in_this = height
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
move_cursor_up(@started_from)
|
|
|
|
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
|
2021-02-03 03:56:49 -05:00
|
|
|
cursor_up_from_last_line = height - 1 - @started_from
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render)
|
|
|
|
@output.write "\e[0m" # clear character decorations
|
|
|
|
end
|
2019-05-29 23:29:38 -04:00
|
|
|
visual_lines.each_with_index do |line, index|
|
2020-12-12 21:30:05 -05:00
|
|
|
Reline::IOGate.move_cursor_column(0)
|
2019-05-21 17:48:27 -04:00
|
|
|
if line.nil?
|
2020-09-09 06:46:30 -04:00
|
|
|
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
|
2021-02-17 09:28:47 -05:00
|
|
|
# reaches the end of line
|
2021-02-17 09:56:34 -05:00
|
|
|
if Reline::IOGate.win? and Reline::IOGate.win_legacy_console?
|
2021-02-17 09:28:47 -05:00
|
|
|
# A newline is automatically inserted if a character is rendered at
|
|
|
|
# eol on command prompt.
|
|
|
|
else
|
|
|
|
# When the cursor is at the end of the line and erases characters
|
|
|
|
# after the cursor, some terminals delete the character at the
|
|
|
|
# cursor position.
|
|
|
|
move_cursor_down(1)
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
end
|
2020-01-31 12:59:46 -05:00
|
|
|
else
|
|
|
|
Reline::IOGate.erase_after_cursor
|
|
|
|
move_cursor_down(1)
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
end
|
2019-05-21 17:48:27 -04:00
|
|
|
next
|
|
|
|
end
|
2020-02-14 08:34:56 -05:00
|
|
|
@output.write line
|
2021-02-17 09:56:34 -05:00
|
|
|
if Reline::IOGate.win? and Reline::IOGate.win_legacy_console? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
|
2021-02-17 09:28:47 -05:00
|
|
|
# A newline is automatically inserted if a character is rendered at eol on command prompt.
|
|
|
|
@rest_height -= 1 if @rest_height > 0
|
|
|
|
end
|
2019-10-05 11:52:20 -04:00
|
|
|
@output.flush
|
2019-05-12 13:14:48 -04:00
|
|
|
if @first_prompt
|
|
|
|
@first_prompt = false
|
|
|
|
@pre_input_hook&.call
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2020-12-12 21:30:05 -05:00
|
|
|
unless visual_lines.empty?
|
|
|
|
Reline::IOGate.erase_after_cursor
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
if with_control
|
2020-09-10 16:48:59 -04:00
|
|
|
# Just after rendring, so the cursor is on the last line.
|
2019-04-27 01:53:09 -04:00
|
|
|
if finished?
|
2020-09-10 16:48:59 -04:00
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
else
|
|
|
|
# Moves up from bottom of lines to the cursor position.
|
2020-12-12 21:30:05 -05:00
|
|
|
move_cursor_up(cursor_up_from_last_line)
|
|
|
|
# This logic is buggy if a fullwidth char is wrapped because there is only one halfwidth at end of a line.
|
2020-09-10 16:48:59 -04:00
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
2019-05-21 17:48:27 -04:00
|
|
|
height
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2019-05-25 00:21:22 -04:00
|
|
|
private def modify_lines(before)
|
2020-10-19 19:39:12 -04:00
|
|
|
return before if before.nil? || before.empty? || simplified_rendering?
|
2019-05-25 00:21:22 -04:00
|
|
|
|
2019-05-30 17:03:18 -04:00
|
|
|
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
|
2020-09-09 04:44:44 -04:00
|
|
|
after.lines("\n").map { |l| l.chomp('') }
|
2019-05-25 00:21:22 -04:00
|
|
|
else
|
|
|
|
before
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-11-19 16:00:25 -05:00
|
|
|
private def show_menu
|
|
|
|
scroll_down(@highest_in_all - @first_line_started_from)
|
|
|
|
@rerender_all = true
|
|
|
|
@menu_info.list.sort!.each do |item|
|
|
|
|
Reline::IOGate.move_cursor_column(0)
|
|
|
|
@output.write item
|
|
|
|
@output.flush
|
|
|
|
scroll_down(1)
|
|
|
|
end
|
|
|
|
scroll_down(@highest_in_all - 1)
|
|
|
|
move_cursor_up(@highest_in_all - 1 - @first_line_started_from)
|
|
|
|
end
|
|
|
|
|
|
|
|
private def clear_screen_buffer(prompt, prompt_list, prompt_width)
|
|
|
|
Reline::IOGate.clear_screen
|
|
|
|
back = 0
|
|
|
|
modify_lines(whole_lines).each_with_index do |line, index|
|
|
|
|
if @prompt_proc
|
|
|
|
pr = prompt_list[index]
|
2020-12-12 21:30:05 -05:00
|
|
|
height = render_partial(pr, calculate_width(pr), line, back, with_control: false)
|
2020-11-19 16:00:25 -05:00
|
|
|
else
|
2020-12-12 21:30:05 -05:00
|
|
|
height = render_partial(prompt, prompt_width, line, back, with_control: false)
|
2020-11-19 16:00:25 -05:00
|
|
|
end
|
|
|
|
if index < (@buffer_of_lines.size - 1)
|
|
|
|
move_cursor_down(height)
|
|
|
|
back += height
|
|
|
|
end
|
|
|
|
end
|
|
|
|
move_cursor_up(back)
|
|
|
|
move_cursor_down(@first_line_started_from + @started_from)
|
2021-04-02 04:18:37 -04:00
|
|
|
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
|
2020-11-19 16:00:25 -05:00
|
|
|
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
|
|
|
|
end
|
|
|
|
|
2019-04-27 01:53:09 -04:00
|
|
|
def editing_mode
|
|
|
|
@config.editing_mode
|
|
|
|
end
|
|
|
|
|
|
|
|
private def menu(target, list)
|
|
|
|
@menu_info = MenuInfo.new(target, list)
|
|
|
|
end
|
|
|
|
|
|
|
|
private def complete_internal_proc(list, is_menu)
|
2019-05-27 19:38:55 -04:00
|
|
|
preposing, target, postposing = retrieve_completion_block
|
2019-05-13 15:08:23 -04:00
|
|
|
list = list.select { |i|
|
2019-12-09 00:20:25 -05:00
|
|
|
if i and not Encoding.compatible?(target.encoding, i.encoding)
|
2019-12-19 22:19:45 -05:00
|
|
|
raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{i.encoding.name}"
|
2019-05-19 15:47:43 -04:00
|
|
|
end
|
2019-12-09 17:01:26 -05:00
|
|
|
if @config.completion_ignore_case
|
2019-12-11 18:40:44 -05:00
|
|
|
i&.downcase&.start_with?(target.downcase)
|
2019-12-09 17:01:26 -05:00
|
|
|
else
|
|
|
|
i&.start_with?(target)
|
|
|
|
end
|
2020-10-14 10:29:17 -04:00
|
|
|
}.uniq
|
2019-04-27 01:53:09 -04:00
|
|
|
if is_menu
|
|
|
|
menu(target, list)
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
completed = list.inject { |memo, item|
|
2019-05-13 13:39:40 -04:00
|
|
|
begin
|
|
|
|
memo_mbchars = memo.unicode_normalize.grapheme_clusters
|
|
|
|
item_mbchars = item.unicode_normalize.grapheme_clusters
|
|
|
|
rescue Encoding::CompatibilityError
|
|
|
|
memo_mbchars = memo.grapheme_clusters
|
|
|
|
item_mbchars = item.grapheme_clusters
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
size = [memo_mbchars.size, item_mbchars.size].min
|
|
|
|
result = ''
|
|
|
|
size.times do |i|
|
2019-12-09 17:01:26 -05:00
|
|
|
if @config.completion_ignore_case
|
|
|
|
if memo_mbchars[i].casecmp?(item_mbchars[i])
|
|
|
|
result << memo_mbchars[i]
|
|
|
|
else
|
|
|
|
break
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
else
|
2019-12-09 17:01:26 -05:00
|
|
|
if memo_mbchars[i] == item_mbchars[i]
|
|
|
|
result << memo_mbchars[i]
|
|
|
|
else
|
|
|
|
break
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
result
|
|
|
|
}
|
|
|
|
[target, preposing, completed, postposing]
|
|
|
|
end
|
|
|
|
|
2019-12-24 04:32:50 -05:00
|
|
|
private def complete(list, just_show_list = false)
|
2019-04-27 01:53:09 -04:00
|
|
|
case @completion_state
|
|
|
|
when CompletionState::NORMAL, CompletionState::JOURNEY
|
|
|
|
@completion_state = CompletionState::COMPLETION
|
|
|
|
when CompletionState::PERFECT_MATCH
|
|
|
|
@dig_perfect_match_proc&.(@perfect_matched)
|
|
|
|
end
|
2019-12-24 04:32:50 -05:00
|
|
|
if just_show_list
|
|
|
|
is_menu = true
|
|
|
|
elsif @completion_state == CompletionState::MENU
|
|
|
|
is_menu = true
|
|
|
|
elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
|
|
|
is_menu = true
|
|
|
|
else
|
|
|
|
is_menu = false
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
result = complete_internal_proc(list, is_menu)
|
2019-12-12 13:57:32 -05:00
|
|
|
if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
|
|
|
|
@completion_state = CompletionState::PERFECT_MATCH
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
return if result.nil?
|
|
|
|
target, preposing, completed, postposing = result
|
|
|
|
return if completed.nil?
|
2019-12-12 13:57:32 -05:00
|
|
|
if target <= completed and (@completion_state == CompletionState::COMPLETION)
|
2019-04-27 01:53:09 -04:00
|
|
|
if list.include?(completed)
|
2019-12-12 13:57:32 -05:00
|
|
|
if list.one?
|
|
|
|
@completion_state = CompletionState::PERFECT_MATCH
|
|
|
|
else
|
|
|
|
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
@perfect_matched = completed
|
2019-12-12 13:57:32 -05:00
|
|
|
else
|
|
|
|
@completion_state = CompletionState::MENU
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2019-12-24 04:32:50 -05:00
|
|
|
if not just_show_list and target < completed
|
2019-10-04 04:03:32 -04:00
|
|
|
@line = preposing + completed + completion_append_character.to_s + postposing
|
|
|
|
line_to_pointer = preposing + completed + completion_append_character.to_s
|
2019-04-27 01:53:09 -04:00
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = calculate_width(line_to_pointer)
|
|
|
|
@byte_pointer = line_to_pointer.bytesize
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def move_completed_list(list, direction)
|
|
|
|
case @completion_state
|
2019-12-12 13:57:32 -05:00
|
|
|
when CompletionState::NORMAL, CompletionState::COMPLETION,
|
|
|
|
CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
|
2019-04-27 01:53:09 -04:00
|
|
|
@completion_state = CompletionState::JOURNEY
|
2019-05-27 19:38:55 -04:00
|
|
|
result = retrieve_completion_block
|
2019-04-27 01:53:09 -04:00
|
|
|
return if result.nil?
|
|
|
|
preposing, target, postposing = result
|
|
|
|
@completion_journey_data = CompletionJourneyData.new(
|
|
|
|
preposing, postposing,
|
|
|
|
[target] + list.select{ |item| item.start_with?(target) }, 0)
|
2021-08-26 12:47:33 -04:00
|
|
|
if @completion_journey_data.list.size == 1
|
|
|
|
@completion_journey_data.pointer = 0
|
|
|
|
else
|
|
|
|
case direction
|
|
|
|
when :up
|
|
|
|
@completion_journey_data.pointer = @completion_journey_data.list.size - 1
|
|
|
|
when :down
|
|
|
|
@completion_journey_data.pointer = 1
|
|
|
|
end
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
@completion_state = CompletionState::JOURNEY
|
|
|
|
else
|
|
|
|
case direction
|
|
|
|
when :up
|
|
|
|
@completion_journey_data.pointer -= 1
|
|
|
|
if @completion_journey_data.pointer < 0
|
|
|
|
@completion_journey_data.pointer = @completion_journey_data.list.size - 1
|
|
|
|
end
|
|
|
|
when :down
|
|
|
|
@completion_journey_data.pointer += 1
|
|
|
|
if @completion_journey_data.pointer >= @completion_journey_data.list.size
|
|
|
|
@completion_journey_data.pointer = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2021-08-26 12:47:33 -04:00
|
|
|
completed = @completion_journey_data.list[@completion_journey_data.pointer]
|
|
|
|
@line = @completion_journey_data.preposing + completed + @completion_journey_data.postposing
|
|
|
|
line_to_pointer = @completion_journey_data.preposing + completed
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = calculate_width(line_to_pointer)
|
|
|
|
@byte_pointer = line_to_pointer.bytesize
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private def run_for_operators(key, method_symbol, &block)
|
|
|
|
if @waiting_operator_proc
|
|
|
|
if VI_MOTIONS.include?(method_symbol)
|
|
|
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
2020-11-14 08:52:38 -05:00
|
|
|
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
|
2020-11-06 22:05:43 -05:00
|
|
|
block.(true)
|
2019-04-27 01:53:09 -04:00
|
|
|
unless @waiting_proc
|
|
|
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
|
|
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
|
|
|
@waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
|
|
|
else
|
|
|
|
old_waiting_proc = @waiting_proc
|
|
|
|
old_waiting_operator_proc = @waiting_operator_proc
|
2020-11-02 00:48:44 -05:00
|
|
|
current_waiting_operator_proc = @waiting_operator_proc
|
2019-11-08 02:17:53 -05:00
|
|
|
@waiting_proc = proc { |k|
|
2019-04-27 01:53:09 -04:00
|
|
|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
|
2019-11-08 02:17:53 -05:00
|
|
|
old_waiting_proc.(k)
|
2019-04-27 01:53:09 -04:00
|
|
|
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
|
|
|
|
@cursor, @byte_pointer = old_cursor, old_byte_pointer
|
2020-11-02 00:48:44 -05:00
|
|
|
current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
|
2019-04-27 01:53:09 -04:00
|
|
|
@waiting_operator_proc = old_waiting_operator_proc
|
|
|
|
}
|
|
|
|
end
|
|
|
|
else
|
|
|
|
# Ignores operator when not motion is given.
|
2020-11-06 22:05:43 -05:00
|
|
|
block.(false)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
@waiting_operator_proc = nil
|
2020-11-14 08:52:38 -05:00
|
|
|
@waiting_operator_vi_arg = nil
|
|
|
|
@vi_arg = nil
|
2019-04-27 01:53:09 -04:00
|
|
|
else
|
2020-11-06 22:05:43 -05:00
|
|
|
block.(false)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-06-02 00:10:03 -04:00
|
|
|
private def argumentable?(method_obj)
|
2020-11-06 22:05:43 -05:00
|
|
|
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
|
2019-06-02 00:10:03 -04:00
|
|
|
end
|
|
|
|
|
2020-11-06 22:05:43 -05:00
|
|
|
private def inclusive?(method_obj)
|
|
|
|
# If a motion method with the keyword argument "inclusive" follows the
|
|
|
|
# operator, it must contain the character at the cursor position.
|
|
|
|
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive }
|
|
|
|
end
|
|
|
|
|
|
|
|
def wrap_method_call(method_symbol, method_obj, key, with_operator = false)
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
if @config.editing_mode_is?(:emacs, :vi_insert) and @waiting_proc.nil? and @waiting_operator_proc.nil?
|
|
|
|
not_insertion = method_symbol != :ed_insert
|
|
|
|
process_insert(force: not_insertion)
|
|
|
|
end
|
2020-11-02 00:55:15 -05:00
|
|
|
if @vi_arg and argumentable?(method_obj)
|
2020-11-06 22:05:43 -05:00
|
|
|
if with_operator and inclusive?(method_obj)
|
|
|
|
method_obj.(key, arg: @vi_arg, inclusive: true)
|
|
|
|
else
|
|
|
|
method_obj.(key, arg: @vi_arg)
|
|
|
|
end
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
else
|
2020-11-06 22:05:43 -05:00
|
|
|
if with_operator and inclusive?(method_obj)
|
|
|
|
method_obj.(key, inclusive: true)
|
|
|
|
else
|
|
|
|
method_obj.(key)
|
|
|
|
end
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-06-01 02:07:22 -04:00
|
|
|
private def process_key(key, method_symbol)
|
|
|
|
if method_symbol and respond_to?(method_symbol, true)
|
|
|
|
method_obj = method(method_symbol)
|
|
|
|
else
|
|
|
|
method_obj = nil
|
|
|
|
end
|
2019-06-01 16:07:04 -04:00
|
|
|
if method_symbol and key.is_a?(Symbol)
|
2019-06-02 00:10:03 -04:00
|
|
|
if @vi_arg and argumentable?(method_obj)
|
2020-11-06 22:05:43 -05:00
|
|
|
run_for_operators(key, method_symbol) do |with_operator|
|
|
|
|
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
2019-06-02 00:10:03 -04:00
|
|
|
end
|
|
|
|
else
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
wrap_method_call(method_symbol, method_obj, key) if method_obj
|
2019-06-02 00:10:03 -04:00
|
|
|
end
|
2019-06-01 16:07:04 -04:00
|
|
|
@kill_ring.process
|
|
|
|
@vi_arg = nil
|
|
|
|
elsif @vi_arg
|
2019-04-27 01:53:09 -04:00
|
|
|
if key.chr =~ /[0-9]/
|
|
|
|
ed_argument_digit(key)
|
|
|
|
else
|
2019-06-02 00:10:03 -04:00
|
|
|
if argumentable?(method_obj)
|
2020-11-06 22:05:43 -05:00
|
|
|
run_for_operators(key, method_symbol) do |with_operator|
|
|
|
|
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
elsif @waiting_proc
|
|
|
|
@waiting_proc.(key)
|
|
|
|
elsif method_obj
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
wrap_method_call(method_symbol, method_obj, key)
|
2019-04-27 01:53:09 -04:00
|
|
|
else
|
2020-11-02 01:22:11 -05:00
|
|
|
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
@kill_ring.process
|
|
|
|
@vi_arg = nil
|
|
|
|
end
|
|
|
|
elsif @waiting_proc
|
|
|
|
@waiting_proc.(key)
|
|
|
|
@kill_ring.process
|
|
|
|
elsif method_obj
|
|
|
|
if method_symbol == :ed_argument_digit
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
wrap_method_call(method_symbol, method_obj, key)
|
2019-04-27 01:53:09 -04:00
|
|
|
else
|
2020-11-06 22:05:43 -05:00
|
|
|
run_for_operators(key, method_symbol) do |with_operator|
|
|
|
|
wrap_method_call(method_symbol, method_obj, key, with_operator)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
@kill_ring.process
|
|
|
|
else
|
2020-11-02 01:22:11 -05:00
|
|
|
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def normal_char(key)
|
|
|
|
method_symbol = method_obj = nil
|
2019-06-01 16:07:04 -04:00
|
|
|
if key.combined_char.is_a?(Symbol)
|
|
|
|
process_key(key.combined_char, key.combined_char)
|
|
|
|
return
|
|
|
|
end
|
2019-05-24 10:38:40 -04:00
|
|
|
@multibyte_buffer << key.combined_char
|
2019-04-27 01:53:09 -04:00
|
|
|
if @multibyte_buffer.size > 1
|
|
|
|
if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding?
|
2019-06-01 02:07:22 -04:00
|
|
|
process_key(@multibyte_buffer.dup.force_encoding(@encoding), nil)
|
2019-04-27 01:53:09 -04:00
|
|
|
@multibyte_buffer.clear
|
|
|
|
else
|
|
|
|
# invalid
|
|
|
|
return
|
|
|
|
end
|
|
|
|
else # single byte
|
2019-05-24 10:38:40 -04:00
|
|
|
return if key.char >= 128 # maybe, first byte of multi byte
|
|
|
|
method_symbol = @config.editing_mode.get_method(key.combined_char)
|
|
|
|
if key.with_meta and method_symbol == :ed_unassigned
|
|
|
|
# split ESC + key
|
|
|
|
method_symbol = @config.editing_mode.get_method("\e".ord)
|
2019-06-01 02:07:22 -04:00
|
|
|
process_key("\e".ord, method_symbol)
|
2019-05-24 10:38:40 -04:00
|
|
|
method_symbol = @config.editing_mode.get_method(key.char)
|
2019-06-01 02:07:22 -04:00
|
|
|
process_key(key.char, method_symbol)
|
2019-05-24 10:38:40 -04:00
|
|
|
else
|
2019-06-01 02:07:22 -04:00
|
|
|
process_key(key.combined_char, method_symbol)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
@multibyte_buffer.clear
|
|
|
|
end
|
|
|
|
if @config.editing_mode_is?(:vi_command) and @cursor > 0 and @cursor == @cursor_max
|
|
|
|
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor -= width
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def input_key(key)
|
2020-11-20 23:08:53 -05:00
|
|
|
@just_cursor_moving = nil
|
2019-11-17 13:23:45 -05:00
|
|
|
if key.char.nil?
|
2019-05-12 12:51:05 -04:00
|
|
|
if @first_char
|
|
|
|
@line = nil
|
|
|
|
end
|
|
|
|
finish
|
|
|
|
return
|
|
|
|
end
|
2020-12-06 03:45:05 -05:00
|
|
|
old_line = @line.dup
|
2019-05-12 12:51:05 -04:00
|
|
|
@first_char = false
|
2019-04-27 01:53:09 -04:00
|
|
|
completion_occurs = false
|
2019-05-24 10:38:40 -04:00
|
|
|
if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord
|
2019-12-04 08:32:12 -05:00
|
|
|
unless @config.disable_completion
|
2019-12-10 21:12:54 -05:00
|
|
|
result = call_completion_proc
|
2019-12-04 08:32:12 -05:00
|
|
|
if result.is_a?(Array)
|
|
|
|
completion_occurs = true
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
process_insert
|
2019-12-04 08:32:12 -05:00
|
|
|
complete(result)
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2019-12-04 08:32:12 -05:00
|
|
|
elsif not @config.disable_completion and @config.editing_mode_is?(:vi_insert) and ["\C-p".ord, "\C-n".ord].include?(key.char)
|
|
|
|
unless @config.disable_completion
|
2019-12-10 21:12:54 -05:00
|
|
|
result = call_completion_proc
|
2019-12-04 08:32:12 -05:00
|
|
|
if result.is_a?(Array)
|
|
|
|
completion_occurs = true
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
process_insert
|
2019-12-04 08:32:12 -05:00
|
|
|
move_completed_list(result, "\C-p".ord == key.char ? :up : :down)
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2019-05-24 10:38:40 -04:00
|
|
|
elsif Symbol === key.char and respond_to?(key.char, true)
|
2019-06-01 02:07:22 -04:00
|
|
|
process_key(key.char, key.char)
|
2019-04-27 01:53:09 -04:00
|
|
|
else
|
|
|
|
normal_char(key)
|
|
|
|
end
|
|
|
|
unless completion_occurs
|
|
|
|
@completion_state = CompletionState::NORMAL
|
2021-08-27 13:29:35 -04:00
|
|
|
@completion_journey_data = nil
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2021-01-25 23:18:05 -05:00
|
|
|
if not @in_pasting and @just_cursor_moving.nil?
|
2020-11-20 03:13:15 -05:00
|
|
|
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
|
|
|
|
@just_cursor_moving = true
|
2020-12-06 03:45:05 -05:00
|
|
|
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line and old_line == @line
|
2020-11-20 03:13:15 -05:00
|
|
|
@just_cursor_moving = true
|
2020-11-20 23:08:53 -05:00
|
|
|
else
|
|
|
|
@just_cursor_moving = false
|
2020-11-20 03:13:15 -05:00
|
|
|
end
|
2020-11-20 23:08:53 -05:00
|
|
|
else
|
|
|
|
@just_cursor_moving = false
|
2020-11-20 03:13:15 -05:00
|
|
|
end
|
2020-10-19 19:39:12 -04:00
|
|
|
if @is_multiline and @auto_indent_proc and not simplified_rendering?
|
2019-06-20 02:56:10 -04:00
|
|
|
process_auto_indent
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-12-10 21:12:54 -05:00
|
|
|
def call_completion_proc
|
|
|
|
result = retrieve_completion_block(true)
|
2021-08-15 04:08:58 -04:00
|
|
|
pre, target, post = result
|
|
|
|
result = call_completion_proc_with_checking_args(pre, target, post)
|
|
|
|
Reline.core.instance_variable_set(:@completion_quote_character, nil)
|
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2021-08-17 06:21:27 -04:00
|
|
|
def call_completion_proc_with_checking_args(pre, target, post)
|
2021-03-23 08:43:39 -04:00
|
|
|
if @completion_proc and target
|
|
|
|
argnum = @completion_proc.parameters.inject(0) { |result, item|
|
|
|
|
case item.first
|
|
|
|
when :req, :opt
|
|
|
|
result + 1
|
|
|
|
when :rest
|
|
|
|
break 3
|
|
|
|
end
|
|
|
|
}
|
|
|
|
case argnum
|
|
|
|
when 1
|
|
|
|
result = @completion_proc.(target)
|
|
|
|
when 2
|
2021-08-15 04:08:58 -04:00
|
|
|
result = @completion_proc.(target, pre)
|
2021-03-23 08:43:39 -04:00
|
|
|
when 3..Float::INFINITY
|
2021-08-15 04:08:58 -04:00
|
|
|
result = @completion_proc.(target, pre, post)
|
2021-03-23 08:43:39 -04:00
|
|
|
end
|
|
|
|
end
|
2019-12-10 21:12:54 -05:00
|
|
|
result
|
|
|
|
end
|
|
|
|
|
2019-06-20 02:56:10 -04:00
|
|
|
private def process_auto_indent
|
|
|
|
return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
|
2019-11-19 20:04:41 -05:00
|
|
|
if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index
|
|
|
|
# Fix indent of a line when a newline is inserted to the next
|
|
|
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
|
|
|
new_indent = @auto_indent_proc.(new_lines[0..-3].push(''), @line_index - 1, 0, true)
|
|
|
|
md = @line.match(/\A */)
|
|
|
|
prev_indent = md[0].count(' ')
|
|
|
|
@line = ' ' * new_indent + @line.lstrip
|
|
|
|
|
|
|
|
new_indent = nil
|
2019-12-02 23:02:01 -05:00
|
|
|
result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[-2].size + 1), false)
|
|
|
|
if result
|
|
|
|
new_indent = result
|
2019-11-19 20:04:41 -05:00
|
|
|
end
|
|
|
|
if new_indent&.>= 0
|
|
|
|
@line = ' ' * new_indent + @line.lstrip
|
|
|
|
end
|
|
|
|
end
|
2019-06-20 02:56:10 -04:00
|
|
|
if @previous_line_index
|
|
|
|
new_lines = whole_lines(index: @previous_line_index, line: @line)
|
|
|
|
else
|
|
|
|
new_lines = whole_lines
|
|
|
|
end
|
|
|
|
new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent)
|
2021-01-05 04:22:00 -05:00
|
|
|
new_indent = @cursor_max if new_indent&.> @cursor_max
|
2019-07-01 07:45:53 -04:00
|
|
|
if new_indent&.>= 0
|
2019-06-26 12:20:44 -04:00
|
|
|
md = new_lines[@line_index].match(/\A */)
|
2019-06-20 02:56:10 -04:00
|
|
|
prev_indent = md[0].count(' ')
|
|
|
|
if @check_new_auto_indent
|
2019-07-04 05:49:42 -04:00
|
|
|
@buffer_of_lines[@line_index] = ' ' * new_indent + @buffer_of_lines[@line_index].lstrip
|
2019-06-20 02:56:10 -04:00
|
|
|
@cursor = new_indent
|
|
|
|
@byte_pointer = new_indent
|
2019-06-18 07:57:58 -04:00
|
|
|
else
|
2019-07-04 05:49:42 -04:00
|
|
|
@line = ' ' * new_indent + @line.lstrip
|
2019-06-26 12:20:44 -04:00
|
|
|
@cursor += new_indent - prev_indent
|
|
|
|
@byte_pointer += new_indent - prev_indent
|
2019-06-18 07:57:58 -04:00
|
|
|
end
|
|
|
|
end
|
2019-06-20 02:56:10 -04:00
|
|
|
@check_new_auto_indent = false
|
2019-05-22 19:23:22 -04:00
|
|
|
end
|
|
|
|
|
2019-12-10 21:12:54 -05:00
|
|
|
def retrieve_completion_block(set_completion_quote_character = false)
|
2021-03-24 01:25:06 -04:00
|
|
|
if Reline.completer_word_break_characters.empty?
|
|
|
|
word_break_regexp = nil
|
|
|
|
else
|
|
|
|
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
|
|
|
|
end
|
|
|
|
if Reline.completer_quote_characters.empty?
|
|
|
|
quote_characters_regexp = nil
|
|
|
|
else
|
|
|
|
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
|
|
|
|
end
|
2019-05-25 11:00:03 -04:00
|
|
|
before = @line.byteslice(0, @byte_pointer)
|
|
|
|
rest = nil
|
2019-05-27 19:38:55 -04:00
|
|
|
break_pointer = nil
|
|
|
|
quote = nil
|
2019-07-27 20:43:26 -04:00
|
|
|
closing_quote = nil
|
|
|
|
escaped_quote = nil
|
2019-05-27 19:38:55 -04:00
|
|
|
i = 0
|
|
|
|
while i < @byte_pointer do
|
|
|
|
slice = @line.byteslice(i, @byte_pointer - i)
|
2019-07-25 04:19:11 -04:00
|
|
|
unless slice.valid_encoding?
|
|
|
|
i += 1
|
|
|
|
next
|
|
|
|
end
|
2019-07-27 20:43:26 -04:00
|
|
|
if quote and slice.start_with?(closing_quote)
|
2019-05-27 19:38:55 -04:00
|
|
|
quote = nil
|
|
|
|
i += 1
|
2019-12-10 21:12:54 -05:00
|
|
|
rest = nil
|
2019-07-27 20:43:26 -04:00
|
|
|
elsif quote and slice.start_with?(escaped_quote)
|
2019-05-27 19:38:55 -04:00
|
|
|
# skip
|
|
|
|
i += 2
|
2021-03-24 01:25:06 -04:00
|
|
|
elsif quote_characters_regexp and slice =~ quote_characters_regexp # find new "
|
2019-12-10 21:12:54 -05:00
|
|
|
rest = $'
|
2019-08-09 03:29:43 -04:00
|
|
|
quote = $&
|
2019-07-27 20:43:26 -04:00
|
|
|
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
|
|
|
|
escaped_quote = /\\#{Regexp.escape(quote)}/
|
2019-05-27 19:38:55 -04:00
|
|
|
i += 1
|
2020-01-05 11:20:24 -05:00
|
|
|
break_pointer = i - 1
|
2021-03-24 01:25:06 -04:00
|
|
|
elsif word_break_regexp and not quote and slice =~ word_break_regexp
|
2019-05-25 11:00:03 -04:00
|
|
|
rest = $'
|
2019-05-27 19:38:55 -04:00
|
|
|
i += 1
|
2019-12-25 04:45:02 -05:00
|
|
|
before = @line.byteslice(i, @byte_pointer - i)
|
2019-05-27 19:38:55 -04:00
|
|
|
break_pointer = i
|
|
|
|
else
|
|
|
|
i += 1
|
2019-05-25 11:00:03 -04:00
|
|
|
end
|
|
|
|
end
|
2019-12-10 21:12:54 -05:00
|
|
|
postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
2019-05-27 19:38:55 -04:00
|
|
|
if rest
|
|
|
|
preposing = @line.byteslice(0, break_pointer)
|
|
|
|
target = rest
|
2019-12-10 21:12:54 -05:00
|
|
|
if set_completion_quote_character and quote
|
|
|
|
Reline.core.instance_variable_set(:@completion_quote_character, quote)
|
|
|
|
if postposing !~ /(?!\\)#{Regexp.escape(quote)}/ # closing quote
|
|
|
|
insert_text(quote)
|
|
|
|
end
|
|
|
|
end
|
2019-05-27 19:38:55 -04:00
|
|
|
else
|
|
|
|
preposing = ''
|
2020-01-05 11:20:24 -05:00
|
|
|
if break_pointer
|
|
|
|
preposing = @line.byteslice(0, break_pointer)
|
|
|
|
else
|
|
|
|
preposing = ''
|
|
|
|
end
|
2019-05-27 19:38:55 -04:00
|
|
|
target = before
|
|
|
|
end
|
2021-03-23 08:43:39 -04:00
|
|
|
if @is_multiline
|
|
|
|
if @previous_line_index
|
|
|
|
lines = whole_lines(index: @previous_line_index, line: @line)
|
|
|
|
else
|
|
|
|
lines = whole_lines
|
|
|
|
end
|
|
|
|
if @line_index > 0
|
|
|
|
preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing
|
|
|
|
end
|
|
|
|
if (lines.size - 1) > @line_index
|
|
|
|
postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n")
|
|
|
|
end
|
|
|
|
end
|
2019-12-10 03:27:43 -05:00
|
|
|
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
|
2019-05-25 11:00:03 -04:00
|
|
|
end
|
|
|
|
|
2019-05-22 19:23:22 -04:00
|
|
|
def confirm_multiline_termination
|
|
|
|
temp_buffer = @buffer_of_lines.dup
|
|
|
|
if @previous_line_index and @line_index == (@buffer_of_lines.size - 1)
|
|
|
|
temp_buffer[@previous_line_index] = @line
|
|
|
|
else
|
|
|
|
temp_buffer[@line_index] = @line
|
|
|
|
end
|
2019-08-21 17:02:21 -04:00
|
|
|
@confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n")
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2019-05-12 13:26:31 -04:00
|
|
|
def insert_text(text)
|
|
|
|
width = calculate_width(text)
|
|
|
|
if @cursor == @cursor_max
|
|
|
|
@line += text
|
|
|
|
else
|
|
|
|
@line = byteinsert(@line, @byte_pointer, text)
|
|
|
|
end
|
|
|
|
@byte_pointer += text.bytesize
|
|
|
|
@cursor += width
|
|
|
|
@cursor_max += width
|
|
|
|
end
|
|
|
|
|
|
|
|
def delete_text(start = nil, length = nil)
|
|
|
|
if start.nil? and length.nil?
|
2021-03-22 11:56:32 -04:00
|
|
|
if @is_multiline
|
|
|
|
if @buffer_of_lines.size == 1
|
|
|
|
@line&.clear
|
|
|
|
@byte_pointer = 0
|
|
|
|
@cursor = 0
|
|
|
|
@cursor_max = 0
|
|
|
|
elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0
|
|
|
|
@buffer_of_lines.pop
|
|
|
|
@line_index -= 1
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
@byte_pointer = 0
|
|
|
|
@cursor = 0
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
elsif @line_index < (@buffer_of_lines.size - 1)
|
|
|
|
@buffer_of_lines.delete_at(@line_index)
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
@byte_pointer = 0
|
|
|
|
@cursor = 0
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
end
|
|
|
|
else
|
|
|
|
@line&.clear
|
|
|
|
@byte_pointer = 0
|
|
|
|
@cursor = 0
|
|
|
|
@cursor_max = 0
|
|
|
|
end
|
2019-05-12 13:26:31 -04:00
|
|
|
elsif not start.nil? and not length.nil?
|
|
|
|
if @line
|
|
|
|
before = @line.byteslice(0, start)
|
|
|
|
after = @line.byteslice(start + length, @line.bytesize)
|
|
|
|
@line = before + after
|
|
|
|
@byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
|
|
|
|
str = @line.byteslice(0, @byte_pointer)
|
|
|
|
@cursor = calculate_width(str)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
end
|
|
|
|
elsif start.is_a?(Range)
|
|
|
|
range = start
|
|
|
|
first = range.first
|
|
|
|
last = range.last
|
|
|
|
last = @line.bytesize - 1 if last > @line.bytesize
|
|
|
|
last += @line.bytesize if last < 0
|
|
|
|
first += @line.bytesize if first < 0
|
|
|
|
range = range.exclude_end? ? first...last : first..last
|
|
|
|
@line = @line.bytes.reject.with_index{ |c, i| range.include?(i) }.map{ |c| c.chr(Encoding::ASCII_8BIT) }.join.force_encoding(@encoding)
|
|
|
|
@byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
|
|
|
|
str = @line.byteslice(0, @byte_pointer)
|
|
|
|
@cursor = calculate_width(str)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
else
|
|
|
|
@line = @line.byteslice(0, start)
|
|
|
|
@byte_pointer = @line.bytesize if @byte_pointer > @line.bytesize
|
|
|
|
str = @line.byteslice(0, @byte_pointer)
|
|
|
|
@cursor = calculate_width(str)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def byte_pointer=(val)
|
|
|
|
@byte_pointer = val
|
|
|
|
str = @line.byteslice(0, @byte_pointer)
|
|
|
|
@cursor = calculate_width(str)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
end
|
|
|
|
|
2019-05-25 00:21:22 -04:00
|
|
|
def whole_lines(index: @line_index, line: @line)
|
2019-04-27 01:53:09 -04:00
|
|
|
temp_lines = @buffer_of_lines.dup
|
2019-05-25 00:21:22 -04:00
|
|
|
temp_lines[index] = line
|
2019-05-22 19:23:22 -04:00
|
|
|
temp_lines
|
|
|
|
end
|
|
|
|
|
|
|
|
def whole_buffer
|
2019-04-27 01:53:09 -04:00
|
|
|
if @buffer_of_lines.size == 1 and @line.nil?
|
|
|
|
nil
|
|
|
|
else
|
2021-02-05 07:39:29 -05:00
|
|
|
if @previous_line_index
|
|
|
|
whole_lines(index: @previous_line_index, line: @line).join("\n")
|
|
|
|
else
|
|
|
|
whole_lines.join("\n")
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def finished?
|
|
|
|
@finished
|
|
|
|
end
|
|
|
|
|
|
|
|
def finish
|
|
|
|
@finished = true
|
2020-10-19 19:39:12 -04:00
|
|
|
@rerender_all = true
|
2019-04-27 01:53:09 -04:00
|
|
|
@config.reset
|
|
|
|
end
|
|
|
|
|
|
|
|
private def byteslice!(str, byte_pointer, size)
|
|
|
|
new_str = str.byteslice(0, byte_pointer)
|
|
|
|
new_str << str.byteslice(byte_pointer + size, str.bytesize)
|
|
|
|
[new_str, str.byteslice(byte_pointer, size)]
|
|
|
|
end
|
|
|
|
|
|
|
|
private def byteinsert(str, byte_pointer, other)
|
|
|
|
new_str = str.byteslice(0, byte_pointer)
|
|
|
|
new_str << other
|
|
|
|
new_str << str.byteslice(byte_pointer, str.bytesize)
|
|
|
|
new_str
|
|
|
|
end
|
|
|
|
|
2019-05-27 11:15:38 -04:00
|
|
|
private def calculate_width(str, allow_escape_code = false)
|
2020-08-27 13:09:34 -04:00
|
|
|
Reline::Unicode.calculate_width(str, allow_escape_code)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2019-05-21 04:46:31 -04:00
|
|
|
private def key_delete(key)
|
|
|
|
if @config.editing_mode_is?(:vi_insert, :emacs)
|
|
|
|
ed_delete_next_char(key)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-05-24 10:38:40 -04:00
|
|
|
private def key_newline(key)
|
|
|
|
if @is_multiline
|
2020-11-19 08:59:16 -05:00
|
|
|
if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer
|
|
|
|
@add_newline_to_end_of_buffer = true
|
|
|
|
end
|
2019-05-24 10:38:40 -04:00
|
|
|
next_line = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
|
|
|
|
cursor_line = @line.byteslice(0, @byte_pointer)
|
|
|
|
insert_new_line(cursor_line, next_line)
|
|
|
|
@cursor = 0
|
2021-01-25 23:18:05 -05:00
|
|
|
@check_new_auto_indent = true unless @in_pasting
|
2019-05-24 10:38:40 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-09-07 14:26:09 -04:00
|
|
|
private def ed_unassigned(key) end # do nothing
|
|
|
|
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
private def process_insert(force: false)
|
2021-01-25 23:18:05 -05:00
|
|
|
return if @continuous_insertion_buffer.empty? or (@in_pasting and not force)
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
width = Reline::Unicode.calculate_width(@continuous_insertion_buffer)
|
|
|
|
bytesize = @continuous_insertion_buffer.bytesize
|
|
|
|
if @cursor == @cursor_max
|
|
|
|
@line += @continuous_insertion_buffer
|
|
|
|
else
|
|
|
|
@line = byteinsert(@line, @byte_pointer, @continuous_insertion_buffer)
|
|
|
|
end
|
|
|
|
@byte_pointer += bytesize
|
|
|
|
@cursor += width
|
|
|
|
@cursor_max += width
|
|
|
|
@continuous_insertion_buffer.clear
|
|
|
|
end
|
|
|
|
|
2019-04-27 01:53:09 -04:00
|
|
|
private def ed_insert(key)
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
str = nil
|
|
|
|
width = nil
|
|
|
|
bytesize = nil
|
2019-04-27 01:53:09 -04:00
|
|
|
if key.instance_of?(String)
|
2019-12-27 02:02:07 -05:00
|
|
|
begin
|
|
|
|
key.encode(Encoding::UTF_8)
|
|
|
|
rescue Encoding::UndefinedConversionError
|
|
|
|
return
|
|
|
|
end
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
str = key
|
|
|
|
bytesize = key.bytesize
|
2019-04-27 01:53:09 -04:00
|
|
|
else
|
2019-12-27 02:02:07 -05:00
|
|
|
begin
|
|
|
|
key.chr.encode(Encoding::UTF_8)
|
|
|
|
rescue Encoding::UndefinedConversionError
|
|
|
|
return
|
|
|
|
end
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
str = key.chr
|
|
|
|
bytesize = 1
|
|
|
|
end
|
2021-01-25 23:18:05 -05:00
|
|
|
if @in_pasting
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
@continuous_insertion_buffer << str
|
|
|
|
return
|
2020-11-08 07:56:27 -05:00
|
|
|
elsif not @continuous_insertion_buffer.empty?
|
|
|
|
process_insert
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
end
|
|
|
|
width = Reline::Unicode.get_mbchar_width(str)
|
|
|
|
if @cursor == @cursor_max
|
|
|
|
@line += str
|
|
|
|
else
|
|
|
|
@line = byteinsert(@line, @byte_pointer, str)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2020-12-11 08:18:36 -05:00
|
|
|
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
@byte_pointer += bytesize
|
2020-12-11 08:18:36 -05:00
|
|
|
last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size)
|
|
|
|
if last_byte_size != 0 and (last_mbchar + str).grapheme_clusters.size == 1
|
|
|
|
width = 0
|
|
|
|
end
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
@cursor += width
|
|
|
|
@cursor_max += width
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
alias_method :ed_digit, :ed_insert
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :self_insert, :ed_insert
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_quoted_insert(str, arg: 1)
|
|
|
|
@waiting_proc = proc { |key|
|
|
|
|
arg.times do
|
2019-05-22 19:33:20 -04:00
|
|
|
if key == "\C-j".ord or key == "\C-m".ord
|
2019-05-24 12:13:02 -04:00
|
|
|
key_newline(key)
|
2019-05-22 19:33:20 -04:00
|
|
|
else
|
|
|
|
ed_insert(key)
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
@waiting_proc = nil
|
|
|
|
}
|
|
|
|
end
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :quoted_insert, :ed_quoted_insert
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_next_char(key, arg: 1)
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
if (@byte_pointer < @line.bytesize)
|
|
|
|
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor += width if width
|
|
|
|
@byte_pointer += byte_size
|
2019-05-26 17:04:42 -04:00
|
|
|
elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == @line.bytesize and @line_index < @buffer_of_lines.size - 1
|
|
|
|
next_line = @buffer_of_lines[@line_index + 1]
|
|
|
|
@cursor = 0
|
|
|
|
@byte_pointer = 0
|
|
|
|
@cursor_max = calculate_width(next_line)
|
|
|
|
@previous_line_index = @line_index
|
|
|
|
@line_index += 1
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
ed_next_char(key, arg: arg) if arg > 0
|
|
|
|
end
|
2019-06-02 14:41:40 -04:00
|
|
|
alias_method :forward_char, :ed_next_char
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_prev_char(key, arg: 1)
|
|
|
|
if @cursor > 0
|
|
|
|
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor -= width
|
2019-05-26 17:04:42 -04:00
|
|
|
elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0
|
|
|
|
prev_line = @buffer_of_lines[@line_index - 1]
|
|
|
|
@cursor = calculate_width(prev_line)
|
|
|
|
@byte_pointer = prev_line.bytesize
|
|
|
|
@cursor_max = calculate_width(prev_line)
|
|
|
|
@previous_line_index = @line_index
|
|
|
|
@line_index -= 1
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
ed_prev_char(key, arg: arg) if arg > 0
|
|
|
|
end
|
2020-11-09 01:55:53 -05:00
|
|
|
alias_method :backward_char, :ed_prev_char
|
2019-04-27 01:53:09 -04:00
|
|
|
|
2019-06-02 14:29:19 -04:00
|
|
|
private def vi_first_print(key)
|
|
|
|
@byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
|
|
|
|
end
|
|
|
|
|
2019-04-27 01:53:09 -04:00
|
|
|
private def ed_move_to_beg(key)
|
2019-06-02 14:29:19 -04:00
|
|
|
@byte_pointer = @cursor = 0
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2019-06-02 14:41:40 -04:00
|
|
|
alias_method :beginning_of_line, :ed_move_to_beg
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_move_to_end(key)
|
|
|
|
@byte_pointer = 0
|
|
|
|
@cursor = 0
|
|
|
|
byte_size = 0
|
|
|
|
while @byte_pointer < @line.bytesize
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
if byte_size > 0
|
|
|
|
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
@cursor += Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
end
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
end
|
|
|
|
end
|
2019-06-02 14:41:40 -04:00
|
|
|
alias_method :end_of_line, :ed_move_to_end
|
2019-04-27 01:53:09 -04:00
|
|
|
|
2019-12-16 22:47:09 -05:00
|
|
|
private def generate_searcher
|
|
|
|
Fiber.new do |first_key|
|
|
|
|
prev_search_key = first_key
|
2019-05-12 12:51:05 -04:00
|
|
|
search_word = String.new(encoding: @encoding)
|
|
|
|
multibyte_buf = String.new(encoding: 'ASCII-8BIT')
|
|
|
|
last_hit = nil
|
2019-12-16 22:47:09 -05:00
|
|
|
case first_key
|
|
|
|
when "\C-r".ord
|
|
|
|
prompt_name = 'reverse-i-search'
|
|
|
|
when "\C-s".ord
|
|
|
|
prompt_name = 'i-search'
|
|
|
|
end
|
2019-05-12 12:51:05 -04:00
|
|
|
loop do
|
|
|
|
key = Fiber.yield(search_word)
|
2019-12-01 14:17:47 -05:00
|
|
|
search_again = false
|
2019-05-12 12:51:05 -04:00
|
|
|
case key
|
2019-12-02 11:17:07 -05:00
|
|
|
when -1 # determined
|
|
|
|
Reline.last_incremental_search = search_word
|
|
|
|
break
|
2019-12-01 13:30:38 -05:00
|
|
|
when "\C-h".ord, "\C-?".ord
|
2019-05-12 12:51:05 -04:00
|
|
|
grapheme_clusters = search_word.grapheme_clusters
|
|
|
|
if grapheme_clusters.size > 0
|
|
|
|
grapheme_clusters.pop
|
|
|
|
search_word = grapheme_clusters.join
|
|
|
|
end
|
2019-12-16 22:47:09 -05:00
|
|
|
when "\C-r".ord, "\C-s".ord
|
2019-12-17 00:20:51 -05:00
|
|
|
search_again = true if prev_search_key == key
|
2019-12-16 22:47:09 -05:00
|
|
|
prev_search_key = key
|
2019-05-12 12:51:05 -04:00
|
|
|
else
|
|
|
|
multibyte_buf << key
|
|
|
|
if multibyte_buf.dup.force_encoding(@encoding).valid_encoding?
|
|
|
|
search_word << multibyte_buf.dup.force_encoding(@encoding)
|
|
|
|
multibyte_buf.clear
|
|
|
|
end
|
|
|
|
end
|
|
|
|
hit = nil
|
2019-12-02 11:17:07 -05:00
|
|
|
if not search_word.empty? and @line_backup_in_history&.include?(search_word)
|
2019-05-12 12:51:05 -04:00
|
|
|
@history_pointer = nil
|
|
|
|
hit = @line_backup_in_history
|
|
|
|
else
|
2019-12-01 14:17:47 -05:00
|
|
|
if search_again
|
2019-12-02 11:17:07 -05:00
|
|
|
if search_word.empty? and Reline.last_incremental_search
|
|
|
|
search_word = Reline.last_incremental_search
|
|
|
|
end
|
2020-05-07 10:44:38 -04:00
|
|
|
if @history_pointer
|
2019-12-16 22:47:09 -05:00
|
|
|
case prev_search_key
|
|
|
|
when "\C-r".ord
|
|
|
|
history_pointer_base = 0
|
2019-12-16 23:47:06 -05:00
|
|
|
history = Reline::HISTORY[0..(@history_pointer - 1)]
|
2019-12-16 22:47:09 -05:00
|
|
|
when "\C-s".ord
|
2019-12-16 23:47:06 -05:00
|
|
|
history_pointer_base = @history_pointer + 1
|
|
|
|
history = Reline::HISTORY[(@history_pointer + 1)..-1]
|
2019-12-16 22:47:09 -05:00
|
|
|
end
|
2019-12-02 11:17:07 -05:00
|
|
|
else
|
2019-12-16 22:47:09 -05:00
|
|
|
history_pointer_base = 0
|
2019-12-02 11:17:07 -05:00
|
|
|
history = Reline::HISTORY
|
2019-12-01 14:17:47 -05:00
|
|
|
end
|
|
|
|
elsif @history_pointer
|
2019-12-16 22:47:09 -05:00
|
|
|
case prev_search_key
|
|
|
|
when "\C-r".ord
|
|
|
|
history_pointer_base = 0
|
2019-12-16 23:47:06 -05:00
|
|
|
history = Reline::HISTORY[0..@history_pointer]
|
2019-12-16 22:47:09 -05:00
|
|
|
when "\C-s".ord
|
2019-12-16 23:47:06 -05:00
|
|
|
history_pointer_base = @history_pointer
|
|
|
|
history = Reline::HISTORY[@history_pointer..-1]
|
2019-12-16 22:47:09 -05:00
|
|
|
end
|
2019-12-01 09:53:59 -05:00
|
|
|
else
|
2019-12-16 22:47:09 -05:00
|
|
|
history_pointer_base = 0
|
2019-12-01 09:53:59 -05:00
|
|
|
history = Reline::HISTORY
|
|
|
|
end
|
2019-12-16 22:47:09 -05:00
|
|
|
case prev_search_key
|
|
|
|
when "\C-r".ord
|
|
|
|
hit_index = history.rindex { |item|
|
|
|
|
item.include?(search_word)
|
|
|
|
}
|
|
|
|
when "\C-s".ord
|
|
|
|
hit_index = history.index { |item|
|
|
|
|
item.include?(search_word)
|
|
|
|
}
|
|
|
|
end
|
2019-05-12 12:51:05 -04:00
|
|
|
if hit_index
|
2019-12-16 22:47:09 -05:00
|
|
|
@history_pointer = history_pointer_base + hit_index
|
2019-05-12 12:51:05 -04:00
|
|
|
hit = Reline::HISTORY[@history_pointer]
|
|
|
|
end
|
|
|
|
end
|
2019-12-16 22:47:09 -05:00
|
|
|
case prev_search_key
|
|
|
|
when "\C-r".ord
|
|
|
|
prompt_name = 'reverse-i-search'
|
|
|
|
when "\C-s".ord
|
|
|
|
prompt_name = 'i-search'
|
|
|
|
end
|
2019-05-12 12:51:05 -04:00
|
|
|
if hit
|
2019-10-16 09:34:58 -04:00
|
|
|
if @is_multiline
|
|
|
|
@buffer_of_lines = hit.split("\n")
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
|
|
|
@line_index = @buffer_of_lines.size - 1
|
|
|
|
@line = @buffer_of_lines.last
|
|
|
|
@rerender_all = true
|
2019-12-16 22:47:09 -05:00
|
|
|
@searching_prompt = "(%s)`%s'" % [prompt_name, search_word]
|
2019-10-16 09:34:58 -04:00
|
|
|
else
|
|
|
|
@line = hit
|
2019-12-16 22:47:09 -05:00
|
|
|
@searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
|
2019-10-16 09:34:58 -04:00
|
|
|
end
|
2019-05-12 12:51:05 -04:00
|
|
|
last_hit = hit
|
|
|
|
else
|
2019-10-16 09:34:58 -04:00
|
|
|
if @is_multiline
|
|
|
|
@rerender_all = true
|
2019-12-16 22:47:09 -05:00
|
|
|
@searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
|
2019-10-16 09:34:58 -04:00
|
|
|
else
|
2019-12-16 22:47:09 -05:00
|
|
|
@searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
|
2019-10-16 09:34:58 -04:00
|
|
|
end
|
2019-05-12 12:51:05 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-12-16 22:47:09 -05:00
|
|
|
end
|
|
|
|
|
2020-04-14 13:10:27 -04:00
|
|
|
private def incremental_search_history(key)
|
2019-12-16 22:47:09 -05:00
|
|
|
unless @history_pointer
|
|
|
|
if @is_multiline
|
|
|
|
@line_backup_in_history = whole_buffer
|
|
|
|
else
|
|
|
|
@line_backup_in_history = @line
|
|
|
|
end
|
|
|
|
end
|
|
|
|
searcher = generate_searcher
|
|
|
|
searcher.resume(key)
|
2019-05-12 12:51:05 -04:00
|
|
|
@searching_prompt = "(reverse-i-search)`': "
|
2020-12-21 14:38:01 -05:00
|
|
|
termination_keys = ["\C-j".ord]
|
|
|
|
termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators
|
2019-11-08 02:17:53 -05:00
|
|
|
@waiting_proc = ->(k) {
|
|
|
|
case k
|
2020-12-21 14:38:01 -05:00
|
|
|
when *termination_keys
|
2019-05-12 12:51:05 -04:00
|
|
|
if @history_pointer
|
2019-12-02 11:17:07 -05:00
|
|
|
buffer = Reline::HISTORY[@history_pointer]
|
2019-05-12 12:51:05 -04:00
|
|
|
else
|
2019-12-02 11:17:07 -05:00
|
|
|
buffer = @line_backup_in_history
|
|
|
|
end
|
|
|
|
if @is_multiline
|
|
|
|
@buffer_of_lines = buffer.split("\n")
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
|
|
|
@line_index = @buffer_of_lines.size - 1
|
|
|
|
@line = @buffer_of_lines.last
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
@line = buffer
|
2019-05-12 12:51:05 -04:00
|
|
|
end
|
|
|
|
@searching_prompt = nil
|
|
|
|
@waiting_proc = nil
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = @byte_pointer = 0
|
2020-12-21 09:02:46 -05:00
|
|
|
@rerender_all = true
|
|
|
|
@cached_prompt_list = nil
|
2019-12-02 11:17:07 -05:00
|
|
|
searcher.resume(-1)
|
2019-05-12 12:51:05 -04:00
|
|
|
when "\C-g".ord
|
2019-12-02 11:17:07 -05:00
|
|
|
if @is_multiline
|
|
|
|
@buffer_of_lines = @line_backup_in_history.split("\n")
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
|
|
|
@line_index = @buffer_of_lines.size - 1
|
|
|
|
@line = @buffer_of_lines.last
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
@line = @line_backup_in_history
|
|
|
|
end
|
2019-05-12 12:51:05 -04:00
|
|
|
@history_pointer = nil
|
|
|
|
@searching_prompt = nil
|
|
|
|
@waiting_proc = nil
|
|
|
|
@line_backup_in_history = nil
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = @byte_pointer = 0
|
2019-12-02 11:17:07 -05:00
|
|
|
@rerender_all = true
|
2019-05-12 12:51:05 -04:00
|
|
|
else
|
2019-11-08 02:17:53 -05:00
|
|
|
chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT)
|
2019-12-16 22:47:09 -05:00
|
|
|
if chr.match?(/[[:print:]]/) or k == "\C-h".ord or k == "\C-?".ord or k == "\C-r".ord or k == "\C-s".ord
|
2019-11-08 02:17:53 -05:00
|
|
|
searcher.resume(k)
|
2019-05-12 12:51:05 -04:00
|
|
|
else
|
|
|
|
if @history_pointer
|
2019-10-17 11:41:17 -04:00
|
|
|
line = Reline::HISTORY[@history_pointer]
|
|
|
|
else
|
|
|
|
line = @line_backup_in_history
|
|
|
|
end
|
|
|
|
if @is_multiline
|
|
|
|
@line_backup_in_history = whole_buffer
|
|
|
|
@buffer_of_lines = line.split("\n")
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
|
|
|
@line_index = @buffer_of_lines.size - 1
|
|
|
|
@line = @buffer_of_lines.last
|
|
|
|
@rerender_all = true
|
2019-05-12 12:51:05 -04:00
|
|
|
else
|
2019-10-17 11:41:17 -04:00
|
|
|
@line_backup_in_history = @line
|
|
|
|
@line = line
|
2019-05-12 12:51:05 -04:00
|
|
|
end
|
|
|
|
@searching_prompt = nil
|
|
|
|
@waiting_proc = nil
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = @byte_pointer = 0
|
2020-12-21 09:02:46 -05:00
|
|
|
@rerender_all = true
|
|
|
|
@cached_prompt_list = nil
|
2019-12-02 11:17:07 -05:00
|
|
|
searcher.resume(-1)
|
2019-05-12 12:51:05 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2020-04-14 13:23:24 -04:00
|
|
|
private def vi_search_prev(key)
|
2020-04-14 13:10:27 -04:00
|
|
|
incremental_search_history(key)
|
2019-12-16 22:47:09 -05:00
|
|
|
end
|
2020-04-14 13:23:24 -04:00
|
|
|
alias_method :reverse_search_history, :vi_search_prev
|
2019-12-16 22:47:09 -05:00
|
|
|
|
2020-04-14 13:23:24 -04:00
|
|
|
private def vi_search_next(key)
|
2020-04-14 13:10:27 -04:00
|
|
|
incremental_search_history(key)
|
2019-05-12 12:51:05 -04:00
|
|
|
end
|
2020-04-14 13:23:24 -04:00
|
|
|
alias_method :forward_search_history, :vi_search_next
|
2019-05-12 12:51:05 -04:00
|
|
|
|
2020-04-16 14:03:12 -04:00
|
|
|
private def ed_search_prev_history(key, arg: 1)
|
|
|
|
history = nil
|
|
|
|
h_pointer = nil
|
|
|
|
line_no = nil
|
|
|
|
substr = @line.slice(0, @byte_pointer)
|
|
|
|
if @history_pointer.nil?
|
|
|
|
return if not @line.empty? and substr.empty?
|
|
|
|
history = Reline::HISTORY
|
|
|
|
elsif @history_pointer.zero?
|
|
|
|
history = nil
|
|
|
|
h_pointer = nil
|
|
|
|
else
|
|
|
|
history = Reline::HISTORY.slice(0, @history_pointer)
|
|
|
|
end
|
|
|
|
return if history.nil?
|
|
|
|
if @is_multiline
|
|
|
|
h_pointer = history.rindex { |h|
|
|
|
|
h.split("\n").each_with_index { |l, i|
|
|
|
|
if l.start_with?(substr)
|
|
|
|
line_no = i
|
|
|
|
break
|
|
|
|
end
|
|
|
|
}
|
|
|
|
not line_no.nil?
|
|
|
|
}
|
|
|
|
else
|
|
|
|
h_pointer = history.rindex { |l|
|
|
|
|
l.start_with?(substr)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
return if h_pointer.nil?
|
|
|
|
@history_pointer = h_pointer
|
|
|
|
if @is_multiline
|
|
|
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
|
|
|
@line_index = line_no
|
2021-01-08 10:40:10 -05:00
|
|
|
@line = @buffer_of_lines[@line_index]
|
2020-04-16 14:03:12 -04:00
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
@line = Reline::HISTORY[@history_pointer]
|
|
|
|
end
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
arg -= 1
|
|
|
|
ed_search_prev_history(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
alias_method :history_search_backward, :ed_search_prev_history
|
|
|
|
|
2020-04-16 15:38:47 -04:00
|
|
|
private def ed_search_next_history(key, arg: 1)
|
|
|
|
substr = @line.slice(0, @byte_pointer)
|
|
|
|
if @history_pointer.nil?
|
|
|
|
return
|
|
|
|
elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty?
|
|
|
|
return
|
|
|
|
end
|
|
|
|
history = Reline::HISTORY.slice((@history_pointer + 1)..-1)
|
|
|
|
h_pointer = nil
|
|
|
|
line_no = nil
|
|
|
|
if @is_multiline
|
|
|
|
h_pointer = history.index { |h|
|
|
|
|
h.split("\n").each_with_index { |l, i|
|
|
|
|
if l.start_with?(substr)
|
|
|
|
line_no = i
|
|
|
|
break
|
|
|
|
end
|
|
|
|
}
|
|
|
|
not line_no.nil?
|
|
|
|
}
|
|
|
|
else
|
|
|
|
h_pointer = history.index { |l|
|
|
|
|
l.start_with?(substr)
|
|
|
|
}
|
|
|
|
end
|
|
|
|
h_pointer += @history_pointer + 1 if h_pointer and @history_pointer
|
|
|
|
return if h_pointer.nil? and not substr.empty?
|
|
|
|
@history_pointer = h_pointer
|
|
|
|
if @is_multiline
|
|
|
|
if @history_pointer.nil? and substr.empty?
|
|
|
|
@buffer_of_lines = []
|
|
|
|
@line_index = 0
|
|
|
|
else
|
|
|
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
|
|
|
@line_index = line_no
|
|
|
|
end
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2021-01-08 10:40:10 -05:00
|
|
|
@line = @buffer_of_lines[@line_index]
|
2020-04-16 15:38:47 -04:00
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
if @history_pointer.nil? and substr.empty?
|
|
|
|
@line = ''
|
|
|
|
else
|
|
|
|
@line = Reline::HISTORY[@history_pointer]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
arg -= 1
|
|
|
|
ed_search_next_history(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
alias_method :history_search_forward, :ed_search_next_history
|
|
|
|
|
2019-04-27 01:53:09 -04:00
|
|
|
private def ed_prev_history(key, arg: 1)
|
|
|
|
if @is_multiline and @line_index > 0
|
|
|
|
@previous_line_index = @line_index
|
|
|
|
@line_index -= 1
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if Reline::HISTORY.empty?
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if @history_pointer.nil?
|
|
|
|
@history_pointer = Reline::HISTORY.size - 1
|
|
|
|
if @is_multiline
|
|
|
|
@line_backup_in_history = whole_buffer
|
|
|
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2019-05-26 19:13:48 -04:00
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2019-04-27 01:53:09 -04:00
|
|
|
@line_index = @buffer_of_lines.size - 1
|
|
|
|
@line = @buffer_of_lines.last
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
@line_backup_in_history = @line
|
|
|
|
@line = Reline::HISTORY[@history_pointer]
|
|
|
|
end
|
|
|
|
elsif @history_pointer.zero?
|
|
|
|
return
|
|
|
|
else
|
|
|
|
if @is_multiline
|
|
|
|
Reline::HISTORY[@history_pointer] = whole_buffer
|
|
|
|
@history_pointer -= 1
|
|
|
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2019-05-26 19:13:48 -04:00
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2019-04-27 01:53:09 -04:00
|
|
|
@line_index = @buffer_of_lines.size - 1
|
|
|
|
@line = @buffer_of_lines.last
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
Reline::HISTORY[@history_pointer] = @line
|
|
|
|
@history_pointer -= 1
|
|
|
|
@line = Reline::HISTORY[@history_pointer]
|
|
|
|
end
|
|
|
|
end
|
2019-09-13 12:03:34 -04:00
|
|
|
if @config.editing_mode_is?(:emacs, :vi_insert)
|
2019-04-27 01:53:09 -04:00
|
|
|
@cursor_max = @cursor = calculate_width(@line)
|
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
elsif @config.editing_mode_is?(:vi_command)
|
|
|
|
@byte_pointer = @cursor = 0
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
ed_prev_history(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def ed_next_history(key, arg: 1)
|
|
|
|
if @is_multiline and @line_index < (@buffer_of_lines.size - 1)
|
|
|
|
@previous_line_index = @line_index
|
|
|
|
@line_index += 1
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if @history_pointer.nil?
|
|
|
|
return
|
|
|
|
elsif @history_pointer == (Reline::HISTORY.size - 1)
|
|
|
|
if @is_multiline
|
|
|
|
@history_pointer = nil
|
|
|
|
@buffer_of_lines = @line_backup_in_history.split("\n")
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
|
|
|
@line_index = 0
|
|
|
|
@line = @buffer_of_lines.first
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
@history_pointer = nil
|
|
|
|
@line = @line_backup_in_history
|
|
|
|
end
|
|
|
|
else
|
|
|
|
if @is_multiline
|
|
|
|
Reline::HISTORY[@history_pointer] = whole_buffer
|
|
|
|
@history_pointer += 1
|
|
|
|
@buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n")
|
2019-05-26 19:13:48 -04:00
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
2019-04-27 01:53:09 -04:00
|
|
|
@line_index = 0
|
|
|
|
@line = @buffer_of_lines.first
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
Reline::HISTORY[@history_pointer] = @line
|
|
|
|
@history_pointer += 1
|
|
|
|
@line = Reline::HISTORY[@history_pointer]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
@line = '' unless @line
|
2019-09-07 16:56:29 -04:00
|
|
|
if @config.editing_mode_is?(:emacs, :vi_insert)
|
2019-04-27 01:53:09 -04:00
|
|
|
@cursor_max = @cursor = calculate_width(@line)
|
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
elsif @config.editing_mode_is?(:vi_command)
|
|
|
|
@byte_pointer = @cursor = 0
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
ed_next_history(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def ed_newline(key)
|
[ruby/reline] Continuous insertion buffering
The rendering time in IRB has been reduced as follows:
start = Time.now
[{"_id"=>"5f9072a4589a06d2d74b6028",
"index"=>0,
"guid"=>"6b3051e2-dbc7-4537-bdb9-6cd7bb5358a7",
"isActive"=>true,
"balance"=>"$1,442.84",
"picture"=>"http://placehold.it/32x32",
"age"=>34,
"eyeColor"=>"blue",
"name"=>{"first"=>"Ward", "last"=>"Levy"},
"company"=>"HYPLEX",
"email"=>"ward.levy@hyplex.us",
"phone"=>"+1 (867) 568-3319",
"address"=>"867 Cobek Court, Clara, Maryland, 3254",
"about"=>
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing.",
"registered"=>"Monday, May 25, 2015 6:51 AM",
"latitude"=>"16.001127",
"longitude"=>"-72.377848",
"tags"=>["dolore", "nostrud", "occaecat", "cillum", "nisi"],
"range"=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
"friends"=>
[{"id"=>0, "name"=>"Alison Bryant"},
{"id"=>1, "name"=>"Ester Espinoza"},
{"id"=>2, "name"=>"Sullivan Kane"}],
"greeting"=>"Hello, Ward! You have 7 unread messages.",
"favoriteFruit"=>"apple"}]
puts "Duration: #{Time.now - start} seconds"
2.17sec -> 0.92sec
start = Time.now
"Exercitation eu ex aliqua sit. Pariatur aliquip incididunt sint id non consectetur ullamco Lorem ea mollit duis amet sint labore. Commodo laborum labore commodo officia in cillum adipisicing esse excepteur cupidatat adipisicing ut. Non esse incididunt voluptate aliquip cillum eu aute duis laboris sit et. Amet enim quis tempor occaecat excepteur exercitation excepteur deserunt amet cillum adipisicing."
puts "Duration: #{Time.now - start} seconds"
1.57sec -> 0.22sec
start = Time.now
def each_top_level_statement
initialize_input
catch(:TERM_INPUT) do
loop do
begin
prompt
unless l = lex
throw :TERM_INPUT if @line == ''
else
@line_no += l.count("\n")
next if l == "\n"
@line.concat l
if @code_block_open or @ltype or @continue or @indent > 0
next
end
end
if @line != "\n"
@line.force_encoding(@io.encoding)
yield @line, @exp_line_no
end
break if @io.eof?
@line = ''
@exp_line_no = @line_no
@indent = 0
rescue TerminateLineInput
initialize_input
prompt
end
end
end
end
puts "Duration: #{Time.now - start} seconds"
0.88sec -> 0.77sec
https://github.com/ruby/reline/commit/7d87ac5a12
2020-10-31 03:45:15 -04:00
|
|
|
process_insert(force: true)
|
2019-04-27 01:53:09 -04:00
|
|
|
if @is_multiline
|
|
|
|
if @config.editing_mode_is?(:vi_command)
|
|
|
|
if @line_index < (@buffer_of_lines.size - 1)
|
2019-05-22 19:23:22 -04:00
|
|
|
ed_next_history(key) # means cursor down
|
2019-04-27 01:53:09 -04:00
|
|
|
else
|
2019-05-24 13:13:14 -04:00
|
|
|
# should check confirm_multiline_termination to finish?
|
|
|
|
finish
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
else
|
2019-05-24 13:13:14 -04:00
|
|
|
if @line_index == (@buffer_of_lines.size - 1)
|
|
|
|
if confirm_multiline_termination
|
|
|
|
finish
|
|
|
|
else
|
|
|
|
key_newline(key)
|
|
|
|
end
|
2019-05-22 19:23:22 -04:00
|
|
|
else
|
2019-05-24 13:13:14 -04:00
|
|
|
# should check confirm_multiline_termination to finish?
|
|
|
|
@previous_line_index = @line_index
|
|
|
|
@line_index = @buffer_of_lines.size - 1
|
|
|
|
finish
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
2019-05-22 19:23:22 -04:00
|
|
|
else
|
|
|
|
if @history_pointer
|
|
|
|
Reline::HISTORY[@history_pointer] = @line
|
|
|
|
@history_pointer = nil
|
|
|
|
end
|
|
|
|
finish
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def em_delete_prev_char(key)
|
|
|
|
if @is_multiline and @cursor == 0 and @line_index > 0
|
|
|
|
@buffer_of_lines[@line_index] = @line
|
|
|
|
@cursor = calculate_width(@buffer_of_lines[@line_index - 1])
|
|
|
|
@byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
|
|
|
|
@buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
|
|
|
|
@line_index -= 1
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@rerender_all = true
|
|
|
|
elsif @cursor > 0
|
|
|
|
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor -= width
|
|
|
|
@cursor_max -= width
|
|
|
|
end
|
|
|
|
end
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :backward_delete_char, :em_delete_prev_char
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_kill_line(key)
|
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
@line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer)
|
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
@cursor = @cursor_max = calculate_width(@line)
|
|
|
|
@kill_ring.append(deleted)
|
2019-11-27 19:32:51 -05:00
|
|
|
elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
|
|
|
|
@cursor = calculate_width(@line)
|
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
@line += @buffer_of_lines.delete_at(@line_index + 1)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@buffer_of_lines[@line_index] = @line
|
|
|
|
@rerender_all = true
|
|
|
|
@rest_height += 1
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def em_kill_line(key)
|
|
|
|
if @byte_pointer > 0
|
|
|
|
@line, deleted = byteslice!(@line, 0, @byte_pointer)
|
|
|
|
@byte_pointer = 0
|
|
|
|
@kill_ring.append(deleted, true)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = 0
|
|
|
|
end
|
|
|
|
end
|
2020-12-18 09:36:33 -05:00
|
|
|
alias_method :kill_line, :em_kill_line
|
2019-04-27 01:53:09 -04:00
|
|
|
|
2019-12-24 04:32:50 -05:00
|
|
|
private def em_delete(key)
|
2019-05-27 12:51:01 -04:00
|
|
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
2019-04-27 01:53:09 -04:00
|
|
|
@line = nil
|
2019-05-26 21:09:21 -04:00
|
|
|
if @buffer_of_lines.size > 1
|
|
|
|
scroll_down(@highest_in_all - @first_line_started_from)
|
|
|
|
end
|
2019-05-26 19:39:39 -04:00
|
|
|
Reline::IOGate.move_cursor_column(0)
|
2019-05-26 21:09:21 -04:00
|
|
|
@eof = true
|
2019-04-27 01:53:09 -04:00
|
|
|
finish
|
|
|
|
elsif @byte_pointer < @line.bytesize
|
|
|
|
splitted_last = @line.byteslice(@byte_pointer, @line.bytesize)
|
|
|
|
mbchar = splitted_last.grapheme_clusters.first
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor_max -= width
|
|
|
|
@line, = byteslice!(@line, @byte_pointer, mbchar.bytesize)
|
2019-05-27 12:52:04 -04:00
|
|
|
elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1
|
|
|
|
@cursor = calculate_width(@line)
|
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
@line += @buffer_of_lines.delete_at(@line_index + 1)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@buffer_of_lines[@line_index] = @line
|
|
|
|
@rerender_all = true
|
|
|
|
@rest_height += 1
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
2019-12-24 04:32:50 -05:00
|
|
|
alias_method :delete_char, :em_delete
|
|
|
|
|
|
|
|
private def em_delete_or_list(key)
|
|
|
|
if @line.empty? or @byte_pointer < @line.bytesize
|
|
|
|
em_delete(key)
|
|
|
|
else # show completed list
|
|
|
|
result = call_completion_proc
|
|
|
|
if result.is_a?(Array)
|
|
|
|
complete(result, true)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
alias_method :delete_char_or_list, :em_delete_or_list
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def em_yank(key)
|
|
|
|
yanked = @kill_ring.yank
|
|
|
|
if yanked
|
|
|
|
@line = byteinsert(@line, @byte_pointer, yanked)
|
|
|
|
yanked_width = calculate_width(yanked)
|
|
|
|
@cursor += yanked_width
|
|
|
|
@cursor_max += yanked_width
|
|
|
|
@byte_pointer += yanked.bytesize
|
|
|
|
end
|
|
|
|
end
|
2020-12-18 09:35:15 -05:00
|
|
|
alias_method :yank, :em_yank
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def em_yank_pop(key)
|
|
|
|
yanked, prev_yank = @kill_ring.yank_pop
|
|
|
|
if yanked
|
|
|
|
prev_yank_width = calculate_width(prev_yank)
|
|
|
|
@cursor -= prev_yank_width
|
|
|
|
@cursor_max -= prev_yank_width
|
|
|
|
@byte_pointer -= prev_yank.bytesize
|
|
|
|
@line, = byteslice!(@line, @byte_pointer, prev_yank.bytesize)
|
|
|
|
@line = byteinsert(@line, @byte_pointer, yanked)
|
|
|
|
yanked_width = calculate_width(yanked)
|
|
|
|
@cursor += yanked_width
|
|
|
|
@cursor_max += yanked_width
|
|
|
|
@byte_pointer += yanked.bytesize
|
|
|
|
end
|
|
|
|
end
|
2020-12-18 09:35:15 -05:00
|
|
|
alias_method :yank_pop, :em_yank_pop
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_clear_screen(key)
|
|
|
|
@cleared = true
|
|
|
|
end
|
2019-06-02 14:41:40 -04:00
|
|
|
alias_method :clear_screen, :ed_clear_screen
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def em_next_word(key)
|
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
end
|
2019-06-02 14:41:40 -04:00
|
|
|
alias_method :forward_word, :em_next_word
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_prev_word(key)
|
|
|
|
if @byte_pointer > 0
|
|
|
|
byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
end
|
|
|
|
end
|
2019-06-02 14:41:40 -04:00
|
|
|
alias_method :backward_word, :ed_prev_word
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def em_delete_next_word(key)
|
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer)
|
|
|
|
@line, word = byteslice!(@line, @byte_pointer, byte_size)
|
|
|
|
@kill_ring.append(word)
|
|
|
|
@cursor_max -= width
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def ed_delete_prev_word(key)
|
|
|
|
if @byte_pointer > 0
|
|
|
|
byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer)
|
|
|
|
@line, word = byteslice!(@line, @byte_pointer - byte_size, byte_size)
|
|
|
|
@kill_ring.append(word, true)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
@cursor_max -= width
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def ed_transpose_chars(key)
|
|
|
|
if @byte_pointer > 0
|
|
|
|
if @cursor_max > @cursor
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
mbchar = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor += width
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
end
|
|
|
|
back1_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
|
|
|
if (@byte_pointer - back1_byte_size) > 0
|
|
|
|
back2_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer - back1_byte_size)
|
|
|
|
back2_pointer = @byte_pointer - back1_byte_size - back2_byte_size
|
|
|
|
@line, back2_mbchar = byteslice!(@line, back2_pointer, back2_byte_size)
|
|
|
|
@line = byteinsert(@line, @byte_pointer - back2_byte_size, back2_mbchar)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :transpose_chars, :ed_transpose_chars
|
2019-04-27 01:53:09 -04:00
|
|
|
|
2019-06-03 17:39:02 -04:00
|
|
|
private def ed_transpose_words(key)
|
|
|
|
left_word_start, middle_start, right_word_start, after_start = Reline::Unicode.ed_transpose_words(@line, @byte_pointer)
|
|
|
|
before = @line.byteslice(0, left_word_start)
|
|
|
|
left_word = @line.byteslice(left_word_start, middle_start - left_word_start)
|
|
|
|
middle = @line.byteslice(middle_start, right_word_start - middle_start)
|
|
|
|
right_word = @line.byteslice(right_word_start, after_start - right_word_start)
|
|
|
|
after = @line.byteslice(after_start, @line.bytesize - after_start)
|
2019-06-05 15:57:52 -04:00
|
|
|
return if left_word.empty? or right_word.empty?
|
2019-06-03 17:39:02 -04:00
|
|
|
@line = before + right_word + middle + left_word + after
|
|
|
|
from_head_to_left_word = before + right_word + middle + left_word
|
|
|
|
@byte_pointer = from_head_to_left_word.bytesize
|
|
|
|
@cursor = calculate_width(from_head_to_left_word)
|
|
|
|
end
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :transpose_words, :ed_transpose_words
|
2019-06-03 17:39:02 -04:00
|
|
|
|
2019-04-27 01:53:09 -04:00
|
|
|
private def em_capitol_case(key)
|
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(@line, @byte_pointer)
|
|
|
|
before = @line.byteslice(0, @byte_pointer)
|
|
|
|
after = @line.byteslice((@byte_pointer + byte_size)..-1)
|
|
|
|
@line = before + new_str + after
|
|
|
|
@byte_pointer += new_str.bytesize
|
|
|
|
@cursor += calculate_width(new_str)
|
|
|
|
end
|
|
|
|
end
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :capitalize_word, :em_capitol_case
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def em_lower_case(key)
|
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
|
|
|
|
part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
|
|
|
mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar
|
|
|
|
}.join
|
|
|
|
rest = @line.byteslice((@byte_pointer + byte_size)..-1)
|
|
|
|
@line = @line.byteslice(0, @byte_pointer) + part
|
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
@cursor = calculate_width(@line)
|
|
|
|
@cursor_max = @cursor + calculate_width(rest)
|
|
|
|
@line += rest
|
|
|
|
end
|
|
|
|
end
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :downcase_word, :em_lower_case
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def em_upper_case(key)
|
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer)
|
|
|
|
part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar|
|
|
|
|
mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar
|
|
|
|
}.join
|
|
|
|
rest = @line.byteslice((@byte_pointer + byte_size)..-1)
|
|
|
|
@line = @line.byteslice(0, @byte_pointer) + part
|
|
|
|
@byte_pointer = @line.bytesize
|
|
|
|
@cursor = calculate_width(@line)
|
|
|
|
@cursor_max = @cursor + calculate_width(rest)
|
|
|
|
@line += rest
|
|
|
|
end
|
|
|
|
end
|
2019-06-03 19:34:10 -04:00
|
|
|
alias_method :upcase_word, :em_upper_case
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def em_kill_region(key)
|
|
|
|
if @byte_pointer > 0
|
|
|
|
byte_size, width = Reline::Unicode.em_big_backward_word(@line, @byte_pointer)
|
|
|
|
@line, deleted = byteslice!(@line, @byte_pointer - byte_size, byte_size)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
@cursor_max -= width
|
2020-12-18 09:36:33 -05:00
|
|
|
@kill_ring.append(deleted, true)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
2020-12-18 09:36:33 -05:00
|
|
|
alias_method :unix_word_rubout, :em_kill_region
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def copy_for_vi(text)
|
|
|
|
if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command)
|
|
|
|
@vi_clipboard = text
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_insert(key)
|
|
|
|
@config.editing_mode = :vi_insert
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_add(key)
|
|
|
|
@config.editing_mode = :vi_insert
|
|
|
|
ed_next_char(key)
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_command_mode(key)
|
|
|
|
ed_prev_char(key)
|
|
|
|
@config.editing_mode = :vi_command
|
|
|
|
end
|
2020-11-09 01:59:52 -05:00
|
|
|
alias_method :vi_movement_mode, :vi_command_mode
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def vi_next_word(key, arg: 1)
|
|
|
|
if @line.bytesize > @byte_pointer
|
2020-12-24 08:53:24 -05:00
|
|
|
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces)
|
2019-04-27 01:53:09 -04:00
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
vi_next_word(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_prev_word(key, arg: 1)
|
|
|
|
if @byte_pointer > 0
|
|
|
|
byte_size, width = Reline::Unicode.vi_backward_word(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
vi_prev_word(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
2020-11-06 22:05:43 -05:00
|
|
|
private def vi_end_word(key, arg: 1, inclusive: false)
|
2019-04-27 01:53:09 -04:00
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer)
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
arg -= 1
|
2020-11-06 22:05:43 -05:00
|
|
|
if inclusive and arg.zero?
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
if byte_size > 0
|
|
|
|
c = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(c)
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
vi_end_word(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_next_big_word(key, arg: 1)
|
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, width = Reline::Unicode.vi_big_forward_word(@line, @byte_pointer)
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
vi_next_big_word(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_prev_big_word(key, arg: 1)
|
|
|
|
if @byte_pointer > 0
|
|
|
|
byte_size, width = Reline::Unicode.vi_big_backward_word(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
vi_prev_big_word(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
2020-11-06 22:05:43 -05:00
|
|
|
private def vi_end_big_word(key, arg: 1, inclusive: false)
|
2019-04-27 01:53:09 -04:00
|
|
|
if @line.bytesize > @byte_pointer
|
|
|
|
byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer)
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
arg -= 1
|
2020-11-06 22:05:43 -05:00
|
|
|
if inclusive and arg.zero?
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
if byte_size > 0
|
|
|
|
c = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(c)
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
vi_end_big_word(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_delete_prev_char(key)
|
|
|
|
if @is_multiline and @cursor == 0 and @line_index > 0
|
|
|
|
@buffer_of_lines[@line_index] = @line
|
|
|
|
@cursor = calculate_width(@buffer_of_lines[@line_index - 1])
|
|
|
|
@byte_pointer = @buffer_of_lines[@line_index - 1].bytesize
|
|
|
|
@buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index)
|
|
|
|
@line_index -= 1
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@rerender_all = true
|
|
|
|
elsif @cursor > 0
|
|
|
|
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor -= width
|
|
|
|
@cursor_max -= width
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-01-16 11:35:13 -05:00
|
|
|
private def vi_insert_at_bol(key)
|
|
|
|
ed_move_to_beg(key)
|
|
|
|
@config.editing_mode = :vi_insert
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_add_at_eol(key)
|
|
|
|
ed_move_to_end(key)
|
|
|
|
@config.editing_mode = :vi_insert
|
|
|
|
end
|
|
|
|
|
2019-04-27 01:53:09 -04:00
|
|
|
private def ed_delete_prev_char(key, arg: 1)
|
|
|
|
deleted = ''
|
|
|
|
arg.times do
|
|
|
|
if @cursor > 0
|
|
|
|
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
|
|
|
|
deleted.prepend(mbchar)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor -= width
|
|
|
|
@cursor_max -= width
|
|
|
|
end
|
|
|
|
end
|
|
|
|
copy_for_vi(deleted)
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_zero(key)
|
|
|
|
@byte_pointer = 0
|
|
|
|
@cursor = 0
|
|
|
|
end
|
|
|
|
|
2020-11-14 08:52:38 -05:00
|
|
|
private def vi_change_meta(key, arg: 1)
|
2020-12-24 08:53:24 -05:00
|
|
|
@drop_terminate_spaces = true
|
2020-01-20 19:30:30 -05:00
|
|
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
|
|
|
if byte_pointer_diff > 0
|
|
|
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
|
|
|
elsif byte_pointer_diff < 0
|
|
|
|
@line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
|
|
|
end
|
|
|
|
copy_for_vi(cut)
|
|
|
|
@cursor += cursor_diff if cursor_diff < 0
|
|
|
|
@cursor_max -= cursor_diff.abs
|
|
|
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
|
|
|
@config.editing_mode = :vi_insert
|
2020-12-24 08:53:24 -05:00
|
|
|
@drop_terminate_spaces = false
|
2020-01-20 19:30:30 -05:00
|
|
|
}
|
2020-11-14 08:52:38 -05:00
|
|
|
@waiting_operator_vi_arg = arg
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2020-11-14 08:52:38 -05:00
|
|
|
private def vi_delete_meta(key, arg: 1)
|
2019-04-27 01:53:09 -04:00
|
|
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
|
|
|
if byte_pointer_diff > 0
|
|
|
|
@line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff)
|
|
|
|
elsif byte_pointer_diff < 0
|
|
|
|
@line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
|
|
|
end
|
|
|
|
copy_for_vi(cut)
|
|
|
|
@cursor += cursor_diff if cursor_diff < 0
|
|
|
|
@cursor_max -= cursor_diff.abs
|
|
|
|
@byte_pointer += byte_pointer_diff if byte_pointer_diff < 0
|
|
|
|
}
|
2020-11-14 08:52:38 -05:00
|
|
|
@waiting_operator_vi_arg = arg
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2020-11-14 08:52:38 -05:00
|
|
|
private def vi_yank(key, arg: 1)
|
2020-11-02 07:57:54 -05:00
|
|
|
@waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff|
|
|
|
|
if byte_pointer_diff > 0
|
|
|
|
cut = @line.byteslice(@byte_pointer, byte_pointer_diff)
|
|
|
|
elsif byte_pointer_diff < 0
|
|
|
|
cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff)
|
|
|
|
end
|
|
|
|
copy_for_vi(cut)
|
|
|
|
}
|
2020-11-14 08:52:38 -05:00
|
|
|
@waiting_operator_vi_arg = arg
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_list_or_eof(key)
|
2019-05-30 16:53:02 -04:00
|
|
|
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
|
2019-04-27 01:53:09 -04:00
|
|
|
@line = nil
|
2019-05-26 21:09:21 -04:00
|
|
|
if @buffer_of_lines.size > 1
|
|
|
|
scroll_down(@highest_in_all - @first_line_started_from)
|
|
|
|
end
|
2019-05-26 19:39:39 -04:00
|
|
|
Reline::IOGate.move_cursor_column(0)
|
2019-05-26 21:09:21 -04:00
|
|
|
@eof = true
|
2019-04-27 01:53:09 -04:00
|
|
|
finish
|
|
|
|
else
|
2019-12-24 19:29:46 -05:00
|
|
|
ed_newline(key)
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
end
|
2019-12-24 19:29:46 -05:00
|
|
|
alias_method :vi_end_of_transmission, :vi_list_or_eof
|
|
|
|
alias_method :vi_eof_maybe, :vi_list_or_eof
|
2019-04-27 01:53:09 -04:00
|
|
|
|
|
|
|
private def ed_delete_next_char(key, arg: 1)
|
2019-08-08 18:38:40 -04:00
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
unless @line.empty? || byte_size == 0
|
2019-04-27 01:53:09 -04:00
|
|
|
@line, mbchar = byteslice!(@line, @byte_pointer, byte_size)
|
|
|
|
copy_for_vi(mbchar)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
@cursor_max -= width
|
|
|
|
if @cursor > 0 and @cursor >= @cursor_max
|
2021-01-08 12:13:23 -05:00
|
|
|
byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer)
|
|
|
|
mbchar = @line.byteslice(@byte_pointer - byte_size, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2019-04-27 01:53:09 -04:00
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
end
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
ed_delete_next_char(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_to_history_line(key)
|
|
|
|
if Reline::HISTORY.empty?
|
|
|
|
return
|
|
|
|
end
|
|
|
|
if @history_pointer.nil?
|
|
|
|
@history_pointer = 0
|
|
|
|
@line_backup_in_history = @line
|
|
|
|
@line = Reline::HISTORY[@history_pointer]
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = 0
|
|
|
|
@byte_pointer = 0
|
|
|
|
elsif @history_pointer.zero?
|
|
|
|
return
|
|
|
|
else
|
|
|
|
Reline::HISTORY[@history_pointer] = @line
|
|
|
|
@history_pointer = 0
|
|
|
|
@line = Reline::HISTORY[@history_pointer]
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@cursor = 0
|
|
|
|
@byte_pointer = 0
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_histedit(key)
|
2020-09-09 07:52:48 -04:00
|
|
|
path = Tempfile.open { |fp|
|
2021-02-02 07:29:20 -05:00
|
|
|
if @is_multiline
|
|
|
|
fp.write whole_lines.join("\n")
|
|
|
|
else
|
|
|
|
fp.write @line
|
|
|
|
end
|
2020-09-09 07:52:48 -04:00
|
|
|
fp.path
|
2019-04-27 01:53:09 -04:00
|
|
|
}
|
2020-09-09 07:52:48 -04:00
|
|
|
system("#{ENV['EDITOR']} #{path}")
|
2021-02-02 07:29:20 -05:00
|
|
|
if @is_multiline
|
|
|
|
@buffer_of_lines = File.read(path).split("\n")
|
|
|
|
@buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty?
|
|
|
|
@line_index = 0
|
|
|
|
@line = @buffer_of_lines[@line_index]
|
|
|
|
@rerender_all = true
|
|
|
|
else
|
|
|
|
@line = File.read(path)
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
finish
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_paste_prev(key, arg: 1)
|
|
|
|
if @vi_clipboard.size > 0
|
|
|
|
@line = byteinsert(@line, @byte_pointer, @vi_clipboard)
|
|
|
|
@cursor_max += calculate_width(@vi_clipboard)
|
|
|
|
cursor_point = @vi_clipboard.grapheme_clusters[0..-2].join
|
|
|
|
@cursor += calculate_width(cursor_point)
|
|
|
|
@byte_pointer += cursor_point.bytesize
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
vi_paste_prev(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_paste_next(key, arg: 1)
|
|
|
|
if @vi_clipboard.size > 0
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
@line = byteinsert(@line, @byte_pointer + byte_size, @vi_clipboard)
|
|
|
|
@cursor_max += calculate_width(@vi_clipboard)
|
|
|
|
@cursor += calculate_width(@vi_clipboard)
|
|
|
|
@byte_pointer += @vi_clipboard.bytesize
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
vi_paste_next(key, arg: arg) if arg > 0
|
|
|
|
end
|
|
|
|
|
|
|
|
private def ed_argument_digit(key)
|
|
|
|
if @vi_arg.nil?
|
|
|
|
unless key.chr.to_i.zero?
|
|
|
|
@vi_arg = key.chr.to_i
|
|
|
|
end
|
|
|
|
else
|
|
|
|
@vi_arg = @vi_arg * 10 + key.chr.to_i
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_to_column(key, arg: 0)
|
|
|
|
@byte_pointer, @cursor = @line.grapheme_clusters.inject([0, 0]) { |total, gc|
|
|
|
|
# total has [byte_size, cursor]
|
|
|
|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
|
|
|
|
if (total.last + mbchar_width) >= arg
|
|
|
|
break total
|
|
|
|
elsif (total.last + mbchar_width) >= @cursor_max
|
|
|
|
break total
|
|
|
|
else
|
|
|
|
total = [total.first + gc.bytesize, total.last + mbchar_width]
|
|
|
|
total
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_replace_char(key, arg: 1)
|
2019-11-08 02:17:53 -05:00
|
|
|
@waiting_proc = ->(k) {
|
2019-04-27 01:53:09 -04:00
|
|
|
if arg == 1
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
before = @line.byteslice(0, @byte_pointer)
|
|
|
|
remaining_point = @byte_pointer + byte_size
|
2020-12-24 07:44:09 -05:00
|
|
|
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2019-11-08 02:17:53 -05:00
|
|
|
@line = before + k.chr + after
|
2019-04-27 01:53:09 -04:00
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@waiting_proc = nil
|
|
|
|
elsif arg > 1
|
|
|
|
byte_size = 0
|
|
|
|
arg.times do
|
|
|
|
byte_size += Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer + byte_size)
|
|
|
|
end
|
|
|
|
before = @line.byteslice(0, @byte_pointer)
|
|
|
|
remaining_point = @byte_pointer + byte_size
|
2020-12-24 07:44:09 -05:00
|
|
|
after = @line.byteslice(remaining_point, @line.bytesize - remaining_point)
|
2019-11-08 02:17:53 -05:00
|
|
|
replaced = k.chr * arg
|
2019-04-27 01:53:09 -04:00
|
|
|
@line = before + replaced + after
|
|
|
|
@byte_pointer += replaced.bytesize
|
|
|
|
@cursor += calculate_width(replaced)
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@waiting_proc = nil
|
|
|
|
end
|
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2020-11-06 22:05:43 -05:00
|
|
|
private def vi_next_char(key, arg: 1, inclusive: false)
|
|
|
|
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
|
|
|
|
2020-11-06 22:05:43 -05:00
|
|
|
private def vi_to_next_char(key, arg: 1, inclusive: false)
|
|
|
|
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) }
|
2020-01-16 23:44:07 -05:00
|
|
|
end
|
|
|
|
|
2020-11-06 22:05:43 -05:00
|
|
|
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
|
2019-04-27 01:53:09 -04:00
|
|
|
if key.instance_of?(String)
|
|
|
|
inputed_char = key
|
|
|
|
else
|
|
|
|
inputed_char = key.chr
|
|
|
|
end
|
2020-01-16 23:44:07 -05:00
|
|
|
prev_total = nil
|
2019-04-27 01:53:09 -04:00
|
|
|
total = nil
|
|
|
|
found = false
|
|
|
|
@line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar|
|
|
|
|
# total has [byte_size, cursor]
|
|
|
|
unless total
|
|
|
|
# skip cursor point
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
total = [mbchar.bytesize, width]
|
|
|
|
else
|
|
|
|
if inputed_char == mbchar
|
|
|
|
arg -= 1
|
|
|
|
if arg.zero?
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
2020-01-16 23:44:07 -05:00
|
|
|
prev_total = total
|
2019-04-27 01:53:09 -04:00
|
|
|
total = [total.first + mbchar.bytesize, total.last + width]
|
|
|
|
end
|
|
|
|
end
|
2020-01-16 23:44:07 -05:00
|
|
|
if not need_prev_char and found and total
|
2019-04-27 01:53:09 -04:00
|
|
|
byte_size, width = total
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
2020-01-16 23:44:07 -05:00
|
|
|
elsif need_prev_char and found and prev_total
|
|
|
|
byte_size, width = prev_total
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2020-11-06 22:05:43 -05:00
|
|
|
if inclusive
|
|
|
|
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
|
|
|
|
if byte_size > 0
|
|
|
|
c = @line.byteslice(@byte_pointer, byte_size)
|
|
|
|
width = Reline::Unicode.get_mbchar_width(c)
|
|
|
|
@byte_pointer += byte_size
|
|
|
|
@cursor += width
|
|
|
|
end
|
|
|
|
end
|
2019-04-27 01:53:09 -04:00
|
|
|
@waiting_proc = nil
|
2020-01-17 06:17:23 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_prev_char(key, arg: 1)
|
|
|
|
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) }
|
|
|
|
end
|
|
|
|
|
|
|
|
private def vi_to_prev_char(key, arg: 1)
|
|
|
|
@waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) }
|
|
|
|
end
|
|
|
|
|
|
|
|
private def search_prev_char(key, arg, need_next_char = false)
|
|
|
|
if key.instance_of?(String)
|
|
|
|
inputed_char = key
|
|
|
|
else
|
|
|
|
inputed_char = key.chr
|
|
|
|
end
|
|
|
|
prev_total = nil
|
|
|
|
total = nil
|
|
|
|
found = false
|
|
|
|
@line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar|
|
|
|
|
# total has [byte_size, cursor]
|
|
|
|
unless total
|
|
|
|
# skip cursor point
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
total = [mbchar.bytesize, width]
|
|
|
|
else
|
|
|
|
if inputed_char == mbchar
|
|
|
|
arg -= 1
|
|
|
|
if arg.zero?
|
|
|
|
found = true
|
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
width = Reline::Unicode.get_mbchar_width(mbchar)
|
|
|
|
prev_total = total
|
|
|
|
total = [total.first + mbchar.bytesize, total.last + width]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if not need_next_char and found and total
|
|
|
|
byte_size, width = total
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
elsif need_next_char and found and prev_total
|
|
|
|
byte_size, width = prev_total
|
|
|
|
@byte_pointer -= byte_size
|
|
|
|
@cursor -= width
|
|
|
|
end
|
|
|
|
@waiting_proc = nil
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|
2019-05-26 16:45:08 -04:00
|
|
|
|
|
|
|
private def vi_join_lines(key, arg: 1)
|
|
|
|
if @is_multiline and @buffer_of_lines.size > @line_index + 1
|
|
|
|
@cursor = calculate_width(@line)
|
|
|
|
@byte_pointer = @line.bytesize
|
2019-07-04 05:49:42 -04:00
|
|
|
@line += ' ' + @buffer_of_lines.delete_at(@line_index + 1).lstrip
|
2019-05-26 16:45:08 -04:00
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@buffer_of_lines[@line_index] = @line
|
|
|
|
@rerender_all = true
|
|
|
|
@rest_height += 1
|
|
|
|
end
|
|
|
|
arg -= 1
|
|
|
|
vi_join_lines(key, arg: arg) if arg > 0
|
|
|
|
end
|
2019-11-15 02:50:11 -05:00
|
|
|
|
|
|
|
private def em_set_mark(key)
|
|
|
|
@mark_pointer = [@byte_pointer, @line_index]
|
|
|
|
end
|
|
|
|
alias_method :set_mark, :em_set_mark
|
|
|
|
|
|
|
|
private def em_exchange_mark(key)
|
2020-11-01 08:59:03 -05:00
|
|
|
return unless @mark_pointer
|
2019-11-15 02:50:11 -05:00
|
|
|
new_pointer = [@byte_pointer, @line_index]
|
|
|
|
@previous_line_index = @line_index
|
|
|
|
@byte_pointer, @line_index = @mark_pointer
|
|
|
|
@cursor = calculate_width(@line.byteslice(0, @byte_pointer))
|
|
|
|
@cursor_max = calculate_width(@line)
|
|
|
|
@mark_pointer = new_pointer
|
|
|
|
end
|
|
|
|
alias_method :exchange_point_and_mark, :em_exchange_mark
|
2019-04-27 01:53:09 -04:00
|
|
|
end
|