diff options
Diffstat (limited to 'lib/reline')
| -rw-r--r-- | lib/reline/ansi.rb | 355 | ||||
| -rw-r--r-- | lib/reline/config.rb | 401 | ||||
| -rw-r--r-- | lib/reline/general_io.rb | 113 | ||||
| -rw-r--r-- | lib/reline/history.rb | 76 | ||||
| -rw-r--r-- | lib/reline/key_actor.rb | 7 | ||||
| -rw-r--r-- | lib/reline/key_actor/base.rb | 19 | ||||
| -rw-r--r-- | lib/reline/key_actor/emacs.rb | 517 | ||||
| -rw-r--r-- | lib/reline/key_actor/vi_command.rb | 518 | ||||
| -rw-r--r-- | lib/reline/key_actor/vi_insert.rb | 517 | ||||
| -rw-r--r-- | lib/reline/key_stroke.rb | 105 | ||||
| -rw-r--r-- | lib/reline/kill_ring.rb | 125 | ||||
| -rw-r--r-- | lib/reline/line_editor.rb | 3347 | ||||
| -rw-r--r-- | lib/reline/reline.gemspec | 25 | ||||
| -rw-r--r-- | lib/reline/terminfo.rb | 174 | ||||
| -rw-r--r-- | lib/reline/unicode.rb | 665 | ||||
| -rw-r--r-- | lib/reline/unicode/east_asian_width.rb | 1196 | ||||
| -rw-r--r-- | lib/reline/version.rb | 3 | ||||
| -rw-r--r-- | lib/reline/windows.rb | 501 |
18 files changed, 0 insertions, 8664 deletions
diff --git a/lib/reline/ansi.rb b/lib/reline/ansi.rb deleted file mode 100644 index 42a3913387..0000000000 --- a/lib/reline/ansi.rb +++ /dev/null @@ -1,355 +0,0 @@ -require 'io/console' -require 'io/wait' -require 'timeout' -require_relative 'terminfo' - -class Reline::ANSI - CAPNAME_KEY_BINDINGS = { - 'khome' => :ed_move_to_beg, - 'kend' => :ed_move_to_end, - 'kdch1' => :key_delete, - 'kcuu1' => :ed_prev_history, - 'kcud1' => :ed_next_history, - 'kcuf1' => :ed_next_char, - 'kcub1' => :ed_prev_char, - 'cuu' => :ed_prev_history, - 'cud' => :ed_next_history, - 'cuf' => :ed_next_char, - 'cub' => :ed_prev_char, - } - - if Reline::Terminfo.enabled? - Reline::Terminfo.setupterm(0, 2) - end - - def self.encoding - Encoding.default_external - end - - def self.win? - false - end - - def self.set_default_key_bindings(config, allow_terminfo: true) - if allow_terminfo && Reline::Terminfo.enabled? - set_default_key_bindings_terminfo(config) - else - set_default_key_bindings_comprehensive_list(config) - end - { - # extended entries of terminfo - [27, 91, 49, 59, 53, 67] => :em_next_word, # Ctrl+→, extended entry - [27, 91, 49, 59, 53, 68] => :ed_prev_word, # Ctrl+←, extended entry - [27, 91, 49, 59, 51, 67] => :em_next_word, # Meta+→, extended entry - [27, 91, 49, 59, 51, 68] => :ed_prev_word, # Meta+←, extended entry - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - config.add_default_key_binding_by_keymap(:vi_insert, key, func) - config.add_default_key_binding_by_keymap(:vi_command, key, func) - end - { - [27, 91, 90] => :completion_journey_up, # S-Tab - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - config.add_default_key_binding_by_keymap(:vi_insert, key, func) - end - { - # default bindings - [27, 32] => :em_set_mark, # M-<space> - [24, 24] => :em_exchange_mark, # C-x C-x - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - end - end - - def self.set_default_key_bindings_terminfo(config) - key_bindings = CAPNAME_KEY_BINDINGS.map do |capname, key_binding| - begin - key_code = Reline::Terminfo.tigetstr(capname) - case capname - # Escape sequences that omit the move distance and are set to defaults - # value 1 may be sometimes sent by pressing the arrow-key. - when 'cuu', 'cud', 'cuf', 'cub' - [ key_code.sub(/%p1%d/, '').bytes, key_binding ] - else - [ key_code.bytes, key_binding ] - end - rescue Reline::Terminfo::TerminfoError - # capname is undefined - end - end.compact.to_h - - key_bindings.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - config.add_default_key_binding_by_keymap(:vi_insert, key, func) - config.add_default_key_binding_by_keymap(:vi_command, key, func) - end - end - - def self.set_default_key_bindings_comprehensive_list(config) - { - # Console (80x25) - [27, 91, 49, 126] => :ed_move_to_beg, # Home - [27, 91, 52, 126] => :ed_move_to_end, # End - [27, 91, 51, 126] => :key_delete, # Del - [27, 91, 65] => :ed_prev_history, # ↑ - [27, 91, 66] => :ed_next_history, # ↓ - [27, 91, 67] => :ed_next_char, # → - [27, 91, 68] => :ed_prev_char, # ← - - # KDE - [27, 91, 72] => :ed_move_to_beg, # Home - [27, 91, 70] => :ed_move_to_end, # End - # Del is 0x08 - [27, 71, 65] => :ed_prev_history, # ↑ - [27, 71, 66] => :ed_next_history, # ↓ - [27, 71, 67] => :ed_next_char, # → - [27, 71, 68] => :ed_prev_char, # ← - - # urxvt / exoterm - [27, 91, 55, 126] => :ed_move_to_beg, # Home - [27, 91, 56, 126] => :ed_move_to_end, # End - - # GNOME - [27, 79, 72] => :ed_move_to_beg, # Home - [27, 79, 70] => :ed_move_to_end, # End - # Del is 0x08 - # Arrow keys are the same of KDE - - # iTerm2 - [27, 27, 91, 67] => :em_next_word, # Option+→, extended entry - [27, 27, 91, 68] => :ed_prev_word, # Option+←, extended entry - [195, 166] => :em_next_word, # Option+f - [195, 162] => :ed_prev_word, # Option+b - - [27, 79, 65] => :ed_prev_history, # ↑ - [27, 79, 66] => :ed_next_history, # ↓ - [27, 79, 67] => :ed_next_char, # → - [27, 79, 68] => :ed_prev_char, # ← - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - config.add_default_key_binding_by_keymap(:vi_insert, key, func) - config.add_default_key_binding_by_keymap(:vi_command, key, func) - end - end - - @@input = STDIN - def self.input=(val) - @@input = val - end - - @@output = STDOUT - def self.output=(val) - @@output = val - end - - def self.with_raw_input - @@input.raw { yield } - end - - @@buf = [] - def self.inner_getc - unless @@buf.empty? - return @@buf.shift - end - until c = @@input.raw(intr: true) { @@input.wait_readable(0.1) && @@input.getbyte } - Reline.core.line_editor.resize - end - (c == 0x16 && @@input.raw(min: 0, time: 0, &:getbyte)) || c - rescue Errno::EIO - # Maybe the I/O has been closed. - nil - rescue Errno::ENOTTY - nil - end - - @@in_bracketed_paste_mode = false - START_BRACKETED_PASTE = String.new("\e[200~,", encoding: Encoding::ASCII_8BIT) - END_BRACKETED_PASTE = String.new("\e[200~.", encoding: Encoding::ASCII_8BIT) - def self.getc_with_bracketed_paste - buffer = String.new(encoding: Encoding::ASCII_8BIT) - buffer << inner_getc - while START_BRACKETED_PASTE.start_with?(buffer) or END_BRACKETED_PASTE.start_with?(buffer) do - if START_BRACKETED_PASTE == buffer - @@in_bracketed_paste_mode = true - return inner_getc - elsif END_BRACKETED_PASTE == buffer - @@in_bracketed_paste_mode = false - ungetc(-1) - return inner_getc - end - begin - succ_c = nil - Timeout.timeout(Reline.core.config.keyseq_timeout * 100) { - succ_c = inner_getc - } - rescue Timeout::Error - break - else - buffer << succ_c - end - end - buffer.bytes.reverse_each do |ch| - ungetc ch - end - inner_getc - end - - def self.getc - if Reline.core.config.enable_bracketed_paste - getc_with_bracketed_paste - else - inner_getc - end - end - - def self.in_pasting? - @@in_bracketed_paste_mode or (not Reline::IOGate.empty_buffer?) - end - - def self.empty_buffer? - unless @@buf.empty? - return false - end - !@@input.wait_readable(0) - end - - def self.ungetc(c) - @@buf.unshift(c) - end - - def self.retrieve_keybuffer - begin - return unless @@input.wait_readable(0.001) - str = @@input.read_nonblock(1024) - str.bytes.each do |c| - @@buf.push(c) - end - rescue EOFError - end - end - - def self.get_screen_size - s = @@input.winsize - return s if s[0] > 0 && s[1] > 0 - s = [ENV["LINES"].to_i, ENV["COLUMNS"].to_i] - return s if s[0] > 0 && s[1] > 0 - [24, 80] - rescue Errno::ENOTTY - [24, 80] - end - - def self.set_screen_size(rows, columns) - @@input.winsize = [rows, columns] - self - rescue Errno::ENOTTY - self - end - - def self.cursor_pos - begin - res = +'' - m = nil - @@input.raw do |stdin| - @@output << "\e[6n" - @@output.flush - loop do - c = stdin.getc - next if c.nil? - res << c - m = res.match(/\e\[(?<row>\d+);(?<column>\d+)R/) - break if m - end - (m.pre_match + m.post_match).chars.reverse_each do |ch| - stdin.ungetc ch - end - end - column = m[:column].to_i - 1 - row = m[:row].to_i - 1 - rescue Errno::ENOTTY - begin - buf = @@output.pread(@@output.pos, 0) - row = buf.count("\n") - column = buf.rindex("\n") ? (buf.size - buf.rindex("\n")) - 1 : 0 - rescue Errno::ESPIPE - # Just returns column 1 for ambiguous width because this I/O is not - # tty and can't seek. - row = 0 - column = 1 - end - end - Reline::CursorPos.new(column, row) - end - - def self.move_cursor_column(x) - @@output.write "\e[#{x + 1}G" - end - - def self.move_cursor_up(x) - if x > 0 - @@output.write "\e[#{x}A" - elsif x < 0 - move_cursor_down(-x) - end - end - - def self.move_cursor_down(x) - if x > 0 - @@output.write "\e[#{x}B" - elsif x < 0 - move_cursor_up(-x) - end - end - - def self.hide_cursor - if Reline::Terminfo.enabled? - begin - @@output.write Reline::Terminfo.tigetstr('civis') - rescue Reline::Terminfo::TerminfoError - # civis is undefined - end - else - # ignored - end - end - - def self.show_cursor - if Reline::Terminfo.enabled? - begin - @@output.write Reline::Terminfo.tigetstr('cnorm') - rescue Reline::Terminfo::TerminfoError - # cnorm is undefined - end - else - # ignored - end - end - - def self.erase_after_cursor - @@output.write "\e[K" - end - - def self.scroll_down(x) - return if x.zero? - @@output.write "\e[#{x}S" - end - - def self.clear_screen - @@output.write "\e[2J" - @@output.write "\e[1;1H" - end - - @@old_winch_handler = nil - def self.set_winch_handler(&handler) - @@old_winch_handler = Signal.trap('WINCH', &handler) - end - - def self.prep - retrieve_keybuffer - nil - end - - def self.deprep(otio) - Signal.trap('WINCH', @@old_winch_handler) if @@old_winch_handler - end -end diff --git a/lib/reline/config.rb b/lib/reline/config.rb deleted file mode 100644 index 87726393a6..0000000000 --- a/lib/reline/config.rb +++ /dev/null @@ -1,401 +0,0 @@ -class Reline::Config - attr_reader :test_mode - - KEYSEQ_PATTERN = /\\(?:C|Control)-[A-Za-z_]|\\(?:M|Meta)-[0-9A-Za-z_]|\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]|\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]|\\e|\\[\\\"\'abdfnrtv]|\\\d{1,3}|\\x\h{1,2}|./ - - class InvalidInputrc < RuntimeError - attr_accessor :file, :lineno - end - - VARIABLE_NAMES = %w{ - bind-tty-special-chars - blink-matching-paren - byte-oriented - completion-ignore-case - convert-meta - disable-completion - enable-keypad - expand-tilde - history-preserve-point - history-size - horizontal-scroll-mode - input-meta - keyseq-timeout - mark-directories - mark-modified-lines - mark-symlinked-directories - match-hidden-files - meta-flag - output-meta - page-completions - prefer-visible-bell - print-completions-horizontally - show-all-if-ambiguous - show-all-if-unmodified - visible-stats - show-mode-in-prompt - vi-cmd-mode-string - vi-ins-mode-string - emacs-mode-string - enable-bracketed-paste - isearch-terminators - } - VARIABLE_NAME_SYMBOLS = VARIABLE_NAMES.map { |v| :"#{v.tr(?-, ?_)}" } - VARIABLE_NAME_SYMBOLS.each do |v| - attr_accessor v - end - - attr_accessor :autocompletion - - def initialize - @additional_key_bindings = {} # from inputrc - @additional_key_bindings[:emacs] = {} - @additional_key_bindings[:vi_insert] = {} - @additional_key_bindings[:vi_command] = {} - @oneshot_key_bindings = {} - @skip_section = nil - @if_stack = nil - @editing_mode_label = :emacs - @keymap_label = :emacs - @keymap_prefix = [] - @key_actors = {} - @key_actors[:emacs] = Reline::KeyActor::Emacs.new - @key_actors[:vi_insert] = Reline::KeyActor::ViInsert.new - @key_actors[:vi_command] = Reline::KeyActor::ViCommand.new - @vi_cmd_mode_string = '(cmd)' - @vi_ins_mode_string = '(ins)' - @emacs_mode_string = '@' - # https://tiswww.case.edu/php/chet/readline/readline.html#IDX25 - @history_size = -1 # unlimited - @keyseq_timeout = 500 - @test_mode = false - @autocompletion = false - @convert_meta = true if seven_bit_encoding?(Reline::IOGate.encoding) - end - - def reset - if editing_mode_is?(:vi_command) - @editing_mode_label = :vi_insert - end - @additional_key_bindings.keys.each do |key| - @additional_key_bindings[key].clear - end - @oneshot_key_bindings.clear - reset_default_key_bindings - end - - def editing_mode - @key_actors[@editing_mode_label] - end - - def editing_mode=(val) - @editing_mode_label = val - end - - def editing_mode_is?(*val) - val.any?(@editing_mode_label) - end - - def keymap - @key_actors[@keymap_label] - end - - def inputrc_path - case ENV['INPUTRC'] - when nil, '' - else - return File.expand_path(ENV['INPUTRC']) - end - - # In the XDG Specification, if ~/.config/readline/inputrc exists, then - # ~/.inputrc should not be read, but for compatibility with GNU Readline, - # if ~/.inputrc exists, then it is given priority. - home_rc_path = File.expand_path('~/.inputrc') - return home_rc_path if File.exist?(home_rc_path) - - case path = ENV['XDG_CONFIG_HOME'] - when nil, '' - else - path = File.join(path, 'readline/inputrc') - return path if File.exist?(path) and path == File.expand_path(path) - end - - path = File.expand_path('~/.config/readline/inputrc') - return path if File.exist?(path) - - return home_rc_path - end - - private def default_inputrc_path - @default_inputrc_path ||= inputrc_path - end - - def read(file = nil) - file ||= default_inputrc_path - begin - if file.respond_to?(:readlines) - lines = file.readlines - else - lines = File.readlines(file) - end - rescue Errno::ENOENT - return nil - end - - read_lines(lines, file) - self - rescue InvalidInputrc => e - warn e.message - nil - end - - def key_bindings - # The key bindings for each editing mode will be overwritten by the user-defined ones. - kb = @key_actors[@editing_mode_label].default_key_bindings.dup - kb.merge!(@additional_key_bindings[@editing_mode_label]) - kb.merge!(@oneshot_key_bindings) - kb - end - - def add_oneshot_key_binding(keystroke, target) - @oneshot_key_bindings[keystroke] = target - end - - def reset_oneshot_key_bindings - @oneshot_key_bindings.clear - end - - def add_default_key_binding_by_keymap(keymap, keystroke, target) - @key_actors[keymap].default_key_bindings[keystroke] = target - end - - def add_default_key_binding(keystroke, target) - @key_actors[@keymap_label].default_key_bindings[keystroke] = target - end - - def reset_default_key_bindings - @key_actors.values.each do |ka| - ka.reset_default_key_bindings - end - end - - def read_lines(lines, file = nil) - if not lines.empty? and lines.first.encoding != Reline.encoding_system_needs - begin - lines = lines.map do |l| - l.encode(Reline.encoding_system_needs) - rescue Encoding::UndefinedConversionError - mes = "The inputrc encoded in #{lines.first.encoding.name} can't be converted to the locale #{Reline.encoding_system_needs.name}." - raise Reline::ConfigEncodingConversionError.new(mes) - end - end - end - conditions = [@skip_section, @if_stack] - @skip_section = nil - @if_stack = [] - - lines.each_with_index do |line, no| - next if line.match(/\A\s*#/) - - no += 1 - - line = line.chomp.lstrip - if line.start_with?('$') - handle_directive(line[1..-1], file, no) - next - end - - next if @skip_section - - case line - when /^set +([^ ]+) +([^ ]+)/i - var, value = $1.downcase, $2 - bind_variable(var, value) - next - when /\s*("#{KEYSEQ_PATTERN}+")\s*:\s*(.*)\s*$/o - key, func_name = $1, $2 - keystroke, func = bind_key(key, func_name) - next unless keystroke - @additional_key_bindings[@keymap_label][@keymap_prefix + keystroke] = func - end - end - unless @if_stack.empty? - raise InvalidInputrc, "#{file}:#{@if_stack.last[1]}: unclosed if" - end - ensure - @skip_section, @if_stack = conditions - end - - def handle_directive(directive, file, no) - directive, args = directive.split(' ') - case directive - when 'if' - condition = false - case args - when 'mode' - when 'term' - when 'version' - else # application name - condition = true if args == 'Ruby' - condition = true if args == 'Reline' - end - @if_stack << [file, no, @skip_section] - @skip_section = !condition - when 'else' - if @if_stack.empty? - raise InvalidInputrc, "#{file}:#{no}: unmatched else" - end - @skip_section = !@skip_section - when 'endif' - if @if_stack.empty? - raise InvalidInputrc, "#{file}:#{no}: unmatched endif" - end - @skip_section = @if_stack.pop - when 'include' - read(args) - end - end - - def bind_variable(name, value) - case name - when 'history-size' - begin - @history_size = Integer(value) - rescue ArgumentError - @history_size = 500 - end - when 'bell-style' - @bell_style = - case value - when 'none', 'off' - :none - when 'audible', 'on' - :audible - when 'visible' - :visible - else - :audible - end - when 'comment-begin' - @comment_begin = value.dup - when 'completion-query-items' - @completion_query_items = value.to_i - when 'isearch-terminators' - @isearch_terminators = retrieve_string(value) - when 'editing-mode' - case value - when 'emacs' - @editing_mode_label = :emacs - @keymap_label = :emacs - @keymap_prefix = [] - when 'vi' - @editing_mode_label = :vi_insert - @keymap_label = :vi_insert - @keymap_prefix = [] - end - when 'keymap' - case value - when 'emacs', 'emacs-standard' - @keymap_label = :emacs - @keymap_prefix = [] - when 'emacs-ctlx' - @keymap_label = :emacs - @keymap_prefix = [?\C-x.ord] - when 'emacs-meta' - @keymap_label = :emacs - @keymap_prefix = [?\e.ord] - when 'vi', 'vi-move', 'vi-command' - @keymap_label = :vi_command - @keymap_prefix = [] - when 'vi-insert' - @keymap_label = :vi_insert - @keymap_prefix = [] - end - when 'keyseq-timeout' - @keyseq_timeout = value.to_i - when 'show-mode-in-prompt' - case value - when 'off' - @show_mode_in_prompt = false - when 'on' - @show_mode_in_prompt = true - else - @show_mode_in_prompt = false - end - when 'vi-cmd-mode-string' - @vi_cmd_mode_string = retrieve_string(value) - when 'vi-ins-mode-string' - @vi_ins_mode_string = retrieve_string(value) - when 'emacs-mode-string' - @emacs_mode_string = retrieve_string(value) - when *VARIABLE_NAMES then - variable_name = :"@#{name.tr(?-, ?_)}" - instance_variable_set(variable_name, value.nil? || value == '1' || value == 'on') - end - end - - def retrieve_string(str) - str = $1 if str =~ /\A"(.*)"\z/ - parse_keyseq(str).map { |c| c.chr(Reline.encoding_system_needs) }.join - end - - def bind_key(key, func_name) - if key =~ /\A"(.*)"\z/ - keyseq = parse_keyseq($1) - else - keyseq = nil - end - if func_name =~ /"(.*)"/ - func = parse_keyseq($1) - else - func = func_name.tr(?-, ?_).to_sym # It must be macro. - end - [keyseq, func] - end - - def key_notation_to_code(notation) - case notation - when /\\(?:C|Control)-([A-Za-z_])/ - (1 + $1.downcase.ord - ?a.ord) - when /\\(?:M|Meta)-([0-9A-Za-z_])/ - modified_key = $1 - case $1 - when /[0-9]/ - ?\M-0.bytes.first + (modified_key.ord - ?0.ord) - when /[A-Z]/ - ?\M-A.bytes.first + (modified_key.ord - ?A.ord) - when /[a-z]/ - ?\M-a.bytes.first + (modified_key.ord - ?a.ord) - end - when /\\(?:C|Control)-(?:M|Meta)-[A-Za-z_]/, /\\(?:M|Meta)-(?:C|Control)-[A-Za-z_]/ - # 129 M-^A - when /\\(\d{1,3})/ then $1.to_i(8) # octal - when /\\x(\h{1,2})/ then $1.to_i(16) # hexadecimal - when "\\e" then ?\e.ord - when "\\\\" then ?\\.ord - when "\\\"" then ?".ord - when "\\'" then ?'.ord - when "\\a" then ?\a.ord - when "\\b" then ?\b.ord - when "\\d" then ?\d.ord - when "\\f" then ?\f.ord - when "\\n" then ?\n.ord - when "\\r" then ?\r.ord - when "\\t" then ?\t.ord - when "\\v" then ?\v.ord - else notation.ord - end - end - - def parse_keyseq(str) - ret = [] - str.scan(KEYSEQ_PATTERN) do - ret << key_notation_to_code($&) - end - ret - end - - private def seven_bit_encoding?(encoding) - encoding == Encoding::US_ASCII - end -end diff --git a/lib/reline/general_io.rb b/lib/reline/general_io.rb deleted file mode 100644 index 9929846568..0000000000 --- a/lib/reline/general_io.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'timeout' -require 'io/wait' - -class Reline::GeneralIO - def self.reset(encoding: nil) - @@pasting = false - @@encoding = encoding - end - - def self.encoding - if defined?(@@encoding) - @@encoding - elsif RUBY_PLATFORM =~ /mswin|mingw/ - Encoding::UTF_8 - else - Encoding::default_external - end - end - - def self.win? - false - end - - def self.set_default_key_bindings(_) - end - - @@buf = [] - @@input = STDIN - - def self.input=(val) - @@input = val - end - - def self.with_raw_input - yield - end - - def self.getc - unless @@buf.empty? - return @@buf.shift - end - c = nil - loop do - result = @@input.wait_readable(0.1) - next if result.nil? - c = @@input.read(1) - break - end - c&.ord - end - - def self.ungetc(c) - @@buf.unshift(c) - end - - def self.get_screen_size - [1, 1] - end - - def self.cursor_pos - Reline::CursorPos.new(1, 1) - end - - def self.hide_cursor - end - - def self.show_cursor - end - - def self.move_cursor_column(val) - end - - def self.move_cursor_up(val) - end - - def self.move_cursor_down(val) - end - - def self.erase_after_cursor - end - - def self.scroll_down(val) - end - - def self.clear_screen - end - - def self.set_screen_size(rows, columns) - end - - def self.set_winch_handler(&handler) - end - - @@pasting = false - - def self.in_pasting? - @@pasting - end - - def self.start_pasting - @@pasting = true - end - - def self.finish_pasting - @@pasting = false - end - - def self.prep - end - - def self.deprep(otio) - end -end diff --git a/lib/reline/history.rb b/lib/reline/history.rb deleted file mode 100644 index 7a1ed6b90b..0000000000 --- a/lib/reline/history.rb +++ /dev/null @@ -1,76 +0,0 @@ -class Reline::History < Array - def initialize(config) - @config = config - end - - def to_s - 'HISTORY' - end - - def delete_at(index) - index = check_index(index) - super(index) - end - - def [](index) - index = check_index(index) unless index.is_a?(Range) - super(index) - end - - def []=(index, val) - index = check_index(index) - super(index, String.new(val, encoding: Reline.encoding_system_needs)) - end - - def concat(*val) - val.each do |v| - push(*v) - end - end - - def push(*val) - # If history_size is zero, all histories are dropped. - return self if @config.history_size.zero? - # If history_size is negative, history size is unlimited. - if @config.history_size.positive? - diff = size + val.size - @config.history_size - if diff > 0 - if diff <= size - shift(diff) - else - diff -= size - clear - val.shift(diff) - end - end - end - super(*(val.map{ |v| - String.new(v, encoding: Reline.encoding_system_needs) - })) - end - - def <<(val) - # If history_size is zero, all histories are dropped. - return self if @config.history_size.zero? - # If history_size is negative, history size is unlimited. - if @config.history_size.positive? - shift if size + 1 > @config.history_size - end - super(String.new(val, encoding: Reline.encoding_system_needs)) - end - - private def check_index(index) - index += size if index < 0 - if index < -2147483648 or 2147483647 < index - raise RangeError.new("integer #{index} too big to convert to `int'") - end - # If history_size is negative, history size is unlimited. - if @config.history_size.positive? - if index < -@config.history_size or @config.history_size < index - raise RangeError.new("index=<#{index}>") - end - end - raise IndexError.new("index=<#{index}>") if index < 0 or size <= index - index - end -end diff --git a/lib/reline/key_actor.rb b/lib/reline/key_actor.rb deleted file mode 100644 index ebe09d2009..0000000000 --- a/lib/reline/key_actor.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Reline::KeyActor -end - -require 'reline/key_actor/base' -require 'reline/key_actor/emacs' -require 'reline/key_actor/vi_command' -require 'reline/key_actor/vi_insert' diff --git a/lib/reline/key_actor/base.rb b/lib/reline/key_actor/base.rb deleted file mode 100644 index a1cd7fb2a1..0000000000 --- a/lib/reline/key_actor/base.rb +++ /dev/null @@ -1,19 +0,0 @@ -class Reline::KeyActor::Base - MAPPING = Array.new(256) - - def get_method(key) - self.class::MAPPING[key] - end - - def initialize - @default_key_bindings = {} - end - - def default_key_bindings - @default_key_bindings - end - - def reset_default_key_bindings - @default_key_bindings.clear - end -end diff --git a/lib/reline/key_actor/emacs.rb b/lib/reline/key_actor/emacs.rb deleted file mode 100644 index a561feee57..0000000000 --- a/lib/reline/key_actor/emacs.rb +++ /dev/null @@ -1,517 +0,0 @@ -class Reline::KeyActor::Emacs < Reline::KeyActor::Base - MAPPING = [ - # 0 ^@ - :em_set_mark, - # 1 ^A - :ed_move_to_beg, - # 2 ^B - :ed_prev_char, - # 3 ^C - :ed_ignore, - # 4 ^D - :em_delete, - # 5 ^E - :ed_move_to_end, - # 6 ^F - :ed_next_char, - # 7 ^G - :ed_unassigned, - # 8 ^H - :em_delete_prev_char, - # 9 ^I - :ed_unassigned, - # 10 ^J - :ed_newline, - # 11 ^K - :ed_kill_line, - # 12 ^L - :ed_clear_screen, - # 13 ^M - :ed_newline, - # 14 ^N - :ed_next_history, - # 15 ^O - :ed_ignore, - # 16 ^P - :ed_prev_history, - # 17 ^Q - :ed_quoted_insert, - # 18 ^R - :vi_search_prev, - # 19 ^S - :vi_search_next, - # 20 ^T - :ed_transpose_chars, - # 21 ^U - :unix_line_discard, - # 22 ^V - :ed_quoted_insert, - # 23 ^W - :em_kill_region, - # 24 ^X - :ed_sequence_lead_in, - # 25 ^Y - :em_yank, - # 26 ^Z - :ed_ignore, - # 27 ^[ - :em_meta_next, - # 28 ^\ - :ed_ignore, - # 29 ^] - :ed_ignore, - # 30 ^^ - :ed_unassigned, - # 31 ^_ - :ed_unassigned, - # 32 SPACE - :ed_insert, - # 33 ! - :ed_insert, - # 34 " - :ed_insert, - # 35 # - :ed_insert, - # 36 $ - :ed_insert, - # 37 % - :ed_insert, - # 38 & - :ed_insert, - # 39 ' - :ed_insert, - # 40 ( - :ed_insert, - # 41 ) - :ed_insert, - # 42 * - :ed_insert, - # 43 + - :ed_insert, - # 44 , - :ed_insert, - # 45 - - :ed_insert, - # 46 . - :ed_insert, - # 47 / - :ed_insert, - # 48 0 - :ed_digit, - # 49 1 - :ed_digit, - # 50 2 - :ed_digit, - # 51 3 - :ed_digit, - # 52 4 - :ed_digit, - # 53 5 - :ed_digit, - # 54 6 - :ed_digit, - # 55 7 - :ed_digit, - # 56 8 - :ed_digit, - # 57 9 - :ed_digit, - # 58 : - :ed_insert, - # 59 ; - :ed_insert, - # 60 < - :ed_insert, - # 61 = - :ed_insert, - # 62 > - :ed_insert, - # 63 ? - :ed_insert, - # 64 @ - :ed_insert, - # 65 A - :ed_insert, - # 66 B - :ed_insert, - # 67 C - :ed_insert, - # 68 D - :ed_insert, - # 69 E - :ed_insert, - # 70 F - :ed_insert, - # 71 G - :ed_insert, - # 72 H - :ed_insert, - # 73 I - :ed_insert, - # 74 J - :ed_insert, - # 75 K - :ed_insert, - # 76 L - :ed_insert, - # 77 M - :ed_insert, - # 78 N - :ed_insert, - # 79 O - :ed_insert, - # 80 P - :ed_insert, - # 81 Q - :ed_insert, - # 82 R - :ed_insert, - # 83 S - :ed_insert, - # 84 T - :ed_insert, - # 85 U - :ed_insert, - # 86 V - :ed_insert, - # 87 W - :ed_insert, - # 88 X - :ed_insert, - # 89 Y - :ed_insert, - # 90 Z - :ed_insert, - # 91 [ - :ed_insert, - # 92 \ - :ed_insert, - # 93 ] - :ed_insert, - # 94 ^ - :ed_insert, - # 95 _ - :ed_insert, - # 96 ` - :ed_insert, - # 97 a - :ed_insert, - # 98 b - :ed_insert, - # 99 c - :ed_insert, - # 100 d - :ed_insert, - # 101 e - :ed_insert, - # 102 f - :ed_insert, - # 103 g - :ed_insert, - # 104 h - :ed_insert, - # 105 i - :ed_insert, - # 106 j - :ed_insert, - # 107 k - :ed_insert, - # 108 l - :ed_insert, - # 109 m - :ed_insert, - # 110 n - :ed_insert, - # 111 o - :ed_insert, - # 112 p - :ed_insert, - # 113 q - :ed_insert, - # 114 r - :ed_insert, - # 115 s - :ed_insert, - # 116 t - :ed_insert, - # 117 u - :ed_insert, - # 118 v - :ed_insert, - # 119 w - :ed_insert, - # 120 x - :ed_insert, - # 121 y - :ed_insert, - # 122 z - :ed_insert, - # 123 { - :ed_insert, - # 124 | - :ed_insert, - # 125 } - :ed_insert, - # 126 ~ - :ed_insert, - # 127 ^? - :em_delete_prev_char, - # 128 M-^@ - :ed_unassigned, - # 129 M-^A - :ed_unassigned, - # 130 M-^B - :ed_unassigned, - # 131 M-^C - :ed_unassigned, - # 132 M-^D - :ed_unassigned, - # 133 M-^E - :ed_unassigned, - # 134 M-^F - :ed_unassigned, - # 135 M-^G - :ed_unassigned, - # 136 M-^H - :ed_delete_prev_word, - # 137 M-^I - :ed_unassigned, - # 138 M-^J - :key_newline, - # 139 M-^K - :ed_unassigned, - # 140 M-^L - :ed_clear_screen, - # 141 M-^M - :key_newline, - # 142 M-^N - :ed_unassigned, - # 143 M-^O - :ed_unassigned, - # 144 M-^P - :ed_unassigned, - # 145 M-^Q - :ed_unassigned, - # 146 M-^R - :ed_unassigned, - # 147 M-^S - :ed_unassigned, - # 148 M-^T - :ed_unassigned, - # 149 M-^U - :ed_unassigned, - # 150 M-^V - :ed_unassigned, - # 151 M-^W - :ed_unassigned, - # 152 M-^X - :ed_unassigned, - # 153 M-^Y - :em_yank_pop, - # 154 M-^Z - :ed_unassigned, - # 155 M-^[ - :ed_unassigned, - # 156 M-^\ - :ed_unassigned, - # 157 M-^] - :ed_unassigned, - # 158 M-^^ - :ed_unassigned, - # 159 M-^_ - :em_copy_prev_word, - # 160 M-SPACE - :ed_unassigned, - # 161 M-! - :ed_unassigned, - # 162 M-" - :ed_unassigned, - # 163 M-# - :ed_unassigned, - # 164 M-$ - :ed_unassigned, - # 165 M-% - :ed_unassigned, - # 166 M-& - :ed_unassigned, - # 167 M-' - :ed_unassigned, - # 168 M-( - :ed_unassigned, - # 169 M-) - :ed_unassigned, - # 170 M-* - :ed_unassigned, - # 171 M-+ - :ed_unassigned, - # 172 M-, - :ed_unassigned, - # 173 M-- - :ed_unassigned, - # 174 M-. - :ed_unassigned, - # 175 M-/ - :ed_unassigned, - # 176 M-0 - :ed_argument_digit, - # 177 M-1 - :ed_argument_digit, - # 178 M-2 - :ed_argument_digit, - # 179 M-3 - :ed_argument_digit, - # 180 M-4 - :ed_argument_digit, - # 181 M-5 - :ed_argument_digit, - # 182 M-6 - :ed_argument_digit, - # 183 M-7 - :ed_argument_digit, - # 184 M-8 - :ed_argument_digit, - # 185 M-9 - :ed_argument_digit, - # 186 M-: - :ed_unassigned, - # 187 M-; - :ed_unassigned, - # 188 M-< - :ed_unassigned, - # 189 M-= - :ed_unassigned, - # 190 M-> - :ed_unassigned, - # 191 M-? - :ed_unassigned, - # 192 M-@ - :ed_unassigned, - # 193 M-A - :ed_unassigned, - # 194 M-B - :ed_prev_word, - # 195 M-C - :em_capitol_case, - # 196 M-D - :em_delete_next_word, - # 197 M-E - :ed_unassigned, - # 198 M-F - :em_next_word, - # 199 M-G - :ed_unassigned, - # 200 M-H - :ed_unassigned, - # 201 M-I - :ed_unassigned, - # 202 M-J - :ed_unassigned, - # 203 M-K - :ed_unassigned, - # 204 M-L - :em_lower_case, - # 205 M-M - :ed_unassigned, - # 206 M-N - :vi_search_next, - # 207 M-O - :ed_sequence_lead_in, - # 208 M-P - :vi_search_prev, - # 209 M-Q - :ed_unassigned, - # 210 M-R - :ed_unassigned, - # 211 M-S - :ed_unassigned, - # 212 M-T - :ed_unassigned, - # 213 M-U - :em_upper_case, - # 214 M-V - :ed_unassigned, - # 215 M-W - :em_copy_region, - # 216 M-X - :ed_command, - # 217 M-Y - :ed_unassigned, - # 218 M-Z - :ed_unassigned, - # 219 M-[ - :ed_sequence_lead_in, - # 220 M-\ - :ed_unassigned, - # 221 M-] - :ed_unassigned, - # 222 M-^ - :ed_unassigned, - # 223 M-_ - :ed_unassigned, - # 224 M-` - :ed_unassigned, - # 225 M-a - :ed_unassigned, - # 226 M-b - :ed_prev_word, - # 227 M-c - :em_capitol_case, - # 228 M-d - :em_delete_next_word, - # 229 M-e - :ed_unassigned, - # 230 M-f - :em_next_word, - # 231 M-g - :ed_unassigned, - # 232 M-h - :ed_unassigned, - # 233 M-i - :ed_unassigned, - # 234 M-j - :ed_unassigned, - # 235 M-k - :ed_unassigned, - # 236 M-l - :em_lower_case, - # 237 M-m - :ed_unassigned, - # 238 M-n - :vi_search_next, - # 239 M-o - :ed_unassigned, - # 240 M-p - :vi_search_prev, - # 241 M-q - :ed_unassigned, - # 242 M-r - :ed_unassigned, - # 243 M-s - :ed_unassigned, - # 244 M-t - :ed_transpose_words, - # 245 M-u - :em_upper_case, - # 246 M-v - :ed_unassigned, - # 247 M-w - :em_copy_region, - # 248 M-x - :ed_command, - # 249 M-y - :ed_unassigned, - # 250 M-z - :ed_unassigned, - # 251 M-{ - :ed_unassigned, - # 252 M-| - :ed_unassigned, - # 253 M-} - :ed_unassigned, - # 254 M-~ - :ed_unassigned, - # 255 M-^? - :ed_delete_prev_word - # EOF - ] -end diff --git a/lib/reline/key_actor/vi_command.rb b/lib/reline/key_actor/vi_command.rb deleted file mode 100644 index 98146d2f77..0000000000 --- a/lib/reline/key_actor/vi_command.rb +++ /dev/null @@ -1,518 +0,0 @@ -class Reline::KeyActor::ViCommand < Reline::KeyActor::Base - MAPPING = [ - # 0 ^@ - :ed_unassigned, - # 1 ^A - :ed_move_to_beg, - # 2 ^B - :ed_unassigned, - # 3 ^C - :ed_ignore, - # 4 ^D - :vi_end_of_transmission, - # 5 ^E - :ed_move_to_end, - # 6 ^F - :ed_unassigned, - # 7 ^G - :ed_unassigned, - # 8 ^H - :ed_unassigned, - # 9 ^I - :ed_unassigned, - # 10 ^J - :ed_newline, - # 11 ^K - :ed_kill_line, - # 12 ^L - :ed_clear_screen, - # 13 ^M - :ed_newline, - # 14 ^N - :ed_next_history, - # 15 ^O - :ed_ignore, - # 16 ^P - :ed_prev_history, - # 17 ^Q - :ed_ignore, - # 18 ^R - :vi_search_prev, - # 19 ^S - :ed_ignore, - # 20 ^T - :ed_unassigned, - # 21 ^U - :vi_kill_line_prev, - # 22 ^V - :ed_quoted_insert, - # 23 ^W - :ed_delete_prev_word, - # 24 ^X - :ed_unassigned, - # 25 ^Y - :ed_unassigned, - # 26 ^Z - :ed_unassigned, - # 27 ^[ - :ed_unassigned, - # 28 ^\ - :ed_ignore, - # 29 ^] - :ed_unassigned, - # 30 ^^ - :ed_unassigned, - # 31 ^_ - :ed_unassigned, - # 32 SPACE - :ed_next_char, - # 33 ! - :ed_unassigned, - # 34 " - :ed_unassigned, - # 35 # - :vi_comment_out, - # 36 $ - :ed_move_to_end, - # 37 % - :vi_match, - # 38 & - :ed_unassigned, - # 39 ' - :ed_unassigned, - # 40 ( - :ed_unassigned, - # 41 ) - :ed_unassigned, - # 42 * - :ed_unassigned, - # 43 + - :ed_next_history, - # 44 , - :vi_repeat_prev_char, - # 45 - - :ed_prev_history, - # 46 . - :vi_redo, - # 47 / - :vi_search_prev, - # 48 0 - :vi_zero, - # 49 1 - :ed_argument_digit, - # 50 2 - :ed_argument_digit, - # 51 3 - :ed_argument_digit, - # 52 4 - :ed_argument_digit, - # 53 5 - :ed_argument_digit, - # 54 6 - :ed_argument_digit, - # 55 7 - :ed_argument_digit, - # 56 8 - :ed_argument_digit, - # 57 9 - :ed_argument_digit, - # 58 : - :ed_command, - # 59 ; - :vi_repeat_next_char, - # 60 < - :ed_unassigned, - # 61 = - :ed_unassigned, - # 62 > - :ed_unassigned, - # 63 ? - :vi_search_next, - # 64 @ - :vi_alias, - # 65 A - :vi_add_at_eol, - # 66 B - :vi_prev_big_word, - # 67 C - :vi_change_to_eol, - # 68 D - :ed_kill_line, - # 69 E - :vi_end_big_word, - # 70 F - :vi_prev_char, - # 71 G - :vi_to_history_line, - # 72 H - :ed_unassigned, - # 73 I - :vi_insert_at_bol, - # 74 J - :vi_join_lines, - # 75 K - :vi_search_prev, - # 76 L - :ed_unassigned, - # 77 M - :ed_unassigned, - # 78 N - :vi_repeat_search_prev, - # 79 O - :ed_sequence_lead_in, - # 80 P - :vi_paste_prev, - # 81 Q - :ed_unassigned, - # 82 R - :vi_replace_mode, - # 83 S - :vi_substitute_line, - # 84 T - :vi_to_prev_char, - # 85 U - :vi_undo_line, - # 86 V - :ed_unassigned, - # 87 W - :vi_next_big_word, - # 88 X - :ed_delete_prev_char, - # 89 Y - :vi_yank_end, - # 90 Z - :ed_unassigned, - # 91 [ - :ed_sequence_lead_in, - # 92 \ - :ed_unassigned, - # 93 ] - :ed_unassigned, - # 94 ^ - :vi_first_print, - # 95 _ - :vi_history_word, - # 96 ` - :ed_unassigned, - # 97 a - :vi_add, - # 98 b - :vi_prev_word, - # 99 c - :vi_change_meta, - # 100 d - :vi_delete_meta, - # 101 e - :vi_end_word, - # 102 f - :vi_next_char, - # 103 g - :ed_unassigned, - # 104 h - :ed_prev_char, - # 105 i - :vi_insert, - # 106 j - :ed_next_history, - # 107 k - :ed_prev_history, - # 108 l - :ed_next_char, - # 109 m - :ed_unassigned, - # 110 n - :vi_repeat_search_next, - # 111 o - :ed_unassigned, - # 112 p - :vi_paste_next, - # 113 q - :ed_unassigned, - # 114 r - :vi_replace_char, - # 115 s - :vi_substitute_char, - # 116 t - :vi_to_next_char, - # 117 u - :vi_undo, - # 118 v - :vi_histedit, - # 119 w - :vi_next_word, - # 120 x - :ed_delete_next_char, - # 121 y - :vi_yank, - # 122 z - :ed_unassigned, - # 123 { - :ed_unassigned, - # 124 | - :vi_to_column, - # 125 } - :ed_unassigned, - # 126 ~ - :vi_change_case, - # 127 ^? - :ed_unassigned, - # 128 M-^@ - :ed_unassigned, - # 129 M-^A - :ed_unassigned, - # 130 M-^B - :ed_unassigned, - # 131 M-^C - :ed_unassigned, - # 132 M-^D - :ed_unassigned, - # 133 M-^E - :ed_unassigned, - # 134 M-^F - :ed_unassigned, - # 135 M-^G - :ed_unassigned, - # 136 M-^H - :ed_unassigned, - # 137 M-^I - :ed_unassigned, - # 138 M-^J - :ed_unassigned, - # 139 M-^K - :ed_unassigned, - # 140 M-^L - :ed_unassigned, - # 141 M-^M - :ed_unassigned, - # 142 M-^N - :ed_unassigned, - # 143 M-^O - :ed_unassigned, - # 144 M-^P - :ed_unassigned, - # 145 M-^Q - :ed_unassigned, - # 146 M-^R - :ed_unassigned, - # 147 M-^S - :ed_unassigned, - # 148 M-^T - :ed_unassigned, - # 149 M-^U - :ed_unassigned, - # 150 M-^V - :ed_unassigned, - # 151 M-^W - :ed_unassigned, - # 152 M-^X - :ed_unassigned, - # 153 M-^Y - :ed_unassigned, - # 154 M-^Z - :ed_unassigned, - # 155 M-^[ - :ed_unassigned, - # 156 M-^\ - :ed_unassigned, - # 157 M-^] - :ed_unassigned, - # 158 M-^^ - :ed_unassigned, - # 159 M-^_ - :ed_unassigned, - # 160 M-SPACE - :ed_unassigned, - # 161 M-! - :ed_unassigned, - # 162 M-" - :ed_unassigned, - # 163 M-# - :ed_unassigned, - # 164 M-$ - :ed_unassigned, - # 165 M-% - :ed_unassigned, - # 166 M-& - :ed_unassigned, - # 167 M-' - :ed_unassigned, - # 168 M-( - :ed_unassigned, - # 169 M-) - :ed_unassigned, - # 170 M-* - :ed_unassigned, - # 171 M-+ - :ed_unassigned, - # 172 M-, - :ed_unassigned, - # 173 M-- - :ed_unassigned, - # 174 M-. - :ed_unassigned, - # 175 M-/ - :ed_unassigned, - # 176 M-0 - :ed_unassigned, - # 177 M-1 - :ed_unassigned, - # 178 M-2 - :ed_unassigned, - # 179 M-3 - :ed_unassigned, - # 180 M-4 - :ed_unassigned, - # 181 M-5 - :ed_unassigned, - # 182 M-6 - :ed_unassigned, - # 183 M-7 - :ed_unassigned, - # 184 M-8 - :ed_unassigned, - # 185 M-9 - :ed_unassigned, - # 186 M-: - :ed_unassigned, - # 187 M-; - :ed_unassigned, - # 188 M-< - :ed_unassigned, - # 189 M-= - :ed_unassigned, - # 190 M-> - :ed_unassigned, - # 191 M-? - :ed_unassigned, - # 192 M-@ - :ed_unassigned, - # 193 M-A - :ed_unassigned, - # 194 M-B - :ed_unassigned, - # 195 M-C - :ed_unassigned, - # 196 M-D - :ed_unassigned, - # 197 M-E - :ed_unassigned, - # 198 M-F - :ed_unassigned, - # 199 M-G - :ed_unassigned, - # 200 M-H - :ed_unassigned, - # 201 M-I - :ed_unassigned, - # 202 M-J - :ed_unassigned, - # 203 M-K - :ed_unassigned, - # 204 M-L - :ed_unassigned, - # 205 M-M - :ed_unassigned, - # 206 M-N - :ed_unassigned, - # 207 M-O - :ed_sequence_lead_in, - # 208 M-P - :ed_unassigned, - # 209 M-Q - :ed_unassigned, - # 210 M-R - :ed_unassigned, - # 211 M-S - :ed_unassigned, - # 212 M-T - :ed_unassigned, - # 213 M-U - :ed_unassigned, - # 214 M-V - :ed_unassigned, - # 215 M-W - :ed_unassigned, - # 216 M-X - :ed_unassigned, - # 217 M-Y - :ed_unassigned, - # 218 M-Z - :ed_unassigned, - # 219 M-[ - :ed_sequence_lead_in, - # 220 M-\ - :ed_unassigned, - # 221 M-] - :ed_unassigned, - # 222 M-^ - :ed_unassigned, - # 223 M-_ - :ed_unassigned, - # 224 M-` - :ed_unassigned, - # 225 M-a - :ed_unassigned, - # 226 M-b - :ed_unassigned, - # 227 M-c - :ed_unassigned, - # 228 M-d - :ed_unassigned, - # 229 M-e - :ed_unassigned, - # 230 M-f - :ed_unassigned, - # 231 M-g - :ed_unassigned, - # 232 M-h - :ed_unassigned, - # 233 M-i - :ed_unassigned, - # 234 M-j - :ed_unassigned, - # 235 M-k - :ed_unassigned, - # 236 M-l - :ed_unassigned, - # 237 M-m - :ed_unassigned, - # 238 M-n - :ed_unassigned, - # 239 M-o - :ed_unassigned, - # 240 M-p - :ed_unassigned, - # 241 M-q - :ed_unassigned, - # 242 M-r - :ed_unassigned, - # 243 M-s - :ed_unassigned, - # 244 M-t - :ed_unassigned, - # 245 M-u - :ed_unassigned, - # 246 M-v - :ed_unassigned, - # 247 M-w - :ed_unassigned, - # 248 M-x - :ed_unassigned, - # 249 M-y - :ed_unassigned, - # 250 M-z - :ed_unassigned, - # 251 M-{ - :ed_unassigned, - # 252 M-| - :ed_unassigned, - # 253 M-} - :ed_unassigned, - # 254 M-~ - :ed_unassigned, - # 255 M-^? - :ed_unassigned - # EOF - ] -end - diff --git a/lib/reline/key_actor/vi_insert.rb b/lib/reline/key_actor/vi_insert.rb deleted file mode 100644 index b8e89f81d8..0000000000 --- a/lib/reline/key_actor/vi_insert.rb +++ /dev/null @@ -1,517 +0,0 @@ -class Reline::KeyActor::ViInsert < Reline::KeyActor::Base - MAPPING = [ - # 0 ^@ - :ed_unassigned, - # 1 ^A - :ed_insert, - # 2 ^B - :ed_insert, - # 3 ^C - :ed_insert, - # 4 ^D - :vi_list_or_eof, - # 5 ^E - :ed_insert, - # 6 ^F - :ed_insert, - # 7 ^G - :ed_insert, - # 8 ^H - :vi_delete_prev_char, - # 9 ^I - :ed_insert, - # 10 ^J - :ed_newline, - # 11 ^K - :ed_insert, - # 12 ^L - :ed_insert, - # 13 ^M - :ed_newline, - # 14 ^N - :ed_insert, - # 15 ^O - :ed_insert, - # 16 ^P - :ed_insert, - # 17 ^Q - :ed_ignore, - # 18 ^R - :vi_search_prev, - # 19 ^S - :vi_search_next, - # 20 ^T - :ed_insert, - # 21 ^U - :vi_kill_line_prev, - # 22 ^V - :ed_quoted_insert, - # 23 ^W - :ed_delete_prev_word, - # 24 ^X - :ed_insert, - # 25 ^Y - :ed_insert, - # 26 ^Z - :ed_insert, - # 27 ^[ - :vi_command_mode, - # 28 ^\ - :ed_ignore, - # 29 ^] - :ed_insert, - # 30 ^^ - :ed_insert, - # 31 ^_ - :ed_insert, - # 32 SPACE - :ed_insert, - # 33 ! - :ed_insert, - # 34 " - :ed_insert, - # 35 # - :ed_insert, - # 36 $ - :ed_insert, - # 37 % - :ed_insert, - # 38 & - :ed_insert, - # 39 ' - :ed_insert, - # 40 ( - :ed_insert, - # 41 ) - :ed_insert, - # 42 * - :ed_insert, - # 43 + - :ed_insert, - # 44 , - :ed_insert, - # 45 - - :ed_insert, - # 46 . - :ed_insert, - # 47 / - :ed_insert, - # 48 0 - :ed_insert, - # 49 1 - :ed_insert, - # 50 2 - :ed_insert, - # 51 3 - :ed_insert, - # 52 4 - :ed_insert, - # 53 5 - :ed_insert, - # 54 6 - :ed_insert, - # 55 7 - :ed_insert, - # 56 8 - :ed_insert, - # 57 9 - :ed_insert, - # 58 : - :ed_insert, - # 59 ; - :ed_insert, - # 60 < - :ed_insert, - # 61 = - :ed_insert, - # 62 > - :ed_insert, - # 63 ? - :ed_insert, - # 64 @ - :ed_insert, - # 65 A - :ed_insert, - # 66 B - :ed_insert, - # 67 C - :ed_insert, - # 68 D - :ed_insert, - # 69 E - :ed_insert, - # 70 F - :ed_insert, - # 71 G - :ed_insert, - # 72 H - :ed_insert, - # 73 I - :ed_insert, - # 74 J - :ed_insert, - # 75 K - :ed_insert, - # 76 L - :ed_insert, - # 77 M - :ed_insert, - # 78 N - :ed_insert, - # 79 O - :ed_insert, - # 80 P - :ed_insert, - # 81 Q - :ed_insert, - # 82 R - :ed_insert, - # 83 S - :ed_insert, - # 84 T - :ed_insert, - # 85 U - :ed_insert, - # 86 V - :ed_insert, - # 87 W - :ed_insert, - # 88 X - :ed_insert, - # 89 Y - :ed_insert, - # 90 Z - :ed_insert, - # 91 [ - :ed_insert, - # 92 \ - :ed_insert, - # 93 ] - :ed_insert, - # 94 ^ - :ed_insert, - # 95 _ - :ed_insert, - # 96 ` - :ed_insert, - # 97 a - :ed_insert, - # 98 b - :ed_insert, - # 99 c - :ed_insert, - # 100 d - :ed_insert, - # 101 e - :ed_insert, - # 102 f - :ed_insert, - # 103 g - :ed_insert, - # 104 h - :ed_insert, - # 105 i - :ed_insert, - # 106 j - :ed_insert, - # 107 k - :ed_insert, - # 108 l - :ed_insert, - # 109 m - :ed_insert, - # 110 n - :ed_insert, - # 111 o - :ed_insert, - # 112 p - :ed_insert, - # 113 q - :ed_insert, - # 114 r - :ed_insert, - # 115 s - :ed_insert, - # 116 t - :ed_insert, - # 117 u - :ed_insert, - # 118 v - :ed_insert, - # 119 w - :ed_insert, - # 120 x - :ed_insert, - # 121 y - :ed_insert, - # 122 z - :ed_insert, - # 123 { - :ed_insert, - # 124 | - :ed_insert, - # 125 } - :ed_insert, - # 126 ~ - :ed_insert, - # 127 ^? - :vi_delete_prev_char, - # 128 M-^@ - :ed_unassigned, - # 129 M-^A - :ed_unassigned, - # 130 M-^B - :ed_unassigned, - # 131 M-^C - :ed_unassigned, - # 132 M-^D - :ed_unassigned, - # 133 M-^E - :ed_unassigned, - # 134 M-^F - :ed_unassigned, - # 135 M-^G - :ed_unassigned, - # 136 M-^H - :ed_unassigned, - # 137 M-^I - :ed_unassigned, - # 138 M-^J - :key_newline, - # 139 M-^K - :ed_unassigned, - # 140 M-^L - :ed_unassigned, - # 141 M-^M - :key_newline, - # 142 M-^N - :ed_unassigned, - # 143 M-^O - :ed_unassigned, - # 144 M-^P - :ed_unassigned, - # 145 M-^Q - :ed_unassigned, - # 146 M-^R - :ed_unassigned, - # 147 M-^S - :ed_unassigned, - # 148 M-^T - :ed_unassigned, - # 149 M-^U - :ed_unassigned, - # 150 M-^V - :ed_unassigned, - # 151 M-^W - :ed_unassigned, - # 152 M-^X - :ed_unassigned, - # 153 M-^Y - :ed_unassigned, - # 154 M-^Z - :ed_unassigned, - # 155 M-^[ - :ed_unassigned, - # 156 M-^\ - :ed_unassigned, - # 157 M-^] - :ed_unassigned, - # 158 M-^^ - :ed_unassigned, - # 159 M-^_ - :ed_unassigned, - # 160 M-SPACE - :ed_unassigned, - # 161 M-! - :ed_unassigned, - # 162 M-" - :ed_unassigned, - # 163 M-# - :ed_unassigned, - # 164 M-$ - :ed_unassigned, - # 165 M-% - :ed_unassigned, - # 166 M-& - :ed_unassigned, - # 167 M-' - :ed_unassigned, - # 168 M-( - :ed_unassigned, - # 169 M-) - :ed_unassigned, - # 170 M-* - :ed_unassigned, - # 171 M-+ - :ed_unassigned, - # 172 M-, - :ed_unassigned, - # 173 M-- - :ed_unassigned, - # 174 M-. - :ed_unassigned, - # 175 M-/ - :ed_unassigned, - # 176 M-0 - :ed_unassigned, - # 177 M-1 - :ed_unassigned, - # 178 M-2 - :ed_unassigned, - # 179 M-3 - :ed_unassigned, - # 180 M-4 - :ed_unassigned, - # 181 M-5 - :ed_unassigned, - # 182 M-6 - :ed_unassigned, - # 183 M-7 - :ed_unassigned, - # 184 M-8 - :ed_unassigned, - # 185 M-9 - :ed_unassigned, - # 186 M-: - :ed_unassigned, - # 187 M-; - :ed_unassigned, - # 188 M-< - :ed_unassigned, - # 189 M-= - :ed_unassigned, - # 190 M-> - :ed_unassigned, - # 191 M-? - :ed_unassigned, - # 192 M-@ - :ed_unassigned, - # 193 M-A - :ed_unassigned, - # 194 M-B - :ed_unassigned, - # 195 M-C - :ed_unassigned, - # 196 M-D - :ed_unassigned, - # 197 M-E - :ed_unassigned, - # 198 M-F - :ed_unassigned, - # 199 M-G - :ed_unassigned, - # 200 M-H - :ed_unassigned, - # 201 M-I - :ed_unassigned, - # 202 M-J - :ed_unassigned, - # 203 M-K - :ed_unassigned, - # 204 M-L - :ed_unassigned, - # 205 M-M - :ed_unassigned, - # 206 M-N - :ed_unassigned, - # 207 M-O - :ed_unassigned, - # 208 M-P - :ed_unassigned, - # 209 M-Q - :ed_unassigned, - # 210 M-R - :ed_unassigned, - # 211 M-S - :ed_unassigned, - # 212 M-T - :ed_unassigned, - # 213 M-U - :ed_unassigned, - # 214 M-V - :ed_unassigned, - # 215 M-W - :ed_unassigned, - # 216 M-X - :ed_unassigned, - # 217 M-Y - :ed_unassigned, - # 218 M-Z - :ed_unassigned, - # 219 M-[ - :ed_unassigned, - # 220 M-\ - :ed_unassigned, - # 221 M-] - :ed_unassigned, - # 222 M-^ - :ed_unassigned, - # 223 M-_ - :ed_unassigned, - # 224 M-` - :ed_unassigned, - # 225 M-a - :ed_unassigned, - # 226 M-b - :ed_unassigned, - # 227 M-c - :ed_unassigned, - # 228 M-d - :ed_unassigned, - # 229 M-e - :ed_unassigned, - # 230 M-f - :ed_unassigned, - # 231 M-g - :ed_unassigned, - # 232 M-h - :ed_unassigned, - # 233 M-i - :ed_unassigned, - # 234 M-j - :ed_unassigned, - # 235 M-k - :ed_unassigned, - # 236 M-l - :ed_unassigned, - # 237 M-m - :ed_unassigned, - # 238 M-n - :ed_unassigned, - # 239 M-o - :ed_unassigned, - # 240 M-p - :ed_unassigned, - # 241 M-q - :ed_unassigned, - # 242 M-r - :ed_unassigned, - # 243 M-s - :ed_unassigned, - # 244 M-t - :ed_unassigned, - # 245 M-u - :ed_unassigned, - # 246 M-v - :ed_unassigned, - # 247 M-w - :ed_unassigned, - # 248 M-x - :ed_unassigned, - # 249 M-y - :ed_unassigned, - # 250 M-z - :ed_unassigned, - # 251 M-{ - :ed_unassigned, - # 252 M-| - :ed_unassigned, - # 253 M-} - :ed_unassigned, - # 254 M-~ - :ed_unassigned, - # 255 M-^? - :ed_unassigned - # EOF - ] -end diff --git a/lib/reline/key_stroke.rb b/lib/reline/key_stroke.rb deleted file mode 100644 index c1c61513a9..0000000000 --- a/lib/reline/key_stroke.rb +++ /dev/null @@ -1,105 +0,0 @@ -class Reline::KeyStroke - def initialize(config) - @config = config - end - - def compress_meta_key(ary) - return ary unless @config.convert_meta - ary.inject([]) { |result, key| - if result.size > 0 and result.last == "\e".ord - result[result.size - 1] = Reline::Key.new(key, key | 0b10000000, true) - else - result << key - end - result - } - end - - def start_with?(me, other) - compressed_me = compress_meta_key(me) - compressed_other = compress_meta_key(other) - i = 0 - loop do - my_c = compressed_me[i] - other_c = compressed_other[i] - other_is_last = (i + 1) == compressed_other.size - me_is_last = (i + 1) == compressed_me.size - if my_c != other_c - if other_c == "\e".ord and other_is_last and my_c.is_a?(Reline::Key) and my_c.with_meta - return true - else - return false - end - elsif other_is_last - return true - elsif me_is_last - return false - end - i += 1 - end - end - - def equal?(me, other) - case me - when Array - compressed_me = compress_meta_key(me) - compressed_other = compress_meta_key(other) - compressed_me.size == compressed_other.size and [compressed_me, compressed_other].transpose.all?{ |i| equal?(i[0], i[1]) } - when Integer - if other.is_a?(Reline::Key) - if other.combined_char == "\e".ord - false - else - other.combined_char == me - end - else - me == other - end - when Reline::Key - if other.is_a?(Integer) - me.combined_char == other - else - me == other - end - end - end - - def match_status(input) - key_mapping.keys.select { |lhs| - start_with?(lhs, input) - }.tap { |it| - return :matched if it.size == 1 && equal?(it[0], input) - return :matching if it.size == 1 && !equal?(it[0], input) - return :matched if it.max_by(&:size)&.size&.< input.size - return :matching if it.size > 1 - } - key_mapping.keys.select { |lhs| - start_with?(input, lhs) - }.tap { |it| - return it.size > 0 ? :matched : :unmatched - } - end - - def expand(input) - input = compress_meta_key(input) - lhs = key_mapping.keys.select { |item| start_with?(input, item) }.sort_by(&:size).last - return input unless lhs - rhs = key_mapping[lhs] - - case rhs - when String - rhs_bytes = rhs.bytes - expand(expand(rhs_bytes) + expand(input.drop(lhs.size))) - when Symbol - [rhs] + expand(input.drop(lhs.size)) - when Array - rhs - end - end - - private - - def key_mapping - @config.key_bindings - end -end diff --git a/lib/reline/kill_ring.rb b/lib/reline/kill_ring.rb deleted file mode 100644 index bb3684b42b..0000000000 --- a/lib/reline/kill_ring.rb +++ /dev/null @@ -1,125 +0,0 @@ -class Reline::KillRing - include Enumerable - - module State - FRESH = :fresh - CONTINUED = :continued - PROCESSED = :processed - YANK = :yank - end - - RingPoint = Struct.new(:backward, :forward, :str) do - def initialize(str) - super(nil, nil, str) - end - - def ==(other) - object_id == other.object_id - end - end - - class RingBuffer - attr_reader :size - attr_reader :head - - def initialize(max = 1024) - @max = max - @size = 0 - @head = nil # reading head of ring-shaped tape - end - - def <<(point) - if @size.zero? - @head = point - @head.backward = @head - @head.forward = @head - @size = 1 - elsif @size >= @max - tail = @head.forward - new_tail = tail.forward - @head.forward = point - point.backward = @head - new_tail.backward = point - point.forward = new_tail - @head = point - else - tail = @head.forward - @head.forward = point - point.backward = @head - tail.backward = point - point.forward = tail - @head = point - @size += 1 - end - end - - def empty? - @size.zero? - end - end - - def initialize(max = 1024) - @ring = RingBuffer.new(max) - @ring_pointer = nil - @buffer = nil - @state = State::FRESH - end - - def append(string, before_p = false) - case @state - when State::FRESH, State::YANK - @ring << RingPoint.new(string) - @state = State::CONTINUED - when State::CONTINUED, State::PROCESSED - if before_p - @ring.head.str.prepend(string) - else - @ring.head.str.concat(string) - end - @state = State::CONTINUED - end - end - - def process - case @state - when State::FRESH - # nothing to do - when State::CONTINUED - @state = State::PROCESSED - when State::PROCESSED - @state = State::FRESH - when State::YANK - # nothing to do - end - end - - def yank - unless @ring.empty? - @state = State::YANK - @ring_pointer = @ring.head - @ring_pointer.str - else - nil - end - end - - def yank_pop - if @state == State::YANK - prev_yank = @ring_pointer.str - @ring_pointer = @ring_pointer.backward - [@ring_pointer.str, prev_yank] - else - nil - end - end - - def each - start = head = @ring.head - loop do - break if head.nil? - yield head.str - head = head.backward - break if head == start - end - end -end diff --git a/lib/reline/line_editor.rb b/lib/reline/line_editor.rb deleted file mode 100644 index 87ce7fba5d..0000000000 --- a/lib/reline/line_editor.rb +++ /dev/null @@ -1,3347 +0,0 @@ -require 'reline/kill_ring' -require 'reline/unicode' - -require 'tempfile' - -class Reline::LineEditor - # TODO: undo - # TODO: Use "private alias_method" idiom after drop Ruby 2.5. - attr_reader :line - attr_reader :byte_pointer - attr_accessor :confirm_multiline_termination_proc - attr_accessor :completion_proc - attr_accessor :completion_append_character - attr_accessor :output_modifier_proc - attr_accessor :prompt_proc - attr_accessor :auto_indent_proc - attr_accessor :pre_input_hook - attr_accessor :dig_perfect_match_proc - attr_writer :output - - VI_MOTIONS = %i{ - ed_prev_char - ed_next_char - vi_zero - ed_move_to_beg - ed_move_to_end - vi_to_column - vi_next_char - vi_prev_char - vi_next_word - vi_prev_word - vi_to_next_char - vi_to_prev_char - vi_end_word - vi_next_big_word - vi_prev_big_word - vi_end_big_word - vi_repeat_next_char - vi_repeat_prev_char - } - - module CompletionState - NORMAL = :normal - COMPLETION = :completion - MENU = :menu - JOURNEY = :journey - MENU_WITH_PERFECT_MATCH = :menu_with_perfect_match - PERFECT_MATCH = :perfect_match - end - - CompletionJourneyData = Struct.new('CompletionJourneyData', :preposing, :postposing, :list, :pointer) - MenuInfo = Struct.new('MenuInfo', :target, :list) - - PROMPT_LIST_CACHE_TIMEOUT = 0.5 - MINIMUM_SCROLLBAR_HEIGHT = 1 - - def initialize(config, encoding) - @config = config - @completion_append_character = '' - reset_variables(encoding: encoding) - end - - def set_pasting_state(in_pasting) - @in_pasting = in_pasting - end - - 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 - - private 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 - - private def check_multiline_prompt(buffer) - 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? - 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 - - 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 - - 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 - - def set_signal_handlers - @old_trap = Signal.trap('INT') { - clear_dialog - 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 - } - begin - @old_tstp_trap = Signal.trap('TSTP') { - Reline::IOGate.ungetc("\C-z".ord) - @old_tstp_trap.call if @old_tstp_trap.respond_to?(:call) - } - rescue ArgumentError - end - end - - def finalize - Signal.trap('INT', @old_trap) - begin - Signal.trap('TSTP', @old_tstp_trap) - rescue ArgumentError - end - end - - def eof? - @eof - end - - 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 = [] - @last_key = nil - @resized = false - reset_line - end - - def reset_line - @cursor = 0 - @cursor_max = 0 - @byte_pointer = 0 - @buffer_of_lines = [String.new(encoding: @encoding)] - @line_index = 0 - @previous_line_index = nil - @line = @buffer_of_lines[0] - @first_line_started_from = 0 - @move_up = 0 - @started_from = 0 - @highest_in_this = 1 - @highest_in_all = 1 - @line_backup_in_history = nil - @multibyte_buffer = String.new(encoding: 'ASCII-8BIT') - @check_new_auto_indent = false - end - - def multiline_on - @is_multiline = true - end - - def multiline_off - @is_multiline = false - end - - private def calculate_height_by_lines(lines, prompt) - result = 0 - prompt_list = prompt.is_a?(Array) ? prompt : nil - lines.each_with_index { |line, i| - prompt = prompt_list[i] if prompt_list and prompt_list[i] - result += calculate_height_by_width(calculate_width(prompt, true) + calculate_width(line)) - } - result - end - - private def insert_new_line(cursor_line, next_line) - @line = cursor_line - @buffer_of_lines.insert(@line_index + 1, String.new(next_line, encoding: @encoding)) - @previous_line_index = @line_index - @line_index += 1 - @just_cursor_moving = false - end - - private def calculate_height_by_width(width) - width.div(@screen_size.last) + 1 - end - - private def split_by_width(str, max_width) - Reline::Unicode.split_by_width(str, max_width, @encoding) - end - - private def scroll_down(val) - if val <= @rest_height - Reline::IOGate.move_cursor_down(val) - @rest_height -= val - else - Reline::IOGate.move_cursor_down(@rest_height) - Reline::IOGate.scroll_down(val - @rest_height) - @rest_height = 0 - end - end - - private def move_cursor_up(val) - if val > 0 - Reline::IOGate.move_cursor_up(val) - @rest_height += val - elsif val < 0 - move_cursor_down(-val) - end - end - - private def move_cursor_down(val) - if val > 0 - Reline::IOGate.move_cursor_down(val) - @rest_height -= val - @rest_height = 0 if @rest_height < 0 - elsif val < 0 - move_cursor_up(-val) - end - end - - private def calculate_nearest_cursor(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 - - def rerender_all - @rerender_all = true - process_insert(force: true) - rerender - end - - 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) - 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 - 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_content - 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_content - rendered = just_move_cursor - @just_cursor_moving = false - return - elsif @previous_line_index or new_highest_in_this != @highest_in_this - clear_dialog_with_content - 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 - 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((prompt_width + @cursor) % @screen_size.last) - 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 - - class DialogProcScope - def initialize(line_editor, config, proc_to_exec, context) - @line_editor = line_editor - @config = config - @proc_to_exec = proc_to_exec - @context = context - @cursor_pos = Reline::CursorPos.new - end - - def context - @context - end - - def retrieve_completion_block(set_completion_quote_character = false) - @line_editor.retrieve_completion_block(set_completion_quote_character) - end - - def call_completion_proc_with_checking_args(pre, target, post) - @line_editor.call_completion_proc_with_checking_args(pre, target, post) - end - - def set_dialog(dialog) - @dialog = dialog - end - - def dialog - @dialog - end - - def set_cursor_pos(col, row) - @cursor_pos.x = col - @cursor_pos.y = row - end - - def set_key(key) - @key = key - end - - def key - @key - end - - def cursor_pos - @cursor_pos - end - - def just_cursor_moving - @line_editor.instance_variable_get(:@just_cursor_moving) - end - - def screen_width - @line_editor.instance_variable_get(:@screen_size).last - end - - def completion_journey_data - @line_editor.instance_variable_get(:@completion_journey_data) - end - - def config - @config - end - - def call - instance_exec(&@proc_to_exec) - end - end - - class Dialog - attr_reader :name, :contents, :width - attr_accessor :scroll_top, :scrollbar_pos, :pointer, :column, :vertical_offset, :lines_backup, :trap_key - - def initialize(name, config, proc_scope) - @name = name - @config = config - @proc_scope = proc_scope - @width = nil - @scroll_top = 0 - @trap_key = nil - end - - def set_cursor_pos(col, row) - @proc_scope.set_cursor_pos(col, row) - end - - def width=(v) - @width = v - end - - def contents=(contents) - @contents = contents - if contents and @width.nil? - @width = contents.map{ |line| Reline::Unicode.calculate_width(line, true) }.max - end - end - - def call(key) - @proc_scope.set_dialog(self) - @proc_scope.set_key(key) - dialog_render_info = @proc_scope.call - if @trap_key - if @trap_key.any?{ |i| i.is_a?(Array) } # multiple trap - @trap_key.each do |t| - @config.add_oneshot_key_binding(t, @name) - end - elsif @trap_key.is_a?(Array) - @config.add_oneshot_key_binding(@trap_key, @name) - elsif @trap_key.is_a?(Integer) or @trap_key.is_a?(Reline::Key) - @config.add_oneshot_key_binding([@trap_key], @name) - end - end - dialog_render_info - end - end - - 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 - - DIALOG_DEFAULT_HEIGHT = 20 - private def render_dialog(cursor_column) - @dialogs.each do |dialog| - render_each_dialog(dialog, cursor_column) - end - end - - private 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 - - private def render_each_dialog(dialog, cursor_column) - if @in_pasting - clear_each_dialog(dialog) - 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? - lines = whole_lines - dialog.lines_backup = { - unmodified_lines: lines, - lines: modify_lines(lines), - line_index: @line_index, - first_line_started_from: @first_line_started_from, - started_from: @started_from, - byte_pointer: @byte_pointer - } - clear_each_dialog(dialog) - dialog.contents = nil - dialog.trap_key = nil - return - end - old_dialog = dialog.clone - dialog.contents = dialog_render_info.contents - pointer = dialog.pointer - if dialog_render_info.width - dialog.width = dialog_render_info.width - else - dialog.width = dialog.contents.map { |l| calculate_width(l, true) }.max - end - height = dialog_render_info.height || DIALOG_DEFAULT_HEIGHT - height = dialog.contents.size if dialog.contents.size < height - if dialog.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 - dialog.contents = dialog.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 * ((dialog.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 - dialog.scrollbar_pos = ((bar_max_height - bar_height) * position_ratio).floor.to_i - else - dialog.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 dialog.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 - if (@rest_height - dialog_render_info.pos.y) < height - scroll_down(height + dialog_render_info.pos.y) - move_cursor_up(height + dialog_render_info.pos.y) - end - dialog.vertical_offset = dialog_render_info.pos.y + 1 - end - Reline::IOGate.hide_cursor - if dialog.column < 0 - dialog.column = 0 - dialog.width = @screen_size.last - end - reset_dialog(dialog, old_dialog) - move_cursor_down(dialog.vertical_offset) - Reline::IOGate.move_cursor_column(dialog.column) - dialog.contents.each_with_index do |item, i| - if i == pointer - fg_color = dialog_render_info.pointer_fg_color - bg_color = dialog_render_info.pointer_bg_color - else - fg_color = dialog_render_info.fg_color - bg_color = dialog_render_info.bg_color - end - str_width = dialog.width - (dialog.scrollbar_pos.nil? ? 0 : @block_elem_width) - str = padding_space_with_escape_sequences(Reline::Unicode.take_range(item, 0, str_width), str_width) - @output.write "\e[#{bg_color}m\e[#{fg_color}m#{str}" - if dialog.scrollbar_pos - @output.write "\e[37m" - if dialog.scrollbar_pos <= (i * 2) and (i * 2 + 1) < (dialog.scrollbar_pos + bar_height) - @output.write @full_block - elsif dialog.scrollbar_pos <= (i * 2) and (i * 2) < (dialog.scrollbar_pos + bar_height) - @output.write @upper_half_block - elsif dialog.scrollbar_pos <= (i * 2 + 1) and (i * 2) < (dialog.scrollbar_pos + bar_height) - @output.write @lower_half_block - else - @output.write ' ' * @block_elem_width - end - end - @output.write "\e[0m" - Reline::IOGate.move_cursor_column(dialog.column) - move_cursor_down(1) if i < (dialog.contents.size - 1) - end - Reline::IOGate.move_cursor_column(cursor_column) - move_cursor_up(dialog.vertical_offset + dialog.contents.size - 1) - Reline::IOGate.show_cursor - lines = whole_lines - dialog.lines_backup = { - unmodified_lines: lines, - lines: modify_lines(lines), - line_index: @line_index, - first_line_started_from: @first_line_started_from, - started_from: @started_from, - byte_pointer: @byte_pointer - } - end - - private def reset_dialog(dialog, old_dialog) - return if dialog.lines_backup.nil? or old_dialog.contents.nil? - prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:unmodified_lines]) - visual_lines = [] - visual_start = nil - dialog.lines_backup[:lines].each_with_index { |l, i| - pr = prompt_list ? prompt_list[i] : prompt - vl, _ = split_by_width(pr + l, @screen_size.last) - vl.compact! - if i == dialog.lines_backup[:line_index] - visual_start = visual_lines.size + dialog.lines_backup[:started_from] - end - visual_lines.concat(vl) - } - old_y = dialog.lines_backup[:first_line_started_from] + dialog.lines_backup[:started_from] - y = @first_line_started_from + @started_from - y_diff = y - old_y - if (old_y + old_dialog.vertical_offset) < (y + dialog.vertical_offset) - # rerender top - move_cursor_down(old_dialog.vertical_offset - y_diff) - start = visual_start + old_dialog.vertical_offset - line_num = dialog.vertical_offset - old_dialog.vertical_offset - line_num.times do |i| - Reline::IOGate.move_cursor_column(old_dialog.column) - if visual_lines[start + i].nil? - s = ' ' * old_dialog.width - else - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width) - s = padding_space_with_escape_sequences(s, old_dialog.width) - end - @output.write "\e[0m#{s}\e[0m" - move_cursor_down(1) if i < (line_num - 1) - end - move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff) - end - if (old_y + old_dialog.vertical_offset + old_dialog.contents.size) > (y + dialog.vertical_offset + dialog.contents.size) - # rerender bottom - move_cursor_down(dialog.vertical_offset + dialog.contents.size - y_diff) - start = visual_start + dialog.vertical_offset + dialog.contents.size - line_num = (old_dialog.vertical_offset + old_dialog.contents.size) - (dialog.vertical_offset + dialog.contents.size) - line_num.times do |i| - Reline::IOGate.move_cursor_column(old_dialog.column) - if visual_lines[start + i].nil? - s = ' ' * old_dialog.width - else - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, old_dialog.width) - s = padding_space_with_escape_sequences(s, old_dialog.width) - end - @output.write "\e[0m#{s}\e[0m" - move_cursor_down(1) if i < (line_num - 1) - end - move_cursor_up(dialog.vertical_offset + dialog.contents.size + line_num - 1 - y_diff) - end - if old_dialog.column < dialog.column - # rerender left - move_cursor_down(old_dialog.vertical_offset - y_diff) - width = dialog.column - old_dialog.column - start = visual_start + old_dialog.vertical_offset - line_num = old_dialog.contents.size - line_num.times do |i| - Reline::IOGate.move_cursor_column(old_dialog.column) - if visual_lines[start + i].nil? - s = ' ' * width - else - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column, width) - s = padding_space_with_escape_sequences(s, dialog.width) - end - @output.write "\e[0m#{s}\e[0m" - move_cursor_down(1) if i < (line_num - 1) - end - move_cursor_up(old_dialog.vertical_offset + line_num - 1 - y_diff) - end - if (old_dialog.column + old_dialog.width) > (dialog.column + dialog.width) - # rerender right - move_cursor_down(old_dialog.vertical_offset + y_diff) - width = (old_dialog.column + old_dialog.width) - (dialog.column + dialog.width) - start = visual_start + old_dialog.vertical_offset - line_num = old_dialog.contents.size - line_num.times do |i| - Reline::IOGate.move_cursor_column(old_dialog.column + dialog.width) - if visual_lines[start + i].nil? - s = ' ' * width - else - s = Reline::Unicode.take_range(visual_lines[start + i], old_dialog.column + dialog.width, width) - rerender_width = old_dialog.width - dialog.width - s = padding_space_with_escape_sequences(s, rerender_width) - end - Reline::IOGate.move_cursor_column(dialog.column + dialog.width) - @output.write "\e[0m#{s}\e[0m" - move_cursor_down(1) if i < (line_num - 1) - end - move_cursor_up(old_dialog.vertical_offset + line_num - 1 + y_diff) - end - Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) - end - - private def clear_dialog - @dialogs.each do |dialog| - clear_each_dialog(dialog) - end - end - - private def clear_dialog_with_content - @dialogs.each do |dialog| - clear_each_dialog(dialog) - dialog.contents = nil - dialog.trap_key = nil - end - end - - private def clear_each_dialog(dialog) - dialog.trap_key = nil - return unless dialog.contents - prompt, prompt_width, prompt_list = check_multiline_prompt(dialog.lines_backup[:unmodified_lines]) - visual_lines = [] - visual_lines_under_dialog = [] - visual_start = nil - dialog.lines_backup[:lines].each_with_index { |l, i| - pr = prompt_list ? prompt_list[i] : prompt - vl, _ = split_by_width(pr + l, @screen_size.last) - vl.compact! - if i == dialog.lines_backup[:line_index] - visual_start = visual_lines.size + dialog.lines_backup[:started_from] + dialog.vertical_offset - end - visual_lines.concat(vl) - } - visual_lines_under_dialog = visual_lines[visual_start, dialog.contents.size] - visual_lines_under_dialog = [] if visual_lines_under_dialog.nil? - Reline::IOGate.hide_cursor - move_cursor_down(dialog.vertical_offset) - dialog_vertical_size = dialog.contents.size - dialog_vertical_size.times do |i| - if i < visual_lines_under_dialog.size - Reline::IOGate.move_cursor_column(dialog.column) - str = Reline::Unicode.take_range(visual_lines_under_dialog[i], dialog.column, dialog.width) - str = padding_space_with_escape_sequences(str, dialog.width) - @output.write "\e[0m#{str}\e[0m" - else - Reline::IOGate.move_cursor_column(dialog.column) - @output.write "\e[0m#{' ' * dialog.width}\e[0m" - end - move_cursor_down(1) if i < (dialog_vertical_size - 1) - end - move_cursor_up(dialog_vertical_size - 1 + dialog.vertical_offset) - Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) - Reline::IOGate.show_cursor - end - - private def calculate_scroll_partial_screen(highest_in_all, cursor_y) - if @screen_height < highest_in_all - old_scroll_partial_screen = @scroll_partial_screen - if cursor_y == 0 - @scroll_partial_screen = 0 - elsif cursor_y == (highest_in_all - 1) - @scroll_partial_screen = highest_in_all - @screen_height - else - if @scroll_partial_screen - if cursor_y <= @scroll_partial_screen - @scroll_partial_screen = cursor_y - elsif (@scroll_partial_screen + @screen_height - 1) < cursor_y - @scroll_partial_screen = cursor_y - (@screen_height - 1) - end - else - if cursor_y > (@screen_height - 1) - @scroll_partial_screen = cursor_y - (@screen_height - 1) - else - @scroll_partial_screen = 0 - end - end - end - if @scroll_partial_screen != old_scroll_partial_screen - @rerender_all = true - end - else - if @scroll_partial_screen - @rerender_all = true - end - @scroll_partial_screen = nil - end - end - - private 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] - 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 - - 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 - new_cursor, new_cursor_max, new_started_from, new_byte_pointer = calculate_nearest_cursor(@buffer_of_lines[@line_index], @cursor, @started_from, @byte_pointer, false) - new_started_from = calculate_height_by_width(prompt_width + new_cursor) - 1 - calculate_scroll_partial_screen(@highest_in_all, new_first_line_started_from + new_started_from) - @previous_line_index = nil - if @rerender_all - @line = @buffer_of_lines[@line_index] - rerender_all_lines - @rerender_all = false - true - else - @line = @buffer_of_lines[@line_index] - @first_line_started_from = new_first_line_started_from - @started_from = new_started_from - @cursor = new_cursor - @cursor_max = new_cursor_max - @byte_pointer = new_byte_pointer - move_cursor_down(first_line_diff + @started_from) - Reline::IOGate.move_cursor_column((prompt_width + @cursor) % @screen_size.last) - false - end - end - - private 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 - - private def rerender_all_lines - move_cursor_up(@first_line_started_from + @started_from) - Reline::IOGate.move_cursor_column(0) - back = 0 - new_buffer = whole_lines - prompt, prompt_width, prompt_list = check_multiline_prompt(new_buffer) - 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 - - private def render_whole_lines(lines, prompt, prompt_width) - rendered_height = 0 - modify_lines(lines).each_with_index do |line, index| - if prompt.is_a?(Array) - line_prompt = prompt[index] - prompt_width = calculate_width(line_prompt, true) - else - line_prompt = prompt - end - height = render_partial(line_prompt, prompt_width, line, 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 - - private 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 - - private def modify_lines(before) - return before if before.nil? || before.empty? || simplified_rendering? - - if after = @output_modifier_proc&.call("#{before.join("\n")}\n", complete: finished?) - after.lines("\n").map { |l| l.chomp('') } - else - before - end - end - - private def show_menu - scroll_down(@highest_in_all - @first_line_started_from) - @rerender_all = true - @menu_info.list.sort!.each do |item| - Reline::IOGate.move_cursor_column(0) - @output.write item - @output.flush - scroll_down(1) - end - scroll_down(@highest_in_all - 1) - move_cursor_up(@highest_in_all - 1 - @first_line_started_from) - end - - private def clear_screen_buffer(prompt, prompt_list, prompt_width) - Reline::IOGate.clear_screen - back = 0 - modify_lines(whole_lines).each_with_index do |line, index| - if @prompt_proc - pr = prompt_list[index] - height = render_partial(pr, calculate_width(pr), line, 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 - - def editing_mode - @config.editing_mode - end - - private def menu(target, list) - @menu_info = MenuInfo.new(target, list) - end - - private def complete_internal_proc(list, is_menu) - preposing, target, postposing = retrieve_completion_block - list = list.select { |i| - if i and not Encoding.compatible?(target.encoding, i.encoding) - raise Encoding::CompatibilityError, "#{target.encoding.name} is not compatible with #{i.encoding.name}" - end - if @config.completion_ignore_case - i&.downcase&.start_with?(target.downcase) - else - i&.start_with?(target) - end - }.uniq - if is_menu - menu(target, list) - return nil - end - completed = list.inject { |memo, item| - begin - memo_mbchars = memo.unicode_normalize.grapheme_clusters - item_mbchars = item.unicode_normalize.grapheme_clusters - rescue Encoding::CompatibilityError - memo_mbchars = memo.grapheme_clusters - item_mbchars = item.grapheme_clusters - end - size = [memo_mbchars.size, item_mbchars.size].min - result = '' - size.times do |i| - if @config.completion_ignore_case - if memo_mbchars[i].casecmp?(item_mbchars[i]) - result << memo_mbchars[i] - else - break - end - else - if memo_mbchars[i] == item_mbchars[i] - result << memo_mbchars[i] - else - break - end - end - end - result - } - [target, preposing, completed, postposing] - end - - private def complete(list, just_show_list = false) - case @completion_state - when CompletionState::NORMAL, CompletionState::JOURNEY - @completion_state = CompletionState::COMPLETION - when CompletionState::PERFECT_MATCH - @dig_perfect_match_proc&.(@perfect_matched) - end - if just_show_list - is_menu = true - elsif @completion_state == CompletionState::MENU - is_menu = true - elsif @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH - is_menu = true - else - is_menu = false - end - result = complete_internal_proc(list, is_menu) - if @completion_state == CompletionState::MENU_WITH_PERFECT_MATCH - @completion_state = CompletionState::PERFECT_MATCH - end - return if result.nil? - target, preposing, completed, postposing = result - return if completed.nil? - if target <= completed and (@completion_state == CompletionState::COMPLETION) - if list.include?(completed) - if list.one? - @completion_state = CompletionState::PERFECT_MATCH - else - @completion_state = CompletionState::MENU_WITH_PERFECT_MATCH - end - @perfect_matched = completed - else - @completion_state = CompletionState::MENU - end - if not just_show_list and target < completed - @line = (preposing + completed + completion_append_character.to_s + postposing).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 - - private def move_completed_list(list, direction) - case @completion_state - when CompletionState::NORMAL, CompletionState::COMPLETION, - CompletionState::MENU, CompletionState::MENU_WITH_PERFECT_MATCH - @completion_state = CompletionState::JOURNEY - result = retrieve_completion_block - return if result.nil? - preposing, target, postposing = result - @completion_journey_data = CompletionJourneyData.new( - preposing, postposing, - [target] + list.select{ |item| item.start_with?(target) }, 0) - 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 - - private def run_for_operators(key, method_symbol, &block) - if @waiting_operator_proc - if VI_MOTIONS.include?(method_symbol) - old_cursor, old_byte_pointer = @cursor, @byte_pointer - @vi_arg = @waiting_operator_vi_arg if @waiting_operator_vi_arg&.> 1 - block.(true) - unless @waiting_proc - cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer - @cursor, @byte_pointer = old_cursor, old_byte_pointer - @waiting_operator_proc.(cursor_diff, byte_pointer_diff) - else - old_waiting_proc = @waiting_proc - old_waiting_operator_proc = @waiting_operator_proc - current_waiting_operator_proc = @waiting_operator_proc - @waiting_proc = proc { |k| - old_cursor, old_byte_pointer = @cursor, @byte_pointer - old_waiting_proc.(k) - cursor_diff, byte_pointer_diff = @cursor - old_cursor, @byte_pointer - old_byte_pointer - @cursor, @byte_pointer = old_cursor, old_byte_pointer - current_waiting_operator_proc.(cursor_diff, byte_pointer_diff) - @waiting_operator_proc = old_waiting_operator_proc - } - end - else - # Ignores operator when not motion is given. - block.(false) - end - @waiting_operator_proc = nil - @waiting_operator_vi_arg = nil - if @vi_arg - @rerender_all = true - @vi_arg = nil - end - else - block.(false) - end - end - - private def argumentable?(method_obj) - method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :arg } - end - - private def inclusive?(method_obj) - # If a motion method with the keyword argument "inclusive" follows the - # operator, it must contain the character at the cursor position. - method_obj and method_obj.parameters.any? { |param| param[0] == :key and param[1] == :inclusive } - end - - def wrap_method_call(method_symbol, method_obj, key, with_operator = false) - 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 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 - - private 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 - # split ESC + key - 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) - 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 - - 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? - process_auto_indent - end - end - - 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 - - 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 - - private def process_auto_indent - return if not @check_new_auto_indent and @previous_line_index # move cursor up or down - if @check_new_auto_indent and @previous_line_index and @previous_line_index > 0 and @line_index > @previous_line_index - # Fix indent of a line when a newline is inserted to the next - new_lines = whole_lines - 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[-2].size + 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 - - 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 - - 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 - - def insert_text(text) - width = calculate_width(text) - if @cursor == @cursor_max - @line += text - else - @line = byteinsert(@line, @byte_pointer, text) - end - @byte_pointer += text.bytesize - @cursor += width - @cursor_max += width - end - - def delete_text(start = nil, length = nil) - if start.nil? and length.nil? - 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 - - def byte_pointer=(val) - @byte_pointer = val - str = @line.byteslice(0, @byte_pointer) - @cursor = calculate_width(str) - @cursor_max = calculate_width(@line) - end - - def whole_lines - index = @previous_line_index || @line_index - temp_lines = @buffer_of_lines.dup - temp_lines[index] = @line - temp_lines - end - - def whole_buffer - if @buffer_of_lines.size == 1 and @line.nil? - nil - else - whole_lines.join("\n") - end - end - - def finished? - @finished - end - - def finish - @finished = true - @rerender_all = true - @config.reset - end - - private def byteslice!(str, byte_pointer, size) - new_str = str.byteslice(0, byte_pointer) - new_str << str.byteslice(byte_pointer + size, str.bytesize) - [new_str, str.byteslice(byte_pointer, size)] - end - - private def byteinsert(str, byte_pointer, other) - new_str = str.byteslice(0, byte_pointer) - new_str << other - new_str << str.byteslice(byte_pointer, str.bytesize) - new_str - end - - private def calculate_width(str, allow_escape_code = false) - Reline::Unicode.calculate_width(str, allow_escape_code) - end - - private def key_delete(key) - if @config.editing_mode_is?(:vi_insert, :emacs) - ed_delete_next_char(key) - end - end - - private def key_newline(key) - if @is_multiline - if (@buffer_of_lines.size - 1) == @line_index and @line.bytesize == @byte_pointer - @add_newline_to_end_of_buffer = true - end - 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 - - # Editline:: +ed-unassigned+ This editor command always results in an error. - # GNU Readline:: There is no corresponding macro. - private def ed_unassigned(key) end # do nothing - - private 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 - - # 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, call - # +ed-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. - private 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 - alias_method :ed_digit, :ed_insert - alias_method :self_insert, :ed_insert - - private def ed_quoted_insert(str, arg: 1) - @waiting_proc = proc { |key| - arg.times do - 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 - alias_method :quoted_insert, :ed_quoted_insert - - private def ed_next_char(key, arg: 1) - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - if (@byte_pointer < @line.bytesize) - mbchar = @line.byteslice(@byte_pointer, byte_size) - width = Reline::Unicode.get_mbchar_width(mbchar) - @cursor += width if width - @byte_pointer += byte_size - elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == @line.bytesize and @line_index < @buffer_of_lines.size - 1 - next_line = @buffer_of_lines[@line_index + 1] - @cursor = 0 - @byte_pointer = 0 - @cursor_max = calculate_width(next_line) - @previous_line_index = @line_index - @line_index += 1 - end - arg -= 1 - ed_next_char(key, arg: arg) if arg > 0 - end - alias_method :forward_char, :ed_next_char - - private def ed_prev_char(key, arg: 1) - if @cursor > 0 - byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer) - @byte_pointer -= byte_size - mbchar = @line.byteslice(@byte_pointer, byte_size) - width = Reline::Unicode.get_mbchar_width(mbchar) - @cursor -= width - elsif @is_multiline and @config.editing_mode_is?(:emacs) and @byte_pointer == 0 and @line_index > 0 - prev_line = @buffer_of_lines[@line_index - 1] - @cursor = calculate_width(prev_line) - @byte_pointer = prev_line.bytesize - @cursor_max = calculate_width(prev_line) - @previous_line_index = @line_index - @line_index -= 1 - end - arg -= 1 - ed_prev_char(key, arg: arg) if arg > 0 - end - alias_method :backward_char, :ed_prev_char - - private def vi_first_print(key) - @byte_pointer, @cursor = Reline::Unicode.vi_first_print(@line) - end - - private def ed_move_to_beg(key) - @byte_pointer = @cursor = 0 - end - alias_method :beginning_of_line, :ed_move_to_beg - - private def ed_move_to_end(key) - @byte_pointer = 0 - @cursor = 0 - byte_size = 0 - while @byte_pointer < @line.bytesize - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - if byte_size > 0 - mbchar = @line.byteslice(@byte_pointer, byte_size) - @cursor += Reline::Unicode.get_mbchar_width(mbchar) - end - @byte_pointer += byte_size - end - end - alias_method :end_of_line, :ed_move_to_end - - private 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 - - private 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 - - private def vi_search_prev(key) - incremental_search_history(key) - end - alias_method :reverse_search_history, :vi_search_prev - - private def vi_search_next(key) - incremental_search_history(key) - end - alias_method :forward_search_history, :vi_search_next - - private def ed_search_prev_history(key, arg: 1) - history = nil - h_pointer = nil - line_no = nil - substr = @line.slice(0, @byte_pointer) - if @history_pointer.nil? - return if not @line.empty? and substr.empty? - history = Reline::HISTORY - elsif @history_pointer.zero? - history = nil - h_pointer = nil - else - history = Reline::HISTORY.slice(0, @history_pointer) - end - return if history.nil? - if @is_multiline - h_pointer = history.rindex { |h| - h.split("\n").each_with_index { |l, i| - if l.start_with?(substr) - line_no = i - break - end - } - not line_no.nil? - } - else - h_pointer = history.rindex { |l| - l.start_with?(substr) - } - end - return if h_pointer.nil? - @history_pointer = h_pointer - if @is_multiline - @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n") - @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty? - @line_index = line_no - @line = @buffer_of_lines[@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 - alias_method :history_search_backward, :ed_search_prev_history - - private def ed_search_next_history(key, arg: 1) - substr = @line.slice(0, @byte_pointer) - if @history_pointer.nil? - return - elsif @history_pointer == (Reline::HISTORY.size - 1) and not substr.empty? - return - end - history = Reline::HISTORY.slice((@history_pointer + 1)..-1) - h_pointer = nil - line_no = nil - if @is_multiline - h_pointer = history.index { |h| - h.split("\n").each_with_index { |l, i| - if l.start_with?(substr) - line_no = i - break - end - } - not line_no.nil? - } - else - h_pointer = history.index { |l| - l.start_with?(substr) - } - end - h_pointer += @history_pointer + 1 if h_pointer and @history_pointer - return if h_pointer.nil? and not substr.empty? - @history_pointer = h_pointer - if @is_multiline - if @history_pointer.nil? and substr.empty? - @buffer_of_lines = [] - @line_index = 0 - else - @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n") - @line_index = line_no - end - @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty? - @line = @buffer_of_lines[@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 - alias_method :history_search_forward, :ed_search_next_history - - private def ed_prev_history(key, arg: 1) - if @is_multiline and @line_index > 0 - @previous_line_index = @line_index - @line_index -= 1 - return - end - if Reline::HISTORY.empty? - return - end - if @history_pointer.nil? - @history_pointer = Reline::HISTORY.size - 1 - if @is_multiline - @line_backup_in_history = whole_buffer - @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n") - @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 - alias_method :previous_history, :ed_prev_history - - private def ed_next_history(key, arg: 1) - if @is_multiline and @line_index < (@buffer_of_lines.size - 1) - @previous_line_index = @line_index - @line_index += 1 - return - end - if @history_pointer.nil? - return - elsif @history_pointer == (Reline::HISTORY.size - 1) - if @is_multiline - @history_pointer = nil - @buffer_of_lines = @line_backup_in_history.split("\n") - @buffer_of_lines = [String.new(encoding: @encoding)] if @buffer_of_lines.empty? - @line_index = 0 - @line = @buffer_of_lines.first - @rerender_all = true - else - @history_pointer = nil - @line = @line_backup_in_history - end - else - if @is_multiline - Reline::HISTORY[@history_pointer] = whole_buffer - @history_pointer += 1 - @buffer_of_lines = Reline::HISTORY[@history_pointer].split("\n") - @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 - alias_method :next_history, :ed_next_history - - private 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 - - private 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 - alias_method :backward_delete_char, :em_delete_prev_char - - # 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. - private def ed_kill_line(key) - if @line.bytesize > @byte_pointer - @line, deleted = byteslice!(@line, @byte_pointer, @line.bytesize - @byte_pointer) - @byte_pointer = @line.bytesize - @cursor = @cursor_max = calculate_width(@line) - @kill_ring.append(deleted) - elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1 - @cursor = calculate_width(@line) - @byte_pointer = @line.bytesize - @line += @buffer_of_lines.delete_at(@line_index + 1) - @cursor_max = calculate_width(@line) - @buffer_of_lines[@line_index] = @line - @rerender_all = true - @rest_height += 1 - end - end - alias_method :kill_line, :ed_kill_line - - # 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. - private 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 - alias_method :unix_line_discard, :vi_kill_line_prev - - # 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. - private 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 - alias_method :kill_whole_line, :em_kill_line - - private def em_delete(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 - elsif @byte_pointer < @line.bytesize - splitted_last = @line.byteslice(@byte_pointer, @line.bytesize) - mbchar = splitted_last.grapheme_clusters.first - width = Reline::Unicode.get_mbchar_width(mbchar) - @cursor_max -= width - @line, = byteslice!(@line, @byte_pointer, mbchar.bytesize) - elsif @is_multiline and @byte_pointer == @line.bytesize and @buffer_of_lines.size > @line_index + 1 - @cursor = calculate_width(@line) - @byte_pointer = @line.bytesize - @line += @buffer_of_lines.delete_at(@line_index + 1) - @cursor_max = calculate_width(@line) - @buffer_of_lines[@line_index] = @line - @rerender_all = true - @rest_height += 1 - end - end - alias_method :delete_char, :em_delete - - private def em_delete_or_list(key) - if @line.empty? or @byte_pointer < @line.bytesize - em_delete(key) - else # show completed list - result = call_completion_proc - if result.is_a?(Array) - complete(result, true) - end - end - end - alias_method :delete_char_or_list, :em_delete_or_list - - private def em_yank(key) - yanked = @kill_ring.yank - if yanked - @line = byteinsert(@line, @byte_pointer, yanked) - yanked_width = calculate_width(yanked) - @cursor += yanked_width - @cursor_max += yanked_width - @byte_pointer += yanked.bytesize - end - end - alias_method :yank, :em_yank - - private def em_yank_pop(key) - yanked, prev_yank = @kill_ring.yank_pop - if yanked - prev_yank_width = calculate_width(prev_yank) - @cursor -= prev_yank_width - @cursor_max -= prev_yank_width - @byte_pointer -= prev_yank.bytesize - @line, = byteslice!(@line, @byte_pointer, prev_yank.bytesize) - @line = byteinsert(@line, @byte_pointer, yanked) - yanked_width = calculate_width(yanked) - @cursor += yanked_width - @cursor_max += yanked_width - @byte_pointer += yanked.bytesize - end - end - alias_method :yank_pop, :em_yank_pop - - private def ed_clear_screen(key) - @cleared = true - end - alias_method :clear_screen, :ed_clear_screen - - private def em_next_word(key) - if @line.bytesize > @byte_pointer - byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer) - @byte_pointer += byte_size - @cursor += width - end - end - alias_method :forward_word, :em_next_word - - private def ed_prev_word(key) - if @byte_pointer > 0 - byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer) - @byte_pointer -= byte_size - @cursor -= width - end - end - alias_method :backward_word, :ed_prev_word - - private def em_delete_next_word(key) - if @line.bytesize > @byte_pointer - byte_size, width = Reline::Unicode.em_forward_word(@line, @byte_pointer) - @line, word = byteslice!(@line, @byte_pointer, byte_size) - @kill_ring.append(word) - @cursor_max -= width - end - end - - private def ed_delete_prev_word(key) - if @byte_pointer > 0 - byte_size, width = Reline::Unicode.em_backward_word(@line, @byte_pointer) - @line, word = byteslice!(@line, @byte_pointer - byte_size, byte_size) - @kill_ring.append(word, true) - @byte_pointer -= byte_size - @cursor -= width - @cursor_max -= width - end - end - - private def ed_transpose_chars(key) - if @byte_pointer > 0 - if @cursor_max > @cursor - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - mbchar = @line.byteslice(@byte_pointer, byte_size) - width = Reline::Unicode.get_mbchar_width(mbchar) - @cursor += width - @byte_pointer += byte_size - end - back1_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer) - if (@byte_pointer - back1_byte_size) > 0 - back2_byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer - back1_byte_size) - back2_pointer = @byte_pointer - back1_byte_size - back2_byte_size - @line, back2_mbchar = byteslice!(@line, back2_pointer, back2_byte_size) - @line = byteinsert(@line, @byte_pointer - back2_byte_size, back2_mbchar) - end - end - end - alias_method :transpose_chars, :ed_transpose_chars - - private def ed_transpose_words(key) - left_word_start, middle_start, right_word_start, after_start = Reline::Unicode.ed_transpose_words(@line, @byte_pointer) - before = @line.byteslice(0, left_word_start) - left_word = @line.byteslice(left_word_start, middle_start - left_word_start) - middle = @line.byteslice(middle_start, right_word_start - middle_start) - right_word = @line.byteslice(right_word_start, after_start - right_word_start) - after = @line.byteslice(after_start, @line.bytesize - after_start) - return if left_word.empty? or right_word.empty? - @line = before + right_word + middle + left_word + after - from_head_to_left_word = before + right_word + middle + left_word - @byte_pointer = from_head_to_left_word.bytesize - @cursor = calculate_width(from_head_to_left_word) - end - alias_method :transpose_words, :ed_transpose_words - - private def em_capitol_case(key) - if @line.bytesize > @byte_pointer - byte_size, _, new_str = Reline::Unicode.em_forward_word_with_capitalization(@line, @byte_pointer) - before = @line.byteslice(0, @byte_pointer) - after = @line.byteslice((@byte_pointer + byte_size)..-1) - @line = before + new_str + after - @byte_pointer += new_str.bytesize - @cursor += calculate_width(new_str) - end - end - alias_method :capitalize_word, :em_capitol_case - - private def em_lower_case(key) - if @line.bytesize > @byte_pointer - byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer) - part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar| - mbchar =~ /[A-Z]/ ? mbchar.downcase : mbchar - }.join - rest = @line.byteslice((@byte_pointer + byte_size)..-1) - @line = @line.byteslice(0, @byte_pointer) + part - @byte_pointer = @line.bytesize - @cursor = calculate_width(@line) - @cursor_max = @cursor + calculate_width(rest) - @line += rest - end - end - alias_method :downcase_word, :em_lower_case - - private def em_upper_case(key) - if @line.bytesize > @byte_pointer - byte_size, = Reline::Unicode.em_forward_word(@line, @byte_pointer) - part = @line.byteslice(@byte_pointer, byte_size).grapheme_clusters.map { |mbchar| - mbchar =~ /[a-z]/ ? mbchar.upcase : mbchar - }.join - rest = @line.byteslice((@byte_pointer + byte_size)..-1) - @line = @line.byteslice(0, @byte_pointer) + part - @byte_pointer = @line.bytesize - @cursor = calculate_width(@line) - @cursor_max = @cursor + calculate_width(rest) - @line += rest - end - end - alias_method :upcase_word, :em_upper_case - - private def em_kill_region(key) - if @byte_pointer > 0 - byte_size, width = Reline::Unicode.em_big_backward_word(@line, @byte_pointer) - @line, deleted = byteslice!(@line, @byte_pointer - byte_size, byte_size) - @byte_pointer -= byte_size - @cursor -= width - @cursor_max -= width - @kill_ring.append(deleted, true) - end - end - alias_method :unix_word_rubout, :em_kill_region - - private def copy_for_vi(text) - if @config.editing_mode_is?(:vi_insert) or @config.editing_mode_is?(:vi_command) - @vi_clipboard = text - end - end - - private def vi_insert(key) - @config.editing_mode = :vi_insert - end - - private def vi_add(key) - @config.editing_mode = :vi_insert - ed_next_char(key) - end - - private def vi_command_mode(key) - ed_prev_char(key) - @config.editing_mode = :vi_command - end - alias_method :vi_movement_mode, :vi_command_mode - - private def vi_next_word(key, arg: 1) - if @line.bytesize > @byte_pointer - byte_size, width = Reline::Unicode.vi_forward_word(@line, @byte_pointer, @drop_terminate_spaces) - @byte_pointer += byte_size - @cursor += width - end - arg -= 1 - vi_next_word(key, arg: arg) if arg > 0 - end - - private def vi_prev_word(key, arg: 1) - if @byte_pointer > 0 - byte_size, width = Reline::Unicode.vi_backward_word(@line, @byte_pointer) - @byte_pointer -= byte_size - @cursor -= width - end - arg -= 1 - vi_prev_word(key, arg: arg) if arg > 0 - end - - private def vi_end_word(key, arg: 1, inclusive: false) - if @line.bytesize > @byte_pointer - byte_size, width = Reline::Unicode.vi_forward_end_word(@line, @byte_pointer) - @byte_pointer += byte_size - @cursor += width - end - arg -= 1 - if inclusive and arg.zero? - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - if byte_size > 0 - c = @line.byteslice(@byte_pointer, byte_size) - width = Reline::Unicode.get_mbchar_width(c) - @byte_pointer += byte_size - @cursor += width - end - end - vi_end_word(key, arg: arg) if arg > 0 - end - - private def vi_next_big_word(key, arg: 1) - if @line.bytesize > @byte_pointer - byte_size, width = Reline::Unicode.vi_big_forward_word(@line, @byte_pointer) - @byte_pointer += byte_size - @cursor += width - end - arg -= 1 - vi_next_big_word(key, arg: arg) if arg > 0 - end - - private def vi_prev_big_word(key, arg: 1) - if @byte_pointer > 0 - byte_size, width = Reline::Unicode.vi_big_backward_word(@line, @byte_pointer) - @byte_pointer -= byte_size - @cursor -= width - end - arg -= 1 - vi_prev_big_word(key, arg: arg) if arg > 0 - end - - private def vi_end_big_word(key, arg: 1, inclusive: false) - if @line.bytesize > @byte_pointer - byte_size, width = Reline::Unicode.vi_big_forward_end_word(@line, @byte_pointer) - @byte_pointer += byte_size - @cursor += width - end - arg -= 1 - if inclusive and arg.zero? - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - if byte_size > 0 - c = @line.byteslice(@byte_pointer, byte_size) - width = Reline::Unicode.get_mbchar_width(c) - @byte_pointer += byte_size - @cursor += width - end - end - vi_end_big_word(key, arg: arg) if arg > 0 - end - - private def vi_delete_prev_char(key) - if @is_multiline and @cursor == 0 and @line_index > 0 - @buffer_of_lines[@line_index] = @line - @cursor = calculate_width(@buffer_of_lines[@line_index - 1]) - @byte_pointer = @buffer_of_lines[@line_index - 1].bytesize - @buffer_of_lines[@line_index - 1] += @buffer_of_lines.delete_at(@line_index) - @line_index -= 1 - @line = @buffer_of_lines[@line_index] - @cursor_max = calculate_width(@line) - @rerender_all = true - elsif @cursor > 0 - byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer) - @byte_pointer -= byte_size - @line, mbchar = byteslice!(@line, @byte_pointer, byte_size) - width = Reline::Unicode.get_mbchar_width(mbchar) - @cursor -= width - @cursor_max -= width - end - end - - private def vi_insert_at_bol(key) - ed_move_to_beg(key) - @config.editing_mode = :vi_insert - end - - private def vi_add_at_eol(key) - ed_move_to_end(key) - @config.editing_mode = :vi_insert - end - - private def ed_delete_prev_char(key, arg: 1) - deleted = '' - arg.times do - if @cursor > 0 - byte_size = Reline::Unicode.get_prev_mbchar_size(@line, @byte_pointer) - @byte_pointer -= byte_size - @line, mbchar = byteslice!(@line, @byte_pointer, byte_size) - deleted.prepend(mbchar) - width = Reline::Unicode.get_mbchar_width(mbchar) - @cursor -= width - @cursor_max -= width - end - end - copy_for_vi(deleted) - end - - private def vi_zero(key) - @byte_pointer = 0 - @cursor = 0 - end - - private def vi_change_meta(key, arg: 1) - @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 - - private def vi_delete_meta(key, arg: 1) - @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff| - if byte_pointer_diff > 0 - @line, cut = byteslice!(@line, @byte_pointer, byte_pointer_diff) - elsif byte_pointer_diff < 0 - @line, cut = byteslice!(@line, @byte_pointer + byte_pointer_diff, -byte_pointer_diff) - end - copy_for_vi(cut) - @cursor += cursor_diff if cursor_diff < 0 - @cursor_max -= cursor_diff.abs - @byte_pointer += byte_pointer_diff if byte_pointer_diff < 0 - } - @waiting_operator_vi_arg = arg - end - - private def vi_yank(key, arg: 1) - @waiting_operator_proc = proc { |cursor_diff, byte_pointer_diff| - if byte_pointer_diff > 0 - cut = @line.byteslice(@byte_pointer, byte_pointer_diff) - elsif byte_pointer_diff < 0 - cut = @line.byteslice(@byte_pointer + byte_pointer_diff, -byte_pointer_diff) - end - copy_for_vi(cut) - } - @waiting_operator_vi_arg = arg - end - - private def vi_list_or_eof(key) - if (not @is_multiline and @line.empty?) or (@is_multiline and @line.empty? and @buffer_of_lines.size == 1) - @line = nil - 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 - alias_method :vi_end_of_transmission, :vi_list_or_eof - alias_method :vi_eof_maybe, :vi_list_or_eof - - private def ed_delete_next_char(key, arg: 1) - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - unless @line.empty? || byte_size == 0 - @line, mbchar = byteslice!(@line, @byte_pointer, byte_size) - copy_for_vi(mbchar) - width = Reline::Unicode.get_mbchar_width(mbchar) - @cursor_max -= width - if @cursor > 0 and @cursor >= @cursor_max - byte_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 - - private def vi_to_history_line(key) - if Reline::HISTORY.empty? - return - end - if @history_pointer.nil? - @history_pointer = 0 - @line_backup_in_history = @line - @line = Reline::HISTORY[@history_pointer] - @cursor_max = calculate_width(@line) - @cursor = 0 - @byte_pointer = 0 - elsif @history_pointer.zero? - return - else - Reline::HISTORY[@history_pointer] = @line - @history_pointer = 0 - @line = Reline::HISTORY[@history_pointer] - @cursor_max = calculate_width(@line) - @cursor = 0 - @byte_pointer = 0 - end - end - - private def vi_histedit(key) - path = Tempfile.open { |fp| - 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 - - private def vi_paste_prev(key, arg: 1) - if @vi_clipboard.size > 0 - @line = byteinsert(@line, @byte_pointer, @vi_clipboard) - @cursor_max += calculate_width(@vi_clipboard) - cursor_point = @vi_clipboard.grapheme_clusters[0..-2].join - @cursor += calculate_width(cursor_point) - @byte_pointer += cursor_point.bytesize - end - arg -= 1 - vi_paste_prev(key, arg: arg) if arg > 0 - end - - private def vi_paste_next(key, arg: 1) - if @vi_clipboard.size > 0 - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - @line = byteinsert(@line, @byte_pointer + byte_size, @vi_clipboard) - @cursor_max += calculate_width(@vi_clipboard) - @cursor += calculate_width(@vi_clipboard) - @byte_pointer += @vi_clipboard.bytesize - end - arg -= 1 - vi_paste_next(key, arg: arg) if arg > 0 - end - - private def ed_argument_digit(key) - if @vi_arg.nil? - 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 - - private def vi_to_column(key, arg: 0) - @byte_pointer, @cursor = @line.grapheme_clusters.inject([0, 0]) { |total, gc| - # total has [byte_size, cursor] - mbchar_width = Reline::Unicode.get_mbchar_width(gc) - if (total.last + mbchar_width) >= arg - break total - elsif (total.last + mbchar_width) >= @cursor_max - break total - else - total = [total.first + gc.bytesize, total.last + mbchar_width] - total - end - } - end - - private def vi_replace_char(key, arg: 1) - @waiting_proc = ->(k) { - if arg == 1 - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - before = @line.byteslice(0, @byte_pointer) - remaining_point = @byte_pointer + byte_size - after = @line.byteslice(remaining_point, @line.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 - - private def vi_next_char(key, arg: 1, inclusive: false) - @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, inclusive: inclusive) } - end - - private def vi_to_next_char(key, arg: 1, inclusive: false) - @waiting_proc = ->(key_for_proc) { search_next_char(key_for_proc, arg, need_prev_char: true, inclusive: inclusive) } - end - - private def search_next_char(key, arg, need_prev_char: false, inclusive: false) - if key.instance_of?(String) - inputed_char = key - else - inputed_char = key.chr - end - prev_total = nil - total = nil - found = false - @line.byteslice(@byte_pointer..-1).grapheme_clusters.each do |mbchar| - # total has [byte_size, cursor] - unless total - # skip cursor point - width = Reline::Unicode.get_mbchar_width(mbchar) - total = [mbchar.bytesize, width] - else - if inputed_char == mbchar - arg -= 1 - if arg.zero? - found = true - break - end - end - width = Reline::Unicode.get_mbchar_width(mbchar) - prev_total = total - total = [total.first + mbchar.bytesize, total.last + width] - end - end - if not need_prev_char and found and total - byte_size, width = total - @byte_pointer += byte_size - @cursor += width - elsif need_prev_char and found and prev_total - byte_size, width = prev_total - @byte_pointer += byte_size - @cursor += width - end - if inclusive - byte_size = Reline::Unicode.get_next_mbchar_size(@line, @byte_pointer) - if byte_size > 0 - c = @line.byteslice(@byte_pointer, byte_size) - width = Reline::Unicode.get_mbchar_width(c) - @byte_pointer += byte_size - @cursor += width - end - end - @waiting_proc = nil - end - - private def vi_prev_char(key, arg: 1) - @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg) } - end - - private def vi_to_prev_char(key, arg: 1) - @waiting_proc = ->(key_for_proc) { search_prev_char(key_for_proc, arg, true) } - end - - private def search_prev_char(key, arg, need_next_char = false) - if key.instance_of?(String) - inputed_char = key - else - inputed_char = key.chr - end - prev_total = nil - total = nil - found = false - @line.byteslice(0..@byte_pointer).grapheme_clusters.reverse_each do |mbchar| - # total has [byte_size, cursor] - unless total - # skip cursor point - width = Reline::Unicode.get_mbchar_width(mbchar) - total = [mbchar.bytesize, width] - else - if inputed_char == mbchar - arg -= 1 - if arg.zero? - found = true - break - end - end - width = Reline::Unicode.get_mbchar_width(mbchar) - prev_total = total - total = [total.first + mbchar.bytesize, total.last + width] - end - end - if not need_next_char and found and total - byte_size, width = total - @byte_pointer -= byte_size - @cursor -= width - elsif need_next_char and found and prev_total - byte_size, width = prev_total - @byte_pointer -= byte_size - @cursor -= width - end - @waiting_proc = nil - end - - private def vi_join_lines(key, arg: 1) - if @is_multiline and @buffer_of_lines.size > @line_index + 1 - @cursor = calculate_width(@line) - @byte_pointer = @line.bytesize - @line += ' ' + @buffer_of_lines.delete_at(@line_index + 1).lstrip - @cursor_max = calculate_width(@line) - @buffer_of_lines[@line_index] = @line - @rerender_all = true - @rest_height += 1 - end - arg -= 1 - vi_join_lines(key, arg: arg) if arg > 0 - end - - private def em_set_mark(key) - @mark_pointer = [@byte_pointer, @line_index] - end - alias_method :set_mark, :em_set_mark - - private def em_exchange_mark(key) - return unless @mark_pointer - new_pointer = [@byte_pointer, @line_index] - @previous_line_index = @line_index - @byte_pointer, @line_index = @mark_pointer - @cursor = calculate_width(@line.byteslice(0, @byte_pointer)) - @cursor_max = calculate_width(@line) - @mark_pointer = new_pointer - end - alias_method :exchange_point_and_mark, :em_exchange_mark -end diff --git a/lib/reline/reline.gemspec b/lib/reline/reline.gemspec deleted file mode 100644 index 7bf1f8758b..0000000000 --- a/lib/reline/reline.gemspec +++ /dev/null @@ -1,25 +0,0 @@ - -begin - require_relative 'lib/reline/version' -rescue LoadError - require_relative 'version' -end - -Gem::Specification.new do |spec| - spec.name = 'reline' - spec.version = Reline::VERSION - spec.authors = ['aycabta'] - spec.email = ['aycabta@gmail.com'] - - spec.summary = %q{Alternative GNU Readline or Editline implementation by pure Ruby.} - spec.description = %q{Alternative GNU Readline or Editline implementation by pure Ruby.} - spec.homepage = 'https://github.com/ruby/reline' - spec.license = 'Ruby' - - spec.files = Dir['BSDL', 'COPYING', 'README.md', 'license_of_rb-readline', 'lib/**/*'] - spec.require_paths = ['lib'] - - spec.required_ruby_version = Gem::Requirement.new('>= 2.6') - - spec.add_dependency 'io-console', '~> 0.5' -end diff --git a/lib/reline/terminfo.rb b/lib/reline/terminfo.rb deleted file mode 100644 index f53642b919..0000000000 --- a/lib/reline/terminfo.rb +++ /dev/null @@ -1,174 +0,0 @@ -begin - require 'fiddle' - require 'fiddle/import' -rescue LoadError - module Reline::Terminfo - def self.curses_dl - false - end - end -end - -module Reline::Terminfo - extend Fiddle::Importer - - class TerminfoError < StandardError; end - - def self.curses_dl_files - case RUBY_PLATFORM - when /mingw/, /mswin/ - # aren't supported - [] - when /cygwin/ - %w[cygncursesw-10.dll cygncurses-10.dll] - when /darwin/ - %w[libncursesw.dylib libcursesw.dylib libncurses.dylib libcurses.dylib] - else - %w[libncursesw.so libcursesw.so libncurses.so libcurses.so] - end - end - - @curses_dl = false - def self.curses_dl - return @curses_dl unless @curses_dl == false - if RUBY_VERSION >= '3.0.0' - # Gem module isn't defined in test-all of the Ruby repository, and - # Fiddle in Ruby 3.0.0 or later supports Fiddle::TYPE_VARIADIC. - fiddle_supports_variadic = true - elsif Fiddle.const_defined?(:VERSION,false) and Gem::Version.create(Fiddle::VERSION) >= Gem::Version.create('1.0.1') - # Fiddle::TYPE_VARIADIC is supported from Fiddle 1.0.1. - fiddle_supports_variadic = true - else - fiddle_supports_variadic = false - end - if fiddle_supports_variadic and not Fiddle.const_defined?(:TYPE_VARIADIC) - # If the libffi version is not 3.0.5 or higher, there isn't TYPE_VARIADIC. - fiddle_supports_variadic = false - end - if fiddle_supports_variadic - curses_dl_files.each do |curses_name| - result = Fiddle::Handle.new(curses_name) - rescue Fiddle::DLError - next - else - @curses_dl = result - break - end - end - @curses_dl = nil if @curses_dl == false - @curses_dl - end -end if not Reline.const_defined?(:Terminfo) or not Reline::Terminfo.respond_to?(:curses_dl) - -module Reline::Terminfo - dlload curses_dl - #extern 'int setupterm(char *term, int fildes, int *errret)' - @setupterm = Fiddle::Function.new(curses_dl['setupterm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_INT, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - #extern 'char *tigetstr(char *capname)' - @tigetstr = Fiddle::Function.new(curses_dl['tigetstr'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_VOIDP) - begin - #extern 'char *tiparm(const char *str, ...)' - @tiparm = Fiddle::Function.new(curses_dl['tiparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP) - rescue Fiddle::DLError - # OpenBSD lacks tiparm - #extern 'char *tparm(const char *str, ...)' - @tiparm = Fiddle::Function.new(curses_dl['tparm'], [Fiddle::TYPE_VOIDP, Fiddle::TYPE_VARIADIC], Fiddle::TYPE_VOIDP) - end - begin - #extern 'int tigetflag(char *str)' - @tigetflag = Fiddle::Function.new(curses_dl['tigetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - rescue Fiddle::DLError - # OpenBSD lacks tigetflag - #extern 'int tgetflag(char *str)' - @tigetflag = Fiddle::Function.new(curses_dl['tgetflag'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - end - begin - #extern 'int tigetnum(char *str)' - @tigetnum = Fiddle::Function.new(curses_dl['tigetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - rescue Fiddle::DLError - # OpenBSD lacks tigetnum - #extern 'int tgetnum(char *str)' - @tigetnum = Fiddle::Function.new(curses_dl['tgetnum'], [Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT) - end - - def self.setupterm(term, fildes) - errret_int = Fiddle::Pointer.malloc(Fiddle::SIZEOF_INT) - ret = @setupterm.(term, fildes, errret_int) - errret = errret_int[0, Fiddle::SIZEOF_INT].unpack1('i') - case ret - when 0 # OK - 0 - when -1 # ERR - case errret - when 1 - raise TerminfoError.new('The terminal is hardcopy, cannot be used for curses applications.') - when 0 - raise TerminfoError.new('The terminal could not be found, or that it is a generic type, having too little information for curses applications to run.') - when -1 - raise TerminfoError.new('The terminfo database could not be found.') - else # unknown - -1 - end - else # unknown - -2 - end - end - - class StringWithTiparm < String - def tiparm(*args) # for method chain - Reline::Terminfo.tiparm(self, *args) - end - end - - def self.tigetstr(capname) - raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String) - capability = @tigetstr.(capname) - case capability.to_i - when 0, -1 - raise TerminfoError, "can't find capability: #{capname}" - end - StringWithTiparm.new(capability.to_s) - end - - def self.tiparm(str, *args) - new_args = [] - args.each do |a| - new_args << Fiddle::TYPE_INT << a - end - @tiparm.(str, *new_args).to_s - end - - def self.tigetflag(capname) - raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String) - flag = @tigetflag.(capname).to_i - case flag - when -1 - raise TerminfoError, "not boolean capability: #{capname}" - when 0 - raise TerminfoError, "can't find capability: #{capname}" - end - flag - end - - def self.tigetnum(capname) - raise TerminfoError, "capname is not String: #{capname.inspect}" unless capname.is_a?(String) - num = @tigetnum.(capname).to_i - case num - when -2 - raise TerminfoError, "not numeric capability: #{capname}" - when -1 - raise TerminfoError, "can't find capability: #{capname}" - end - num - end - - def self.enabled? - true - end -end if Reline::Terminfo.curses_dl - -module Reline::Terminfo - def self.enabled? - false - end -end unless Reline::Terminfo.curses_dl diff --git a/lib/reline/unicode.rb b/lib/reline/unicode.rb deleted file mode 100644 index 6000c9f82a..0000000000 --- a/lib/reline/unicode.rb +++ /dev/null @@ -1,665 +0,0 @@ -class Reline::Unicode - EscapedPairs = { - 0x00 => '^@', - 0x01 => '^A', # C-a - 0x02 => '^B', - 0x03 => '^C', - 0x04 => '^D', - 0x05 => '^E', - 0x06 => '^F', - 0x07 => '^G', - 0x08 => '^H', # Backspace - 0x09 => '^I', - 0x0A => '^J', - 0x0B => '^K', - 0x0C => '^L', - 0x0D => '^M', # Enter - 0x0E => '^N', - 0x0F => '^O', - 0x10 => '^P', - 0x11 => '^Q', - 0x12 => '^R', - 0x13 => '^S', - 0x14 => '^T', - 0x15 => '^U', - 0x16 => '^V', - 0x17 => '^W', - 0x18 => '^X', - 0x19 => '^Y', - 0x1A => '^Z', # C-z - 0x1B => '^[', # C-[ C-3 - 0x1D => '^]', # C-] - 0x1E => '^^', # C-~ C-6 - 0x1F => '^_', # C-_ C-7 - 0x7F => '^?', # C-? C-8 - } - EscapedChars = EscapedPairs.keys.map(&:chr) - - NON_PRINTING_START = "\1" - NON_PRINTING_END = "\2" - CSI_REGEXP = /\e\[[\d;]*[ABCDEFGHJKSTfminsuhl]/ - OSC_REGEXP = /\e\]\d+(?:;[^;]+)*\a/ - WIDTH_SCANNER = /\G(?:(#{NON_PRINTING_START})|(#{NON_PRINTING_END})|(#{CSI_REGEXP})|(#{OSC_REGEXP})|(\X))/o - NON_PRINTING_START_INDEX = 0 - NON_PRINTING_END_INDEX = 1 - CSI_REGEXP_INDEX = 2 - OSC_REGEXP_INDEX = 3 - GRAPHEME_CLUSTER_INDEX = 4 - - def self.get_mbchar_byte_size_by_first_char(c) - # Checks UTF-8 character byte size - case c.ord - # 0b0xxxxxxx - when ->(code) { (code ^ 0b10000000).allbits?(0b10000000) } then 1 - # 0b110xxxxx - when ->(code) { (code ^ 0b00100000).allbits?(0b11100000) } then 2 - # 0b1110xxxx - when ->(code) { (code ^ 0b00010000).allbits?(0b11110000) } then 3 - # 0b11110xxx - when ->(code) { (code ^ 0b00001000).allbits?(0b11111000) } then 4 - # 0b111110xx - when ->(code) { (code ^ 0b00000100).allbits?(0b11111100) } then 5 - # 0b1111110x - when ->(code) { (code ^ 0b00000010).allbits?(0b11111110) } then 6 - # successor of mbchar - else 0 - end - end - - def self.escape_for_print(str) - str.chars.map! { |gr| - escaped = EscapedPairs[gr.ord] - if escaped && gr != -"\n" && gr != -"\t" - escaped - else - gr - end - }.join - end - - require 'reline/unicode/east_asian_width' - - HalfwidthDakutenHandakuten = /[\u{FF9E}\u{FF9F}]/ - - MBCharWidthRE = / - (?<width_2_1> - [#{ EscapedChars.map {|c| "\\x%02x" % c.ord }.join }] (?# ^ + char, such as ^M, ^H, ^[, ...) - ) - | (?<width_3>^\u{2E3B}) (?# THREE-EM DASH) - | (?<width_0>^\p{M}) - | (?<width_2_2> - #{ EastAsianWidth::TYPE_F } - | #{ EastAsianWidth::TYPE_W } - ) - | (?<width_1> - #{ EastAsianWidth::TYPE_H } - | #{ EastAsianWidth::TYPE_NA } - | #{ EastAsianWidth::TYPE_N } - )(?!#{ HalfwidthDakutenHandakuten }) - | (?<width_2_3> - (?: #{ EastAsianWidth::TYPE_H } - | #{ EastAsianWidth::TYPE_NA } - | #{ EastAsianWidth::TYPE_N }) - #{ HalfwidthDakutenHandakuten } - ) - | (?<ambiguous_width> - #{EastAsianWidth::TYPE_A} - ) - /x - - def self.get_mbchar_width(mbchar) - ord = mbchar.ord - if (0x00 <= ord and ord <= 0x1F) # in EscapedPairs - return 2 - elsif (0x20 <= ord and ord <= 0x7E) # printable ASCII chars - return 1 - end - m = mbchar.encode(Encoding::UTF_8).match(MBCharWidthRE) - case - when m.nil? then 1 # TODO should be U+FFFD � REPLACEMENT CHARACTER - when m[:width_2_1], m[:width_2_2], m[:width_2_3] then 2 - when m[:width_3] then 3 - when m[:width_0] then 0 - when m[:width_1] then 1 - when m[:ambiguous_width] then Reline.ambiguous_width - else - nil - end - end - - def self.calculate_width(str, allow_escape_code = false) - if allow_escape_code - width = 0 - rest = str.encode(Encoding::UTF_8) - in_zero_width = false - rest.scan(WIDTH_SCANNER) do |gc| - case - when gc[NON_PRINTING_START_INDEX] - in_zero_width = true - when gc[NON_PRINTING_END_INDEX] - in_zero_width = false - when gc[CSI_REGEXP_INDEX], gc[OSC_REGEXP_INDEX] - when gc[GRAPHEME_CLUSTER_INDEX] - gc = gc[GRAPHEME_CLUSTER_INDEX] - unless in_zero_width - width += get_mbchar_width(gc) - end - end - end - width - else - str.encode(Encoding::UTF_8).grapheme_clusters.inject(0) { |w, gc| - w + get_mbchar_width(gc) - } - end - end - - def self.split_by_width(str, max_width, encoding = str.encoding) - lines = [String.new(encoding: encoding)] - height = 1 - width = 0 - rest = str.encode(Encoding::UTF_8) - in_zero_width = false - rest.scan(WIDTH_SCANNER) do |gc| - case - when gc[NON_PRINTING_START_INDEX] - in_zero_width = true - when gc[NON_PRINTING_END_INDEX] - in_zero_width = false - when gc[CSI_REGEXP_INDEX] - lines.last << gc[CSI_REGEXP_INDEX] - when gc[OSC_REGEXP_INDEX] - lines.last << gc[OSC_REGEXP_INDEX] - when gc[GRAPHEME_CLUSTER_INDEX] - gc = gc[GRAPHEME_CLUSTER_INDEX] - unless in_zero_width - mbchar_width = get_mbchar_width(gc) - if (width += mbchar_width) > max_width - width = mbchar_width - lines << nil - lines << String.new(encoding: encoding) - height += 1 - end - end - lines.last << gc - end - end - # The cursor moves to next line in first - if width == max_width - lines << nil - lines << String.new(encoding: encoding) - height += 1 - end - [lines, height] - end - - # Take a chunk of a String cut by width with escape sequences. - def self.take_range(str, start_col, max_width, encoding = str.encoding) - chunk = String.new(encoding: encoding) - total_width = 0 - rest = str.encode(Encoding::UTF_8) - in_zero_width = false - rest.scan(WIDTH_SCANNER) do |gc| - case - when gc[NON_PRINTING_START_INDEX] - in_zero_width = true - when gc[NON_PRINTING_END_INDEX] - in_zero_width = false - when gc[CSI_REGEXP_INDEX] - chunk << gc[CSI_REGEXP_INDEX] - when gc[OSC_REGEXP_INDEX] - chunk << gc[OSC_REGEXP_INDEX] - when gc[GRAPHEME_CLUSTER_INDEX] - gc = gc[GRAPHEME_CLUSTER_INDEX] - if in_zero_width - chunk << gc - else - mbchar_width = get_mbchar_width(gc) - total_width += mbchar_width - break if (start_col + max_width) < total_width - chunk << gc if start_col < total_width - end - end - end - chunk - end - - def self.get_next_mbchar_size(line, byte_pointer) - grapheme = line.byteslice(byte_pointer..-1).grapheme_clusters.first - grapheme ? grapheme.bytesize : 0 - end - - def self.get_prev_mbchar_size(line, byte_pointer) - if byte_pointer.zero? - 0 - else - grapheme = line.byteslice(0..(byte_pointer - 1)).grapheme_clusters.last - grapheme ? grapheme.bytesize : 0 - end - end - - def self.em_forward_word(line, byte_pointer) - width = 0 - byte_size = 0 - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - width += get_mbchar_width(mbchar) - byte_size += size - end - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end - - def self.em_forward_word_with_capitalization(line, byte_pointer) - width = 0 - byte_size = 0 - new_str = String.new - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - new_str += mbchar - width += get_mbchar_width(mbchar) - byte_size += size - end - first = true - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - if first - new_str += mbchar.upcase - first = false - else - new_str += mbchar.downcase - end - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width, new_str] - end - - def self.em_backward_word(line, byte_pointer) - width = 0 - byte_size = 0 - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - width += get_mbchar_width(mbchar) - byte_size += size - end - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end - - def self.em_big_backward_word(line, byte_pointer) - width = 0 - byte_size = 0 - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - break if mbchar =~ /\S/ - width += get_mbchar_width(mbchar) - byte_size += size - end - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - break if mbchar =~ /\s/ - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end - - def self.ed_transpose_words(line, byte_pointer) - right_word_start = nil - size = get_next_mbchar_size(line, byte_pointer) - mbchar = line.byteslice(byte_pointer, size) - if size.zero? - # ' aaa bbb [cursor]' - byte_size = 0 - while 0 < (byte_pointer + byte_size) - size = get_prev_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - byte_size -= size - end - while 0 < (byte_pointer + byte_size) - size = get_prev_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - byte_size -= size - end - right_word_start = byte_pointer + byte_size - byte_size = 0 - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - byte_size += size - end - after_start = byte_pointer + byte_size - elsif mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - # ' aaa bb[cursor]b' - byte_size = 0 - while 0 < (byte_pointer + byte_size) - size = get_prev_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - byte_size -= size - end - right_word_start = byte_pointer + byte_size - byte_size = 0 - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - byte_size += size - end - after_start = byte_pointer + byte_size - else - byte_size = 0 - while (line.bytesize - 1) > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - byte_size += size - end - if (byte_pointer + byte_size) == (line.bytesize - 1) - # ' aaa bbb [cursor] ' - after_start = line.bytesize - while 0 < (byte_pointer + byte_size) - size = get_prev_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - byte_size -= size - end - while 0 < (byte_pointer + byte_size) - size = get_prev_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - byte_size -= size - end - right_word_start = byte_pointer + byte_size - else - # ' aaa [cursor] bbb ' - right_word_start = byte_pointer + byte_size - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - byte_size += size - end - after_start = byte_pointer + byte_size - end - end - byte_size = right_word_start - byte_pointer - while 0 < (byte_pointer + byte_size) - size = get_prev_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\p{Word}/ - byte_size -= size - end - middle_start = byte_pointer + byte_size - byte_size = middle_start - byte_pointer - while 0 < (byte_pointer + byte_size) - size = get_prev_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size - size, size) - break if mbchar.encode(Encoding::UTF_8) =~ /\P{Word}/ - byte_size -= size - end - left_word_start = byte_pointer + byte_size - [left_word_start, middle_start, right_word_start, after_start] - end - - def self.vi_big_forward_word(line, byte_pointer) - width = 0 - byte_size = 0 - while (line.bytesize - 1) > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar =~ /\s/ - width += get_mbchar_width(mbchar) - byte_size += size - end - while (line.bytesize - 1) > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar =~ /\S/ - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end - - def self.vi_big_forward_end_word(line, byte_pointer) - if (line.bytesize - 1) > byte_pointer - size = get_next_mbchar_size(line, byte_pointer) - mbchar = line.byteslice(byte_pointer, size) - width = get_mbchar_width(mbchar) - byte_size = size - else - return [0, 0] - end - while (line.bytesize - 1) > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar =~ /\S/ - width += get_mbchar_width(mbchar) - byte_size += size - end - prev_width = width - prev_byte_size = byte_size - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar =~ /\s/ - prev_width = width - prev_byte_size = byte_size - width += get_mbchar_width(mbchar) - byte_size += size - end - [prev_byte_size, prev_width] - end - - def self.vi_big_backward_word(line, byte_pointer) - width = 0 - byte_size = 0 - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - break if mbchar =~ /\S/ - width += get_mbchar_width(mbchar) - byte_size += size - end - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - break if mbchar =~ /\s/ - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end - - def self.vi_forward_word(line, byte_pointer, drop_terminate_spaces = false) - if line.bytesize > byte_pointer - size = get_next_mbchar_size(line, byte_pointer) - mbchar = line.byteslice(byte_pointer, size) - if mbchar =~ /\w/ - started_by = :word - elsif mbchar =~ /\s/ - started_by = :space - else - started_by = :non_word_printable - end - width = get_mbchar_width(mbchar) - byte_size = size - else - return [0, 0] - end - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - case started_by - when :word - break if mbchar =~ /\W/ - when :space - break if mbchar =~ /\S/ - when :non_word_printable - break if mbchar =~ /\w|\s/ - end - width += get_mbchar_width(mbchar) - byte_size += size - end - return [byte_size, width] if drop_terminate_spaces - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - break if mbchar =~ /\S/ - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end - - def self.vi_forward_end_word(line, byte_pointer) - if (line.bytesize - 1) > byte_pointer - size = get_next_mbchar_size(line, byte_pointer) - mbchar = line.byteslice(byte_pointer, size) - if mbchar =~ /\w/ - started_by = :word - elsif mbchar =~ /\s/ - started_by = :space - else - started_by = :non_word_printable - end - width = get_mbchar_width(mbchar) - byte_size = size - else - return [0, 0] - end - if (line.bytesize - 1) > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - if mbchar =~ /\w/ - second = :word - elsif mbchar =~ /\s/ - second = :space - else - second = :non_word_printable - end - second_width = get_mbchar_width(mbchar) - second_byte_size = size - else - return [byte_size, width] - end - if second == :space - width += second_width - byte_size += second_byte_size - while (line.bytesize - 1) > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - if mbchar =~ /\S/ - if mbchar =~ /\w/ - started_by = :word - else - started_by = :non_word_printable - end - break - end - width += get_mbchar_width(mbchar) - byte_size += size - end - else - case [started_by, second] - when [:word, :non_word_printable], [:non_word_printable, :word] - started_by = second - else - width += second_width - byte_size += second_byte_size - started_by = second - end - end - prev_width = width - prev_byte_size = byte_size - while line.bytesize > (byte_pointer + byte_size) - size = get_next_mbchar_size(line, byte_pointer + byte_size) - mbchar = line.byteslice(byte_pointer + byte_size, size) - case started_by - when :word - break if mbchar =~ /\W/ - when :non_word_printable - break if mbchar =~ /[\w\s]/ - end - prev_width = width - prev_byte_size = byte_size - width += get_mbchar_width(mbchar) - byte_size += size - end - [prev_byte_size, prev_width] - end - - def self.vi_backward_word(line, byte_pointer) - width = 0 - byte_size = 0 - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - if mbchar =~ /\S/ - if mbchar =~ /\w/ - started_by = :word - else - started_by = :non_word_printable - end - break - end - width += get_mbchar_width(mbchar) - byte_size += size - end - while 0 < (byte_pointer - byte_size) - size = get_prev_mbchar_size(line, byte_pointer - byte_size) - mbchar = line.byteslice(byte_pointer - byte_size - size, size) - case started_by - when :word - break if mbchar =~ /\W/ - when :non_word_printable - break if mbchar =~ /[\w\s]/ - end - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end - - def self.vi_first_print(line) - width = 0 - byte_size = 0 - while (line.bytesize - 1) > byte_size - size = get_next_mbchar_size(line, byte_size) - mbchar = line.byteslice(byte_size, size) - if mbchar =~ /\S/ - break - end - width += get_mbchar_width(mbchar) - byte_size += size - end - [byte_size, width] - end -end diff --git a/lib/reline/unicode/east_asian_width.rb b/lib/reline/unicode/east_asian_width.rb deleted file mode 100644 index 97dfec4c52..0000000000 --- a/lib/reline/unicode/east_asian_width.rb +++ /dev/null @@ -1,1196 +0,0 @@ -class Reline::Unicode::EastAsianWidth - # This is based on EastAsianWidth.txt - # EastAsianWidth.txt - - # Fullwidth - TYPE_F = /^[#{ %W( - \u{3000} - \u{FF01}-\u{FF60} - \u{FFE0}-\u{FFE6} - ).join }]/ - - # Halfwidth - TYPE_H = /^[#{ %W( - \u{20A9} - \u{FF61}-\u{FFBE} - \u{FFC2}-\u{FFC7} - \u{FFCA}-\u{FFCF} - \u{FFD2}-\u{FFD7} - \u{FFDA}-\u{FFDC} - \u{FFE8}-\u{FFEE} - ).join }]/ - - # Wide - TYPE_W = /^[#{ %W( - \u{1100}-\u{115F} - \u{231A}-\u{231B} - \u{2329}-\u{232A} - \u{23E9}-\u{23EC} - \u{23F0} - \u{23F3} - \u{25FD}-\u{25FE} - \u{2614}-\u{2615} - \u{2648}-\u{2653} - \u{267F} - \u{2693} - \u{26A1} - \u{26AA}-\u{26AB} - \u{26BD}-\u{26BE} - \u{26C4}-\u{26C5} - \u{26CE} - \u{26D4} - \u{26EA} - \u{26F2}-\u{26F3} - \u{26F5} - \u{26FA} - \u{26FD} - \u{2705} - \u{270A}-\u{270B} - \u{2728} - \u{274C} - \u{274E} - \u{2753}-\u{2755} - \u{2757} - \u{2795}-\u{2797} - \u{27B0} - \u{27BF} - \u{2B1B}-\u{2B1C} - \u{2B50} - \u{2B55} - \u{2E80}-\u{2E99} - \u{2E9B}-\u{2EF3} - \u{2F00}-\u{2FD5} - \u{2FF0}-\u{2FFB} - \u{3001}-\u{303E} - \u{3041}-\u{3096} - \u{3099}-\u{30FF} - \u{3105}-\u{312F} - \u{3131}-\u{318E} - \u{3190}-\u{31E3} - \u{31F0}-\u{321E} - \u{3220}-\u{3247} - \u{3250}-\u{4DBF} - \u{4E00}-\u{A48C} - \u{A490}-\u{A4C6} - \u{A960}-\u{A97C} - \u{AC00}-\u{D7A3} - \u{F900}-\u{FAFF} - \u{FE10}-\u{FE19} - \u{FE30}-\u{FE52} - \u{FE54}-\u{FE66} - \u{FE68}-\u{FE6B} - \u{16FE0}-\u{16FE4} - \u{16FF0}-\u{16FF1} - \u{17000}-\u{187F7} - \u{18800}-\u{18CD5} - \u{18D00}-\u{18D08} - \u{1AFF0}-\u{1AFF3} - \u{1AFF5}-\u{1AFFB} - \u{1AFFD}-\u{1AFFE} - \u{1B000}-\u{1B122} - \u{1B132} - \u{1B150}-\u{1B152} - \u{1B155} - \u{1B164}-\u{1B167} - \u{1B170}-\u{1B2FB} - \u{1F004} - \u{1F0CF} - \u{1F18E} - \u{1F191}-\u{1F19A} - \u{1F200}-\u{1F202} - \u{1F210}-\u{1F23B} - \u{1F240}-\u{1F248} - \u{1F250}-\u{1F251} - \u{1F260}-\u{1F265} - \u{1F300}-\u{1F320} - \u{1F32D}-\u{1F335} - \u{1F337}-\u{1F37C} - \u{1F37E}-\u{1F393} - \u{1F3A0}-\u{1F3CA} - \u{1F3CF}-\u{1F3D3} - \u{1F3E0}-\u{1F3F0} - \u{1F3F4} - \u{1F3F8}-\u{1F43E} - \u{1F440} - \u{1F442}-\u{1F4FC} - \u{1F4FF}-\u{1F53D} - \u{1F54B}-\u{1F54E} - \u{1F550}-\u{1F567} - \u{1F57A} - \u{1F595}-\u{1F596} - \u{1F5A4} - \u{1F5FB}-\u{1F64F} - \u{1F680}-\u{1F6C5} - \u{1F6CC} - \u{1F6D0}-\u{1F6D2} - \u{1F6D5}-\u{1F6D7} - \u{1F6DC}-\u{1F6DF} - \u{1F6EB}-\u{1F6EC} - \u{1F6F4}-\u{1F6FC} - \u{1F7E0}-\u{1F7EB} - \u{1F7F0} - \u{1F90C}-\u{1F93A} - \u{1F93C}-\u{1F945} - \u{1F947}-\u{1F9FF} - \u{1FA70}-\u{1FA7C} - \u{1FA80}-\u{1FA88} - \u{1FA90}-\u{1FABD} - \u{1FABF}-\u{1FAC5} - \u{1FACE}-\u{1FADB} - \u{1FAE0}-\u{1FAE8} - \u{1FAF0}-\u{1FAF8} - \u{20000}-\u{2FFFD} - \u{30000}-\u{3FFFD} - ).join }]/ - - # Narrow - TYPE_NA = /^[#{ %W( - \u{0020}-\u{007E} - \u{00A2}-\u{00A3} - \u{00A5}-\u{00A6} - \u{00AC} - \u{00AF} - \u{27E6}-\u{27ED} - \u{2985}-\u{2986} - ).join }]/ - - # Ambiguous - TYPE_A = /^[#{ %W( - \u{00A1} - \u{00A4} - \u{00A7}-\u{00A8} - \u{00AA} - \u{00AD}-\u{00AE} - \u{00B0}-\u{00B4} - \u{00B6}-\u{00BA} - \u{00BC}-\u{00BF} - \u{00C6} - \u{00D0} - \u{00D7}-\u{00D8} - \u{00DE}-\u{00E1} - \u{00E6} - \u{00E8}-\u{00EA} - \u{00EC}-\u{00ED} - \u{00F0} - \u{00F2}-\u{00F3} - \u{00F7}-\u{00FA} - \u{00FC} - \u{00FE} - \u{0101} - \u{0111} - \u{0113} - \u{011B} - \u{0126}-\u{0127} - \u{012B} - \u{0131}-\u{0133} - \u{0138} - \u{013F}-\u{0142} - \u{0144} - \u{0148}-\u{014B} - \u{014D} - \u{0152}-\u{0153} - \u{0166}-\u{0167} - \u{016B} - \u{01CE} - \u{01D0} - \u{01D2} - \u{01D4} - \u{01D6} - \u{01D8} - \u{01DA} - \u{01DC} - \u{0251} - \u{0261} - \u{02C4} - \u{02C7} - \u{02C9}-\u{02CB} - \u{02CD} - \u{02D0} - \u{02D8}-\u{02DB} - \u{02DD} - \u{02DF} - \u{0300}-\u{036F} - \u{0391}-\u{03A1} - \u{03A3}-\u{03A9} - \u{03B1}-\u{03C1} - \u{03C3}-\u{03C9} - \u{0401} - \u{0410}-\u{044F} - \u{0451} - \u{2010} - \u{2013}-\u{2016} - \u{2018}-\u{2019} - \u{201C}-\u{201D} - \u{2020}-\u{2022} - \u{2024}-\u{2027} - \u{2030} - \u{2032}-\u{2033} - \u{2035} - \u{203B} - \u{203E} - \u{2074} - \u{207F} - \u{2081}-\u{2084} - \u{20AC} - \u{2103} - \u{2105} - \u{2109} - \u{2113} - \u{2116} - \u{2121}-\u{2122} - \u{2126} - \u{212B} - \u{2153}-\u{2154} - \u{215B}-\u{215E} - \u{2160}-\u{216B} - \u{2170}-\u{2179} - \u{2189} - \u{2190}-\u{2199} - \u{21B8}-\u{21B9} - \u{21D2} - \u{21D4} - \u{21E7} - \u{2200} - \u{2202}-\u{2203} - \u{2207}-\u{2208} - \u{220B} - \u{220F} - \u{2211} - \u{2215} - \u{221A} - \u{221D}-\u{2220} - \u{2223} - \u{2225} - \u{2227}-\u{222C} - \u{222E} - \u{2234}-\u{2237} - \u{223C}-\u{223D} - \u{2248} - \u{224C} - \u{2252} - \u{2260}-\u{2261} - \u{2264}-\u{2267} - \u{226A}-\u{226B} - \u{226E}-\u{226F} - \u{2282}-\u{2283} - \u{2286}-\u{2287} - \u{2295} - \u{2299} - \u{22A5} - \u{22BF} - \u{2312} - \u{2460}-\u{24E9} - \u{24EB}-\u{254B} - \u{2550}-\u{2573} - \u{2580}-\u{258F} - \u{2592}-\u{2595} - \u{25A0}-\u{25A1} - \u{25A3}-\u{25A9} - \u{25B2}-\u{25B3} - \u{25B6}-\u{25B7} - \u{25BC}-\u{25BD} - \u{25C0}-\u{25C1} - \u{25C6}-\u{25C8} - \u{25CB} - \u{25CE}-\u{25D1} - \u{25E2}-\u{25E5} - \u{25EF} - \u{2605}-\u{2606} - \u{2609} - \u{260E}-\u{260F} - \u{261C} - \u{261E} - \u{2640} - \u{2642} - \u{2660}-\u{2661} - \u{2663}-\u{2665} - \u{2667}-\u{266A} - \u{266C}-\u{266D} - \u{266F} - \u{269E}-\u{269F} - \u{26BF} - \u{26C6}-\u{26CD} - \u{26CF}-\u{26D3} - \u{26D5}-\u{26E1} - \u{26E3} - \u{26E8}-\u{26E9} - \u{26EB}-\u{26F1} - \u{26F4} - \u{26F6}-\u{26F9} - \u{26FB}-\u{26FC} - \u{26FE}-\u{26FF} - \u{273D} - \u{2776}-\u{277F} - \u{2B56}-\u{2B59} - \u{3248}-\u{324F} - \u{E000}-\u{F8FF} - \u{FE00}-\u{FE0F} - \u{FFFD} - \u{1F100}-\u{1F10A} - \u{1F110}-\u{1F12D} - \u{1F130}-\u{1F169} - \u{1F170}-\u{1F18D} - \u{1F18F}-\u{1F190} - \u{1F19B}-\u{1F1AC} - \u{E0100}-\u{E01EF} - \u{F0000}-\u{FFFFD} - \u{100000}-\u{10FFFD} - ).join }]/ - - # Neutral - TYPE_N = /^[#{ %W( - \u{0000}-\u{001F} - \u{007F}-\u{00A0} - \u{00A9} - \u{00AB} - \u{00B5} - \u{00BB} - \u{00C0}-\u{00C5} - \u{00C7}-\u{00CF} - \u{00D1}-\u{00D6} - \u{00D9}-\u{00DD} - \u{00E2}-\u{00E5} - \u{00E7} - \u{00EB} - \u{00EE}-\u{00EF} - \u{00F1} - \u{00F4}-\u{00F6} - \u{00FB} - \u{00FD} - \u{00FF}-\u{0100} - \u{0102}-\u{0110} - \u{0112} - \u{0114}-\u{011A} - \u{011C}-\u{0125} - \u{0128}-\u{012A} - \u{012C}-\u{0130} - \u{0134}-\u{0137} - \u{0139}-\u{013E} - \u{0143} - \u{0145}-\u{0147} - \u{014C} - \u{014E}-\u{0151} - \u{0154}-\u{0165} - \u{0168}-\u{016A} - \u{016C}-\u{01CD} - \u{01CF} - \u{01D1} - \u{01D3} - \u{01D5} - \u{01D7} - \u{01D9} - \u{01DB} - \u{01DD}-\u{0250} - \u{0252}-\u{0260} - \u{0262}-\u{02C3} - \u{02C5}-\u{02C6} - \u{02C8} - \u{02CC} - \u{02CE}-\u{02CF} - \u{02D1}-\u{02D7} - \u{02DC} - \u{02DE} - \u{02E0}-\u{02FF} - \u{0370}-\u{0377} - \u{037A}-\u{037F} - \u{0384}-\u{038A} - \u{038C} - \u{038E}-\u{0390} - \u{03AA}-\u{03B0} - \u{03C2} - \u{03CA}-\u{0400} - \u{0402}-\u{040F} - \u{0450} - \u{0452}-\u{052F} - \u{0531}-\u{0556} - \u{0559}-\u{058A} - \u{058D}-\u{058F} - \u{0591}-\u{05C7} - \u{05D0}-\u{05EA} - \u{05EF}-\u{05F4} - \u{0600}-\u{070D} - \u{070F}-\u{074A} - \u{074D}-\u{07B1} - \u{07C0}-\u{07FA} - \u{07FD}-\u{082D} - \u{0830}-\u{083E} - \u{0840}-\u{085B} - \u{085E} - \u{0860}-\u{086A} - \u{0870}-\u{088E} - \u{0890}-\u{0891} - \u{0898}-\u{0983} - \u{0985}-\u{098C} - \u{098F}-\u{0990} - \u{0993}-\u{09A8} - \u{09AA}-\u{09B0} - \u{09B2} - \u{09B6}-\u{09B9} - \u{09BC}-\u{09C4} - \u{09C7}-\u{09C8} - \u{09CB}-\u{09CE} - \u{09D7} - \u{09DC}-\u{09DD} - \u{09DF}-\u{09E3} - \u{09E6}-\u{09FE} - \u{0A01}-\u{0A03} - \u{0A05}-\u{0A0A} - \u{0A0F}-\u{0A10} - \u{0A13}-\u{0A28} - \u{0A2A}-\u{0A30} - \u{0A32}-\u{0A33} - \u{0A35}-\u{0A36} - \u{0A38}-\u{0A39} - \u{0A3C} - \u{0A3E}-\u{0A42} - \u{0A47}-\u{0A48} - \u{0A4B}-\u{0A4D} - \u{0A51} - \u{0A59}-\u{0A5C} - \u{0A5E} - \u{0A66}-\u{0A76} - \u{0A81}-\u{0A83} - \u{0A85}-\u{0A8D} - \u{0A8F}-\u{0A91} - \u{0A93}-\u{0AA8} - \u{0AAA}-\u{0AB0} - \u{0AB2}-\u{0AB3} - \u{0AB5}-\u{0AB9} - \u{0ABC}-\u{0AC5} - \u{0AC7}-\u{0AC9} - \u{0ACB}-\u{0ACD} - \u{0AD0} - \u{0AE0}-\u{0AE3} - \u{0AE6}-\u{0AF1} - \u{0AF9}-\u{0AFF} - \u{0B01}-\u{0B03} - \u{0B05}-\u{0B0C} - \u{0B0F}-\u{0B10} - \u{0B13}-\u{0B28} - \u{0B2A}-\u{0B30} - \u{0B32}-\u{0B33} - \u{0B35}-\u{0B39} - \u{0B3C}-\u{0B44} - \u{0B47}-\u{0B48} - \u{0B4B}-\u{0B4D} - \u{0B55}-\u{0B57} - \u{0B5C}-\u{0B5D} - \u{0B5F}-\u{0B63} - \u{0B66}-\u{0B77} - \u{0B82}-\u{0B83} - \u{0B85}-\u{0B8A} - \u{0B8E}-\u{0B90} - \u{0B92}-\u{0B95} - \u{0B99}-\u{0B9A} - \u{0B9C} - \u{0B9E}-\u{0B9F} - \u{0BA3}-\u{0BA4} - \u{0BA8}-\u{0BAA} - \u{0BAE}-\u{0BB9} - \u{0BBE}-\u{0BC2} - \u{0BC6}-\u{0BC8} - \u{0BCA}-\u{0BCD} - \u{0BD0} - \u{0BD7} - \u{0BE6}-\u{0BFA} - \u{0C00}-\u{0C0C} - \u{0C0E}-\u{0C10} - \u{0C12}-\u{0C28} - \u{0C2A}-\u{0C39} - \u{0C3C}-\u{0C44} - \u{0C46}-\u{0C48} - \u{0C4A}-\u{0C4D} - \u{0C55}-\u{0C56} - \u{0C58}-\u{0C5A} - \u{0C5D} - \u{0C60}-\u{0C63} - \u{0C66}-\u{0C6F} - \u{0C77}-\u{0C8C} - \u{0C8E}-\u{0C90} - \u{0C92}-\u{0CA8} - \u{0CAA}-\u{0CB3} - \u{0CB5}-\u{0CB9} - \u{0CBC}-\u{0CC4} - \u{0CC6}-\u{0CC8} - \u{0CCA}-\u{0CCD} - \u{0CD5}-\u{0CD6} - \u{0CDD}-\u{0CDE} - \u{0CE0}-\u{0CE3} - \u{0CE6}-\u{0CEF} - \u{0CF1}-\u{0CF3} - \u{0D00}-\u{0D0C} - \u{0D0E}-\u{0D10} - \u{0D12}-\u{0D44} - \u{0D46}-\u{0D48} - \u{0D4A}-\u{0D4F} - \u{0D54}-\u{0D63} - \u{0D66}-\u{0D7F} - \u{0D81}-\u{0D83} - \u{0D85}-\u{0D96} - \u{0D9A}-\u{0DB1} - \u{0DB3}-\u{0DBB} - \u{0DBD} - \u{0DC0}-\u{0DC6} - \u{0DCA} - \u{0DCF}-\u{0DD4} - \u{0DD6} - \u{0DD8}-\u{0DDF} - \u{0DE6}-\u{0DEF} - \u{0DF2}-\u{0DF4} - \u{0E01}-\u{0E3A} - \u{0E3F}-\u{0E5B} - \u{0E81}-\u{0E82} - \u{0E84} - \u{0E86}-\u{0E8A} - \u{0E8C}-\u{0EA3} - \u{0EA5} - \u{0EA7}-\u{0EBD} - \u{0EC0}-\u{0EC4} - \u{0EC6} - \u{0EC8}-\u{0ECE} - \u{0ED0}-\u{0ED9} - \u{0EDC}-\u{0EDF} - \u{0F00}-\u{0F47} - \u{0F49}-\u{0F6C} - \u{0F71}-\u{0F97} - \u{0F99}-\u{0FBC} - \u{0FBE}-\u{0FCC} - \u{0FCE}-\u{0FDA} - \u{1000}-\u{10C5} - \u{10C7} - \u{10CD} - \u{10D0}-\u{10FF} - \u{1160}-\u{1248} - \u{124A}-\u{124D} - \u{1250}-\u{1256} - \u{1258} - \u{125A}-\u{125D} - \u{1260}-\u{1288} - \u{128A}-\u{128D} - \u{1290}-\u{12B0} - \u{12B2}-\u{12B5} - \u{12B8}-\u{12BE} - \u{12C0} - \u{12C2}-\u{12C5} - \u{12C8}-\u{12D6} - \u{12D8}-\u{1310} - \u{1312}-\u{1315} - \u{1318}-\u{135A} - \u{135D}-\u{137C} - \u{1380}-\u{1399} - \u{13A0}-\u{13F5} - \u{13F8}-\u{13FD} - \u{1400}-\u{169C} - \u{16A0}-\u{16F8} - \u{1700}-\u{1715} - \u{171F}-\u{1736} - \u{1740}-\u{1753} - \u{1760}-\u{176C} - \u{176E}-\u{1770} - \u{1772}-\u{1773} - \u{1780}-\u{17DD} - \u{17E0}-\u{17E9} - \u{17F0}-\u{17F9} - \u{1800}-\u{1819} - \u{1820}-\u{1878} - \u{1880}-\u{18AA} - \u{18B0}-\u{18F5} - \u{1900}-\u{191E} - \u{1920}-\u{192B} - \u{1930}-\u{193B} - \u{1940} - \u{1944}-\u{196D} - \u{1970}-\u{1974} - \u{1980}-\u{19AB} - \u{19B0}-\u{19C9} - \u{19D0}-\u{19DA} - \u{19DE}-\u{1A1B} - \u{1A1E}-\u{1A5E} - \u{1A60}-\u{1A7C} - \u{1A7F}-\u{1A89} - \u{1A90}-\u{1A99} - \u{1AA0}-\u{1AAD} - \u{1AB0}-\u{1ACE} - \u{1B00}-\u{1B4C} - \u{1B50}-\u{1B7E} - \u{1B80}-\u{1BF3} - \u{1BFC}-\u{1C37} - \u{1C3B}-\u{1C49} - \u{1C4D}-\u{1C88} - \u{1C90}-\u{1CBA} - \u{1CBD}-\u{1CC7} - \u{1CD0}-\u{1CFA} - \u{1D00}-\u{1F15} - \u{1F18}-\u{1F1D} - \u{1F20}-\u{1F45} - \u{1F48}-\u{1F4D} - \u{1F50}-\u{1F57} - \u{1F59} - \u{1F5B} - \u{1F5D} - \u{1F5F}-\u{1F7D} - \u{1F80}-\u{1FB4} - \u{1FB6}-\u{1FC4} - \u{1FC6}-\u{1FD3} - \u{1FD6}-\u{1FDB} - \u{1FDD}-\u{1FEF} - \u{1FF2}-\u{1FF4} - \u{1FF6}-\u{1FFE} - \u{2000}-\u{200F} - \u{2011}-\u{2012} - \u{2017} - \u{201A}-\u{201B} - \u{201E}-\u{201F} - \u{2023} - \u{2028}-\u{202F} - \u{2031} - \u{2034} - \u{2036}-\u{203A} - \u{203C}-\u{203D} - \u{203F}-\u{2064} - \u{2066}-\u{2071} - \u{2075}-\u{207E} - \u{2080} - \u{2085}-\u{208E} - \u{2090}-\u{209C} - \u{20A0}-\u{20A8} - \u{20AA}-\u{20AB} - \u{20AD}-\u{20C0} - \u{20D0}-\u{20F0} - \u{2100}-\u{2102} - \u{2104} - \u{2106}-\u{2108} - \u{210A}-\u{2112} - \u{2114}-\u{2115} - \u{2117}-\u{2120} - \u{2123}-\u{2125} - \u{2127}-\u{212A} - \u{212C}-\u{2152} - \u{2155}-\u{215A} - \u{215F} - \u{216C}-\u{216F} - \u{217A}-\u{2188} - \u{218A}-\u{218B} - \u{219A}-\u{21B7} - \u{21BA}-\u{21D1} - \u{21D3} - \u{21D5}-\u{21E6} - \u{21E8}-\u{21FF} - \u{2201} - \u{2204}-\u{2206} - \u{2209}-\u{220A} - \u{220C}-\u{220E} - \u{2210} - \u{2212}-\u{2214} - \u{2216}-\u{2219} - \u{221B}-\u{221C} - \u{2221}-\u{2222} - \u{2224} - \u{2226} - \u{222D} - \u{222F}-\u{2233} - \u{2238}-\u{223B} - \u{223E}-\u{2247} - \u{2249}-\u{224B} - \u{224D}-\u{2251} - \u{2253}-\u{225F} - \u{2262}-\u{2263} - \u{2268}-\u{2269} - \u{226C}-\u{226D} - \u{2270}-\u{2281} - \u{2284}-\u{2285} - \u{2288}-\u{2294} - \u{2296}-\u{2298} - \u{229A}-\u{22A4} - \u{22A6}-\u{22BE} - \u{22C0}-\u{2311} - \u{2313}-\u{2319} - \u{231C}-\u{2328} - \u{232B}-\u{23E8} - \u{23ED}-\u{23EF} - \u{23F1}-\u{23F2} - \u{23F4}-\u{2426} - \u{2440}-\u{244A} - \u{24EA} - \u{254C}-\u{254F} - \u{2574}-\u{257F} - \u{2590}-\u{2591} - \u{2596}-\u{259F} - \u{25A2} - \u{25AA}-\u{25B1} - \u{25B4}-\u{25B5} - \u{25B8}-\u{25BB} - \u{25BE}-\u{25BF} - \u{25C2}-\u{25C5} - \u{25C9}-\u{25CA} - \u{25CC}-\u{25CD} - \u{25D2}-\u{25E1} - \u{25E6}-\u{25EE} - \u{25F0}-\u{25FC} - \u{25FF}-\u{2604} - \u{2607}-\u{2608} - \u{260A}-\u{260D} - \u{2610}-\u{2613} - \u{2616}-\u{261B} - \u{261D} - \u{261F}-\u{263F} - \u{2641} - \u{2643}-\u{2647} - \u{2654}-\u{265F} - \u{2662} - \u{2666} - \u{266B} - \u{266E} - \u{2670}-\u{267E} - \u{2680}-\u{2692} - \u{2694}-\u{269D} - \u{26A0} - \u{26A2}-\u{26A9} - \u{26AC}-\u{26BC} - \u{26C0}-\u{26C3} - \u{26E2} - \u{26E4}-\u{26E7} - \u{2700}-\u{2704} - \u{2706}-\u{2709} - \u{270C}-\u{2727} - \u{2729}-\u{273C} - \u{273E}-\u{274B} - \u{274D} - \u{274F}-\u{2752} - \u{2756} - \u{2758}-\u{2775} - \u{2780}-\u{2794} - \u{2798}-\u{27AF} - \u{27B1}-\u{27BE} - \u{27C0}-\u{27E5} - \u{27EE}-\u{2984} - \u{2987}-\u{2B1A} - \u{2B1D}-\u{2B4F} - \u{2B51}-\u{2B54} - \u{2B5A}-\u{2B73} - \u{2B76}-\u{2B95} - \u{2B97}-\u{2CF3} - \u{2CF9}-\u{2D25} - \u{2D27} - \u{2D2D} - \u{2D30}-\u{2D67} - \u{2D6F}-\u{2D70} - \u{2D7F}-\u{2D96} - \u{2DA0}-\u{2DA6} - \u{2DA8}-\u{2DAE} - \u{2DB0}-\u{2DB6} - \u{2DB8}-\u{2DBE} - \u{2DC0}-\u{2DC6} - \u{2DC8}-\u{2DCE} - \u{2DD0}-\u{2DD6} - \u{2DD8}-\u{2DDE} - \u{2DE0}-\u{2E5D} - \u{303F} - \u{4DC0}-\u{4DFF} - \u{A4D0}-\u{A62B} - \u{A640}-\u{A6F7} - \u{A700}-\u{A7CA} - \u{A7D0}-\u{A7D1} - \u{A7D3} - \u{A7D5}-\u{A7D9} - \u{A7F2}-\u{A82C} - \u{A830}-\u{A839} - \u{A840}-\u{A877} - \u{A880}-\u{A8C5} - \u{A8CE}-\u{A8D9} - \u{A8E0}-\u{A953} - \u{A95F} - \u{A980}-\u{A9CD} - \u{A9CF}-\u{A9D9} - \u{A9DE}-\u{A9FE} - \u{AA00}-\u{AA36} - \u{AA40}-\u{AA4D} - \u{AA50}-\u{AA59} - \u{AA5C}-\u{AAC2} - \u{AADB}-\u{AAF6} - \u{AB01}-\u{AB06} - \u{AB09}-\u{AB0E} - \u{AB11}-\u{AB16} - \u{AB20}-\u{AB26} - \u{AB28}-\u{AB2E} - \u{AB30}-\u{AB6B} - \u{AB70}-\u{ABED} - \u{ABF0}-\u{ABF9} - \u{D7B0}-\u{D7C6} - \u{D7CB}-\u{D7FB} - \u{FB00}-\u{FB06} - \u{FB13}-\u{FB17} - \u{FB1D}-\u{FB36} - \u{FB38}-\u{FB3C} - \u{FB3E} - \u{FB40}-\u{FB41} - \u{FB43}-\u{FB44} - \u{FB46}-\u{FBC2} - \u{FBD3}-\u{FD8F} - \u{FD92}-\u{FDC7} - \u{FDCF} - \u{FDF0}-\u{FDFF} - \u{FE20}-\u{FE2F} - \u{FE70}-\u{FE74} - \u{FE76}-\u{FEFC} - \u{FEFF} - \u{FFF9}-\u{FFFC} - \u{10000}-\u{1000B} - \u{1000D}-\u{10026} - \u{10028}-\u{1003A} - \u{1003C}-\u{1003D} - \u{1003F}-\u{1004D} - \u{10050}-\u{1005D} - \u{10080}-\u{100FA} - \u{10100}-\u{10102} - \u{10107}-\u{10133} - \u{10137}-\u{1018E} - \u{10190}-\u{1019C} - \u{101A0} - \u{101D0}-\u{101FD} - \u{10280}-\u{1029C} - \u{102A0}-\u{102D0} - \u{102E0}-\u{102FB} - \u{10300}-\u{10323} - \u{1032D}-\u{1034A} - \u{10350}-\u{1037A} - \u{10380}-\u{1039D} - \u{1039F}-\u{103C3} - \u{103C8}-\u{103D5} - \u{10400}-\u{1049D} - \u{104A0}-\u{104A9} - \u{104B0}-\u{104D3} - \u{104D8}-\u{104FB} - \u{10500}-\u{10527} - \u{10530}-\u{10563} - \u{1056F}-\u{1057A} - \u{1057C}-\u{1058A} - \u{1058C}-\u{10592} - \u{10594}-\u{10595} - \u{10597}-\u{105A1} - \u{105A3}-\u{105B1} - \u{105B3}-\u{105B9} - \u{105BB}-\u{105BC} - \u{10600}-\u{10736} - \u{10740}-\u{10755} - \u{10760}-\u{10767} - \u{10780}-\u{10785} - \u{10787}-\u{107B0} - \u{107B2}-\u{107BA} - \u{10800}-\u{10805} - \u{10808} - \u{1080A}-\u{10835} - \u{10837}-\u{10838} - \u{1083C} - \u{1083F}-\u{10855} - \u{10857}-\u{1089E} - \u{108A7}-\u{108AF} - \u{108E0}-\u{108F2} - \u{108F4}-\u{108F5} - \u{108FB}-\u{1091B} - \u{1091F}-\u{10939} - \u{1093F} - \u{10980}-\u{109B7} - \u{109BC}-\u{109CF} - \u{109D2}-\u{10A03} - \u{10A05}-\u{10A06} - \u{10A0C}-\u{10A13} - \u{10A15}-\u{10A17} - \u{10A19}-\u{10A35} - \u{10A38}-\u{10A3A} - \u{10A3F}-\u{10A48} - \u{10A50}-\u{10A58} - \u{10A60}-\u{10A9F} - \u{10AC0}-\u{10AE6} - \u{10AEB}-\u{10AF6} - \u{10B00}-\u{10B35} - \u{10B39}-\u{10B55} - \u{10B58}-\u{10B72} - \u{10B78}-\u{10B91} - \u{10B99}-\u{10B9C} - \u{10BA9}-\u{10BAF} - \u{10C00}-\u{10C48} - \u{10C80}-\u{10CB2} - \u{10CC0}-\u{10CF2} - \u{10CFA}-\u{10D27} - \u{10D30}-\u{10D39} - \u{10E60}-\u{10E7E} - \u{10E80}-\u{10EA9} - \u{10EAB}-\u{10EAD} - \u{10EB0}-\u{10EB1} - \u{10EFD}-\u{10F27} - \u{10F30}-\u{10F59} - \u{10F70}-\u{10F89} - \u{10FB0}-\u{10FCB} - \u{10FE0}-\u{10FF6} - \u{11000}-\u{1104D} - \u{11052}-\u{11075} - \u{1107F}-\u{110C2} - \u{110CD} - \u{110D0}-\u{110E8} - \u{110F0}-\u{110F9} - \u{11100}-\u{11134} - \u{11136}-\u{11147} - \u{11150}-\u{11176} - \u{11180}-\u{111DF} - \u{111E1}-\u{111F4} - \u{11200}-\u{11211} - \u{11213}-\u{11241} - \u{11280}-\u{11286} - \u{11288} - \u{1128A}-\u{1128D} - \u{1128F}-\u{1129D} - \u{1129F}-\u{112A9} - \u{112B0}-\u{112EA} - \u{112F0}-\u{112F9} - \u{11300}-\u{11303} - \u{11305}-\u{1130C} - \u{1130F}-\u{11310} - \u{11313}-\u{11328} - \u{1132A}-\u{11330} - \u{11332}-\u{11333} - \u{11335}-\u{11339} - \u{1133B}-\u{11344} - \u{11347}-\u{11348} - \u{1134B}-\u{1134D} - \u{11350} - \u{11357} - \u{1135D}-\u{11363} - \u{11366}-\u{1136C} - \u{11370}-\u{11374} - \u{11400}-\u{1145B} - \u{1145D}-\u{11461} - \u{11480}-\u{114C7} - \u{114D0}-\u{114D9} - \u{11580}-\u{115B5} - \u{115B8}-\u{115DD} - \u{11600}-\u{11644} - \u{11650}-\u{11659} - \u{11660}-\u{1166C} - \u{11680}-\u{116B9} - \u{116C0}-\u{116C9} - \u{11700}-\u{1171A} - \u{1171D}-\u{1172B} - \u{11730}-\u{11746} - \u{11800}-\u{1183B} - \u{118A0}-\u{118F2} - \u{118FF}-\u{11906} - \u{11909} - \u{1190C}-\u{11913} - \u{11915}-\u{11916} - \u{11918}-\u{11935} - \u{11937}-\u{11938} - \u{1193B}-\u{11946} - \u{11950}-\u{11959} - \u{119A0}-\u{119A7} - \u{119AA}-\u{119D7} - \u{119DA}-\u{119E4} - \u{11A00}-\u{11A47} - \u{11A50}-\u{11AA2} - \u{11AB0}-\u{11AF8} - \u{11B00}-\u{11B09} - \u{11C00}-\u{11C08} - \u{11C0A}-\u{11C36} - \u{11C38}-\u{11C45} - \u{11C50}-\u{11C6C} - \u{11C70}-\u{11C8F} - \u{11C92}-\u{11CA7} - \u{11CA9}-\u{11CB6} - \u{11D00}-\u{11D06} - \u{11D08}-\u{11D09} - \u{11D0B}-\u{11D36} - \u{11D3A} - \u{11D3C}-\u{11D3D} - \u{11D3F}-\u{11D47} - \u{11D50}-\u{11D59} - \u{11D60}-\u{11D65} - \u{11D67}-\u{11D68} - \u{11D6A}-\u{11D8E} - \u{11D90}-\u{11D91} - \u{11D93}-\u{11D98} - \u{11DA0}-\u{11DA9} - \u{11EE0}-\u{11EF8} - \u{11F00}-\u{11F10} - \u{11F12}-\u{11F3A} - \u{11F3E}-\u{11F59} - \u{11FB0} - \u{11FC0}-\u{11FF1} - \u{11FFF}-\u{12399} - \u{12400}-\u{1246E} - \u{12470}-\u{12474} - \u{12480}-\u{12543} - \u{12F90}-\u{12FF2} - \u{13000}-\u{13455} - \u{14400}-\u{14646} - \u{16800}-\u{16A38} - \u{16A40}-\u{16A5E} - \u{16A60}-\u{16A69} - \u{16A6E}-\u{16ABE} - \u{16AC0}-\u{16AC9} - \u{16AD0}-\u{16AED} - \u{16AF0}-\u{16AF5} - \u{16B00}-\u{16B45} - \u{16B50}-\u{16B59} - \u{16B5B}-\u{16B61} - \u{16B63}-\u{16B77} - \u{16B7D}-\u{16B8F} - \u{16E40}-\u{16E9A} - \u{16F00}-\u{16F4A} - \u{16F4F}-\u{16F87} - \u{16F8F}-\u{16F9F} - \u{1BC00}-\u{1BC6A} - \u{1BC70}-\u{1BC7C} - \u{1BC80}-\u{1BC88} - \u{1BC90}-\u{1BC99} - \u{1BC9C}-\u{1BCA3} - \u{1CF00}-\u{1CF2D} - \u{1CF30}-\u{1CF46} - \u{1CF50}-\u{1CFC3} - \u{1D000}-\u{1D0F5} - \u{1D100}-\u{1D126} - \u{1D129}-\u{1D1EA} - \u{1D200}-\u{1D245} - \u{1D2C0}-\u{1D2D3} - \u{1D2E0}-\u{1D2F3} - \u{1D300}-\u{1D356} - \u{1D360}-\u{1D378} - \u{1D400}-\u{1D454} - \u{1D456}-\u{1D49C} - \u{1D49E}-\u{1D49F} - \u{1D4A2} - \u{1D4A5}-\u{1D4A6} - \u{1D4A9}-\u{1D4AC} - \u{1D4AE}-\u{1D4B9} - \u{1D4BB} - \u{1D4BD}-\u{1D4C3} - \u{1D4C5}-\u{1D505} - \u{1D507}-\u{1D50A} - \u{1D50D}-\u{1D514} - \u{1D516}-\u{1D51C} - \u{1D51E}-\u{1D539} - \u{1D53B}-\u{1D53E} - \u{1D540}-\u{1D544} - \u{1D546} - \u{1D54A}-\u{1D550} - \u{1D552}-\u{1D6A5} - \u{1D6A8}-\u{1D7CB} - \u{1D7CE}-\u{1DA8B} - \u{1DA9B}-\u{1DA9F} - \u{1DAA1}-\u{1DAAF} - \u{1DF00}-\u{1DF1E} - \u{1DF25}-\u{1DF2A} - \u{1E000}-\u{1E006} - \u{1E008}-\u{1E018} - \u{1E01B}-\u{1E021} - \u{1E023}-\u{1E024} - \u{1E026}-\u{1E02A} - \u{1E030}-\u{1E06D} - \u{1E08F} - \u{1E100}-\u{1E12C} - \u{1E130}-\u{1E13D} - \u{1E140}-\u{1E149} - \u{1E14E}-\u{1E14F} - \u{1E290}-\u{1E2AE} - \u{1E2C0}-\u{1E2F9} - \u{1E2FF} - \u{1E4D0}-\u{1E4F9} - \u{1E7E0}-\u{1E7E6} - \u{1E7E8}-\u{1E7EB} - \u{1E7ED}-\u{1E7EE} - \u{1E7F0}-\u{1E7FE} - \u{1E800}-\u{1E8C4} - \u{1E8C7}-\u{1E8D6} - \u{1E900}-\u{1E94B} - \u{1E950}-\u{1E959} - \u{1E95E}-\u{1E95F} - \u{1EC71}-\u{1ECB4} - \u{1ED01}-\u{1ED3D} - \u{1EE00}-\u{1EE03} - \u{1EE05}-\u{1EE1F} - \u{1EE21}-\u{1EE22} - \u{1EE24} - \u{1EE27} - \u{1EE29}-\u{1EE32} - \u{1EE34}-\u{1EE37} - \u{1EE39} - \u{1EE3B} - \u{1EE42} - \u{1EE47} - \u{1EE49} - \u{1EE4B} - \u{1EE4D}-\u{1EE4F} - \u{1EE51}-\u{1EE52} - \u{1EE54} - \u{1EE57} - \u{1EE59} - \u{1EE5B} - \u{1EE5D} - \u{1EE5F} - \u{1EE61}-\u{1EE62} - \u{1EE64} - \u{1EE67}-\u{1EE6A} - \u{1EE6C}-\u{1EE72} - \u{1EE74}-\u{1EE77} - \u{1EE79}-\u{1EE7C} - \u{1EE7E} - \u{1EE80}-\u{1EE89} - \u{1EE8B}-\u{1EE9B} - \u{1EEA1}-\u{1EEA3} - \u{1EEA5}-\u{1EEA9} - \u{1EEAB}-\u{1EEBB} - \u{1EEF0}-\u{1EEF1} - \u{1F000}-\u{1F003} - \u{1F005}-\u{1F02B} - \u{1F030}-\u{1F093} - \u{1F0A0}-\u{1F0AE} - \u{1F0B1}-\u{1F0BF} - \u{1F0C1}-\u{1F0CE} - \u{1F0D1}-\u{1F0F5} - \u{1F10B}-\u{1F10F} - \u{1F12E}-\u{1F12F} - \u{1F16A}-\u{1F16F} - \u{1F1AD} - \u{1F1E6}-\u{1F1FF} - \u{1F321}-\u{1F32C} - \u{1F336} - \u{1F37D} - \u{1F394}-\u{1F39F} - \u{1F3CB}-\u{1F3CE} - \u{1F3D4}-\u{1F3DF} - \u{1F3F1}-\u{1F3F3} - \u{1F3F5}-\u{1F3F7} - \u{1F43F} - \u{1F441} - \u{1F4FD}-\u{1F4FE} - \u{1F53E}-\u{1F54A} - \u{1F54F} - \u{1F568}-\u{1F579} - \u{1F57B}-\u{1F594} - \u{1F597}-\u{1F5A3} - \u{1F5A5}-\u{1F5FA} - \u{1F650}-\u{1F67F} - \u{1F6C6}-\u{1F6CB} - \u{1F6CD}-\u{1F6CF} - \u{1F6D3}-\u{1F6D4} - \u{1F6E0}-\u{1F6EA} - \u{1F6F0}-\u{1F6F3} - \u{1F700}-\u{1F776} - \u{1F77B}-\u{1F7D9} - \u{1F800}-\u{1F80B} - \u{1F810}-\u{1F847} - \u{1F850}-\u{1F859} - \u{1F860}-\u{1F887} - \u{1F890}-\u{1F8AD} - \u{1F8B0}-\u{1F8B1} - \u{1F900}-\u{1F90B} - \u{1F93B} - \u{1F946} - \u{1FA00}-\u{1FA53} - \u{1FA60}-\u{1FA6D} - \u{1FB00}-\u{1FB92} - \u{1FB94}-\u{1FBCA} - \u{1FBF0}-\u{1FBF9} - \u{E0001} - \u{E0020}-\u{E007F} - ).join }]/ -end diff --git a/lib/reline/version.rb b/lib/reline/version.rb deleted file mode 100644 index 67a3d694bd..0000000000 --- a/lib/reline/version.rb +++ /dev/null @@ -1,3 +0,0 @@ -module Reline - VERSION = '0.3.2' -end diff --git a/lib/reline/windows.rb b/lib/reline/windows.rb deleted file mode 100644 index 7ea2a00f63..0000000000 --- a/lib/reline/windows.rb +++ /dev/null @@ -1,501 +0,0 @@ -require 'fiddle/import' - -class Reline::Windows - def self.encoding - Encoding::UTF_8 - end - - def self.win? - true - end - - def self.win_legacy_console? - @@legacy_console - end - - def self.set_default_key_bindings(config) - { - [224, 72] => :ed_prev_history, # ↑ - [224, 80] => :ed_next_history, # ↓ - [224, 77] => :ed_next_char, # → - [224, 75] => :ed_prev_char, # ← - [224, 83] => :key_delete, # Del - [224, 71] => :ed_move_to_beg, # Home - [224, 79] => :ed_move_to_end, # End - [ 0, 41] => :ed_unassigned, # input method on/off - [ 0, 72] => :ed_prev_history, # ↑ - [ 0, 80] => :ed_next_history, # ↓ - [ 0, 77] => :ed_next_char, # → - [ 0, 75] => :ed_prev_char, # ← - [ 0, 83] => :key_delete, # Del - [ 0, 71] => :ed_move_to_beg, # Home - [ 0, 79] => :ed_move_to_end # End - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - config.add_default_key_binding_by_keymap(:vi_insert, key, func) - config.add_default_key_binding_by_keymap(:vi_command, key, func) - end - - { - [27, 32] => :em_set_mark, # M-<space> - [24, 24] => :em_exchange_mark, # C-x C-x - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - end - - # Emulate ANSI key sequence. - { - [27, 91, 90] => :completion_journey_up, # S-Tab - }.each_pair do |key, func| - config.add_default_key_binding_by_keymap(:emacs, key, func) - config.add_default_key_binding_by_keymap(:vi_insert, key, func) - end - end - - if defined? JRUBY_VERSION - require 'win32api' - else - class Win32API - DLL = {} - TYPEMAP = {"0" => Fiddle::TYPE_VOID, "S" => Fiddle::TYPE_VOIDP, "I" => Fiddle::TYPE_LONG} - POINTER_TYPE = Fiddle::SIZEOF_VOIDP == Fiddle::SIZEOF_LONG_LONG ? 'q*' : 'l!*' - - WIN32_TYPES = "VPpNnLlIi" - DL_TYPES = "0SSI" - - def initialize(dllname, func, import, export = "0", calltype = :stdcall) - @proto = [import].join.tr(WIN32_TYPES, DL_TYPES).sub(/^(.)0*$/, '\1') - import = @proto.chars.map {|win_type| TYPEMAP[win_type.tr(WIN32_TYPES, DL_TYPES)]} - export = TYPEMAP[export.tr(WIN32_TYPES, DL_TYPES)] - calltype = Fiddle::Importer.const_get(:CALL_TYPE_TO_ABI)[calltype] - - handle = DLL[dllname] ||= - begin - Fiddle.dlopen(dllname) - rescue Fiddle::DLError - raise unless File.extname(dllname).empty? - Fiddle.dlopen(dllname + ".dll") - end - - @func = Fiddle::Function.new(handle[func], import, export, calltype) - rescue Fiddle::DLError => e - raise LoadError, e.message, e.backtrace - end - - def call(*args) - import = @proto.split("") - args.each_with_index do |x, i| - args[i], = [x == 0 ? nil : x].pack("p").unpack(POINTER_TYPE) if import[i] == "S" - args[i], = [x].pack("I").unpack("i") if import[i] == "I" - end - ret, = @func.call(*args) - return ret || 0 - end - end - end - - VK_RETURN = 0x0D - VK_MENU = 0x12 # ALT key - VK_LMENU = 0xA4 - VK_CONTROL = 0x11 - VK_SHIFT = 0x10 - VK_DIVIDE = 0x6F - - KEY_EVENT = 0x01 - WINDOW_BUFFER_SIZE_EVENT = 0x04 - - CAPSLOCK_ON = 0x0080 - ENHANCED_KEY = 0x0100 - LEFT_ALT_PRESSED = 0x0002 - LEFT_CTRL_PRESSED = 0x0008 - NUMLOCK_ON = 0x0020 - RIGHT_ALT_PRESSED = 0x0001 - RIGHT_CTRL_PRESSED = 0x0004 - SCROLLLOCK_ON = 0x0040 - SHIFT_PRESSED = 0x0010 - - VK_TAB = 0x09 - VK_END = 0x23 - VK_HOME = 0x24 - VK_LEFT = 0x25 - VK_UP = 0x26 - VK_RIGHT = 0x27 - VK_DOWN = 0x28 - VK_DELETE = 0x2E - - STD_INPUT_HANDLE = -10 - STD_OUTPUT_HANDLE = -11 - FILE_TYPE_PIPE = 0x0003 - FILE_NAME_INFO = 2 - @@getwch = Win32API.new('msvcrt', '_getwch', [], 'I') - @@kbhit = Win32API.new('msvcrt', '_kbhit', [], 'I') - @@GetKeyState = Win32API.new('user32', 'GetKeyState', ['L'], 'L') - @@GetConsoleScreenBufferInfo = Win32API.new('kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L') - @@SetConsoleCursorPosition = Win32API.new('kernel32', 'SetConsoleCursorPosition', ['L', 'L'], 'L') - @@GetStdHandle = Win32API.new('kernel32', 'GetStdHandle', ['L'], 'L') - @@FillConsoleOutputCharacter = Win32API.new('kernel32', 'FillConsoleOutputCharacter', ['L', 'L', 'L', 'L', 'P'], 'L') - @@ScrollConsoleScreenBuffer = Win32API.new('kernel32', 'ScrollConsoleScreenBuffer', ['L', 'P', 'P', 'L', 'P'], 'L') - @@hConsoleHandle = @@GetStdHandle.call(STD_OUTPUT_HANDLE) - @@hConsoleInputHandle = @@GetStdHandle.call(STD_INPUT_HANDLE) - @@GetNumberOfConsoleInputEvents = Win32API.new('kernel32', 'GetNumberOfConsoleInputEvents', ['L', 'P'], 'L') - @@ReadConsoleInputW = Win32API.new('kernel32', 'ReadConsoleInputW', ['L', 'P', 'L', 'P'], 'L') - @@GetFileType = Win32API.new('kernel32', 'GetFileType', ['L'], 'L') - @@GetFileInformationByHandleEx = Win32API.new('kernel32', 'GetFileInformationByHandleEx', ['L', 'I', 'P', 'L'], 'I') - @@FillConsoleOutputAttribute = Win32API.new('kernel32', 'FillConsoleOutputAttribute', ['L', 'L', 'L', 'L', 'P'], 'L') - @@SetConsoleCursorInfo = Win32API.new('kernel32', 'SetConsoleCursorInfo', ['L', 'P'], 'L') - - @@GetConsoleMode = Win32API.new('kernel32', 'GetConsoleMode', ['L', 'P'], 'L') - @@SetConsoleMode = Win32API.new('kernel32', 'SetConsoleMode', ['L', 'L'], 'L') - @@WaitForSingleObject = Win32API.new('kernel32', 'WaitForSingleObject', ['L', 'L'], 'L') - ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4 - - private_class_method def self.getconsolemode - mode = "\000\000\000\000" - @@GetConsoleMode.call(@@hConsoleHandle, mode) - mode.unpack1('L') - end - - private_class_method def self.setconsolemode(mode) - @@SetConsoleMode.call(@@hConsoleHandle, mode) - end - - @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0) - #if @@legacy_console - # setconsolemode(getconsolemode() | ENABLE_VIRTUAL_TERMINAL_PROCESSING) - # @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0) - #end - - @@input_buf = [] - @@output_buf = [] - - @@output = STDOUT - - def self.msys_tty?(io = @@hConsoleInputHandle) - # check if fd is a pipe - if @@GetFileType.call(io) != FILE_TYPE_PIPE - return false - end - - bufsize = 1024 - p_buffer = "\0" * bufsize - res = @@GetFileInformationByHandleEx.call(io, FILE_NAME_INFO, p_buffer, bufsize - 2) - return false if res == 0 - - # get pipe name: p_buffer layout is: - # struct _FILE_NAME_INFO { - # DWORD FileNameLength; - # WCHAR FileName[1]; - # } FILE_NAME_INFO - len = p_buffer[0, 4].unpack1("L") - name = p_buffer[4, len].encode(Encoding::UTF_8, Encoding::UTF_16LE, invalid: :replace) - - # Check if this could be a MSYS2 pty pipe ('\msys-XXXX-ptyN-XX') - # or a cygwin pty pipe ('\cygwin-XXXX-ptyN-XX') - name =~ /(msys-|cygwin-).*-pty/ ? true : false - end - - KEY_MAP = [ - # It's treated as Meta+Enter on Windows. - [ { control_keys: :CTRL, virtual_key_code: 0x0D }, "\e\r".bytes ], - [ { control_keys: :SHIFT, virtual_key_code: 0x0D }, "\e\r".bytes ], - - # It's treated as Meta+Space on Windows. - [ { control_keys: :CTRL, char_code: 0x20 }, "\e ".bytes ], - - # Emulate getwch() key sequences. - [ { control_keys: [], virtual_key_code: VK_UP }, [0, 72] ], - [ { control_keys: [], virtual_key_code: VK_DOWN }, [0, 80] ], - [ { control_keys: [], virtual_key_code: VK_RIGHT }, [0, 77] ], - [ { control_keys: [], virtual_key_code: VK_LEFT }, [0, 75] ], - [ { control_keys: [], virtual_key_code: VK_DELETE }, [0, 83] ], - [ { control_keys: [], virtual_key_code: VK_HOME }, [0, 71] ], - [ { control_keys: [], virtual_key_code: VK_END }, [0, 79] ], - - # Emulate ANSI key sequence. - [ { control_keys: :SHIFT, virtual_key_code: VK_TAB }, [27, 91, 90] ], - ] - - @@hsg = nil - - def self.process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state) - - # high-surrogate - if 0xD800 <= char_code and char_code <= 0xDBFF - @@hsg = char_code - return - end - # low-surrogate - if 0xDC00 <= char_code and char_code <= 0xDFFF - if @@hsg - char_code = 0x10000 + (@@hsg - 0xD800) * 0x400 + char_code - 0xDC00 - @@hsg = nil - else - # no high-surrogate. ignored. - return - end - else - # ignore high-surrogate without low-surrogate if there - @@hsg = nil - end - - key = KeyEventRecord.new(virtual_key_code, char_code, control_key_state) - - match = KEY_MAP.find { |args,| key.matches?(**args) } - unless match.nil? - @@output_buf.concat(match.last) - return - end - - # no char, only control keys - return if key.char_code == 0 and key.control_keys.any? - - @@output_buf.push("\e".ord) if key.control_keys.include?(:ALT) and !key.control_keys.include?(:CTRL) - - @@output_buf.concat(key.char.bytes) - end - - def self.check_input_event - num_of_events = 0.chr * 8 - while @@output_buf.empty? - Reline.core.line_editor.resize - if @@WaitForSingleObject.(@@hConsoleInputHandle, 100) != 0 # max 0.1 sec - # prevent for background consolemode change - @@legacy_console = (getconsolemode() & ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0) - next - end - next if @@GetNumberOfConsoleInputEvents.(@@hConsoleInputHandle, num_of_events) == 0 or num_of_events.unpack1('L') == 0 - input_records = 0.chr * 20 * 80 - read_event = 0.chr * 4 - if @@ReadConsoleInputW.(@@hConsoleInputHandle, input_records, 80, read_event) != 0 - read_events = read_event.unpack1('L') - 0.upto(read_events) do |idx| - input_record = input_records[idx * 20, 20] - event = input_record[0, 2].unpack1('s*') - case event - when WINDOW_BUFFER_SIZE_EVENT - @@winch_handler.() - when KEY_EVENT - key_down = input_record[4, 4].unpack1('l*') - repeat_count = input_record[8, 2].unpack1('s*') - virtual_key_code = input_record[10, 2].unpack1('s*') - virtual_scan_code = input_record[12, 2].unpack1('s*') - char_code = input_record[14, 2].unpack1('S*') - control_key_state = input_record[16, 2].unpack1('S*') - is_key_down = key_down.zero? ? false : true - if is_key_down - process_key_event(repeat_count, virtual_key_code, virtual_scan_code, char_code, control_key_state) - end - end - end - end - end - end - - def self.with_raw_input - yield - end - - def self.getc - check_input_event - @@output_buf.shift - end - - def self.ungetc(c) - @@output_buf.unshift(c) - end - - def self.in_pasting? - not self.empty_buffer? - end - - def self.empty_buffer? - if not @@output_buf.empty? - false - elsif @@kbhit.call == 0 - true - else - false - end - end - - def self.get_console_screen_buffer_info - # CONSOLE_SCREEN_BUFFER_INFO - # [ 0,2] dwSize.X - # [ 2,2] dwSize.Y - # [ 4,2] dwCursorPositions.X - # [ 6,2] dwCursorPositions.Y - # [ 8,2] wAttributes - # [10,2] srWindow.Left - # [12,2] srWindow.Top - # [14,2] srWindow.Right - # [16,2] srWindow.Bottom - # [18,2] dwMaximumWindowSize.X - # [20,2] dwMaximumWindowSize.Y - csbi = 0.chr * 22 - return if @@GetConsoleScreenBufferInfo.call(@@hConsoleHandle, csbi) == 0 - csbi - end - - def self.get_screen_size - unless csbi = get_console_screen_buffer_info - return [1, 1] - end - csbi[0, 4].unpack('SS').reverse - end - - def self.cursor_pos - unless csbi = get_console_screen_buffer_info - return Reline::CursorPos.new(0, 0) - end - x = csbi[4, 2].unpack1('s') - y = csbi[6, 2].unpack1('s') - Reline::CursorPos.new(x, y) - end - - def self.move_cursor_column(val) - @@SetConsoleCursorPosition.call(@@hConsoleHandle, cursor_pos.y * 65536 + val) - end - - def self.move_cursor_up(val) - if val > 0 - y = cursor_pos.y - val - y = 0 if y < 0 - @@SetConsoleCursorPosition.call(@@hConsoleHandle, y * 65536 + cursor_pos.x) - elsif val < 0 - move_cursor_down(-val) - end - end - - def self.move_cursor_down(val) - if val > 0 - return unless csbi = get_console_screen_buffer_info - screen_height = get_screen_size.first - y = cursor_pos.y + val - y = screen_height - 1 if y > (screen_height - 1) - @@SetConsoleCursorPosition.call(@@hConsoleHandle, (cursor_pos.y + val) * 65536 + cursor_pos.x) - elsif val < 0 - move_cursor_up(-val) - end - end - - def self.erase_after_cursor - return unless csbi = get_console_screen_buffer_info - attributes = csbi[8, 2].unpack1('S') - cursor = csbi[4, 4].unpack1('L') - written = 0.chr * 4 - @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, get_screen_size.last - cursor_pos.x, cursor, written) - @@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, get_screen_size.last - cursor_pos.x, cursor, written) - end - - def self.scroll_down(val) - return if val < 0 - return unless csbi = get_console_screen_buffer_info - buffer_width, buffer_lines, x, y, attributes, window_left, window_top, window_bottom = csbi.unpack('ssssSssx2s') - screen_height = window_bottom - window_top + 1 - val = screen_height if val > screen_height - - if @@legacy_console || window_left != 0 - # unless ENABLE_VIRTUAL_TERMINAL, - # if srWindow.Left != 0 then it's conhost.exe hosted console - # and puts "\n" causes horizontal scroll. its glitch. - # FYI irb write from culumn 1, so this gives no gain. - scroll_rectangle = [0, val, buffer_width, buffer_lines - val].pack('s4') - destination_origin = 0 # y * 65536 + x - fill = [' '.ord, attributes].pack('SS') - @@ScrollConsoleScreenBuffer.call(@@hConsoleHandle, scroll_rectangle, nil, destination_origin, fill) - else - origin_x = x + 1 - origin_y = y - window_top + 1 - @@output.write [ - (origin_y != screen_height) ? "\e[#{screen_height};H" : nil, - "\n" * val, - (origin_y != screen_height or !x.zero?) ? "\e[#{origin_y};#{origin_x}H" : nil - ].join - end - end - - def self.clear_screen - if @@legacy_console - return unless csbi = get_console_screen_buffer_info - buffer_width, _buffer_lines, attributes, window_top, window_bottom = csbi.unpack('ss@8S@12sx2s') - fill_length = buffer_width * (window_bottom - window_top + 1) - screen_topleft = window_top * 65536 - written = 0.chr * 4 - @@FillConsoleOutputCharacter.call(@@hConsoleHandle, 0x20, fill_length, screen_topleft, written) - @@FillConsoleOutputAttribute.call(@@hConsoleHandle, attributes, fill_length, screen_topleft, written) - @@SetConsoleCursorPosition.call(@@hConsoleHandle, screen_topleft) - else - @@output.write "\e[2J" "\e[H" - end - end - - def self.set_screen_size(rows, columns) - raise NotImplementedError - end - - def self.hide_cursor - size = 100 - visible = 0 # 0 means false - cursor_info = [size, visible].pack('Li') - @@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info) - end - - def self.show_cursor - size = 100 - visible = 1 # 1 means true - cursor_info = [size, visible].pack('Li') - @@SetConsoleCursorInfo.call(@@hConsoleHandle, cursor_info) - end - - def self.set_winch_handler(&handler) - @@winch_handler = handler - end - - def self.prep - # do nothing - nil - end - - def self.deprep(otio) - # do nothing - end - - class KeyEventRecord - - attr_reader :virtual_key_code, :char_code, :control_key_state, :control_keys - - def initialize(virtual_key_code, char_code, control_key_state) - @virtual_key_code = virtual_key_code - @char_code = char_code - @control_key_state = control_key_state - @enhanced = control_key_state & ENHANCED_KEY != 0 - - (@control_keys = []).tap do |control_keys| - # symbols must be sorted to make comparison is easier later on - control_keys << :ALT if control_key_state & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0 - control_keys << :CTRL if control_key_state & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0 - control_keys << :SHIFT if control_key_state & SHIFT_PRESSED != 0 - end.freeze - end - - def char - @char_code.chr(Encoding::UTF_8) - end - - def enhanced? - @enhanced - end - - # Verifies if the arguments match with this key event. - # Nil arguments are ignored, but at least one must be passed as non-nil. - # To verify that no control keys were pressed, pass an empty array: `control_keys: []`. - def matches?(control_keys: nil, virtual_key_code: nil, char_code: nil) - raise ArgumentError, 'No argument was passed to match key event' if control_keys.nil? && virtual_key_code.nil? && char_code.nil? - - (control_keys.nil? || [*control_keys].sort == @control_keys) && - (virtual_key_code.nil? || @virtual_key_code == virtual_key_code) && - (char_code.nil? || char_code == @char_code) - end - - end -end |
