1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/lib/reline/line_editor.rb

2496 lines
80 KiB
Ruby
Raw Normal View History

require 'reline/kill_ring'
require 'reline/unicode'
require 'tempfile'
class Reline::LineEditor
# TODO: undo
attr_reader :line
attr_reader :byte_pointer
attr_accessor :confirm_multiline_termination_proc
attr_accessor :completion_proc
attr_accessor :completion_append_character
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
attr_accessor :dig_perfect_match_proc
attr_writer :output
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
MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match
PERFECT_MATCH = :perfect_match
end
CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer)
MenuInfo = Struct.new('MenuInfo', :target, :list)
def initialize(config, encoding)
@config = config
@completion_append_character = ''
reset_variables(encoding: encoding)
end
def simplified_rendering?
if finished?
false
elsif @just_cursor_moving and not @rerender_all
true
else
not @rerender_all and not finished? and Reline::IOGate.in_pasting?
end
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
return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] if simplified_rendering?
if @prompt_proc
if @cached_prompt_list and Time.now.to_f < (@prompt_cache_time + 0.5)
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
if @config.show_mode_in_prompt
if @config.editing_mode_is?(:vi_command)
mode_icon = @config.vi_cmd_mode_icon
elsif @config.editing_mode_is?(:vi_insert)
mode_icon = @config.vi_ins_mode_icon
elsif @config.editing_mode_is?(:emacs)
mode_icon = @config.emacs_mode_string
else
mode_icon = '?'
end
prompt_list.map!{ |pr| mode_icon + pr }
end
prompt = prompt_list[@line_index]
prompt_width = calculate_width(prompt, true)
2019-10-16 13:19:01 -04:00
[prompt, prompt_width, prompt_list]
else
2019-10-16 13:19:01 -04:00
prompt_width = calculate_width(prompt, true)
if @config.show_mode_in_prompt
if @config.editing_mode_is?(:vi_command)
mode_icon = @config.vi_cmd_mode_icon
elsif @config.editing_mode_is?(:vi_insert)
mode_icon = @config.vi_ins_mode_icon
elsif @config.editing_mode_is?(:emacs)
mode_icon = @config.emacs_mode_string
else
mode_icon = '?'
end
prompt = mode_icon + prompt
end
2019-10-16 13:19:01 -04:00
[prompt, prompt_width, nil]
end
end
def reset(prompt = '', encoding:)
@rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y
@screen_size = Reline::IOGate.get_screen_size
reset_variables(prompt, encoding: encoding)
2019-05-26 18:35:35 -04:00
@old_trap = Signal.trap('SIGINT') {
@old_trap.call if @old_trap.respond_to?(:call) # can also be string, ex: "DEFAULT"
raise Interrupt
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
old_screen_size = @screen_size
2019-08-29 01:36:45 -04:00
@screen_size = Reline::IOGate.get_screen_size
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)
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
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
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)
end
2019-05-26 21:09:21 -04:00
def eof?
@eof
end
def reset_variables(prompt = '', encoding:)
@prompt = prompt
@mark_pointer = nil
@encoding = encoding
@is_multiline = false
@finished = false
@cleared = false
@rerender_all = false
@history_pointer = nil
@kill_ring = Reline::KillRing.new
@vi_clipboard = ''
@vi_arg = nil
@waiting_proc = nil
@waiting_operator_proc = nil
@waiting_operator_vi_arg = nil
@completion_journey_data = nil
@completion_state = CompletionState::NORMAL
@perfect_matched = nil
@menu_info = nil
@first_prompt = true
@searching_prompt = nil
@first_char = true
@add_newline_to_end_of_buffer = false
@just_cursor_moving = false
@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)
reset_line
end
def reset_line
@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]
@first_line_started_from = 0
@move_up = 0
@started_from = 0
@highest_in_this = 1
@highest_in_all = 1
@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
end
def multiline_on
@is_multiline = true
end
def multiline_off
@is_multiline = false
end
private def calculate_height_by_lines(lines, prompt)
result = 0
prompt_list = prompt.is_a?(Array) ? prompt : nil
lines.each_with_index { |line, i|
prompt = prompt_list[i] if prompt_list and prompt_list[i]
result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line))
}
result
end
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
end
private def calculate_height_by_width(width)
2019-07-05 23:11:12 -04:00
width.div(@screen_size.last) + 1
end
private def split_by_width(str, max_width)
Reline::Unicode.split_by_width(str, max_width, @encoding)
end
private def scroll_down(val)
if val <= @rest_height
Reline::IOGate.move_cursor_down(val)
@rest_height -= val
else
Reline::IOGate.move_cursor_down(@rest_height)
Reline::IOGate.scroll_down(val - @rest_height)
@rest_height = 0
end
end
private def move_cursor_up(val)
if val > 0
Reline::IOGate.move_cursor_up(val)
@rest_height += val
elsif val < 0
move_cursor_down(-val)
end
end
private def move_cursor_down(val)
if val > 0
Reline::IOGate.move_cursor_down(val)
@rest_height -= val
@rest_height = 0 if @rest_height < 0
elsif val < 0
move_cursor_up(-val)
end
end
private def calculate_nearest_cursor
@cursor_max = calculate_width(line)
new_cursor = 0
new_byte_pointer = 0
height = 1
max_width = @screen_size.last
if @config.editing_mode_is?(:vi_command)
last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @line.bytesize)
if last_byte_size > 0
last_mbchar = @line.byteslice(@line.bytesize - last_byte_size, last_byte_size)
last_width = Reline::Unicode.get_mbchar_width(last_mbchar)
cursor_max = @cursor_max - last_width
else
cursor_max = @cursor_max
end
else
cursor_max = @cursor_max
end
@line.encode(Encoding::UTF_8).grapheme_clusters.each do |gc|
mbchar_width = Reline::Unicode.get_mbchar_width(gc)
now = new_cursor + mbchar_width
if now > cursor_max or now > @cursor
break
end
new_cursor += mbchar_width
if new_cursor > max_width * height
height += 1
end
new_byte_pointer += gc.bytesize
end
@started_from = height - 1
@cursor = new_cursor
@byte_pointer = new_byte_pointer
end
def rerender_all
@rerender_all = true
rerender
end
2019-07-30 13:13:21 -04:00
def rerender
2019-05-26 19:39:39 -04:00
return if @line.nil?
if @menu_info
2019-05-25 12:32:12 -04:00
scroll_down(@highest_in_all - @first_line_started_from)
@rerender_all = true
end
if @menu_info
show_menu
@menu_info = nil
end
2019-10-17 03:35:11 -04:00
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
if @cleared
clear_screen_buffer(prompt, prompt_list, prompt_width)
@cleared = false
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))
# FIXME: end of logical line sometimes breaks
if @add_newline_to_end_of_buffer
rerender_added_newline
@add_newline_to_end_of_buffer = false
elsif @just_cursor_moving and not @rerender_all
just_move_cursor
@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
end
line = modify_lines(whole_lines)[@line_index]
2019-05-26 21:09:21 -04:00
if @is_multiline
2019-10-17 03:35:11 -04:00
prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines, prompt)
2019-05-26 21:09:21 -04:00
if finished?
# Always rerender on finish because output_modifier_proc may return a different output.
render_partial(prompt, prompt_width, line)
2019-05-26 21:09:21 -04:00
scroll_down(1)
Reline::IOGate.move_cursor_column(0)
Reline::IOGate.erase_after_cursor
elsif not rendered
2019-05-26 21:09:21 -04:00
render_partial(prompt, prompt_width, line)
end
else
2019-05-26 21:09:21 -04:00
render_partial(prompt, prompt_width, line)
if finished?
scroll_down(1)
Reline::IOGate.move_cursor_column(0)
Reline::IOGate.erase_after_cursor
end
end
end
private def rerender_added_newline
scroll_down(1)
new_lines = whole_lines(index: @previous_line_index, line: @line)
prompt, prompt_width, = check_multiline_prompt(new_lines, prompt)
@buffer_of_lines[@previous_line_index] = @line
@line = @buffer_of_lines[@line_index]
render_partial(prompt, prompt_width, @line, false)
@cursor = @cursor_max = calculate_width(@line)
@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
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
@line = @buffer_of_lines[@line_index]
move_cursor_down(new_first_line_started_from - @first_line_started_from)
@first_line_started_from = new_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)
@previous_line_index = nil
end
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
if back > @highest_in_all
scroll_down(back - 1)
move_cursor_up(back - 1)
elsif back < @highest_in_all
scroll_down(back)
Reline::IOGate.erase_after_cursor
(@highest_in_all - back - 1).times do
scroll_down(1)
Reline::IOGate.erase_after_cursor
end
move_cursor_up(@highest_in_all - 1)
end
render_whole_lines(new_buffer, prompt_list || prompt, prompt_width)
move_cursor_up(back - 1)
if @prompt_proc
prompt = prompt_list[@line_index]
prompt_width = calculate_width(prompt, true)
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
calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt)
end
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
move_cursor_down(@first_line_started_from + @started_from)
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
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
height = render_partial(line_prompt, prompt_width, line, false)
if index < (lines.size - 1)
scroll_down(1)
rendered_height += height
else
rendered_height += height - 1
end
end
rendered_height
end
private def render_partial(prompt, prompt_width, line_to_render, with_control = true)
visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last)
if with_control
2019-05-21 17:48:27 -04:00
if height > @highest_in_this
diff = height - @highest_in_this
scroll_down(diff)
@highest_in_all += diff
2019-05-21 17:48:27 -04:00
@highest_in_this = height
move_cursor_up(diff)
elsif height < @highest_in_this
diff = @highest_in_this - height
@highest_in_all -= diff
@highest_in_this = height
end
move_cursor_up(@started_from)
@started_from = calculate_height_by_width(prompt_width + @cursor) - 1
end
2019-05-21 17:48:27 -04:00
Reline::IOGate.move_cursor_column(0)
visual_lines.each_with_index do |line, index|
2019-05-21 17:48:27 -04:00
if line.nil?
if calculate_width(visual_lines[index - 1], true) == Reline::IOGate.get_screen_size.last
# reaches the end of line
if Reline::IOGate.win?
# 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
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
@output.write line
if Reline::IOGate.win? and calculate_width(line, true) == Reline::IOGate.get_screen_size.last
# 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
if @first_prompt
@first_prompt = false
@pre_input_hook&.call
end
end
2019-05-21 17:48:27 -04:00
Reline::IOGate.erase_after_cursor
Reline::IOGate.move_cursor_column(0)
if with_control
# Just after rendring, so the cursor is on the last line.
if finished?
Reline::IOGate.move_cursor_column(0)
else
# Moves up from bottom of lines to the cursor position.
move_cursor_up(height - 1 - @started_from)
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
end
end
2019-05-21 17:48:27 -04:00
height
end
private def modify_lines(before)
return before if before.nil? || before.empty? || simplified_rendering?
if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?)
after.lines("\n").map { |l| l.chomp('') }
else
before
end
end
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]
height = render_partial(pr, calculate_width(pr), line, false)
else
height = render_partial(prompt, prompt_width, line, false)
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)
Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last)
end
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)
preposing, target, postposing = retrieve_completion_block
list = list.select { |i|
if i and not Encoding.compatible?(target.encoding, i.encoding)
raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{i.encoding.name}"
end
if @config.completion_ignore_case
i&.downcase&.start_with?(target.downcase)
else
i&.start_with?(target)
end
}.uniq
if is_menu
menu(target, list)
return nil
end
completed = list.inject { |memo, item|
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
size = [memo_mbchars.size, item_mbchars.size].min
result = ''
size.times do |i|
if @config.completion_ignore_case
if memo_mbchars[i].casecmp?(item_mbchars[i])
result << memo_mbchars[i]
else
break
end
else
if memo_mbchars[i] == item_mbchars[i]
result << memo_mbchars[i]
else
break
end
end
end
result
}
[target, preposing, completed, postposing]
end
private def complete(list, just_show_list = false)
case @completion_state
when CompletionState::NORMAL, CompletionState::JOURNEY
@completion_state = CompletionState::COMPLETION
when CompletionState::PERFECT_MATCH
@dig_perfect_match_proc&.(@perfect_matched)
end
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
result = complete_internal_proc(list, is_menu)
if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH
@completion_state = CompletionState::PERFECT_MATCH
end
return if result.nil?
target, preposing, completed, postposing = result
return if completed.nil?
if target <= completed and (@completion_state == CompletionState::COMPLETION)
if list.include?(completed)
if list.one?
@completion_state = CompletionState::PERFECT_MATCH
else
@completion_state = CompletionState::MENU_WITH_PERFECT_MATCH
end
@perfect_matched = completed
else
@completion_state = CompletionState::MENU
end
if not just_show_list and target < completed
@line = preposing + completed + completion_append_character.to_s + postposing
line_to_pointer = preposing + completed + completion_append_character.to_s
@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
when CompletionState::NORMAL, CompletionState::COMPLETION,
CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH
@completion_state = CompletionState::JOURNEY
result = retrieve_completion_block
return if result.nil?
preposing, target, postposing = result
@completion_journey_data = CompletionJourneyData.new(
preposing, postposing,
[target] + list.select{ |item| item.start_with?(target) }, 0)
@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
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
end
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
@vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg > 1
block.(true)
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
current_waiting_operator_proc = @waiting_operator_proc
@waiting_proc = proc { |k|
old_cursor, old_byte_pointer = @cursor, @byte_pointer
old_waiting_proc.(k)
cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer
@cursor, @byte_pointer = old_cursor, old_byte_pointer
current_waiting_operator_proc.(cursor_diff, byte_pointer_diff)
@waiting_operator_proc = old_waiting_operator_proc
}
end
else
# Ignores operator when not motion is given.
block.(false)
end
@waiting_operator_proc = nil
@waiting_operator_vi_arg = nil
@vi_arg = nil
else
block.(false)
end
end
private def argumentable?(method_obj)
method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg }
end
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
if @vi_arg and argumentable?(method_obj)
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
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)
if @vi_arg and argumentable?(method_obj)
run_for_operators(key, method_symbol) do |with_operator|
wrap_method_call(method_symbol, method_obj, key, with_operator)
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
end
2019-06-01 16:07:04 -04:00
@kill_ring.process
@vi_arg = nil
elsif @vi_arg
if key.chr =~ /[0-9]/
ed_argument_digit(key)
else
if argumentable?(method_obj)
run_for_operators(key, method_symbol) do |with_operator|
wrap_method_call(method_symbol, method_obj, key, with_operator)
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)
else
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
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)
else
run_for_operators(key, method_symbol) do |with_operator|
wrap_method_call(method_symbol, method_obj, key, with_operator)
end
end
@kill_ring.process
else
ed_insert(key) unless @config.editing_mode_is?(:vi_command)
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
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)
@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)
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)
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
@first_char = false
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
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
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
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
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)
else
normal_char(key)
end
unless completion_occurs
@completion_state = CompletionState::NORMAL
end
unless Reline::IOGate.in_pasting?
if @previous_line_index and @buffer_of_lines[@previous_line_index] == @line
@just_cursor_moving = true
elsif @previous_line_index.nil? and @buffer_of_lines[@line_index] == @line
@just_cursor_moving = true
end
end
if @is_multiline and @auto_indent_proc and not simplified_rendering?
process_auto_indent
end
end
def call_completion_proc
result = retrieve_completion_block(true)
slice = result[1]
result = @completion_proc.(slice) if @completion_proc and slice
Reline.core.instance_variable_set(:@completion_quote_character, nil)
result
end
private def process_auto_indent
return if not @check_new_auto_indent and @previous_line_index # move cursor up or down
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
end
if new_indent&.>= 0
@line = ' ' * new_indent + @line.lstrip
end
end
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)
2019-07-01 07:45:53 -04:00
if new_indent&.>= 0
md = new_lines[@line_index].match(/\A */)
prev_indent = md[0].count(' ')
if @check_new_auto_indent
@buffer_of_lines[@line_index] = ' ' * new_indent + @buffer_of_lines[@line_index].lstrip
@cursor = new_indent
@byte_pointer = new_indent
2019-06-18 07:57:58 -04:00
else
@line = ' ' * new_indent + @line.lstrip
@cursor += new_indent - prev_indent
@byte_pointer += new_indent - prev_indent
2019-06-18 07:57:58 -04:00
end
end
@check_new_auto_indent = false
end
def retrieve_completion_block(set_completion_quote_character = false)
word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/
quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/
before = @line.byteslice(0, @byte_pointer)
rest = nil
break_pointer = nil
quote = nil
closing_quote = nil
escaped_quote = nil
i = 0
while i < @byte_pointer do
slice = @line.byteslice(i, @byte_pointer - i)
unless slice.valid_encoding?
i += 1
next
end
if quote and slice.start_with?(closing_quote)
quote = nil
i += 1
rest = nil
elsif quote and slice.start_with?(escaped_quote)
# skip
i += 2
elsif slice =~ quote_characters_regexp # find new "
rest = $'
quote = $&
closing_quote = /(?!\\)#{Regexp.escape(quote)}/
escaped_quote = /\\#{Regexp.escape(quote)}/
i += 1
break_pointer = i - 1
elsif not quote and slice =~ word_break_regexp
rest = $'
i += 1
2019-12-25 04:45:02 -05:00
before = @line.byteslice(i, @byte_pointer - i)
break_pointer = i
else
i += 1
end
end
postposing = @line.byteslice(@byte_pointer, @line.bytesize - @byte_pointer)
if rest
preposing = @line.byteslice(0, break_pointer)
target = rest
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
else
preposing = ''
if break_pointer
preposing = @line.byteslice(0, break_pointer)
else
preposing = ''
end
target = before
end
2019-12-10 03:27:43 -05:00
[preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)]
end
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")
end
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?
@line&.clear
@byte_pointer = 0
@cursor = 0
@cursor_max = 0
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
def whole_lines(index: @line_index, line: @line)
temp_lines = @buffer_of_lines.dup
temp_lines[index] = line
temp_lines
end
def whole_buffer
if @buffer_of_lines.size == 1 and @line.nil?
nil
else
whole_lines.join("\n")
end
end
def finished?
@finished
end
def finish
@finished = true
@rerender_all = true
@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)
Reline::Unicode.calculate_width(str, allow_escape_code)
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
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
2019-06-18 07:57:58 -04:00
@check_new_auto_indent = true
2019-05-24 10:38:40 -04:00
end
end
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)
return if @continuous_insertion_buffer.empty? or (Reline::IOGate.in_pasting? and not force)
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
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
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
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
if Reline::IOGate.in_pasting?
@continuous_insertion_buffer << str
return
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)
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
@byte_pointer += bytesize
@cursor += width
@cursor_max += width
end
alias_method :ed_digit, :ed_insert
alias_method :self_insert, :ed_insert
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
end
@waiting_proc = nil
}
end
alias_method :quoted_insert, :ed_quoted_insert
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
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
end
arg -= 1
ed_next_char(key, arg: arg) if arg > 0
end
alias_method :forward_char, :ed_next_char
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
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
end
arg -= 1
ed_prev_char(key, arg: arg) if arg > 0
end
alias_method :backward_char, :ed_prev_char
private def vi_first_print(key)
@byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line)
end
private def ed_move_to_beg(key)
@byte_pointer = @cursor = 0
end
alias_method :beginning_of_line, :ed_move_to_beg
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
alias_method :end_of_line, :ed_move_to_end
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)
search_again = false
2019-05-12 12:51:05 -04:00
case key
when -1 # determined
Reline.last_incremental_search = search_word
break
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
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
if search_again
if search_word.empty? and Reline.last_incremental_search
search_word = Reline.last_incremental_search
end
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
else
2019-12-16 22:47:09 -05:00
history_pointer_base = 0
history = Reline::HISTORY
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
else
2019-12-16 22:47:09 -05:00
history_pointer_base = 0
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
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]
else
@line = hit
2019-12-16 22:47:09 -05:00
@searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit]
end
2019-05-12 12:51:05 -04:00
last_hit = hit
else
if @is_multiline
@rerender_all = true
2019-12-16 22:47:09 -05:00
@searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word]
else
2019-12-16 22:47:09 -05:00
@searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit]
end
2019-05-12 12:51:05 -04:00
end
end
end
2019-12-16 22:47:09 -05:00
end
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)`': "
@waiting_proc = ->(k) {
case k
when "\C-j".ord
2019-05-12 12:51:05 -04:00
if @history_pointer
buffer = Reline::HISTORY[@history_pointer]
2019-05-12 12:51:05 -04:00
else
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
searcher.resume(-1)
2019-05-12 12:51:05 -04:00
when "\C-g".ord
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
@rerender_all = true
2019-05-12 12:51:05 -04:00
else
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
searcher.resume(k)
2019-05-12 12:51:05 -04:00
else
if @history_pointer
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
@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
searcher.resume(-1)
2019-05-12 12:51:05 -04:00
end
end
}
end
private def vi_search_prev(key)
incremental_search_history(key)
2019-12-16 22:47:09 -05:00
end
alias_method :reverse_search_history, :vi_search_prev
2019-12-16 22:47:09 -05:00
private def vi_search_next(key)
incremental_search_history(key)
2019-05-12 12:51:05 -04:00
end
alias_method :forward_search_history, :vi_search_next
2019-05-12 12:51:05 -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
@line = @buffer_of_lines.last
@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
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?
@line = @buffer_of_lines.last
@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
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?
@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?
@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)
@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?
@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
if @config.editing_mode_is?(:emacs, :vi_insert)
@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)
if @is_multiline
if @config.editing_mode_is?(:vi_command)
if @line_index < (@buffer_of_lines.size - 1)
ed_next_history(key) # means cursor down
else
# should check confirm_multiline_termination to finish?
finish
end
else
if @line_index == (@buffer_of_lines.size - 1)
if confirm_multiline_termination
finish
else
key_newline(key)
end
else
# should check confirm_multiline_termination to finish?
@previous_line_index = @line_index
@line_index = @buffer_of_lines.size - 1
finish
end
end
else
if @history_pointer
Reline::HISTORY[@history_pointer] = @line
@history_pointer = nil
end
finish
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
alias_method :backward_delete_char, :em_delete_prev_char
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)
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
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
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)
@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
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)
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
end
end
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
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
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
private def ed_clear_screen(key)
@cleared = true
end
alias_method :clear_screen, :ed_clear_screen
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
alias_method :forward_word, :em_next_word
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
alias_method :backward_word, :ed_prev_word
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
alias_method :transpose_chars, :ed_transpose_chars
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)
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
alias_method :transpose_words, :ed_transpose_words
2019-06-03 17:39:02 -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
alias_method :capitalize_word, :em_capitol_case
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
alias_method :downcase_word, :em_lower_case
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
alias_method :upcase_word, :em_upper_case
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
@kill_ring.append(deleted)
end
end
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
alias_method :vi_movement_mode, :vi_command_mode
private def vi_next_word(key, arg: 1)
if @line.bytesize > @byte_pointer
byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer)
@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
private def vi_end_word(key, arg: 1, inclusive: false)
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
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
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
private def vi_end_big_word(key, arg: 1, inclusive: false)
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
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
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
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
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
private def vi_change_meta(key, arg: 1)
@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
}
@waiting_operator_vi_arg = arg
end
private def vi_delete_meta(key, arg: 1)
@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
}
@waiting_operator_vi_arg = arg
end
private def vi_yank(key, arg: 1)
@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)
}
@waiting_operator_vi_arg = arg
end
private def vi_list_or_eof(key)
if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1)
@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
finish
else
ed_newline(key)
end
end
alias_method :vi_end_of_transmission, :vi_list_or_eof
alias_method :vi_eof_maybe, :vi_list_or_eof
private def ed_delete_next_char(key, arg: 1)
byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer)
unless @line.empty? || byte_size == 0
@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
@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)
path = Tempfile.open { |fp|
fp.write @line
fp.path
}
system("#{ENV['EDITOR']} #{path}")
@line = File.read(path)
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)
@waiting_proc = ->(k) {
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
after = @line.byteslice(remaining_point, @line.size - remaining_point)
@line = before + k.chr + after
@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
after = @line.byteslice(remaining_point, @line.size - remaining_point)
replaced = k.chr * arg
@line = before + replaced + after
@byte_pointer += replaced.bytesize
@cursor += calculate_width(replaced)
@cursor_max = calculate_width(@line)
@waiting_proc = nil
end
}
end
private def vi_next_char(key, arg: 1, inclusive: false)
@waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) }
end
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) }
end
private def search_next_char(key, arg, need_prev_char: false, inclusive: false)
if key.instance_of?(String)
inputed_char = key
else
inputed_char = key.chr
end
prev_total = nil
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)
prev_total = total
total = [total.first + mbchar.bytesize, total.last + width]
end
end
if not need_prev_char and found and total
byte_size, width = total
@byte_pointer += byte_size
@cursor += width
elsif need_prev_char and found and prev_total
byte_size, width = prev_total
@byte_pointer += byte_size
@cursor += width
end
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
@waiting_proc = nil
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
end
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
@line += ' ' + @buffer_of_lines.delete_at(@line_index + 1).lstrip
@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
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)
return unless @mark_pointer
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
end