class Reline::LineEditor
Constants
- CompletionJourneyData
- DIALOG_DEFAULT_HEIGHT
- MINIMUM_SCROLLBAR_HEIGHT
- MenuInfo
- PROMPT_LIST_CACHE_TIMEOUT
- VI_MOTIONS
Attributes
TODO: undo TODO: Use “private alias_method” idiom after drop Ruby 2.5.
Public Class Methods
# File reline/line_editor.rb, line 57 def initialize(config, encoding) @config = config @completion_append_character = '' reset_variables(encoding: encoding) end
Public Instance Methods
# File reline/line_editor.rb, line 639 def add_dialog_proc(name, p, context = nil) dialog = Dialog.new(name, @config, DialogProcScope.new(self, @config, p, context)) if index = @dialogs.find_index { |d| d.name == name } @dialogs[index] = dialog else @dialogs << dialog end end
# File reline/line_editor.rb, line 1839 def byte_pointer=(val) @byte_pointer = val str = @line.byteslice(0, @byte_pointer) @cursor = calculate_width(str) @cursor_max = calculate_width(@line) end
# File reline/line_editor.rb, line 1610 def call_completion_proc result = retrieve_completion_block(true) pre, target, post = result result = call_completion_proc_with_checking_args(pre, target, post) Reline.core.instance_variable_set(:@completion_quote_character, nil) result end
# File reline/line_editor.rb, line 1618 def call_completion_proc_with_checking_args(pre, target, post) if @completion_proc and target argnum = @completion_proc.parameters.inject(0) { |result, item| case item.first when :req, :opt result + 1 when :rest break 3 end } case argnum when 1 result = @completion_proc.(target) when 2 result = @completion_proc.(target, pre) when 3..Float::INFINITY result = @completion_proc.(target, pre, post) end end result end
# File reline/line_editor.rb, line 1757 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 @confirm_multiline_termination_proc.(temp_buffer.join("\n") + "\n") end
# File reline/line_editor.rb, line 1779 def delete_text(start = nil, length = nil) if start.nil? and length.nil? if @is_multiline if @buffer_of_lines.size == 1 @line&.clear @byte_pointer = 0 @cursor = 0 @cursor_max = 0 elsif @line_index == (@buffer_of_lines.size - 1) and @line_index > 0 @buffer_of_lines.pop @line_index -= 1 @line = @buffer_of_lines[@line_index] @byte_pointer = 0 @cursor = 0 @cursor_max = calculate_width(@line) elsif @line_index < (@buffer_of_lines.size - 1) @buffer_of_lines.delete_at(@line_index) @line = @buffer_of_lines[@line_index] @byte_pointer = 0 @cursor = 0 @cursor_max = calculate_width(@line) end else @line&.clear @byte_pointer = 0 @cursor = 0 @cursor_max = 0 end 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
# File reline/line_editor.rb, line 1226 def editing_mode @config.editing_mode end
# File reline/line_editor.rb, line 252 def eof? @eof end
# File reline/line_editor.rb, line 248 def finalize Signal.trap('INT', @old_trap) end
# File reline/line_editor.rb, line 1865 def finish @finished = true @rerender_all = true @config.reset end
# File reline/line_editor.rb, line 1861 def finished? @finished end
# File reline/line_editor.rb, line 1535 def input_key(key) @last_key = key @config.reset_oneshot_key_bindings @dialogs.each do |dialog| if key.char.instance_of?(Symbol) and key.char == dialog.name return end end @just_cursor_moving = nil if key.char.nil? if @first_char @line = nil end finish return end old_line = @line.dup @first_char = false completion_occurs = false if @config.editing_mode_is?(:emacs, :vi_insert) and key.char == "\C-i".ord unless @config.disable_completion result = call_completion_proc if result.is_a?(Array) completion_occurs = true process_insert if @config.autocompletion move_completed_list(result, :down) else complete(result) end end end elsif @config.editing_mode_is?(:emacs, :vi_insert) and key.char == :completion_journey_up if not @config.disable_completion and @config.autocompletion result = call_completion_proc if result.is_a?(Array) completion_occurs = true process_insert move_completed_list(result, :up) end end 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 if result.is_a?(Array) completion_occurs = true process_insert move_completed_list(result, "\C-p".ord == key.char ? :up : :down) end end elsif Symbol === key.char and respond_to?(key.char, true) process_key(key.char, key.char) else normal_char(key) end unless completion_occurs @completion_state = CompletionState::NORMAL @completion_journey_data = nil end if not @in_pasting and @just_cursor_moving.nil? 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 and old_line == @line @just_cursor_moving = true else @just_cursor_moving = false end else @just_cursor_moving = false end if @is_multiline and @auto_indent_proc and not simplified_rendering? and @line process_auto_indent end end
# File reline/line_editor.rb, line 1767 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
# File reline/line_editor.rb, line 63 def io_gate Reline::IOGate end
# File reline/line_editor.rb, line 932 def just_move_cursor prompt, prompt_width, prompt_list = check_multiline_prompt(@buffer_of_lines) 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 first_line_diff = new_first_line_started_from - @first_line_started_from @cursor, @cursor_max, _, @byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false) new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1 calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from) @previous_line_index = nil @line = @buffer_of_lines[@line_index] if @rerender_all rerender_all_lines @rerender_all = false true else @first_line_started_from = new_first_line_started_from @started_from = new_started_from move_cursor_down(first_line_diff + @started_from) Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) false end end
# File reline/line_editor.rb, line 318 def multiline_off @is_multiline = false end
# File reline/line_editor.rb, line 314 def multiline_on @is_multiline = true end
# File reline/line_editor.rb, line 426 def rerender return if @line.nil? if @menu_info scroll_down(@highest_in_all - @first_line_started_from) @rerender_all = true end if @menu_info show_menu @menu_info = nil end prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines) cursor_column = (prompt_width + @cursor) % @screen_size.last if @cleared clear_screen_buffer(prompt, prompt_list, prompt_width) @cleared = false return end if @is_multiline and finished? and @scroll_partial_screen # Re-output all code higher than the screen when finished. Reline::IOGate.move_cursor_up(@first_line_started_from + @started_from - @scroll_partial_screen) Reline::IOGate.move_cursor_column(0) @scroll_partial_screen = nil new_lines = whole_lines prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines) modify_lines(new_lines).each_with_index do |line, index| @output.write "#{prompt_list ? prompt_list[index] : prompt}#{line}\r\n" Reline::IOGate.erase_after_cursor end @output.flush clear_dialog(cursor_column) return end new_highest_in_this = calculate_height_by_width(prompt_width + calculate_width(@line.nil? ? '' : @line)) rendered = false if @add_newline_to_end_of_buffer clear_dialog_with_trap_key(cursor_column) rerender_added_newline(prompt, prompt_width, prompt_list) @add_newline_to_end_of_buffer = false else if @just_cursor_moving and not @rerender_all clear_dialog_with_trap_key(cursor_column) rendered = just_move_cursor @just_cursor_moving = false return elsif @previous_line_index or new_highest_in_this != @highest_in_this clear_dialog_with_trap_key(cursor_column) rerender_changed_current_line @previous_line_index = nil rendered = true elsif @rerender_all rerender_all_lines @rerender_all = false rendered = true else end end if @is_multiline if finished? # Always rerender on finish because output_modifier_proc may return a different output. new_lines = whole_lines line = modify_lines(new_lines)[@line_index] clear_dialog(cursor_column) prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines) render_partial(prompt, prompt_width, line, @first_line_started_from) move_cursor_down(@highest_in_all - (@first_line_started_from + @highest_in_this - 1) - 1) scroll_down(1) Reline::IOGate.move_cursor_column(0) Reline::IOGate.erase_after_cursor else if not rendered and not @in_pasting line = modify_lines(whole_lines)[@line_index] prompt, prompt_width, prompt_list = check_multiline_prompt(whole_lines) render_partial(prompt, prompt_width, line, @first_line_started_from) end render_dialog(cursor_column) end @buffer_of_lines[@line_index] = @line @rest_height = 0 if @scroll_partial_screen else line = modify_lines(whole_lines)[@line_index] render_partial(prompt, prompt_width, line, 0) if finished? scroll_down(1) Reline::IOGate.move_cursor_column(0) Reline::IOGate.erase_after_cursor end end end
# File reline/line_editor.rb, line 420 def rerender_all @rerender_all = true process_insert(force: true) rerender end
# File reline/line_editor.rb, line 154 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 @screen_height = @screen_size.first reset_variables(prompt, encoding: encoding) Reline::IOGate.set_winch_handler do @resized = true end if ENV.key?('RELINE_ALT_SCROLLBAR') @full_block = '::' @upper_half_block = "''" @lower_half_block = '..' @block_elem_width = 2 elsif Reline::IOGate.win? @full_block = '█' @upper_half_block = '▀' @lower_half_block = '▄' @block_elem_width = 1 elsif @encoding == Encoding::UTF_8 @full_block = '█' @upper_half_block = '▀' @lower_half_block = '▄' @block_elem_width = Reline::Unicode.calculate_width('█') else @full_block = '::' @upper_half_block = "''" @lower_half_block = '..' @block_elem_width = 2 end end
# File reline/line_editor.rb, line 296 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') @check_new_auto_indent = false end
# File reline/line_editor.rb, line 256 def reset_variables(prompt = '', encoding:) @prompt = prompt.gsub("\n", "\\n") @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 = nil @cached_prompt_list = nil @prompt_cache_time = nil @eof = false @continuous_insertion_buffer = String.new(encoding: @encoding) @scroll_partial_screen = nil @prev_mode_string = nil @drop_terminate_spaces = false @in_pasting = false @auto_indent_proc = nil @dialogs = [] @previous_rendered_dialog_y = 0 @last_key = nil @resized = false reset_line end
# File reline/line_editor.rb, line 185 def resize return unless @resized @resized = false @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y old_screen_size = @screen_size @screen_size = Reline::IOGate.get_screen_size @screen_height = @screen_size.first if old_screen_size.last < @screen_size.last # columns increase @rerender_all = true rerender else back = 0 new_buffer = whole_lines prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer) 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 end
# File reline/line_editor.rb, line 1679 def retrieve_completion_block(set_completion_quote_character = false) if Reline.completer_word_break_characters.empty? word_break_regexp = nil else word_break_regexp = /\A[#{Regexp.escape(Reline.completer_word_break_characters)}]/ end if Reline.completer_quote_characters.empty? quote_characters_regexp = nil else quote_characters_regexp = /\A[#{Regexp.escape(Reline.completer_quote_characters)}]/ end 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 quote_characters_regexp and 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 word_break_regexp and not quote and slice =~ word_break_regexp rest = $' i += 1 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 if @is_multiline lines = whole_lines if @line_index > 0 preposing = lines[0..(@line_index - 1)].join("\n") + "\n" + preposing end if (lines.size - 1) > @line_index postposing = postposing + "\n" + lines[(@line_index + 1)..-1].join("\n") end end [preposing.encode(@encoding), target.encode(@encoding), postposing.encode(@encoding)] end
# File reline/line_editor.rb, line 67 def set_pasting_state(in_pasting) @in_pasting = in_pasting end
# File reline/line_editor.rb, line 225 def set_signal_handlers @old_trap = Signal.trap('INT') { clear_dialog(0) if @scroll_partial_screen move_cursor_down(@screen_height - (@line_index - @scroll_partial_screen) - 1) else move_cursor_down(@highest_in_all - @line_index - 1) end Reline::IOGate.move_cursor_column(0) scroll_down(1) case @old_trap when 'DEFAULT', 'SYSTEM_DEFAULT' raise Interrupt when 'IGNORE' # Do nothing when 'EXIT' exit else @old_trap.call if @old_trap.respond_to?(:call) end } end
# File reline/line_editor.rb, line 71 def simplified_rendering? if finished? false elsif @just_cursor_moving and not @rerender_all true else not @rerender_all and not finished? and @in_pasting end end
# File reline/line_editor.rb, line 1853 def whole_buffer if @buffer_of_lines.size == 1 and @line.nil? nil else whole_lines.join("\n") end end
# File reline/line_editor.rb, line 1846 def whole_lines index = @previous_line_index || @line_index temp_lines = @buffer_of_lines.dup temp_lines[index] = @line temp_lines end
# File reline/line_editor.rb, line 1418 def wrap_method_call(method_symbol, method_obj, key, with_operator = false) 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 else if with_operator and inclusive?(method_obj) method_obj.(key, inclusive: true) else method_obj.(key) end end end
Private Instance Methods
# File reline/line_editor.rb, line 1408 def argumentable?(method_obj) method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg } end
# File reline/line_editor.rb, line 1877 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
# File reline/line_editor.rb, line 1871 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
# File reline/line_editor.rb, line 322 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
# File reline/line_editor.rb, line 340 def calculate_height_by_width(width) width.div(@screen_size.last) + 1 end
# File reline/line_editor.rb, line 378 def calculate_nearest_cursor(line_to_calc = @line, cursor = @cursor, started_from = @started_from, byte_pointer = @byte_pointer, update = true) new_cursor_max = calculate_width(line_to_calc) 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_to_calc, line_to_calc.bytesize) if last_byte_size > 0 last_mbchar = line_to_calc.byteslice(line_to_calc.bytesize - last_byte_size, last_byte_size) last_width = Reline::Unicode.get_mbchar_width(last_mbchar) end_of_line_cursor = new_cursor_max - last_width else end_of_line_cursor = new_cursor_max end else end_of_line_cursor = new_cursor_max end line_to_calc.grapheme_clusters.each do |gc| mbchar = gc.encode(Encoding::UTF_8) mbchar_width = Reline::Unicode.get_mbchar_width(mbchar) now = new_cursor + mbchar_width if now > end_of_line_cursor or now > cursor break end new_cursor += mbchar_width if new_cursor > max_width * height height += 1 end new_byte_pointer += gc.bytesize end new_started_from = height - 1 if update @cursor = new_cursor @cursor_max = new_cursor_max @started_from = new_started_from @byte_pointer = new_byte_pointer else [new_cursor, new_cursor_max, new_started_from, new_byte_pointer] end end
# File reline/line_editor.rb, line 875 def calculate_scroll_partial_screen(highest_in_all, cursor_y) if @screen_height < highest_in_all old_scroll_partial_screen = @scroll_partial_screen if cursor_y == 0 @scroll_partial_screen = 0 elsif cursor_y == (highest_in_all - 1) @scroll_partial_screen = highest_in_all - @screen_height else if @scroll_partial_screen if cursor_y <= @scroll_partial_screen @scroll_partial_screen = cursor_y elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y @scroll_partial_screen = cursor_y - (@screen_height - 1) end else if cursor_y > (@screen_height - 1) @scroll_partial_screen = cursor_y - (@screen_height - 1) else @scroll_partial_screen = 0 end end end if @scroll_partial_screen != old_scroll_partial_screen @rerender_all = true end else if @scroll_partial_screen @rerender_all = true end @scroll_partial_screen = nil end end
# File reline/line_editor.rb, line 1884 def calculate_width(str, allow_escape_code = false) Reline::Unicode.calculate_width(str, allow_escape_code) end
# File reline/line_editor.rb, line 81 def check_mode_string mode_string = nil if @config.show_mode_in_prompt if @config.editing_mode_is?(:vi_command) mode_string = @config.vi_cmd_mode_string elsif @config.editing_mode_is?(:vi_insert) mode_string = @config.vi_ins_mode_string elsif @config.editing_mode_is?(:emacs) mode_string = @config.emacs_mode_string else mode_string = '?' end end if mode_string != @prev_mode_string @rerender_all = true end @prev_mode_string = mode_string mode_string end
# File reline/line_editor.rb, line 101 def check_multiline_prompt(buffer, force_recalc: false) if @vi_arg prompt = "(arg: #{@vi_arg}) " @rerender_all = true elsif @searching_prompt prompt = @searching_prompt @rerender_all = true else prompt = @prompt end if simplified_rendering? && !force_recalc mode_string = check_mode_string prompt = mode_string + prompt if mode_string return [prompt, calculate_width(prompt, true), [prompt] * buffer.size] end if @prompt_proc use_cached_prompt_list = false if @cached_prompt_list if @just_cursor_moving use_cached_prompt_list = true elsif Time.now.to_f < (@prompt_cache_time + PROMPT_LIST_CACHE_TIMEOUT) and buffer.size == @cached_prompt_list.size use_cached_prompt_list = true end end use_cached_prompt_list = false if @rerender_all if use_cached_prompt_list prompt_list = @cached_prompt_list else prompt_list = @cached_prompt_list = @prompt_proc.(buffer).map { |pr| pr.gsub("\n", "\\n") } @prompt_cache_time = Time.now.to_f end prompt_list.map!{ prompt } if @vi_arg or @searching_prompt prompt_list = [prompt] if prompt_list.empty? mode_string = check_mode_string prompt_list = prompt_list.map{ |pr| mode_string + pr } if mode_string prompt = prompt_list[@line_index] prompt = prompt_list[0] if prompt.nil? prompt = prompt_list.last if prompt.nil? if buffer.size > prompt_list.size (buffer.size - prompt_list.size).times do prompt_list << prompt_list.last end end prompt_width = calculate_width(prompt, true) [prompt, prompt_width, prompt_list] else mode_string = check_mode_string prompt = mode_string + prompt if mode_string prompt_width = calculate_width(prompt, true) [prompt, prompt_width, nil] end end
# File reline/line_editor.rb, line 859 def clear_dialog(cursor_column) changes = @dialogs.map do |dialog| old_dialog = dialog.dup dialog.contents = nil [old_dialog, dialog] end render_dialog_changes(changes, cursor_column) end
# File reline/line_editor.rb, line 868 def clear_dialog_with_trap_key(cursor_column) clear_dialog(cursor_column) @dialogs.each do |dialog| dialog.trap_key = nil end end
# File reline/line_editor.rb, line 1205 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, back, with_control: false) else height = render_partial(prompt, prompt_width, line, back, with_control: false) end if index < (@buffer_of_lines.size - 1) move_cursor_down(1) back += height end end move_cursor_up(back) move_cursor_down(@first_line_started_from + @started_from) @rest_height = (Reline::IOGate.get_screen_size.first - 1) - Reline::IOGate.cursor_pos.y Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) end
# File reline/line_editor.rb, line 1280 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).split("\n")[@line_index] || String.new(encoding: @encoding) line_to_pointer = (preposing + completed + completion_append_character.to_s).split("\n").last || String.new(encoding: @encoding) @cursor_max = calculate_width(@line) @cursor = calculate_width(line_to_pointer) @byte_pointer = line_to_pointer.bytesize end end end
# File reline/line_editor.rb, line 1234 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
# File reline/line_editor.rb, line 2801 def copy_for_vi(text) if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command) @vi_clipboard = text end end
# File reline/line_editor.rb, line 671 def dialog_range(dialog, dialog_y) x_range = dialog.column...dialog.column + dialog.width y_range = dialog_y + dialog.vertical_offset...dialog_y + dialog.vertical_offset + dialog.contents.size [x_range, y_range] end
# File reline/line_editor.rb, line 3100 def ed_argument_digit(key) if @vi_arg.nil? if key.chr.to_i.zero? if key.anybits?(0b10000000) unescaped_key = key ^ 0b10000000 unless unescaped_key.chr.to_i.zero? @vi_arg = unescaped_key.chr.to_i end end else @vi_arg = key.chr.to_i end else @vi_arg = @vi_arg * 10 + key.chr.to_i end end
# File reline/line_editor.rb, line 2665 def ed_clear_screen(key) @cleared = true end
# File reline/line_editor.rb, line 3012 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_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer) mbchar = @line.byteslice(@byte_pointer - byte_size, byte_size) width = Reline::Unicode.get_mbchar_width(mbchar) @byte_pointer -= byte_size @cursor -= width end end arg -= 1 ed_delete_next_char(key, arg: arg) if arg > 0 end
# File reline/line_editor.rb, line 2930 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
# File reline/line_editor.rb, line 2698 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
- Editline
-
ed-insert
(vi input: almost all; emacs: printable characters) In insert mode, insert the input character left of the cursor position. In replace mode, overwrite the character at the cursor and move the cursor to the right by one character position. Accept an argument to do this repeatedly. It is an error if the input character is the NUL character (Ctrl-@
). Failure to enlarge the edit buffer also results in an error. - Editline
-
ed-digit
(emacs: 0 to 9) If in argument input mode, append the input digit to the argument being read. Otherwise, called-insert
. It is an error if the input character is not a digit or if the existing argument is already greater than a million. - GNU Readline
-
self-insert
(a, b, A, 1, !, …) Insert yourself.
# File reline/line_editor.rb, line 1941 def ed_insert(key) str = nil width = nil bytesize = nil if key.instance_of?(String) begin key.encode(Encoding::UTF_8) rescue Encoding::UndefinedConversionError return end str = key bytesize = key.bytesize else begin key.chr.encode(Encoding::UTF_8) rescue Encoding::UndefinedConversionError return end str = key.chr bytesize = 1 end if @in_pasting @continuous_insertion_buffer << str return elsif not @continuous_insertion_buffer.empty? process_insert end width = Reline::Unicode.get_mbchar_width(str) if @cursor == @cursor_max @line += str else @line = byteinsert(@line, @byte_pointer, str) end last_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer) @byte_pointer += bytesize last_mbchar = @line.byteslice((@byte_pointer - bytesize - last_byte_size), last_byte_size) combined_char = last_mbchar + str if last_byte_size != 0 and combined_char.grapheme_clusters.size == 1 # combined char last_mbchar_width = Reline::Unicode.get_mbchar_width(last_mbchar) combined_char_width = Reline::Unicode.get_mbchar_width(combined_char) if combined_char_width > last_mbchar_width width = combined_char_width - last_mbchar_width else width = 0 end end @cursor += width @cursor_max += width end
- Editline
-
ed-kill-line
(vi command:D
,Ctrl-K
; emacs:Ctrl-K
,Ctrl-U
) + Kill from the cursor to the end of the line. - GNU Readline
-
kill-line
(C-k
) Kill the text from point to the end of the line. With a negative numeric argument, kill backward from the cursor to the beginning of the current line.
# File reline/line_editor.rb, line 2548 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
# File reline/line_editor.rb, line 2054 def ed_move_to_beg(key) @byte_pointer = @cursor = 0 end
# File reline/line_editor.rb, line 2059 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
# File reline/line_editor.rb, line 2487 def ed_newline(key) 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
# File reline/line_editor.rb, line 2010 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
# File reline/line_editor.rb, line 2439 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") @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
# File reline/line_editor.rb, line 2030 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
# File reline/line_editor.rb, line 2388 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") @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") @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 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
# File reline/line_editor.rb, line 2679 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
# File reline/line_editor.rb, line 1994 def ed_quoted_insert(str, arg: 1) @waiting_proc = proc { |key| arg.times do if key == "\C-j".ord or key == "\C-m".ord key_newline(key) elsif key == 0 # Ignore NUL. else ed_insert(key) end end @waiting_proc = nil } end
# File reline/line_editor.rb, line 2336 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[@line_index] @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
# File reline/line_editor.rb, line 2289 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[@line_index] @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
# File reline/line_editor.rb, line 2710 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
# File reline/line_editor.rb, line 2730 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? @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
- Editline
-
ed-unassigned
This editor command always results in an error. - GNU Readline
-
There is no corresponding macro.
# File reline/line_editor.rb, line 1911 def ed_unassigned(key) end
# File reline/line_editor.rb, line 2745 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
# File reline/line_editor.rb, line 2597 def em_delete(key) if @line.empty? and (not @is_multiline or @buffer_of_lines.size == 1) and key == "\C-d".ord @line = nil if @buffer_of_lines.size > 1 scroll_down(@highest_in_all - @first_line_started_from) end Reline::IOGate.move_cursor_column(0) @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
# File reline/line_editor.rb, line 2688 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
# File reline/line_editor.rb, line 2624 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
# File reline/line_editor.rb, line 2520 def em_delete_prev_char(key, arg: 1) 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 arg -= 1 em_delete_prev_char(key, arg: arg) if arg > 0 end
# File reline/line_editor.rb, line 3284 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
- Editline
-
em-kill-line
(not bound) Delete the entire contents of the edit buffer and save it to the cut buffer.vi-kill-line-prev
- GNU Readline
-
kill-whole-line
(not bound) Kill all characters on the current line, no matter where point is.
# File reline/line_editor.rb, line 2586 def em_kill_line(key) if @line.size > 0 @kill_ring.append(@line.dup, true) @line.clear @byte_pointer = 0 @cursor_max = 0 @cursor = 0 end end
# File reline/line_editor.rb, line 2789 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, true) end end
# File reline/line_editor.rb, line 2757 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
# File reline/line_editor.rb, line 3295 def em_meta_next(key) end
# File reline/line_editor.rb, line 2670 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
# File reline/line_editor.rb, line 3279 def em_set_mark(key) @mark_pointer = [@byte_pointer, @line_index] end
# File reline/line_editor.rb, line 2773 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
# File reline/line_editor.rb, line 2636 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
# File reline/line_editor.rb, line 2648 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
# File reline/line_editor.rb, line 2074 def generate_searcher Fiber.new do |first_key| prev_search_key = first_key search_word = String.new(encoding: @encoding) multibyte_buf = String.new(encoding: 'ASCII-8BIT') last_hit = nil case first_key when "\C-r".ord prompt_name = 'reverse-i-search' when "\C-s".ord prompt_name = 'i-search' end loop do key = Fiber.yield(search_word) search_again = false case key when -1 # determined Reline.last_incremental_search = search_word break when "\C-h".ord, "\C-?".ord grapheme_clusters = search_word.grapheme_clusters if grapheme_clusters.size > 0 grapheme_clusters.pop search_word = grapheme_clusters.join end when "\C-r".ord, "\C-s".ord search_again = true if prev_search_key == key prev_search_key = key 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) @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 case prev_search_key when "\C-r".ord history_pointer_base = 0 history = Reline::HISTORY[0..(@history_pointer - 1)] when "\C-s".ord history_pointer_base = @history_pointer + 1 history = Reline::HISTORY[(@history_pointer + 1)..-1] end else history_pointer_base = 0 history = Reline::HISTORY end elsif @history_pointer case prev_search_key when "\C-r".ord history_pointer_base = 0 history = Reline::HISTORY[0..@history_pointer] when "\C-s".ord history_pointer_base = @history_pointer history = Reline::HISTORY[@history_pointer..-1] end else history_pointer_base = 0 history = Reline::HISTORY end 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 if hit_index @history_pointer = history_pointer_base + hit_index hit = Reline::HISTORY[@history_pointer] end end case prev_search_key when "\C-r".ord prompt_name = 'reverse-i-search' when "\C-s".ord prompt_name = 'i-search' end 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 @byte_pointer = @line.bytesize @cursor = @cursor_max = calculate_width(@line) @rerender_all = true @searching_prompt = "(%s)`%s'" % [prompt_name, search_word] else @line = hit @searching_prompt = "(%s)`%s': %s" % [prompt_name, search_word, hit] end last_hit = hit else if @is_multiline @rerender_all = true @searching_prompt = "(failed %s)`%s'" % [prompt_name, search_word] else @searching_prompt = "(failed %s)`%s': %s" % [prompt_name, search_word, last_hit] end end end end end
# File reline/line_editor.rb, line 1412 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
# File reline/line_editor.rb, line 2192 def incremental_search_history(key) 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) @searching_prompt = "(reverse-i-search)`': " termination_keys = ["\C-j".ord] termination_keys.concat(@config.isearch_terminators&.chars&.map(&:ord)) if @config.isearch_terminators @waiting_proc = ->(k) { case k when *termination_keys if @history_pointer buffer = Reline::HISTORY[@history_pointer] 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 end @searching_prompt = nil @waiting_proc = nil @cursor_max = calculate_width(@line) @cursor = @byte_pointer = 0 @rerender_all = true @cached_prompt_list = nil searcher.resume(-1) 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 @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 else chr = k.is_a?(String) ? k : k.chr(Encoding::ASCII_8BIT) 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) 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 else @line_backup_in_history = @line @line = line end @searching_prompt = nil @waiting_proc = nil @cursor_max = calculate_width(@line) @cursor = @byte_pointer = 0 @rerender_all = true @cached_prompt_list = nil searcher.resume(-1) end end } end
# File reline/line_editor.rb, line 332 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 @just_cursor_moving = false end
# File reline/line_editor.rb, line 1888 def key_delete(key) if @config.editing_mode_is?(:vi_insert) ed_delete_next_char(key) elsif @config.editing_mode_is?(:emacs) em_delete(key) end end
# File reline/line_editor.rb, line 1896 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 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 @check_new_auto_indent = true unless @in_pasting end end
# File reline/line_editor.rb, line 1182 def modify_lines(before, force_recalc: false) return before if !force_recalc && (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
# File reline/line_editor.rb, line 1324 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) if @completion_journey_data.list.size == 1 @completion_journey_data.pointer = 0 else case direction when :up @completion_journey_data.pointer = @completion_journey_data.list.size - 1 when :down @completion_journey_data.pointer = 1 end end @completion_state = CompletionState::JOURNEY else case direction when :up @completion_journey_data.pointer -= 1 if @completion_journey_data.pointer < 0 @completion_journey_data.pointer = @completion_journey_data.list.size - 1 end when :down @completion_journey_data.pointer += 1 if @completion_journey_data.pointer >= @completion_journey_data.list.size @completion_journey_data.pointer = 0 end end end completed = @completion_journey_data.list[@completion_journey_data.pointer] new_line = (@completion_journey_data.preposing + completed + @completion_journey_data.postposing).split("\n")[@line_index] @line = new_line.nil? ? String.new(encoding: @encoding) : new_line line_to_pointer = (@completion_journey_data.preposing + completed).split("\n").last line_to_pointer = String.new(encoding: @encoding) if line_to_pointer.nil? @cursor_max = calculate_width(@line) @cursor = calculate_width(line_to_pointer) @byte_pointer = line_to_pointer.bytesize end
# File reline/line_editor.rb, line 368 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
# File reline/line_editor.rb, line 359 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
# File reline/line_editor.rb, line 1495 def normal_char(key) method_symbol = method_obj = nil if key.combined_char.is_a?(Symbol) process_key(key.combined_char, key.combined_char) return end @multibyte_buffer << key.combined_char if @multibyte_buffer.size > 1 if @multibyte_buffer.dup.force_encoding(@encoding).valid_encoding? process_key(@multibyte_buffer.dup.force_encoding(@encoding), nil) @multibyte_buffer.clear else # invalid return end else # single byte 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 if @config.editing_mode_is?(:vi_command, :vi_insert) # split ESC + key in vi mode method_symbol = @config.editing_mode.get_method("\e".ord) process_key("\e".ord, method_symbol) method_symbol = @config.editing_mode.get_method(key.char) process_key(key.char, method_symbol) end else 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
# File reline/line_editor.rb, line 658 def padding_space_with_escape_sequences(str, width) padding_width = width - calculate_width(str, true) # padding_width should be only positive value. But macOS and Alacritty returns negative value. padding_width = 0 if padding_width < 0 str + (' ' * padding_width) end
# File reline/line_editor.rb, line 1640 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 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 result = @auto_indent_proc.(new_lines[0..-2], @line_index - 1, (new_lines[@line_index - 1].bytesize + 1), false) if result new_indent = result end if new_indent&.>= 0 @line = ' ' * new_indent + @line.lstrip end end new_lines = whole_lines new_indent = @auto_indent_proc.(new_lines, @line_index, @byte_pointer, @check_new_auto_indent) if new_indent&.>= 0 md = new_lines[@line_index].match(/\A */) prev_indent = md[0].count(' ') if @check_new_auto_indent line = @buffer_of_lines[@line_index] = ' ' * new_indent + @buffer_of_lines[@line_index].lstrip @cursor = new_indent @cursor_max = calculate_width(line) @byte_pointer = new_indent else @line = ' ' * new_indent + @line.lstrip @cursor += new_indent - prev_indent @cursor_max = calculate_width(@line) @byte_pointer += new_indent - prev_indent end end @check_new_auto_indent = false end
# File reline/line_editor.rb, line 1913 def process_insert(force: false) return if @continuous_insertion_buffer.empty? or (@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
# File reline/line_editor.rb, line 1438 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 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 wrap_method_call(method_symbol, method_obj, key) if method_obj end @kill_ring.process if @vi_arg @rerender_al = true @vi_arg = nil end 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 wrap_method_call(method_symbol, method_obj, key) else ed_insert(key) unless @config.editing_mode_is?(:vi_command) end @kill_ring.process if @vi_arg @rerender_all = true @vi_arg = nil end end elsif @waiting_proc @waiting_proc.(key) @kill_ring.process elsif method_obj if method_symbol == :ed_argument_digit 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
# File reline/line_editor.rb, line 665 def range_subtract(base_ranges, subtract_ranges) indices = base_ranges.flat_map(&:to_a).uniq.sort - subtract_ranges.flat_map(&:to_a) chunks = indices.chunk_while { |a, b| a + 1 == b } chunks.map { |a| a.first...a.last + 1 } end
# File reline/line_editor.rb, line 649 def render_dialog(cursor_column) changes = @dialogs.map do |dialog| old_dialog = dialog.dup update_each_dialog(dialog, cursor_column) [old_dialog, dialog] end render_dialog_changes(changes, cursor_column) end
# File reline/line_editor.rb, line 677 def render_dialog_changes(changes, cursor_column) # Collect x-coordinate range and content of previous and current dialogs for each line old_dialog_ranges = {} new_dialog_ranges = {} new_dialog_contents = {} changes.each do |old_dialog, new_dialog| if old_dialog.contents x_range, y_range = dialog_range(old_dialog, @previous_rendered_dialog_y) y_range.each do |y| (old_dialog_ranges[y] ||= []) << x_range end end if new_dialog.contents x_range, y_range = dialog_range(new_dialog, @first_line_started_from + @started_from) y_range.each do |y| (new_dialog_ranges[y] ||= []) << x_range (new_dialog_contents[y] ||= []) << [x_range, new_dialog.contents[y - y_range.begin]] end end end return if old_dialog_ranges.empty? && new_dialog_ranges.empty? # Calculate x-coordinate ranges to restore text that was hidden behind dialogs for each line ranges_to_restore = {} subtract_cache = {} old_dialog_ranges.each do |y, old_x_ranges| new_x_ranges = new_dialog_ranges[y] || [] ranges = subtract_cache[[old_x_ranges, new_x_ranges]] ||= range_subtract(old_x_ranges, new_x_ranges) ranges_to_restore[y] = ranges if ranges.any? end # Create visual_lines for restoring text hidden behind dialogs if ranges_to_restore.any? lines = whole_lines prompt, _prompt_width, prompt_list = check_multiline_prompt(lines, force_recalc: true) modified_lines = modify_lines(lines, force_recalc: true) visual_lines = [] modified_lines.each_with_index { |l, i| pr = prompt_list ? prompt_list[i] : prompt vl, = split_by_width(pr + l, @screen_size.last) vl.compact! visual_lines.concat(vl) } end # Clear and rerender all dialogs line by line Reline::IOGate.hide_cursor ymin, ymax = (ranges_to_restore.keys + new_dialog_ranges.keys).minmax scroll_partial_screen = @scroll_partial_screen || 0 screen_y_range = scroll_partial_screen..(scroll_partial_screen + @screen_height - 1) ymin = ymin.clamp(screen_y_range.begin, screen_y_range.end) ymax = ymax.clamp(screen_y_range.begin, screen_y_range.end) dialog_y = @first_line_started_from + @started_from cursor_y = dialog_y if @highest_in_all <= ymax scroll_down(ymax - cursor_y) move_cursor_up(ymax - cursor_y) end (ymin..ymax).each do |y| move_cursor_down(y - cursor_y) cursor_y = y new_x_ranges = new_dialog_ranges[y] restore_ranges = ranges_to_restore[y] # Restore text that was hidden behind dialogs if restore_ranges line = visual_lines[y] || '' restore_ranges.each do |range| col = range.begin width = range.end - range.begin s = padding_space_with_escape_sequences(Reline::Unicode.take_range(line, col, width), width) Reline::IOGate.move_cursor_column(col) @output.write "\e[0m#{s}\e[0m" end max_column = [calculate_width(line, true), new_x_ranges&.map(&:end)&.max || 0].max if max_column < restore_ranges.map(&:end).max Reline::IOGate.move_cursor_column(max_column) Reline::IOGate.erase_after_cursor end end # Render dialog contents new_dialog_contents[y]&.each do |x_range, content| Reline::IOGate.move_cursor_column(x_range.begin) @output.write "\e[0m#{content}\e[0m" end end move_cursor_up(cursor_y - dialog_y) Reline::IOGate.move_cursor_column(cursor_column) Reline::IOGate.show_cursor @previous_rendered_dialog_y = dialog_y end
# File reline/line_editor.rb, line 1085 def render_partial(prompt, prompt_width, line_to_render, this_started_from, with_control: true) visual_lines, height = split_by_width(line_to_render.nil? ? prompt : prompt + line_to_render, @screen_size.last) cursor_up_from_last_line = 0 if @scroll_partial_screen last_visual_line = this_started_from + (height - 1) last_screen_line = @scroll_partial_screen + (@screen_height - 1) if (@scroll_partial_screen - this_started_from) >= height # Render nothing because this line is before the screen. visual_lines = [] elsif this_started_from > last_screen_line # Render nothing because this line is after the screen. visual_lines = [] else deleted_lines_before_screen = [] if @scroll_partial_screen > this_started_from and last_visual_line >= @scroll_partial_screen # A part of visual lines are before the screen. deleted_lines_before_screen = visual_lines.shift((@scroll_partial_screen - this_started_from) * 2) deleted_lines_before_screen.compact! end if this_started_from <= last_screen_line and last_screen_line < last_visual_line # A part of visual lines are after the screen. visual_lines.pop((last_visual_line - last_screen_line) * 2) end move_cursor_up(deleted_lines_before_screen.size - @started_from) cursor_up_from_last_line = @started_from - deleted_lines_before_screen.size end end if with_control if height > @highest_in_this diff = height - @highest_in_this scroll_down(diff) @highest_in_all += diff @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 cursor_up_from_last_line = height - 1 - @started_from end if Reline::Unicode::CSI_REGEXP.match?(prompt + line_to_render) @output.write "\e[0m" # clear character decorations end visual_lines.each_with_index do |line, index| Reline::IOGate.move_cursor_column(0) 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? and Reline::IOGate.win_legacy_console? # 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 next end @output.write line if Reline::IOGate.win? and Reline::IOGate.win_legacy_console? 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 @output.flush if @first_prompt @first_prompt = false @pre_input_hook&.call end end unless visual_lines.empty? Reline::IOGate.erase_after_cursor Reline::IOGate.move_cursor_column(0) end 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(cursor_up_from_last_line) # This logic is buggy if a fullwidth char is wrapped because there is only one halfwidth at end of a line. Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) end end height end
# File reline/line_editor.rb, line 1059 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, rendered_height, with_control: false) if index < (lines.size - 1) if @scroll_partial_screen if (@scroll_partial_screen - height) < rendered_height and (@scroll_partial_screen + @screen_height - 1) >= (rendered_height + height) move_cursor_down(1) end else scroll_down(1) end rendered_height += height else rendered_height += height - 1 end end rendered_height end
# File reline/line_editor.rb, line 908 def rerender_added_newline(prompt, prompt_width, prompt_list) @buffer_of_lines[@previous_line_index] = @line @line = @buffer_of_lines[@line_index] @previous_line_index = nil if @in_pasting scroll_down(1) else lines = whole_lines prev_line_prompt = @prompt_proc ? prompt_list[@line_index - 1] : prompt prev_line_prompt_width = @prompt_proc ? calculate_width(prev_line_prompt, true) : prompt_width prev_line = modify_lines(lines)[@line_index - 1] move_cursor_up(@started_from) render_partial(prev_line_prompt, prev_line_prompt_width, prev_line, @first_line_started_from + @started_from, with_control: false) scroll_down(1) render_partial(prompt, prompt_width, @line, @first_line_started_from + @started_from + 1, with_control: false) end @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 end
# File reline/line_editor.rb, line 1004 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) 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 old_highest_in_all = @highest_in_all if @line_index.zero? new_first_line_started_from = 0 else new_first_line_started_from = calculate_height_by_lines(new_buffer[0..(@line_index - 1)], prompt_list || prompt) end new_started_from = calculate_height_by_width(prompt_width + @cursor) - 1 calculate_scroll_partial_screen(back, new_first_line_started_from + new_started_from) if @scroll_partial_screen move_cursor_up(@first_line_started_from + @started_from) scroll_down(@screen_height - 1) move_cursor_up(@screen_height) Reline::IOGate.move_cursor_column(0) elsif back > old_highest_in_all scroll_down(back - 1) move_cursor_up(back - 1) elsif back < old_highest_in_all scroll_down(back) Reline::IOGate.erase_after_cursor (old_highest_in_all - back - 1).times do scroll_down(1) Reline::IOGate.erase_after_cursor end move_cursor_up(old_highest_in_all - 1) end render_whole_lines(new_buffer, prompt_list || prompt, prompt_width) if @prompt_proc prompt = prompt_list[@line_index] prompt_width = calculate_width(prompt, true) end @highest_in_this = calculate_height_by_width(prompt_width + @cursor_max) @highest_in_all = back @first_line_started_from = new_first_line_started_from @started_from = new_started_from if @scroll_partial_screen Reline::IOGate.move_cursor_up(@screen_height - (@first_line_started_from + @started_from - @scroll_partial_screen) - 1) Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) else move_cursor_down(@first_line_started_from + @started_from - back + 1) Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) end end
# File reline/line_editor.rb, line 960 def rerender_changed_current_line new_lines = whole_lines prompt, prompt_width, prompt_list = check_multiline_prompt(new_lines) 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
# File reline/line_editor.rb, line 1370 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 if @vi_arg @rerender_all = true @vi_arg = nil end else block.(false) end end
# File reline/line_editor.rb, line 348 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
# File reline/line_editor.rb, line 3168 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
# File reline/line_editor.rb, line 3225 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
# File reline/line_editor.rb, line 344 def split_by_width(str, max_width) Reline::Unicode.split_by_width(str, max_width, @encoding) end
# File reline/line_editor.rb, line 769 def update_each_dialog(dialog, cursor_column) if @in_pasting dialog.contents = nil dialog.trap_key = nil return end dialog.set_cursor_pos(cursor_column, @first_line_started_from + @started_from) dialog_render_info = dialog.call(@last_key) if dialog_render_info.nil? or dialog_render_info.contents.nil? or dialog_render_info.contents.empty? dialog.contents = nil dialog.trap_key = nil return end contents = dialog_render_info.contents pointer = dialog.pointer if dialog_render_info.width dialog.width = dialog_render_info.width else dialog.width = contents.map { |l| calculate_width(l, true) }.max end height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT height = contents.size if contents.size < height if contents.size > height if dialog.pointer if dialog.pointer < 0 dialog.scroll_top = 0 elsif (dialog.pointer - dialog.scroll_top) >= (height - 1) dialog.scroll_top = dialog.pointer - (height - 1) elsif (dialog.pointer - dialog.scroll_top) < 0 dialog.scroll_top = dialog.pointer end pointer = dialog.pointer - dialog.scroll_top else dialog.scroll_top = 0 end contents = contents[dialog.scroll_top, height] end if dialog_render_info.scrollbar and dialog_render_info.contents.size > height bar_max_height = height * 2 moving_distance = (dialog_render_info.contents.size - height) * 2 position_ratio = dialog.scroll_top.zero? ? 0.0 : ((dialog.scroll_top * 2).to_f / moving_distance) bar_height = (bar_max_height * ((contents.size * 2).to_f / (dialog_render_info.contents.size * 2))).floor.to_i bar_height = MINIMUM_SCROLLBAR_HEIGHT if bar_height < MINIMUM_SCROLLBAR_HEIGHT scrollbar_pos = ((bar_max_height - bar_height) * position_ratio).floor.to_i else scrollbar_pos = nil end upper_space = @first_line_started_from - @started_from dialog.column = dialog_render_info.pos.x dialog.width += @block_elem_width if scrollbar_pos diff = (dialog.column + dialog.width) - (@screen_size.last) if diff > 0 dialog.column -= diff end if (@rest_height - dialog_render_info.pos.y) >= height dialog.vertical_offset = dialog_render_info.pos.y + 1 elsif upper_space >= height dialog.vertical_offset = dialog_render_info.pos.y - height else dialog.vertical_offset = dialog_render_info.pos.y + 1 end if dialog.column < 0 dialog.column = 0 dialog.width = @screen_size.last end face = Reline::Face[dialog_render_info.face || :default] scrollbar_sgr = face[:scrollbar] default_sgr = face[:default] enhanced_sgr = face[:enhanced] dialog.contents = contents.map.with_index do |item, i| line_sgr = i == pointer ? enhanced_sgr : default_sgr str_width = dialog.width - (scrollbar_pos.nil? ? 0 : @block_elem_width) str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width) colored_content = "#{line_sgr}#{str}" if scrollbar_pos if scrollbar_pos <= (i * 2) and (i * 2 + 1) < (scrollbar_pos + bar_height) colored_content + scrollbar_sgr + @full_block elsif scrollbar_pos <= (i * 2) and (i * 2) < (scrollbar_pos + bar_height) colored_content + scrollbar_sgr + @upper_half_block elsif scrollbar_pos <= (i * 2 + 1) and (i * 2) < (scrollbar_pos + bar_height) colored_content + scrollbar_sgr + @lower_half_block else colored_content + scrollbar_sgr + ' ' * @block_elem_width end else colored_content end end end
# File reline/line_editor.rb, line 2811 def vi_add(key) @config.editing_mode = :vi_insert ed_next_char(key) end
# File reline/line_editor.rb, line 2925 def vi_add_at_eol(key) ed_move_to_end(key) @config.editing_mode = :vi_insert end
# File reline/line_editor.rb, line 2951 def vi_change_meta(key, arg: 1) @drop_terminate_spaces = true @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 @drop_terminate_spaces = false } @waiting_operator_vi_arg = arg end
# File reline/line_editor.rb, line 2816 def vi_command_mode(key) ed_prev_char(key) @config.editing_mode = :vi_command end
# File reline/line_editor.rb, line 2969 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
# File reline/line_editor.rb, line 2900 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
# File reline/line_editor.rb, line 2881 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
# File reline/line_editor.rb, line 2842 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
# File reline/line_editor.rb, line 2050 def vi_first_print(key) @byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line) end
# File reline/line_editor.rb, line 3054 def vi_histedit(key) path = Tempfile.open { |fp| if @is_multiline fp.write whole_lines.join("\n") else fp.write @line end fp.path } system("#{ENV['EDITOR']} #{path}") if @is_multiline @buffer_of_lines = File.read(path).split("\n") @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty? @line_index = 0 @line = @buffer_of_lines[@line_index] @rerender_all = true else @line = File.read(path) end finish end
# File reline/line_editor.rb, line 2807 def vi_insert(key) @config.editing_mode = :vi_insert end
# File reline/line_editor.rb, line 2920 def vi_insert_at_bol(key) ed_move_to_beg(key) @config.editing_mode = :vi_insert end
# File reline/line_editor.rb, line 3265 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
- Editline
-
vi-kill-line-prev
(vi:Ctrl-U
) Delete the string from the beginning of the edit buffer to the cursor and save it to the cut buffer. - GNU Readline
-
unix-line-discard
(C-u
) Kill backward from the cursor to the beginning of the current line.
# File reline/line_editor.rb, line 2571 def vi_kill_line_prev(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
# File reline/line_editor.rb, line 2996 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 if @buffer_of_lines.size > 1 scroll_down(@highest_in_all - @first_line_started_from) end Reline::IOGate.move_cursor_column(0) @eof = true finish else ed_newline(key) end end
# File reline/line_editor.rb, line 2861 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
# File reline/line_editor.rb, line 3160 def vi_next_char(key, arg: 1, inclusive: false) @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) } end
# File reline/line_editor.rb, line 2822 def vi_next_word(key, arg: 1) if @line.bytesize > @byte_pointer byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces) @byte_pointer += byte_size @cursor += width end arg -= 1 vi_next_word(key, arg: arg) if arg > 0 end
# File reline/line_editor.rb, line 3088 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
# File reline/line_editor.rb, line 3076 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
# File reline/line_editor.rb, line 2871 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
# File reline/line_editor.rb, line 3217 def vi_prev_char(key, arg: 1) @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) } end
# File reline/line_editor.rb, line 2832 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
# File reline/line_editor.rb, line 3132 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.bytesize - 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.bytesize - 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
# File reline/line_editor.rb, line 2284 def vi_search_next(key) incremental_search_history(key) end
# File reline/line_editor.rb, line 2279 def vi_search_prev(key) incremental_search_history(key) end
# File reline/line_editor.rb, line 3117 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
# File reline/line_editor.rb, line 3031 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
# File reline/line_editor.rb, line 3164 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
# File reline/line_editor.rb, line 3221 def vi_to_prev_char(key, arg: 1) @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) } end
# File reline/line_editor.rb, line 2984 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
# File reline/line_editor.rb, line 2946 def vi_zero(key) @byte_pointer = 0 @cursor = 0 end