Files
ruby-windows-scripts/ruby/lib/ruby/site_ruby/2.1.0/rbreadline.rb
2015-02-17 20:53:15 -05:00

8864 lines
268 KiB
Ruby

# encoding: US-ASCII
#
# rbreadline.rb -- a general facility for reading lines of input
# with emacs style editing and completion.
#
# Inspired by GNU Readline, translation to Ruby
# Copyright (C) 2009 by Park Heesob phasis@gmail.com
#
class Fixnum
def ord; self; end
end
module RbReadline
require 'etc'
RL_LIBRARY_VERSION = "5.2"
RL_READLINE_VERSION = 0x0502
RB_READLINE_VERSION = "0.5.0"
EOF = "\xFF"
ESC = "\C-["
PAGE = "\C-L"
SPACE = "\x20"
RETURN = "\C-M"
ABORT_CHAR = "\C-G"
TAB = "\t"
RUBOUT = "\x7f"
NEWLINE = "\n"
DEFAULT_BUFFER_SIZE = 256
DEFAULT_MAX_KILLS = 10
MB_FIND_NONZERO = 1
MB_FIND_ANY = 0
MB_LEN_MAX = 4
DEFAULT_INPUTRC = "~/.inputrc"
SYS_INPUTRC = "/etc/inputrc"
UpCase = 1
DownCase = 2
CapCase = 3
# Possible history errors passed to hist_error.
EVENT_NOT_FOUND = 0
BAD_WORD_SPEC = 1
SUBST_FAILED = 2
BAD_MODIFIER = 3
NO_PREV_SUBST = 4
# Possible definitions for history starting point specification.
ANCHORED_SEARCH = 1
NON_ANCHORED_SEARCH = 0
# Possible definitions for what style of writing the history file we want.
HISTORY_APPEND = 0
HISTORY_OVERWRITE = 1
# Input error; can be returned by (*rl_getc_function) if readline is reading
# a top-level command (RL_ISSTATE (RL_STATE_READCMD)).
READERR = 0xFE.chr
# Definitions available for use by readline clients.
RL_PROMPT_START_IGNORE = 1.chr
RL_PROMPT_END_IGNORE = 2.chr
# Possible values for do_replace argument to rl_filename_quoting_function,
# called by rl_complete_internal.
NO_MATCH = 0
SINGLE_MATCH = 1
MULT_MATCH = 2
# Callback data for reading numeric arguments
NUM_SAWMINUS = 0x01
NUM_SAWDIGITS = 0x02
NUM_READONE = 0x04
# A context for reading key sequences longer than a single character when
# using the callback interface.
KSEQ_DISPATCHED = 0x01
KSEQ_SUBSEQ = 0x02
KSEQ_RECURSIVE = 0x04
# Possible state values for rl_readline_state
RL_STATE_NONE = 0x000000 # no state before first call
RL_STATE_INITIALIZING = 0x000001 # initializing
RL_STATE_INITIALIZED = 0x000002 # initialization done
RL_STATE_TERMPREPPED = 0x000004 # terminal is prepped
RL_STATE_READCMD = 0x000008 # reading a command key
RL_STATE_METANEXT = 0x000010 # reading input after ESC
RL_STATE_DISPATCHING = 0x000020 # dispatching to a command
RL_STATE_MOREINPUT = 0x000040 # reading more input in a command function
RL_STATE_ISEARCH = 0x000080 # doing incremental search
RL_STATE_NSEARCH = 0x000100 # doing non-inc search
RL_STATE_SEARCH = 0x000200 # doing a history search
RL_STATE_NUMERICARG = 0x000400 # reading numeric argument
RL_STATE_MACROINPUT = 0x000800 # getting input from a macro
RL_STATE_MACRODEF = 0x001000 # defining keyboard macro
RL_STATE_OVERWRITE = 0x002000 # overwrite mode
RL_STATE_COMPLETING = 0x004000 # doing completion
RL_STATE_SIGHANDLER = 0x008000 # in readline sighandler
RL_STATE_UNDOING = 0x010000 # doing an undo
RL_STATE_INPUTPENDING = 0x020000 # rl_execute_next called
RL_STATE_TTYCSAVED = 0x040000 # tty special chars saved
RL_STATE_CALLBACK = 0x080000 # using the callback interface
RL_STATE_VIMOTION = 0x100000 # reading vi motion arg
RL_STATE_MULTIKEY = 0x200000 # reading multiple-key command
RL_STATE_VICMDONCE = 0x400000 # entered vi command mode at least once
RL_STATE_DONE = 0x800000 # done accepted line
NO_BELL = 0
AUDIBLE_BELL = 1
VISIBLE_BELL = 2
# The actions that undo knows how to undo. Notice that UNDO_DELETE means
# to insert some text, and UNDO_INSERT means to delete some text. I.e.,
# the code tells undo what to undo, not how to undo it.
UNDO_DELETE, UNDO_INSERT, UNDO_BEGIN, UNDO_END = 0,1,2,3
# Definitions used when searching the line for characters.
# NOTE: it is necessary that opposite directions are inverses
FTO = 1 # forward to
BTO = -1 # backward to
FFIND = 2 # forward find
BFIND = -2 # backward find
# Possible values for the found_quote flags word used by the completion
# functions. It says what kind of (shell-like) quoting we found anywhere
# in the line.
RL_QF_SINGLE_QUOTE = 0x01
RL_QF_DOUBLE_QUOTE = 0x02
RL_QF_BACKSLASH = 0x04
RL_QF_OTHER_QUOTE = 0x08
KEYMAP_SIZE = 257
ANYOTHERKEY = KEYMAP_SIZE-1
@hConsoleHandle = nil
@MessageBeep = nil
RL_IM_INSERT = 1
RL_IM_OVERWRITE = 0
RL_IM_DEFAULT = RL_IM_INSERT
@no_mode = -1
@vi_mode = 0
@emacs_mode = 1
ISFUNC = 0
ISKMAP = 1
ISMACR = 2
HISTORY_WORD_DELIMITERS = " \t\n;&()|<>"
HISTORY_QUOTE_CHARACTERS = "\"'`"
RL_SEARCH_ISEARCH = 0x01 # incremental search
RL_SEARCH_NSEARCH = 0x02 # non-incremental search
RL_SEARCH_CSEARCH = 0x04 # intra-line char search
# search flags
SF_REVERSE = 0x01
SF_FOUND = 0x02
SF_FAILED = 0x04
@slashify_in_quotes = "\\`\"$"
@sigint_proc = nil
@sigint_blocked = false
@rl_prep_term_function = :rl_prep_terminal
@rl_deprep_term_function = :rl_deprep_terminal
@_rl_history_saved_point = -1
@rl_max_kills = DEFAULT_MAX_KILLS
@rl_kill_ring = nil
@rl_kill_index = 0
@rl_kill_ring_length = 0
@pending_bytes = ''
@stored_count = 0
@_rl_isearch_terminators = nil
@_rl_iscxt = nil
@last_isearch_string = nil
@last_isearch_string_len = 0
@default_isearch_terminators = "\033\012"
@_rl_history_preserve_point = false
@terminal_prepped = false
@otio = nil
@msg_saved_prompt = false
@_rl_nscxt = nil
@noninc_search_string = nil
@noninc_history_pos = 0
@prev_line_found = nil
@_rl_tty_chars = Struct.new(:t_eol,:t_eol2,:t_erase,:t_werase,:t_kill,:t_reprint,:t_intr,:t_eof,
:t_quit,:t_susp,:t_dsusp,:t_start,:t_stop,:t_lnext,:t_flush,:t_status).new
@_rl_last_tty_chars = nil
@_keyboard_input_timeout = 0.001
# Variables exported by this file.
# The character that represents the start of a history expansion
# request. This is usually `!'.
@history_expansion_char = "!"
# The character that invokes word substitution if found at the start of
# a line. This is usually `^'.
@history_subst_char = "^"
# During tokenization, if this character is seen as the first character
# of a word, then it, and all subsequent characters upto a newline are
# ignored. For a Bourne shell, this should be '#'. Bash special cases
# the interactive comment character to not be a comment delimiter.
@history_comment_char = 0.chr
# The list of characters which inhibit the expansion of text if found
# immediately following history_expansion_char.
@history_no_expand_chars = " \t\n\r="
# If set to a non-zero value, single quotes inhibit history expansion.
# The default is 0.
@history_quotes_inhibit_expansion = 0
# Used to split words by history_tokenize_internal.
@history_word_delimiters = HISTORY_WORD_DELIMITERS
# If set, this points to a function that is called to verify that a
# particular history expansion should be performed.
@history_inhibit_expansion_function = nil
@rl_event_hook = nil
# The visible cursor position. If you print some text, adjust this.
# NOTE: _rl_last_c_pos is used as a buffer index when not in a locale
# supporting multibyte characters, and an absolute cursor position when
# in such a locale. This is an artifact of the donated multibyte support.
# Care must be taken when modifying its value.
@_rl_last_c_pos = 0
@_rl_last_v_pos = 0
@cpos_adjusted = false
@cpos_buffer_position = 0
# Number of lines currently on screen minus 1.
@_rl_vis_botlin = 0
# Variables used only in this file.
# The last left edge of text that was displayed. This is used when
# doing horizontal scrolling. It shifts in thirds of a screenwidth.
@last_lmargin = 0
# The line display buffers. One is the line currently displayed on
# the screen. The other is the line about to be displayed.
@visible_line = nil
@invisible_line = nil
# A buffer for `modeline' messages.
@msg_buf = 0.chr * 128
# Non-zero forces the redisplay even if we thought it was unnecessary.
@forced_display = false
# Default and initial buffer size. Can grow.
@line_size = 1024
# Variables to keep track of the expanded prompt string, which may
# include invisible characters.
@local_prompt = nil
@local_prompt_prefix = nil
@local_prompt_len = 0
@prompt_visible_length = 0
@prompt_prefix_length = 0
# The number of invisible characters in the line currently being
# displayed on the screen.
@visible_wrap_offset = 0
# The number of invisible characters in the prompt string. Static so it
# can be shared between rl_redisplay and update_line
@wrap_offset = 0
@prompt_last_invisible = 0
# The length (buffer offset) of the first line of the last (possibly
# multi-line) buffer displayed on the screen.
@visible_first_line_len = 0
# Number of invisible characters on the first physical line of the prompt.
# Only valid when the number of physical characters in the prompt exceeds
# (or is equal to) _rl_screenwidth.
@prompt_invis_chars_first_line = 0
@prompt_last_screen_line = 0
@prompt_physical_chars = 0
# Variables to save and restore prompt and display information.
# These are getting numerous enough that it's time to create a struct.
@saved_local_prompt = nil
@saved_local_prefix = nil
@saved_last_invisible = 0
@saved_visible_length = 0
@saved_prefix_length = 0
@saved_local_length = 0
@saved_invis_chars_first_line = 0
@saved_physical_chars = 0
@inv_lbreaks = nil
@vis_lbreaks = nil
@_rl_wrapped_line = nil
@term_buffer = nil
@term_string_buffer = nil
@tcap_initialized = false
# While we are editing the history, this is the saved
# version of the original line.
@_rl_saved_line_for_history = nil
# An array of HIST_ENTRY. This is where we store the history.
@the_history = nil
@history_base = 1
# Non-zero means that we have enforced a limit on the amount of
# history that we save.
@history_stifled = false
# If HISTORY_STIFLED is non-zero, then this is the maximum number of
# entries to remember.
@history_max_entries = 0
@max_input_history = 0 # backwards compatibility
# The current location of the interactive history pointer. Just makes
# life easier for outside callers.
@history_offset = 0
# The number of strings currently stored in the history list.
@history_length = 0
@_rl_vi_last_command = 'i' # default `.' puts you in insert mode
# Non-zero means enter insertion mode.
@_rl_vi_doing_insert = 0
# Command keys which do movement for xxx_to commands.
@vi_motion = " hl^$0ftFT;,%wbeWBE|"
# Keymap used for vi replace characters. Created dynamically since
# rarely used.
@vi_replace_map = nil
# The number of characters inserted in the last replace operation.
@vi_replace_count = 0
# If non-zero, we have text inserted after a c[motion] command that put
# us implicitly into insert mode. Some people want this text to be
# attached to the command so that it is `redoable' with `.'.
@vi_continued_command = false
@vi_insert_buffer = nil
@vi_insert_buffer_size = 0
@_rl_vi_last_repeat = 1
@_rl_vi_last_arg_sign = 1
@_rl_vi_last_motion = 0
@_rl_vi_last_search_char = 0
@_rl_vi_last_replacement = 0
@_rl_vi_last_key_before_insert = 0
@vi_redoing = 0
# Text modification commands. These are the `redoable' commands.
@vi_textmod = "_*\\AaIiCcDdPpYyRrSsXx~"
# Arrays for the saved marks.
@vi_mark_chars = Array.new(26,-1)
@emacs_standard_keymap = {
"\C-@" => :rl_set_mark ,
"\C-a" => :rl_beg_of_line ,
"\C-b" => :rl_backward_char ,
"\C-d" => :rl_delete ,
"\C-e" => :rl_end_of_line ,
"\C-f" => :rl_forward_char ,
"\C-g" => :rl_abort ,
"\C-h" => :rl_rubout ,
"\C-i" => :rl_complete ,
"\C-j" => :rl_newline ,
"\C-k" => :rl_kill_line ,
"\C-l" => :rl_clear_screen ,
"\C-m" => :rl_newline ,
"\C-n" => :rl_get_next_history ,
"\C-p" => :rl_get_previous_history ,
"\C-q" => :rl_quoted_insert ,
"\C-r" => :rl_reverse_search_history ,
"\C-s" => :rl_forward_search_history ,
"\C-t" => :rl_transpose_chars ,
"\C-u" => :rl_unix_line_discard ,
"\C-v" => :rl_quoted_insert ,
"\C-w" => :rl_unix_word_rubout ,
"\C-y" => :rl_yank ,
"\C-]" => :rl_char_search ,
"\C-_" => :rl_undo_command ,
"\x7F" => :rl_rubout ,
"\e\C-g" => :rl_abort ,
"\e\C-h" => :rl_backward_kill_word ,
"\e\C-i" => :rl_tab_insert ,
"\e\C-j" => :rl_vi_editing_mode ,
"\e\C-m" => :rl_vi_editing_mode ,
"\e\C-r" => :rl_revert_line ,
"\e\C-y" => :rl_yank_nth_arg ,
"\e\C-[" => :rl_complete ,
"\e\C-]" => :rl_backward_char_search ,
"\e " => :rl_set_mark ,
"\e#" => :rl_insert_comment ,
"\e&" => :rl_tilde_expand ,
"\e*" => :rl_insert_completions ,
"\e-" => :rl_digit_argument ,
"\e." => :rl_yank_last_arg ,
"\e0" => :rl_digit_argument ,
"\e1" => :rl_digit_argument ,
"\e2" => :rl_digit_argument ,
"\e3" => :rl_digit_argument ,
"\e4" => :rl_digit_argument ,
"\e5" => :rl_digit_argument ,
"\e6" => :rl_digit_argument ,
"\e7" => :rl_digit_argument ,
"\e8" => :rl_digit_argument ,
"\e9" => :rl_digit_argument ,
"\e<" => :rl_beginning_of_history ,
"\e=" => :rl_possible_completions ,
"\e>" => :rl_end_of_history ,
"\e?" => :rl_possible_completions ,
"\eB" => :rl_backward_word ,
"\eC" => :rl_capitalize_word ,
"\eD" => :rl_kill_word ,
"\eF" => :rl_forward_word ,
"\eL" => :rl_downcase_word ,
"\eN" => :rl_noninc_forward_search ,
"\eP" => :rl_noninc_reverse_search ,
"\eR" => :rl_revert_line ,
"\eT" => :rl_transpose_words ,
"\eU" => :rl_upcase_word ,
"\eY" => :rl_yank_pop ,
"\e\\" => :rl_delete_horizontal_space ,
"\e_" => :rl_yank_last_arg ,
"\eb" => :rl_backward_word ,
"\ec" => :rl_capitalize_word ,
"\ed" => :rl_kill_word ,
"\ef" => :rl_forward_word ,
"\el" => :rl_downcase_word ,
"\en" => :rl_noninc_forward_search ,
"\ep" => :rl_noninc_reverse_search ,
"\er" => :rl_revert_line ,
"\et" => :rl_transpose_words ,
"\eu" => :rl_upcase_word ,
"\ey" => :rl_yank_pop ,
"\e~" => :rl_tilde_expand ,
"\377" => :rl_backward_kill_word ,
"\C-x\C-g" => :rl_abort ,
"\C-x\C-r" => :rl_re_read_init_file ,
"\C-x\C-u" => :rl_undo_command ,
"\C-x\C-x" => :rl_exchange_point_and_mark ,
"\C-x(" => :rl_start_kbd_macro ,
"\C-x)" => :rl_end_kbd_macro ,
"\C-xE" => :rl_call_last_kbd_macro ,
"\C-xe" => :rl_call_last_kbd_macro ,
"\C-x\x7F" => :rl_backward_kill_line
}
@vi_movement_keymap = {
"\C-d" => :rl_vi_eof_maybe ,
"\C-e" => :rl_emacs_editing_mode ,
"\C-g" => :rl_abort ,
"\C-h" => :rl_backward_char ,
"\C-j" => :rl_newline ,
"\C-k" => :rl_kill_line ,
"\C-l" => :rl_clear_screen ,
"\C-m" => :rl_newline ,
"\C-n" => :rl_get_next_history ,
"\C-p" => :rl_get_previous_history ,
"\C-q" => :rl_quoted_insert ,
"\C-r" => :rl_reverse_search_history ,
"\C-s" => :rl_forward_search_history ,
"\C-t" => :rl_transpose_chars ,
"\C-u" => :rl_unix_line_discard ,
"\C-v" => :rl_quoted_insert ,
"\C-w" => :rl_unix_word_rubout ,
"\C-y" => :rl_yank ,
"\C-_" => :rl_vi_undo ,
" " => :rl_forward_char ,
"#" => :rl_insert_comment ,
"$" => :rl_end_of_line ,
"%" => :rl_vi_match ,
"&" => :rl_vi_tilde_expand ,
"*" => :rl_vi_complete ,
"+" => :rl_get_next_history ,
"," => :rl_vi_char_search ,
"-" => :rl_get_previous_history ,
"." => :rl_vi_redo ,
"/" => :rl_vi_search ,
"0" => :rl_beg_of_line ,
"1" => :rl_vi_arg_digit ,
"2" => :rl_vi_arg_digit ,
"3" => :rl_vi_arg_digit ,
"4" => :rl_vi_arg_digit ,
"5" => :rl_vi_arg_digit ,
"6" => :rl_vi_arg_digit ,
"7" => :rl_vi_arg_digit ,
"8" => :rl_vi_arg_digit ,
"9" => :rl_vi_arg_digit ,
"" => :rl_vi_char_search ,
"=" => :rl_vi_complete ,
"?" => :rl_vi_search ,
"A" => :rl_vi_append_eol ,
"B" => :rl_vi_prev_word ,
"C" => :rl_vi_change_to ,
"D" => :rl_vi_delete_to ,
"E" => :rl_vi_end_word ,
"F" => :rl_vi_char_search ,
"G" => :rl_vi_fetch_history ,
"I" => :rl_vi_insert_beg ,
"N" => :rl_vi_search_again ,
"P" => :rl_vi_put ,
"R" => :rl_vi_replace ,
"S" => :rl_vi_subst ,
"T" => :rl_vi_char_search ,
"U" => :rl_revert_line ,
"W" => :rl_vi_next_word ,
"X" => :rl_vi_rubout ,
"Y" => :rl_vi_yank_to ,
"\\" => :rl_vi_complete ,
"^" => :rl_vi_first_print ,
"_" => :rl_vi_yank_arg ,
"`" => :rl_vi_goto_mark ,
"a" => :rl_vi_append_mode ,
"b" => :rl_vi_prev_word ,
"c" => :rl_vi_change_to ,
"d" => :rl_vi_delete_to ,
"e" => :rl_vi_end_word ,
"f" => :rl_vi_char_search ,
"h" => :rl_backward_char ,
"i" => :rl_vi_insertion_mode ,
"j" => :rl_get_next_history ,
"k" => :rl_get_previous_history ,
"l" => :rl_forward_char ,
"m" => :rl_vi_set_mark ,
"n" => :rl_vi_search_again ,
"p" => :rl_vi_put ,
"r" => :rl_vi_change_char ,
"s" => :rl_vi_subst ,
"t" => :rl_vi_char_search ,
"u" => :rl_vi_undo ,
"w" => :rl_vi_next_word ,
"x" => :rl_vi_delete ,
"y" => :rl_vi_yank_to ,
"|" => :rl_vi_column ,
"~" => :rl_vi_change_case
}
@vi_insertion_keymap = {
"\C-a" => :rl_insert ,
"\C-b" => :rl_insert ,
"\C-c" => :rl_insert ,
"\C-d" => :rl_vi_eof_maybe ,
"\C-e" => :rl_insert ,
"\C-f" => :rl_insert ,
"\C-g" => :rl_insert ,
"\C-h" => :rl_rubout ,
"\C-i" => :rl_complete ,
"\C-j" => :rl_newline ,
"\C-k" => :rl_insert ,
"\C-l" => :rl_insert ,
"\C-m" => :rl_newline ,
"\C-n" => :rl_insert ,
"\C-o" => :rl_insert ,
"\C-p" => :rl_insert ,
"\C-q" => :rl_insert ,
"\C-r" => :rl_reverse_search_history ,
"\C-s" => :rl_forward_search_history ,
"\C-t" => :rl_transpose_chars ,
"\C-u" => :rl_unix_line_discard ,
"\C-v" => :rl_quoted_insert ,
"\C-w" => :rl_unix_word_rubout ,
"\C-x" => :rl_insert ,
"\C-y" => :rl_yank ,
"\C-z" => :rl_insert ,
"\C-[" => :rl_vi_movement_mode ,
"\C-\\" => :rl_insert ,
"\C-]" => :rl_insert ,
"\C-^" => :rl_insert ,
"\C-_" => :rl_vi_undo ,
"\x7F" => :rl_rubout
}
@rl_library_version = RL_LIBRARY_VERSION
@rl_readline_version = RL_READLINE_VERSION
@rl_readline_name = "other"
@rl_getc_function = :rl_getc
# Non-zero tells rl_delete_text and rl_insert_text to not add to
# the undo list.
@_rl_doing_an_undo = false
# How many unclosed undo groups we currently have.
@_rl_undo_group_level = 0
# The current undo list for THE_LINE.
@rl_undo_list = nil
# Application-specific redisplay function.
@rl_redisplay_function = :rl_redisplay
# Global variables declared here.
# What YOU turn on when you have handled all redisplay yourself.
@rl_display_fixed = false
@_rl_suppress_redisplay = 0
@_rl_want_redisplay = false
# The stuff that gets printed out before the actual text of the line.
# This is usually pointing to rl_prompt.
@rl_display_prompt = nil
# True if this is `real' readline as opposed to some stub substitute.
@rl_gnu_readline_p = true
for i in 32 .. 255
@emacs_standard_keymap[i.chr] = :rl_insert unless @emacs_standard_keymap[i.chr]
@vi_insertion_keymap[i.chr] = :rl_insert unless @vi_insertion_keymap[i.chr]
end
# A pointer to the keymap that is currently in use.
# By default, it is the standard emacs keymap.
@_rl_keymap = @emacs_standard_keymap
# The current style of editing.
@rl_editing_mode = @emacs_mode
# The current insert mode: input (the default) or overwrite
@rl_insert_mode = RL_IM_DEFAULT
# Non-zero if we called this function from _rl_dispatch(). It's present
# so functions can find out whether they were called from a key binding
# or directly from an application.
@rl_dispatching = false
# Non-zero if the previous command was a kill command.
@_rl_last_command_was_kill = false
# The current value of the numeric argument specified by the user.
@rl_numeric_arg = 1
# Non-zero if an argument was typed.
@rl_explicit_arg = false
# Temporary value used while generating the argument.
@rl_arg_sign = 1
# Non-zero means we have been called at least once before.
@rl_initialized = false
# Flags word encapsulating the current readline state.
@rl_readline_state = RL_STATE_NONE
# The current offset in the current input line.
@rl_point = 0
# Mark in the current input line.
@rl_mark = 0
# Length of the current input line.
@rl_end = 0
# Make this non-zero to return the current input_line.
@rl_done = false
# The last function executed by readline.
@rl_last_func = nil
# Top level environment for readline_internal ().
@readline_top_level = nil
# The streams we interact with.
@_rl_in_stream = nil
@_rl_out_stream = nil
# The names of the streams that we do input and output to.
@rl_instream = nil
@rl_outstream = nil
@pop_index = 0
@push_index = 0
@ibuffer = 0.chr * 512
@ibuffer_len = @ibuffer.length - 1
# Non-zero means echo characters as they are read. Defaults to no echo
# set to 1 if there is a controlling terminal, we can get its attributes,
# and the attributes include `echo'. Look at rltty.c:prepare_terminal_settings
# for the code that sets it.
@readline_echoing_p = false
# Current prompt.
@rl_prompt = nil
@rl_visible_prompt_length = 0
# Set to non-zero by calling application if it has already printed rl_prompt
# and does not want readline to do it the first time.
@rl_already_prompted = false
# The number of characters read in order to type this complete command.
@rl_key_sequence_length = 0
# If non-zero, then this is the address of a function to call just
# before readline_internal_setup () prints the first prompt.
@rl_startup_hook = nil
# If non-zero, this is the address of a function to call just before
# readline_internal_setup () returns and readline_internal starts
# reading input characters.
@rl_pre_input_hook = nil
# The character that can generate an EOF. Really read from
# the terminal driver... just defaulted here.
@_rl_eof_char = "\cD"
# Non-zero makes this the next keystroke to read.
@rl_pending_input = 0
# Pointer to a useful terminal name.
@rl_terminal_name = nil
# Non-zero means to always use horizontal scrolling in line display.
@_rl_horizontal_scroll_mode = false
# Non-zero means to display an asterisk at the starts of history lines
# which have been modified.
@_rl_mark_modified_lines = false
# The style of `bell' notification preferred. This can be set to NO_BELL,
# AUDIBLE_BELL, or VISIBLE_BELL.
@_rl_bell_preference = AUDIBLE_BELL
# String inserted into the line by rl_insert_comment ().
@_rl_comment_begin = nil
# Keymap holding the function currently being executed.
@rl_executing_keymap = nil
# Keymap we're currently using to dispatch.
@_rl_dispatching_keymap = nil
# Non-zero means to erase entire line, including prompt, on empty input lines.
@rl_erase_empty_line = false
# Non-zero means to read only this many characters rather than up to a
# character bound to accept-line.
@rl_num_chars_to_read = 0
# Line buffer and maintenence.
@rl_line_buffer = ""
# Key sequence `contexts'
@_rl_kscxt = nil
# Non-zero means do not parse any lines other than comments and
# parser directives.
@_rl_parsing_conditionalized_out = false
# Non-zero means to convert characters with the meta bit set to
# escape-prefixed characters so we can indirect through
# emacs_meta_keymap or vi_escape_keymap.
@_rl_convert_meta_chars_to_ascii = true
# Non-zero means to output characters with the meta bit set directly
# rather than as a meta-prefixed escape sequence.
@_rl_output_meta_chars = false
# Non-zero means to look at the termios special characters and bind
# them to equivalent readline functions at startup.
@_rl_bind_stty_chars = true
@rl_completion_display_matches_hook = nil
XOK = 1
@_rl_term_clreol = nil
@_rl_term_clrpag = nil
@_rl_term_cr = nil
@_rl_term_backspace = nil
@_rl_term_goto = nil
@_rl_term_pc = nil
# "An application program can assume that the terminal can do character
# insertion if *any one of* the capabilities `IC', `im', `ic' or `ip' is
# provided.". But we can't do anything if only `ip' is provided, so...
#
# Currently rb-readline can't tgoto(). Setting this to false means that
# insert_some_chars doesn't get called and some other method is used.
@_rl_terminal_can_insert = false
# How to insert characters.
@_rl_term_im = nil
@_rl_term_ei = nil
@_rl_term_ic = nil
@_rl_term_ip = nil
@_rl_term_IC = nil
# How to delete characters.
@_rl_term_dc = nil
@_rl_term_DC = nil
@_rl_term_forward_char = nil
# How to go up a line.
@_rl_term_up = nil
# A visible bell; char if the terminal can be made to flash the screen.
@_rl_visible_bell = nil
# Non-zero means the terminal can auto-wrap lines.
@_rl_term_autowrap = true
# Non-zero means that this terminal has a meta key.
@term_has_meta = 0
# The sequences to write to turn on and off the meta key, if this
# terminal has one.
@_rl_term_mm = nil
@_rl_term_mo = nil
# The key sequences output by the arrow keys, if this terminal has any.
@_rl_term_ku = nil
@_rl_term_kd = nil
@_rl_term_kr = nil
@_rl_term_kl = nil
# How to initialize and reset the arrow keys, if this terminal has any.
@_rl_term_ks = nil
@_rl_term_ke = nil
# The key sequences sent by the Home and End keys, if any.
@_rl_term_kh = nil
@_rl_term_kH = nil
@_rl_term_at7 = nil
# Delete key
@_rl_term_kD = nil
# Insert key
@_rl_term_kI = nil
# Cursor control
@_rl_term_vs = nil # very visible
@_rl_term_ve = nil # normal
# Variables that hold the screen dimensions, used by the display code.
@_rl_screenwidth = @_rl_screenheight = @_rl_screenchars = 0
# Non-zero means the user wants to enable the keypad.
@_rl_enable_keypad = false
# Non-zero means the user wants to enable a meta key.
@_rl_enable_meta = true
# ****************************************************************
#
# Completion matching, from readline's point of view.
#
# ****************************************************************
# Variables known only to the readline library.
# If non-zero, non-unique completions always show the list of matches.
@_rl_complete_show_all = false
# If non-zero, non-unique completions show the list of matches, unless it
# is not possible to do partial completion and modify the line.
@_rl_complete_show_unmodified = false
# If non-zero, completed directory names have a slash appended.
@_rl_complete_mark_directories = true
# If non-zero, the symlinked directory completion behavior introduced in
# readline-4.2a is disabled, and symlinks that point to directories have
# a slash appended (subject to the value of _rl_complete_mark_directories).
# This is user-settable via the mark-symlinked-directories variable.
@_rl_complete_mark_symlink_dirs = false
# If non-zero, completions are printed horizontally in alphabetical order,
# like `ls -x'.
@_rl_print_completions_horizontally = false
@_rl_completion_case_fold = false
# If non-zero, don't match hidden files (filenames beginning with a `.' on
# Unix) when doing filename completion.
@_rl_match_hidden_files = true
# Global variables available to applications using readline.
# Non-zero means add an additional character to each filename displayed
# during listing completion iff rl_filename_completion_desired which helps
# to indicate the type of file being listed.
@rl_visible_stats = false
# If non-zero, then this is the address of a function to call when
# completing on a directory name. The function is called with
# the address of a string (the current directory name) as an arg.
@rl_directory_completion_hook = nil
@rl_directory_rewrite_hook = nil
# Non-zero means readline completion functions perform tilde expansion.
@rl_complete_with_tilde_expansion = false
# Pointer to the generator function for completion_matches ().
# NULL means to use rl_filename_completion_function (), the default filename
# completer.
@rl_completion_entry_function = nil
# Pointer to alternative function to create matches.
# Function is called with TEXT, START, and END.
# START and END are indices in RL_LINE_BUFFER saying what the boundaries
# of TEXT are.
# If this function exists and returns NULL then call the value of
# rl_completion_entry_function to try to match, otherwise use the
# array of strings returned.
@rl_attempted_completion_function = nil
# Non-zero means to suppress normal filename completion after the
# user-specified completion function has been called.
@rl_attempted_completion_over = false
# Set to a character indicating the type of completion being performed
# by rl_complete_internal, available for use by application completion
# functions.
@rl_completion_type = 0
# Up to this many items will be displayed in response to a
# possible-completions call. After that, we ask the user if
# she is sure she wants to see them all. A negative value means
# don't ask.
@rl_completion_query_items = 100
@_rl_page_completions = 1
# The basic list of characters that signal a break between words for the
# completer routine. The contents of this variable is what breaks words
# in the shell, i.e. " \t\n\"\\'`@$><="
@rl_basic_word_break_characters = " \t\n\"\\'`@$><=|&{(" # })
# List of basic quoting characters.
@rl_basic_quote_characters = "\"'"
# The list of characters that signal a break between words for
# rl_complete_internal. The default list is the contents of
# rl_basic_word_break_characters.
@rl_completer_word_break_characters = nil
# Hook function to allow an application to set the completion word
# break characters before readline breaks up the line. Allows
# position-dependent word break characters.
@rl_completion_word_break_hook = nil
# List of characters which can be used to quote a substring of the line.
# Completion occurs on the entire substring, and within the substring
# rl_completer_word_break_characters are treated as any other character,
# unless they also appear within this list.
@rl_completer_quote_characters = nil
# List of characters that should be quoted in filenames by the completer.
@rl_filename_quote_characters = nil
# List of characters that are word break characters, but should be left
# in TEXT when it is passed to the completion function. The shell uses
# this to help determine what kind of completing to do.
@rl_special_prefixes = nil
# If non-zero, then disallow duplicates in the matches.
@rl_ignore_completion_duplicates = true
# Non-zero means that the results of the matches are to be treated
# as filenames. This is ALWAYS zero on entry, and can only be changed
# within a completion entry finder function.
@rl_filename_completion_desired = false
# Non-zero means that the results of the matches are to be quoted using
# double quotes (or an application-specific quoting mechanism) if the
# filename contains any characters in rl_filename_quote_chars. This is
# ALWAYS non-zero on entry, and can only be changed within a completion
# entry finder function.
@rl_filename_quoting_desired = true
# This function, if defined, is called by the completer when real
# filename completion is done, after all the matching names have been
# generated. It is passed a (char**) known as matches in the code below.
# It consists of a NULL-terminated array of pointers to potential
# matching strings. The 1st element (matches[0]) is the maximal
# substring that is common to all matches. This function can re-arrange
# the list of matches as required, but all elements of the array must be
# free()'d if they are deleted. The main intent of this function is
# to implement FIGNORE a la SunOS csh.
@rl_ignore_some_completions_function = nil
# Set to a function to quote a filename in an application-specific fashion.
# Called with the text to quote, the type of match found (single or multiple)
# and a pointer to the quoting character to be used, which the function can
# reset if desired.
#rl_filename_quoting_function = rl_quote_filename
# Function to call to remove quoting characters from a filename. Called
# before completion is attempted, so the embedded quotes do not interfere
# with matching names in the file system. Readline doesn't do anything
# with this it's set only by applications.
@rl_filename_dequoting_function = nil
# Function to call to decide whether or not a word break character is
# quoted. If a character is quoted, it does not break words for the
# completer.
@rl_char_is_quoted_p = nil
# If non-zero, the completion functions don't append anything except a
# possible closing quote. This is set to 0 by rl_complete_internal and
# may be changed by an application-specific completion function.
@rl_completion_suppress_append = false
# Character appended to completed words when at the end of the line. The
# default is a space.
@rl_completion_append_character = ' '
# If non-zero, the completion functions don't append any closing quote.
# This is set to 0 by rl_complete_internal and may be changed by an
# application-specific completion function.
@rl_completion_suppress_quote = false
# Set to any quote character readline thinks it finds before any application
# completion function is called.
@rl_completion_quote_character = 0
# Set to a non-zero value if readline found quoting anywhere in the word to
# be completed set before any application completion function is called.
@rl_completion_found_quote = false
# If non-zero, a slash will be appended to completed filenames that are
# symbolic links to directory names, subject to the value of the
# mark-directories variable (which is user-settable). This exists so
# that application completion functions can override the user's preference
# (set via the mark-symlinked-directories variable) if appropriate.
# It's set to the value of _rl_complete_mark_symlink_dirs in
# rl_complete_internal before any application-specific completion
# function is called, so without that function doing anything, the user's
# preferences are honored.
@rl_completion_mark_symlink_dirs = false
# If non-zero, inhibit completion (temporarily).
@rl_inhibit_completion = false
# Variables local to this file.
# Local variable states what happened during the last completion attempt.
@completion_changed_buffer = nil
# Non-zero means treat 0200 bit in terminal input as Meta bit.
@_rl_meta_flag = false
# Stack of previous values of parsing_conditionalized_out.
@if_stack = []
@if_stack_depth = 0
# The last key bindings file read.
@last_readline_init_file = nil
# The file we're currently reading key bindings from.
@current_readline_init_file = nil
@current_readline_init_include_level = 0
@current_readline_init_lineno = 0
ENV["HOME"] ||= ENV["HOMEDRIVE"]+ENV["HOMEPATH"]
@directory = nil
@filename = nil
@dirname = nil
@users_dirname = nil
@filename_len = 0
attr_accessor :rl_attempted_completion_function,:rl_deprep_term_function,
:rl_event_hook,:rl_attempted_completion_over,:rl_basic_quote_characters,
:rl_basic_word_break_characters,:rl_completer_quote_characters,
:rl_completer_word_break_characters,:rl_completion_append_character,
:rl_filename_quote_characters,:rl_instream,:rl_library_version,:rl_outstream,
:rl_readline_name,:history_length,:history_base,:rl_point
module_function
# Okay, now we write the entry_function for filename completion. In the
# general case. Note that completion in the shell is a little different
# because of all the pathnames that must be followed when looking up the
# completion for a command.
def rl_filename_completion_function(text, state)
# If we don't have any state, then do some initialization.
if (state == 0)
# If we were interrupted before closing the directory or reading
#all of its contents, close it.
if(@directory)
@directory.close
@directory = nil
end
text.delete!(0.chr)
if text.length == 0
@dirname = "."
@filename = ""
elsif text.rindex(File::SEPARATOR) == text.length-1
@dirname = text
@filename = ""
else
@dirname, @filename = File.split(text)
# This preserves the "./" when the user types "./dirname<tab>".
if @dirname == "." && text[0,2] == ".#{File::SEPARATOR}"
@dirname += File::SEPARATOR
end
end
# We aren't done yet. We also support the "~user" syntax.
# Save the version of the directory that the user typed.
@users_dirname = @dirname.dup
if (@dirname[0,1] == '~')
@dirname = File.expand_path(@dirname)
end
# The directory completion hook should perform any necessary
# dequoting.
if (@rl_directory_completion_hook && send(rl_directory_completion_hook,@dirname))
@users_dirname = @dirname.dup
elsif (@rl_completion_found_quote && @rl_filename_dequoting_function)
# delete single and double quotes
temp = send(@rl_filename_dequoting_function, @users_dirname, @rl_completion_quote_character)
@users_dirname = temp
@dirname = @users_dirname.dup
end
begin
@directory = Dir.new(@dirname)
rescue Errno::ENOENT, Errno::ENOTDIR, Errno::EACCES
end
# Now dequote a non-null filename.
if (@filename && @filename.length>0 && @rl_completion_found_quote && @rl_filename_dequoting_function)
# delete single and double quotes
temp = send(@rl_filename_dequoting_function, @filename, @rl_completion_quote_character)
@filename = temp
end
@filename_len = @filename.length
@rl_filename_completion_desired = true
end
# At this point we should entertain the possibility of hacking wildcarded
# filenames, like /usr/man/man<WILD>/te<TAB>. If the directory name
# contains globbing characters, then build an array of directories, and
# then map over that list while completing.
# *** UNIMPLEMENTED ***
# Now that we have some state, we can read the directory.
entry = nil
while(@directory && (entry = @directory.read))
d_name = entry
# Special case for no filename. If the user has disabled the
# `match-hidden-files' variable, skip filenames beginning with `.'.
#All other entries except "." and ".." match.
if (@filename_len == 0)
next if (!@_rl_match_hidden_files && d_name[0,1] == '.')
break if (d_name != '.' && d_name != '..')
else
# Otherwise, if these match up to the length of filename, then
# it is a match.
if (@_rl_completion_case_fold)
break if d_name =~ /^#{Regexp.escape(@filename)}/i
else
break if d_name =~ /^#{Regexp.escape(@filename)}/
end
end
end
if entry.nil?
if @directory
@directory.close
@directory = nil
end
@dirname = nil
@filename = nil
@users_dirname = nil
return nil
else
if (@dirname != '.')
if (@rl_complete_with_tilde_expansion && @users_dirname[0,1] == "~")
temp = @dirname
if(temp[-1,1] != File::SEPARATOR)
temp += File::SEPARATOR
end
else
temp = @users_dirname
if(temp[-1,1] != File::SEPARATOR)
temp += File::SEPARATOR
end
end
temp += entry
else
temp = entry.dup
end
return (temp)
end
end
# A completion function for usernames.
# TEXT contains a partial username preceded by a random
# character (usually `~').
def rl_username_completion_function(text, state)
return nil if RUBY_PLATFORM =~ /mswin|mingw/
if (state == 0)
first_char = text[0,1]
first_char_loc = (first_char == '~' ? 1 : 0)
username = text[first_char_loc..-1]
namelen = username.length
Etc.setpwent()
end
while (entry = Etc.getpwent())
# Null usernames should result in all users as possible completions.
break if (namelen == 0 || entry.name =~ /^#{username}/ )
end
if entry.nil?
Etc.endpwent()
return nil
else
value = text.dup
value[first_char_loc..-1] = entry.name
if (first_char == '~')
@rl_filename_completion_desired = true
end
return (value)
end
end
#*************************************************************
#
# Application-callable completion match generator functions
#
#*************************************************************
# Return an array of (char *) which is a list of completions for TEXT.
# If there are no completions, return a NULL pointer.
# The first entry in the returned array is the substitution for TEXT.
# The remaining entries are the possible completions.
# The array is terminated with a NULL pointer.
#
# ENTRY_FUNCTION is a function of two args, and returns a (char *).
# The first argument is TEXT.
# The second is a state argument it should be zero on the first call, and
# non-zero on subsequent calls. It returns a NULL pointer to the caller
# when there are no more matches.
#
def rl_completion_matches(text, entry_function)
matches = 0
match_list = []
match_list[1] = nil
while (string = send(entry_function, text, matches))
match_list[matches+=1] = string
match_list[matches+1] = nil
end
# If there were any matches, then look through them finding out the
# lowest common denominator. That then becomes match_list[0].
if (matches!=0)
compute_lcd_of_matches(match_list, matches, text)
else # There were no matches.
match_list = nil
end
return (match_list)
end
def _rl_to_lower(char)
char.nil? ? nil : char.chr.downcase
end
# Find the common prefix of the list of matches, and put it into
# matches[0].
def compute_lcd_of_matches(match_list, matches, text)
# If only one match, just use that. Otherwise, compare each
# member of the list with the next, finding out where they
# stop matching.
if (matches == 1)
match_list[0] = match_list[1]
match_list[1] = nil
return 1
end
i = 1
low = 100000
while(i<matches)
if (@_rl_completion_case_fold)
si = 0
while((c1 = _rl_to_lower(match_list[i][si])) &&
(c2 = _rl_to_lower(match_list[i + 1][si])))
if !@rl_byte_oriented
if(!_rl_compare_chars(match_list[i],si,match_list[i+1],si))
break
elsif ((v = _rl_get_char_len(match_list[i][si..-1])) > 1)
si += v - 1
end
else
break if (c1 != c2)
end
si += 1
end
else
si = 0
while((c1 = match_list[i][si]) &&
(c2 = match_list[i + 1][si]))
if !@rl_byte_oriented
if(!_rl_compare_chars(match_list[i],si,match_list[i+1],si))
break
elsif ((v = _rl_get_char_len(match_list[i][si..-1])) > 1)
si += v - 1
end
else
break if (c1 != c2)
end
si += 1
end
end
if (low > si)
low = si
end
i += 1
end
# If there were multiple matches, but none matched up to even the
# first character, and the user typed something, use that as the
# value of matches[0].
if (low == 0 && text && text.length>0 )
match_list[0] = text.dup
else
# XXX - this might need changes in the presence of multibyte chars
# If we are ignoring case, try to preserve the case of the string
# the user typed in the face of multiple matches differing in case.
if (@_rl_completion_case_fold)
# We're making an assumption here:
# IF we're completing filenames AND
# the application has defined a filename dequoting function AND
# we found a quote character AND
# the application has requested filename quoting
# THEN
# we assume that TEXT was dequoted before checking against
# the file system and needs to be dequoted here before we
# check against the list of matches
# FI
if (@rl_filename_completion_desired &&
@rl_filename_dequoting_function &&
@rl_completion_found_quote &&
@rl_filename_quoting_desired)
dtext = send(@rl_filename_dequoting_function,text, @rl_completion_quote_character)
text = dtext
end
# sort the list to get consistent answers.
match_list = [match_list[0]] + match_list[1..-1].sort
si = text.length
if (si <= low)
for i in 1 .. matches
if match_list[i][0,si] == text
match_list[0] = match_list[i][0,low]
break
end
# no casematch, use first entry
if (i > matches)
match_list[0] = match_list[1][0,low]
end
end
else
# otherwise, just use the text the user typed.
match_list[0] = text[0,low]
end
else
match_list[0] = match_list[1][0,low]
end
end
return matches
end
# This is a NOOP until the rest of Vi-mode is working.
def rl_vi_editing_mode(count, key)
0
end
# Switching from one mode to the other really just involves
# switching keymaps.
def rl_vi_insertion_mode(count, key)
@_rl_keymap = @vi_insertion_keymap
@_rl_vi_last_key_before_insert = key
0
end
def rl_emacs_editing_mode(count, key)
@rl_editing_mode = @emacs_mode
_rl_set_insert_mode(RL_IM_INSERT, 1) # emacs mode default is insert mode
@_rl_keymap = @emacs_standard_keymap
0
end
# Function for the rest of the library to use to set insert/overwrite mode.
def _rl_set_insert_mode(im, force)
@rl_insert_mode = im
end
# Toggle overwrite mode. A positive explicit argument selects overwrite
# mode. A negative or zero explicit argument selects insert mode.
def rl_overwrite_mode(count, key)
if (!@rl_explicit_arg)
_rl_set_insert_mode(@rl_insert_mode ^ 1, 0)
elsif (count > 0)
_rl_set_insert_mode(RL_IM_OVERWRITE, 0)
else
_rl_set_insert_mode(RL_IM_INSERT, 0)
end
0
end
# A function for simple tilde expansion.
def rl_tilde_expand(ignore, key)
_end = @rl_point
start = _end - 1
if (@rl_point == @rl_end && @rl_line_buffer[@rl_point,1] == '~' )
homedir = File.expand_path("~")
_rl_replace_text(homedir, start, _end)
return (0)
elsif (@rl_line_buffer[start,1] != '~')
while(!whitespace(@rl_line_buffer[start,1]) && start >= 0)
start -= 1
end
start+=1
end
_end = start
begin
_end+=1
end while(!whitespace(@rl_line_buffer[_end,1]) && _end < @rl_end)
if (whitespace(@rl_line_buffer[_end,1]) || _end >= @rl_end)
_end-=1
end
# If the first character of the current word is a tilde, perform
#tilde expansion and insert the result. If not a tilde, do
# nothing.
if (@rl_line_buffer[start,1] == '~')
len = _end - start + 1
temp = @rl_line_buffer[start,len]
homedir = File.expand_path(temp)
temp = nil
_rl_replace_text(homedir, start, _end)
end
0
end
# Clean up the terminal and readline state after catching a signal, before
# resending it to the calling application.
def rl_cleanup_after_signal()
_rl_clean_up_for_exit()
if (@rl_deprep_term_function)
send(@rl_deprep_term_function)
end
rl_clear_pending_input()
rl_clear_signals()
end
def _rl_clean_up_for_exit()
if @readline_echoing_p
_rl_move_vert(@_rl_vis_botlin)
@_rl_vis_botlin = 0
@rl_outstream.flush
rl_restart_output(1, 0)
end
end
# Move the cursor from _rl_last_c_pos to NEW, which are buffer indices.
# (Well, when we don't have multibyte characters, _rl_last_c_pos is a
# buffer index.)
# DATA is the contents of the screen line of interest; i.e., where
# the movement is being done.
def _rl_move_cursor_relative(new, data, start=0)
woff = w_offset(@_rl_last_v_pos, @wrap_offset)
cpos = @_rl_last_c_pos
if !@rl_byte_oriented
dpos = _rl_col_width(data, start, start+new)
# Use NEW when comparing against the last invisible character in the
# prompt string, since they're both buffer indices and DPOS is a desired
# display position.
if (new > @prompt_last_invisible) # XXX - don't use woff here
dpos -= woff
# Since this will be assigned to _rl_last_c_pos at the end (more
# precisely, _rl_last_c_pos == dpos when this function returns),
# let the caller know.
@cpos_adjusted = true
end
else
dpos = new
end
# If we don't have to do anything, then return.
if (cpos == dpos)
return
end
if @hConsoleHandle
csbi = 0.chr * 24
@GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
x,y = csbi[4,4].unpack('SS')
x = dpos
@SetConsoleCursorPosition.Call(@hConsoleHandle,y*65536+x)
@_rl_last_c_pos = dpos
return
end
# It may be faster to output a CR, and then move forwards instead
# of moving backwards.
# i == current physical cursor position.
if !@rl_byte_oriented
i = @_rl_last_c_pos
else
i = @_rl_last_c_pos - woff
end
if (dpos == 0 || cr_faster(dpos, @_rl_last_c_pos) ||
(@_rl_term_autowrap && i == @_rl_screenwidth))
@rl_outstream.write(@_rl_term_cr)
cpos = @_rl_last_c_pos = 0
end
if (cpos < dpos)
# Move the cursor forward. We do it by printing the command
# to move the cursor forward if there is one, else print that
# portion of the output buffer again. Which is cheaper?
# The above comment is left here for posterity. It is faster
# to print one character (non-control) than to print a control
# sequence telling the terminal to move forward one character.
# That kind of control is for people who don't know what the
# data is underneath the cursor.
# However, we need a handle on where the current display position is
# in the buffer for the immediately preceding comment to be true.
# In multibyte locales, we don't currently have that info available.
# Without it, we don't know where the data we have to display begins
# in the buffer and we have to go back to the beginning of the screen
# line. In this case, we can use the terminal sequence to move forward
# if it's available.
if !@rl_byte_oriented
if (@_rl_term_forward_char)
@rl_outstream.write(@_rl_term_forward_char * (dpos-cpos))
else
@rl_outstream.write(@_rl_term_cr)
@rl_outstream.write(data[start,new])
end
else
@rl_outstream.write(data[start+cpos,new-cpos])
end
elsif (cpos > dpos)
_rl_backspace(cpos - dpos)
end
@_rl_last_c_pos = dpos
end
# PWP: move the cursor up or down.
def _rl_move_vert(to)
if (@_rl_last_v_pos == to || to > @_rl_screenheight)
return
end
if ((delta = to - @_rl_last_v_pos) > 0)
@rl_outstream.write("\n"*delta)
@rl_outstream.write("\r")
@_rl_last_c_pos = 0
else
if(@_rl_term_up)
@rl_outstream.write(@_rl_term_up*(-delta))
end
end
@_rl_last_v_pos = to # Now TO is here
end
def rl_setstate(x)
(@rl_readline_state |= (x))
end
def rl_unsetstate(x)
(@rl_readline_state &= ~(x))
end
def rl_isstate(x)
(@rl_readline_state & (x))!=0
end
# Clear any pending input pushed with rl_execute_next()
def rl_clear_pending_input()
@rl_pending_input = 0
rl_unsetstate(RL_STATE_INPUTPENDING)
0
end
def rl_restart_output(count, key)
0
end
def rl_clear_signals()
if Signal.list['WINCH']
trap "WINCH",@def_proc
end
end
def rl_set_signals()
if Signal.list['WINCH']
@def_proc = trap "WINCH",Proc.new{rl_sigwinch_handler(0)}
end
end
# Current implementation:
# \001 (^A) start non-visible characters
# \002 (^B) end non-visible characters
# all characters except \001 and \002 (following a \001) are copied to
# the returned string all characters except those between \001 and
# \002 are assumed to be `visible'.
def expand_prompt(pmt)
# Short-circuit if we can.
if (@rl_byte_oriented && pmt[RL_PROMPT_START_IGNORE].nil?)
r = pmt.dup
lp = r.length
lip = 0
niflp = 0
vlp = lp
return [r,lp,lip,niflp,vlp]
end
l = pmt.length
ret = ''
invfl = 0 # invisible chars in first line of prompt
invflset = 0 # we only want to set invfl once
igstart = 0
rl = 0
ignoring = false
last = ninvis = physchars = 0
for pi in 0 ... pmt.length
# This code strips the invisible character string markers
#RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE
if (!ignoring && pmt[pi,1] == RL_PROMPT_START_IGNORE) # XXX - check ignoring?
ignoring = true
igstart = pi
next
elsif (ignoring && pmt[pi,1] == RL_PROMPT_END_IGNORE)
ignoring = false
if (pi != (igstart + 1))
last = ret.length - 1
end
next
else
if !@rl_byte_oriented
pind = pi
ind = _rl_find_next_mbchar(pmt, pind, 1, MB_FIND_NONZERO)
l = ind - pind
while (l>0)
l-=1
ret << pmt[pi]
pi += 1
end
if (!ignoring)
rl += ind - pind
physchars += _rl_col_width(pmt, pind, ind)
else
ninvis += ind - pind
end
pi-=1 # compensate for later increment
else
ret << pmt[pi]
if (!ignoring)
rl+=1 # visible length byte counter
physchars+=1
else
ninvis+=1 # invisible chars byte counter
end
if (invflset == 0 && rl >= @_rl_screenwidth)
invfl = ninvis
invflset = 1
end
end
end
end
if (rl < @_rl_screenwidth)
invfl = ninvis
end
lp = rl
lip = last
niflp = invfl
vlp = physchars
return [ret,lp,lip,niflp,vlp]
end
#*
#* Expand the prompt string into the various display components, if
#* necessary.
#*
#* local_prompt = expanded last line of string in rl_display_prompt
#* (portion after the final newline)
#* local_prompt_prefix = portion before last newline of rl_display_prompt,
#* expanded via expand_prompt
#* prompt_visible_length = number of visible characters in local_prompt
#* prompt_prefix_length = number of visible characters in local_prompt_prefix
#*
#* This function is called once per call to readline(). It may also be
#* called arbitrarily to expand the primary prompt.
#*
#* The return value is the number of visible characters on the last line
#* of the (possibly multi-line) prompt.
#*
def rl_expand_prompt(prompt)
@local_prompt = @local_prompt_prefix = nil
@local_prompt_len = 0
@prompt_last_invisible = @prompt_invis_chars_first_line = 0
@prompt_visible_length = @prompt_physical_chars = 0
if (prompt.nil? || prompt == '')
return (0)
end
pi = prompt.rindex("\n")
if pi.nil?
# The prompt is only one logical line, though it might wrap.
@local_prompt,@prompt_visible_length,@prompt_last_invisible,@prompt_invis_chars_first_line,@prompt_physical_chars = expand_prompt(prompt)
@local_prompt_prefix = nil
@local_prompt_len = @local_prompt ? @local_prompt.length : 0
return (@prompt_visible_length)
else
# The prompt spans multiple lines.
pi += 1 if prompt.length!=pi+1
t = pi
@local_prompt,@prompt_visible_length,@prompt_last_invisible,@prompt_invis_chars_first_line,@prompt_physical_chars = expand_prompt(prompt[pi..-1])
c = prompt[t]
prompt[t] = 0.chr
# The portion of the prompt string up to and including the
#final newline is now null-terminated.
@local_prompt_prefix,@prompt_prefix_length,_,_, = expand_prompt(prompt)
prompt[t] = c
@local_prompt_len = @local_prompt ? @local_prompt.length : 0
return (@prompt_prefix_length)
end
end
# Set up the prompt and expand it. Called from readline() and
# rl_callback_handler_install ().
def rl_set_prompt(prompt)
@rl_prompt = prompt ? prompt.dup : nil
@rl_display_prompt = @rl_prompt ? @rl_prompt : ""
@rl_visible_prompt_length = rl_expand_prompt(@rl_prompt)
0
end
def get_term_capabilities(buffer)
hash = {}
`infocmp -C`.split(':').select{|x| x =~ /(.*)=(.*)/ and hash[$1]=$2.gsub('\\E',"\e").gsub(/\^(.)/){($1[0].ord ^ ((?a..?z).include?($1[0]) ? 0x60 : 0x40)).chr}}
@_rl_term_at7 = hash["@7"]
@_rl_term_DC = hash["DC"]
@_rl_term_IC = hash["IC"]
@_rl_term_clreol = hash["ce"]
@_rl_term_clrpag = hash["cl"]
@_rl_term_cr = hash["cr"]
@_rl_term_dc = hash["dc"]
@_rl_term_ei = hash["ei"]
@_rl_term_ic = hash["ic"]
@_rl_term_im = hash["im"]
@_rl_term_kD = hash["kD"]
@_rl_term_kH = hash["kH"]
@_rl_term_kI = hash["kI"]
@_rl_term_kd = hash["kd"]
@_rl_term_ke = hash["ke"]
@_rl_term_kh = hash["kh"]
@_rl_term_kl = hash["kl"]
@_rl_term_kr = hash["kr"]
@_rl_term_ks = hash["ks"]
@_rl_term_ku = hash["ku"]
@_rl_term_backspace = hash["le"]
@_rl_term_mm = hash["mm"]
@_rl_term_mo = hash["mo"]
@_rl_term_forward_char = hash["nd"]
@_rl_term_pc = hash["pc"]
@_rl_term_up = hash["up"]
@_rl_visible_bell = hash["vb"]
@_rl_term_vs = hash["vs"]
@_rl_term_ve = hash["ve"]
@tcap_initialized = true
end
# Set the environment variables LINES and COLUMNS to lines and cols,
# respectively.
def sh_set_lines_and_columns(lines, cols)
ENV["LINES"] = lines.to_s
ENV["COLUMNS"] = cols.to_s
end
# Get readline's idea of the screen size. TTY is a file descriptor open
# to the terminal. If IGNORE_ENV is true, we do not pay attention to the
# values of $LINES and $COLUMNS. The tests for TERM_STRING_BUFFER being
# non-null serve to check whether or not we have initialized termcap.
def _rl_get_screen_size(tty, ignore_env)
if @hConsoleHandle
csbi = 0.chr * 24
@GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
wc,wr = csbi[0,4].unpack('SS')
# wr,wc, = `mode con`.scan(/\d+\n/).map{|x| x.to_i}
@_rl_screenwidth = wc
@_rl_screenheight = wr
else
wr, wc = 0
retry_if_interrupted do
wr, wc = `stty size`.split(' ').map { |x| x.to_i }
end
@_rl_screenwidth = wc
@_rl_screenheight = wr
if ignore_env==0 && ENV['LINES']
@_rl_screenheight = ENV['LINES'].to_i
end
if ignore_env==0 && ENV['COLUMNS']
@_rl_screenwidth = ENV['COLUMNS'].to_i
end
end
# If all else fails, default to 80x24 terminal.
if @_rl_screenwidth.nil? || @_rl_screenwidth <= 1
@_rl_screenwidth = 80
end
if @_rl_screenheight.nil? || @_rl_screenheight <= 0
@_rl_screenheight = 24
end
# If we're being compiled as part of bash, set the environment
# variables $LINES and $COLUMNS to new values. Otherwise, just
# do a pair of putenv () or setenv () calls.
sh_set_lines_and_columns(@_rl_screenheight, @_rl_screenwidth)
if !@_rl_term_autowrap
@_rl_screenwidth-=1
end
@_rl_screenchars = @_rl_screenwidth * @_rl_screenheight
end
def tgetflag(name)
`infocmp -C -r`.scan(/\w{2}/).include?(name)
end
# Return the function (or macro) definition which would be invoked via
# KEYSEQ if executed in MAP. If MAP is NULL, then the current keymap is
# used. TYPE, if non-NULL, is a pointer to an int which will receive the
# type of the object pointed to. One of ISFUNC (function), ISKMAP (keymap),
# or ISMACR (macro).
def rl_function_of_keyseq(keyseq, map, type)
map ||= @_rl_keymap
map[keyseq]
end
# Bind the key sequence represented by the string KEYSEQ to
# the arbitrary pointer DATA. TYPE says what kind of data is
# pointed to by DATA, right now this can be a function (ISFUNC),
# a macro (ISMACR), or a keymap (ISKMAP). This makes new keymaps
# as necessary. The initial place to do bindings is in MAP.
def rl_generic_bind(type, keyseq, data, map)
map[keyseq] = data
0
end
# Bind the key sequence represented by the string KEYSEQ to
# FUNCTION. This makes new keymaps as necessary. The initial
# place to do bindings is in MAP.
def rl_bind_keyseq_in_map(keyseq, function, map)
rl_generic_bind(ISFUNC, keyseq, function, map)
end
# Bind key sequence KEYSEQ to DEFAULT_FUNC if KEYSEQ is unbound. Right
# now, this is always used to attempt to bind the arrow keys, hence the
# check for rl_vi_movement_mode.
def rl_bind_keyseq_if_unbound_in_map(keyseq, default_func, kmap)
if (keyseq)
func = rl_function_of_keyseq(keyseq, kmap, nil)
if (func.nil? || func == :rl_vi_movement_mode)
return (rl_bind_keyseq_in_map(keyseq, default_func, kmap))
else
return 1
end
end
0
end
def rl_bind_keyseq_if_unbound(keyseq, default_func)
rl_bind_keyseq_if_unbound_in_map(keyseq, default_func, @_rl_keymap)
end
# Bind the arrow key sequences from the termcap description in MAP.
def bind_termcap_arrow_keys(map)
xkeymap = @_rl_keymap
@_rl_keymap = map
rl_bind_keyseq_if_unbound(@_rl_term_ku, :rl_get_previous_history)
rl_bind_keyseq_if_unbound(@_rl_term_kd, :rl_get_next_history)
rl_bind_keyseq_if_unbound(@_rl_term_kr, :rl_forward_char)
rl_bind_keyseq_if_unbound(@_rl_term_kl, :rl_backward_char)
rl_bind_keyseq_if_unbound(@_rl_term_kh, :rl_beg_of_line) # Home
rl_bind_keyseq_if_unbound(@_rl_term_at7, :rl_end_of_line) # End
rl_bind_keyseq_if_unbound(@_rl_term_kD, :rl_delete)
rl_bind_keyseq_if_unbound(@_rl_term_kI, :rl_overwrite_mode)
@_rl_keymap = xkeymap
end
def _rl_init_terminal_io(terminal_name)
term = terminal_name ? terminal_name : ENV["TERM"]
@_rl_term_clrpag = @_rl_term_cr = @_rl_term_clreol = nil
tty = @rl_instream ? @rl_instream.fileno : 0
if no_terminal?
term = "dumb"
@_rl_bind_stty_chars = false
end
@term_string_buffer ||= 0.chr * 2032
@term_buffer ||= 0.chr * 4080
buffer = @term_string_buffer
tgetent_ret = (term != "dumb") ? 1 : -1
if (tgetent_ret <= 0)
buffer = @term_buffer = @term_string_buffer = nil
@_rl_term_autowrap = false # used by _rl_get_screen_size
# Allow calling application to set default height and width, using
#rl_set_screen_size
if (@_rl_screenwidth <= 0 || @_rl_screenheight <= 0)
_rl_get_screen_size(tty, 0)
end
# Defaults.
if (@_rl_screenwidth <= 0 || @_rl_screenheight <= 0)
@_rl_screenwidth = 79
@_rl_screenheight = 24
end
# Everything below here is used by the redisplay code (tputs).
@_rl_screenchars = @_rl_screenwidth * @_rl_screenheight
@_rl_term_cr = "\r"
@_rl_term_im = @_rl_term_ei = @_rl_term_ic = @_rl_term_IC = nil
@_rl_term_up = @_rl_term_dc = @_rl_term_DC = @_rl_visible_bell = nil
@_rl_term_ku = @_rl_term_kd = @_rl_term_kl = @_rl_term_kr = nil
@_rl_term_kh = @_rl_term_kH = @_rl_term_kI = @_rl_term_kD = nil
@_rl_term_ks = @_rl_term_ke = @_rl_term_at7 = nil
@_rl_term_mm = @_rl_term_mo = nil
@_rl_term_ve = @_rl_term_vs = nil
@_rl_term_forward_char = nil
@_rl_terminal_can_insert = @term_has_meta = false
# Reasonable defaults for tgoto(). Readline currently only uses
# tgoto if _rl_term_IC or _rl_term_DC is defined, but just in case we
# change that later...
@_rl_term_backspace = "\b"
return 0
end
get_term_capabilities(buffer)
@_rl_term_cr ||= "\r"
@_rl_term_autowrap = !!(tgetflag("am") && tgetflag("xn"))
# Allow calling application to set default height and width, using
# rl_set_screen_size
if (@_rl_screenwidth <= 0 || @_rl_screenheight <= 0)
_rl_get_screen_size(tty, 0)
end
# Check to see if this terminal has a meta key and clear the capability
# variables if there is none.
@term_has_meta = !!(tgetflag("km") || tgetflag("MT"))
if !@term_has_meta
@_rl_term_mm = @_rl_term_mo = nil
end
# Attempt to find and bind the arrow keys. Do not override already
# bound keys in an overzealous attempt, however.
bind_termcap_arrow_keys(@emacs_standard_keymap)
bind_termcap_arrow_keys(@vi_movement_keymap)
bind_termcap_arrow_keys(@vi_insertion_keymap)
return 0
end
# New public way to set the system default editing chars to their readline
# equivalents.
def rl_tty_set_default_bindings(kmap)
h = {}
retry_if_interrupted do
h = Hash[*`stty -a`.scan(/(\w+) = ([^;]+);/).flatten]
end
h.each {|k,v| v.gsub!(/\^(.)/){($1[0].ord ^ ((?a..?z).include?($1[0]) ? 0x60 : 0x40)).chr}}
kmap[h['erase']] = :rl_rubout
kmap[h['kill']] = :rl_unix_line_discard
kmap[h['werase']] = :rl_unix_word_rubout
kmap[h['lnext']] = :rl_quoted_insert
end
# If this system allows us to look at the values of the regular
# input editing characters, then bind them to their readline
# equivalents, iff the characters are not bound to keymaps.
def readline_default_bindings()
if @_rl_bind_stty_chars
rl_tty_set_default_bindings(@_rl_keymap)
end
end
def _rl_init_eightbit()
end
# Do key bindings from a file. If FILENAME is NULL it defaults
# to the first non-null filename from this list:
# 1. the filename used for the previous call
# 2. the value of the shell variable `INPUTRC'
# 3. ~/.inputrc
# 4. /etc/inputrc
# If the file existed and could be opened and read, 0 is returned,
# otherwise errno is returned.
def rl_read_init_file(filename)
# Default the filename.
filename ||= @last_readline_init_file
filename ||= ENV["INPUTRC"]
if (filename.nil? || filename == '')
filename = DEFAULT_INPUTRC
# Try to read DEFAULT_INPUTRC; fall back to SYS_INPUTRC on failure
if (_rl_read_init_file(filename, 0) == 0)
return 0
end
filename = SYS_INPUTRC
end
if RUBY_PLATFORM =~ /mswin|mingw/
return 0 if (_rl_read_init_file(filename, 0) == 0)
filename = "~/_inputrc"
end
return (_rl_read_init_file(filename, 0))
end
def _rl_read_init_file(filename, include_level)
@current_readline_init_file = filename
@current_readline_init_include_level = include_level
openname = File.expand_path(filename)
begin
buffer = nil
File.open(openname) do |file|
buffer = file.read
end
rescue
return -1
end
if (include_level == 0 && filename != @last_readline_init_file)
@last_readline_init_file = filename.dup
end
@currently_reading_init_file = true
# Loop over the lines in the file. Lines that start with `#' are
# comments; all other lines are commands for readline initialization.
@current_readline_init_lineno = 1
buffer.each_line do |line|
line.strip!
next if line =~ /^#/
next if line == ''
rl_parse_and_bind(line)
end
return 0
end
# Push _rl_parsing_conditionalized_out, and set parser state based
# on ARGS.
def parser_if(args)
# Push parser state.
@if_stack << @_rl_parsing_conditionalized_out
# If parsing is turned off, then nothing can turn it back on except
# for finding the matching endif. In that case, return right now.
if @_rl_parsing_conditionalized_out
return 0
end
args.downcase!
# Handle "$if term=foo" and "$if mode=emacs" constructs. If this
# isn't term=foo, or mode=emacs, then check to see if the first
# word in ARGS is the same as the value stored in rl_readline_name.
if (@rl_terminal_name && args =~ /^term=/)
# Terminals like "aaa-60" are equivalent to "aaa".
tname = @rl_terminal_name.downcase.gsub(/-.*$/,'')
# Test the `long' and `short' forms of the terminal name so that
#if someone has a `sun-cmd' and does not want to have bindings
#that will be executed if the terminal is a `sun', they can put
#`$if term=sun-cmd' into their .inputrc.
@_rl_parsing_conditionalized_out = (args[5..-1] != tname && args[5..-1] != @rl_terminal_name.downcase)
elsif args =~ /^mode=/
if args[5..-1] == "emacs"
mode = @emacs_mode
elsif args[5..-1] == "vi"
mode = @vi_mode
else
mode = @no_mode
end
@_rl_parsing_conditionalized_out = (mode != @rl_editing_mode)
# Check to see if the first word in ARGS is the same as the
# value stored in rl_readline_name.
elsif (args == @rl_readline_name)
@_rl_parsing_conditionalized_out = false
else
@_rl_parsing_conditionalized_out = true
end
return 0
end
# Invert the current parser state if there is anything on the stack.
def parser_else(args)
if @if_stack.empty?
#_rl_init_file_error ("$else found without matching $if")
return 0
end
# Check the previous (n) levels of the stack to make sure that
# we haven't previously turned off parsing.
return 0 if @if_stack.detect {|x| x }
# Invert the state of parsing if at top level.
@_rl_parsing_conditionalized_out = !@_rl_parsing_conditionalized_out
return 0
end
# Terminate a conditional, popping the value of
# _rl_parsing_conditionalized_out from the stack.
def parser_endif(args)
if (@if_stack.length>0)
@_rl_parsing_conditionalized_out = @if_stack.pop
else
#_rl_init_file_error ("$endif without matching $if")
end
0
end
def parser_include(args)
return 0 if (@_rl_parsing_conditionalized_out)
old_init_file = @current_readline_init_file
old_line_number = @current_readline_init_lineno
old_include_level = @current_readline_init_include_level
r = _rl_read_init_file(args, old_include_level + 1)
@current_readline_init_file = old_init_file
@current_readline_init_lineno = old_line_number
@current_readline_init_include_level = old_include_level
return r
end
# Handle a parser directive. STATEMENT is the line of the directive
# without any leading `$'.
def handle_parser_directive(statement)
directive,args = statement.split(' ')
case directive.downcase
when "if"
parser_if(args)
return 0
when "endif"
parser_endif(args)
return 0
when "else"
parser_else(args)
return 0
when "include"
parser_include(args)
return 0
end
#_rl_init_file_error("unknown parser directive")
return 1
end
def rl_variable_bind(name,value)
case name
when "bind-tty-special-chars"
@_rl_bind_stty_chars = value.nil? || value=='1' || value == 'on'
when "blink-matching-paren"
@rl_blink_matching_paren = value.nil? || value=='1' || value == 'on'
when "byte-oriented"
@rl_byte_oriented = value.nil? || value=='1' || value == 'on'
when "completion-ignore-case"
@_rl_completion_case_fold = value.nil? || value=='1' || value == 'on'
when "convert-meta"
@_rl_convert_meta_chars_to_ascii = value.nil? || value=='1' || value == 'on'
when "disable-completion"
@rl_inhibit_completion = value.nil? || value=='1' || value == 'on'
when "enable-keypad"
@_rl_enable_keypad = value.nil? || value=='1' || value == 'on'
when "expand-tilde"
@rl_complete_with_tilde_expansion = value.nil? || value=='1' || value == 'on'
when "history-preserve-point"
@_rl_history_preserve_point = value.nil? || value=='1' || value == 'on'
when "horizontal-scroll-mode"
@_rl_horizontal_scroll_mode = value.nil? || value=='1' || value == 'on'
when "input-meta"
@_rl_meta_flag = value.nil? || value=='1' || value == 'on'
when "mark-directories"
@_rl_complete_mark_directories = value.nil? || value=='1' || value == 'on'
when "mark-modified-lines"
@_rl_mark_modified_lines = value.nil? || value=='1' || value == 'on'
when "mark-symlinked-directories"
@_rl_complete_mark_symlink_dirs = value.nil? || value=='1' || value == 'on'
when "match-hidden-files"
@_rl_match_hidden_files = value.nil? || value=='1' || value == 'on'
when "meta-flag"
@_rl_meta_flag = value.nil? || value=='1' || value == 'on'
when "output-meta"
@_rl_output_meta_chars = value.nil? || value=='1' || value == 'on'
when "page-completions"
@_rl_page_completions = value.nil? || value=='1' || value == 'on'
when "prefer-visible-bell"
@_rl_prefer_visible_bell = value.nil? || value=='1' || value == 'on'
when "print-completions-horizontally"
@_rl_print_completions_horizontally = value.nil? || value=='1' || value == 'on'
when "show-all-if-ambiguous"
@_rl_complete_show_all = value.nil? || value=='1' || value == 'on'
when "show-all-if-unmodified"
@_rl_complete_show_unmodified = value.nil? || value=='1' || value == 'on'
when "visible-stats"
@rl_visible_stats = value.nil? || value=='1' || value == 'on'
when "bell-style"
case value
when "none","off"
@_rl_bell_preference = NO_BELL
when "audible", "on"
@_rl_bell_preference = AUDIBLE_BELL
when "visible"
@_rl_bell_preference = VISIBLE_BELL
else
@_rl_bell_preference = AUDIBLE_BELL
end
when "comment-begin"
@_rl_comment_begin = value.dup
when "completion-query-items"
@rl_completion_query_items = value.to_i
when "editing-mode"
case value
when "vi"
# This is a NOOP until the rest of Vi-mode is working.
when "emacs"
@_rl_keymap = @emacs_standard_keymap
@rl_editing_mode = @emacs_mode
end
when "isearch-terminators"
@_rl_isearch_terminators = instance_eval(value)
when "keymap"
case value
when "emacs","emacs-standard","emacs-meta","emacs-ctlx"
@_rl_keymap = @emacs_standard_keymap
when "vi","vi-move","vi-command"
# This is a NOOP until the rest of Vi-mode is working.
when "vi-insert"
# This is a NOOP until the rest of Vi-mode is working.
end
end
end
def rl_named_function(name)
case name
when "accept-line"
return :rl_newline
when "arrow-key-prefix"
return :rl_arrow_keys
when "backward-delete-char"
return :rl_rubout
when "character-search"
return :rl_char_search
when "character-search-backward"
return :rl_backward_char_search
when "copy-region-as-kill"
return :rl_copy_region_to_kill
when "delete-char"
return :rl_delete
when "delete-char-or-list"
return :rl_delete_or_show_completions
when "forward-backward-delete-char"
return :rl_rubout_or_delete
when "kill-whole-line"
return :rl_kill_full_line
when "non-incremental-forward-search-history"
return :rl_noninc_forward_search
when "non-incremental-reverse-search-history"
return :rl_noninc_reverse_search
when "non-incremental-forward-search-history-again"
return :rl_noninc_forward_search_again
when "non-incremental-reverse-search-history-again"
return :rl_noninc_reverse_search_again
when "redraw-current-line"
return :rl_refresh_line
when "self-insert"
return :rl_insert
when "undo"
return :rl_undo_command
when "beginning-of-line"
return :rl_beg_of_line
else
if name =~ /^[-a-z]+$/
return ('rl_'+name.gsub('-','_')).to_sym
end
end
nil
end
# Bind KEY to FUNCTION. Returns non-zero if KEY is out of range.
def rl_bind_key(key, function)
@_rl_keymap[key] = function
@rl_binding_keymap = @_rl_keymap
0
end
# Read the binding command from STRING and perform it.
# A key binding command looks like: Keyname: function-name\0,
# a variable binding command looks like: set variable value.
# A new-style keybinding looks like "\C-x\C-x": exchange-point-and-mark.
def rl_parse_and_bind(string)
# If this is a parser directive, act on it.
if (string[0,1] == "$")
handle_parser_directive(string[1..-1])
return 0
end
# If we aren't supposed to be parsing right now, then we're done.
return 0 if @_rl_parsing_conditionalized_out
if string =~ /^set/i
_,var,value = string.downcase.split(' ')
rl_variable_bind(var, value)
return 0
end
if string =~ /"(.*)"\s*:\s*(.*)$/
key, funname = $1, $2
rl_bind_key(key, rl_named_function(funname))
end
0
end
def _rl_enable_meta_key()
if(@term_has_meta && @_rl_term_mm)
@_rl_out_stream.write(@_rl_term_mm)
end
end
def rl_set_keymap_from_edit_mode()
if (@rl_editing_mode == @emacs_mode)
@_rl_keymap = @emacs_standard_keymap
elsif (@rl_editing_mode == @vi_mode)
@_rl_keymap = @vi_insertion_keymap
end
end
def rl_get_keymap_name_from_edit_mode()
if (@rl_editing_mode == @emacs_mode)
"emacs"
elsif (@rl_editing_mode == @vi_mode)
"vi"
else
"none"
end
end
# Bind some common arrow key sequences in MAP.
def bind_arrow_keys_internal(map)
xkeymap = @_rl_keymap
@_rl_keymap = map
if RUBY_PLATFORM =~ /mswin|mingw/
rl_bind_keyseq_if_unbound("\340H", :rl_get_previous_history) # Up
rl_bind_keyseq_if_unbound("\340P", :rl_get_next_history) # Down
rl_bind_keyseq_if_unbound("\340M", :rl_forward_char) # Right
rl_bind_keyseq_if_unbound("\340K", :rl_backward_char) # Left
rl_bind_keyseq_if_unbound("\340G", :rl_beg_of_line) # Home
rl_bind_keyseq_if_unbound("\340O", :rl_end_of_line) # End
rl_bind_keyseq_if_unbound("\340s", :rl_backward_word) # Ctrl-Left
rl_bind_keyseq_if_unbound("\340t", :rl_forward_word) # Ctrl-Right
rl_bind_keyseq_if_unbound("\340S", :rl_delete) # Delete
rl_bind_keyseq_if_unbound("\340R", :rl_overwrite_mode) # Insert
else
rl_bind_keyseq_if_unbound("\033[A", :rl_get_previous_history)
rl_bind_keyseq_if_unbound("\033[B", :rl_get_next_history)
rl_bind_keyseq_if_unbound("\033[C", :rl_forward_char)
rl_bind_keyseq_if_unbound("\033[D", :rl_backward_char)
rl_bind_keyseq_if_unbound("\033[H", :rl_beg_of_line)
rl_bind_keyseq_if_unbound("\033[F", :rl_end_of_line)
rl_bind_keyseq_if_unbound("\033OA", :rl_get_previous_history)
rl_bind_keyseq_if_unbound("\033OB", :rl_get_next_history)
rl_bind_keyseq_if_unbound("\033OC", :rl_forward_char)
rl_bind_keyseq_if_unbound("\033OD", :rl_backward_char)
rl_bind_keyseq_if_unbound("\033OH", :rl_beg_of_line)
rl_bind_keyseq_if_unbound("\033OF", :rl_end_of_line)
end
@_rl_keymap = xkeymap
end
# Try and bind the common arrow key prefixes after giving termcap and
# the inputrc file a chance to bind them and create `real' keymaps
# for the arrow key prefix.
def bind_arrow_keys()
bind_arrow_keys_internal(@emacs_standard_keymap)
bind_arrow_keys_internal(@vi_movement_keymap)
bind_arrow_keys_internal(@vi_insertion_keymap)
end
# Initialize the entire state of the world.
def readline_initialize_everything()
# Set up input and output if they are not already set up.
@rl_instream ||= $stdin
@rl_outstream ||= $stdout
# Bind _rl_in_stream and _rl_out_stream immediately. These values
# may change, but they may also be used before readline_internal ()
# is called.
@_rl_in_stream = @rl_instream
@_rl_out_stream = @rl_outstream
# Allocate data structures.
@rl_line_buffer = ""
# Initialize the terminal interface.
@rl_terminal_name ||= ENV["TERM"]
_rl_init_terminal_io(@rl_terminal_name)
# Bind tty characters to readline functions.
readline_default_bindings()
# Decide whether we should automatically go into eight-bit mode.
_rl_init_eightbit()
# Read in the init file.
rl_read_init_file(nil)
# XXX
if (@_rl_horizontal_scroll_mode && @_rl_term_autowrap)
@_rl_screenwidth -= 1
@_rl_screenchars -= @_rl_screenheight
end
# Override the effect of any `set keymap' assignments in the
# inputrc file.
rl_set_keymap_from_edit_mode()
# Try to bind a common arrow key prefix, if not already bound.
bind_arrow_keys()
# Enable the meta key, if this terminal has one.
if @_rl_enable_meta
_rl_enable_meta_key()
end
# If the completion parser's default word break characters haven't
# been set yet, then do so now.
@rl_completer_word_break_characters ||= @rl_basic_word_break_characters
end
def _rl_init_line_state()
@rl_point = @rl_end = @rl_mark = 0
@rl_line_buffer = ""
end
# Set the history pointer back to the last entry in the history.
def _rl_start_using_history()
using_history()
@_rl_saved_line_for_history = nil
end
def cr_faster(new, cur)
(new + 1) < (cur - new)
end
#* _rl_last_c_pos is an absolute cursor position in multibyte locales and a
# buffer index in others. This macro is used when deciding whether the
# current cursor position is in the middle of a prompt string containing
# invisible characters.
def prompt_ending_index()
if !@rl_byte_oriented
@prompt_physical_chars
else
(@prompt_last_invisible+1)
end
end
# Initialize the VISIBLE_LINE and INVISIBLE_LINE arrays, and their associated
# arrays of line break markers. MINSIZE is the minimum size of VISIBLE_LINE
# and INVISIBLE_LINE; if it is greater than LINE_SIZE, LINE_SIZE is
# increased. If the lines have already been allocated, this ensures that
# they can hold at least MINSIZE characters.
def init_line_structures(minsize)
if @invisible_line.nil? # initialize it
if (@line_size < minsize)
@line_size = minsize
end
@visible_line = 0.chr * @line_size
@invisible_line = 0.chr * @line_size # 1.chr
elsif (@line_size < minsize) # ensure it can hold MINSIZE chars
@line_size *= 2
if (@line_size < minsize)
@line_size = minsize
end
@visible_line << 0.chr * (@line_size - @visible_line.length)
@invisible_line << 1.chr * (@line_size - @invisible_line.length)
end
@visible_line[minsize,@line_size-minsize] = 0.chr * (@line_size-minsize)
@invisible_line[minsize,@line_size-minsize] = 1.chr * (@line_size-minsize)
if @vis_lbreaks.nil?
@inv_lbreaks = []
@vis_lbreaks = []
@_rl_wrapped_line = []
@inv_lbreaks[0] = @vis_lbreaks[0] = 0
end
end
# Return the history entry at the current position, as determined by
# history_offset. If there is no entry there, return a NULL pointer.
def current_history()
return ((@history_offset == @history_length) || @the_history.nil?) ? nil : @the_history[@history_offset]
end
def meta_char(c)
c > "\x7f" && c <= "\xff"
end
def ctrl_char(c)
c < "\x20"
end
def isprint(c)
c >= "\x20" && c < "\x7f"
end
def whitespace(c)
(c == ' ' || c == "\t")
end
def w_offset(line, offset)
((line) == 0 ? offset : 0)
end
def vis_llen(l)
((l) > @_rl_vis_botlin ? 0 : (@vis_lbreaks[l+1] - @vis_lbreaks[l]))
end
def inv_llen(l)
(@inv_lbreaks[l+1] - @inv_lbreaks[l])
end
def vis_chars(line)
@visible_line[@vis_lbreaks[line] .. -1]
end
def vis_pos(line)
@vis_lbreaks[line]
end
def vis_line(line)
((line) > @_rl_vis_botlin) ? "" : vis_chars(line)
end
def inv_line(line)
@invisible_line[@inv_lbreaks[line] .. -1]
end
def m_offset(margin, offset)
((margin) == 0 ? offset : 0)
end
# PWP: update_line() is based on finding the middle difference of each
# line on the screen; vis:
#
# /old first difference
# /beginning of line | /old last same /old EOL
# v v v v
# old: eddie> Oh, my little gruntle-buggy is to me, as lurgid as
# new: eddie> Oh, my little buggy says to me, as lurgid as
# ^ ^ ^ ^
# \beginning of line | \new last same \new end of line
# \new first difference
#
# All are character pointers for the sake of speed. Special cases for
# no differences, as well as for end of line additions must be handled.
#
# Could be made even smarter, but this works well enough
def update_line(old, ostart, new, current_line, omax, nmax, inv_botlin)
# If we're at the right edge of a terminal that supports xn, we're
# ready to wrap around, so do so. This fixes problems with knowing
# the exact cursor position and cut-and-paste with certain terminal
# emulators. In this calculation, TEMP is the physical screen
# position of the cursor.
if @encoding == 'X'
old.force_encoding('ASCII-8BIT')
new.force_encoding('ASCII-8BIT')
end
if !@rl_byte_oriented
temp = @_rl_last_c_pos
else
temp = @_rl_last_c_pos - w_offset(@_rl_last_v_pos, @visible_wrap_offset)
end
if (temp == @_rl_screenwidth && @_rl_term_autowrap && !@_rl_horizontal_scroll_mode &&
@_rl_last_v_pos == current_line - 1)
if (!@rl_byte_oriented)
# This fixes only double-column characters, but if the wrapped
# character comsumes more than three columns, spaces will be
# inserted in the string buffer.
if (@_rl_wrapped_line[current_line] > 0)
_rl_clear_to_eol(@_rl_wrapped_line[current_line])
end
if new[0,1] != 0.chr
case @encoding
when 'E'
wc = new.scan(/./me)[0]
ret = wc.length
tempwidth = wc.length
when 'S'
wc = new.scan(/./ms)[0]
ret = wc.length
tempwidth = wc.length
when 'U'
wc = new.scan(/./mu)[0]
ret = wc.length
tempwidth = wc.unpack('U').first >= 0x1000 ? 2 : 1
when 'X'
wc = new[0..-1].force_encoding(@encoding_name)[0]
ret = wc.bytesize
tempwidth = wc.ord >= 0x1000 ? 2 : 1
else
ret = 1
tempwidth = 1
end
else
tempwidth = 0
end
if (tempwidth > 0)
bytes = ret
@rl_outstream.write(new[0,bytes])
@_rl_last_c_pos = tempwidth
@_rl_last_v_pos+=1
if old[ostart,1] != 0.chr
case @encoding
when 'E'
wc = old[ostart..-1].scan(/./me)[0]
ret = wc.length
when 'S'
wc = old[ostart..-1].scan(/./ms)[0]
ret = wc.length
when 'U'
wc = old[ostart..-1].scan(/./mu)[0]
ret = wc.length
when 'X'
wc = old[ostart..-1].force_encoding(@encoding_name)[0]
ret = wc.bytesize
end
else
ret = 0
end
if (ret != 0 && bytes != 0)
if ret != bytes
len = old[ostart..-1].index(0.chr,ret)
old[ostart+bytes,len-ret] = old[ostart+ret,len-ret]
end
old[ostart,bytes] = new[0,bytes]
end
else
@rl_outstream.write(' ')
@_rl_last_c_pos = 1
@_rl_last_v_pos+=1
if (old[ostart,1] != 0.chr && new[0,1] != 0.chr)
old[ostart,1] = new[0,1]
end
end
else
if (new[0,1] != 0.chr)
@rl_outstream.write(new[0,1])
else
@rl_outstream.write(' ')
end
@_rl_last_c_pos = 1
@_rl_last_v_pos+=1
if (old[ostart,1] != 0.chr && new[0,1] != 0.chr)
old[ostart,1] = new[0,1]
end
end
end
# Find first difference.
if (!@rl_byte_oriented)
# See if the old line is a subset of the new line, so that the
# only change is adding characters.
temp = (omax < nmax) ? omax : nmax
if old[ostart,temp]==new[0,temp]
ofd = temp
nfd = temp
else
if (omax == nmax && new[0,omax]==old[ostart,omax])
ofd = omax
nfd = nmax
else
new_offset = 0
old_offset = ostart
ofd = 0
nfd = 0
while(ofd < omax && old[ostart+ofd,1] != 0.chr &&
_rl_compare_chars(old, old_offset, new, new_offset))
old_offset = _rl_find_next_mbchar(old, old_offset, 1, MB_FIND_ANY)
new_offset = _rl_find_next_mbchar(new, new_offset, 1, MB_FIND_ANY)
ofd = old_offset - ostart
nfd = new_offset
end
end
end
else
ofd = 0
nfd = 0
while(ofd < omax && old[ostart+ofd,1] != 0.chr && old[ostart+ofd,1] == new[nfd,1])
ofd += 1
nfd += 1
end
end
# Move to the end of the screen line. ND and OD are used to keep track
# of the distance between ne and new and oe and old, respectively, to
# move a subtraction out of each loop.
oe = old.index(0.chr,ostart+ofd) - ostart
if oe.nil? || oe>omax
oe = omax
end
ne = new.index(0.chr,nfd)
if ne.nil? || ne>omax
ne = nmax
end
# If no difference, continue to next line.
if (ofd == oe && nfd == ne)
return
end
wsatend = true # flag for trailing whitespace
if (!@rl_byte_oriented)
ols = _rl_find_prev_mbchar(old, ostart+oe, MB_FIND_ANY) - ostart
nls = _rl_find_prev_mbchar(new, ne, MB_FIND_ANY)
while ((ols > ofd) && (nls > nfd))
if (!_rl_compare_chars(old, ostart+ols, new, nls))
break
end
if (old[ostart+ols,1] == " ")
wsatend = false
end
ols = _rl_find_prev_mbchar(old, ols+ostart, MB_FIND_ANY) - ostart
nls = _rl_find_prev_mbchar(new, nls, MB_FIND_ANY)
end
else
ols = oe - 1 # find last same
nls = ne - 1
while ((ols > ofd) && (nls > nfd) && old[ostart+ols,1] == new[nls,1])
if (old[ostart+ols,1] != " ")
wsatend = false
end
ols-=1
nls-=1
end
end
if (wsatend)
ols = oe
nls = ne
elsif (!_rl_compare_chars(old, ostart+ols, new, nls))
if (old[ostart+ols,1] != 0.chr) # don't step past the NUL
if !@rl_byte_oriented
ols = _rl_find_next_mbchar(old, ostart+ols, 1, MB_FIND_ANY) - ostart
else
ols+=1
end
end
if (new[nls,1] != 0.chr )
if !@rl_byte_oriented
nls = _rl_find_next_mbchar(new, nls, 1, MB_FIND_ANY)
else
nls+=1
end
end
end
# count of invisible characters in the current invisible line.
current_invis_chars = w_offset(current_line, @wrap_offset)
if (@_rl_last_v_pos != current_line)
_rl_move_vert(current_line)
if (@rl_byte_oriented && current_line == 0 && @visible_wrap_offset!=0)
@_rl_last_c_pos += @visible_wrap_offset
end
end
# If this is the first line and there are invisible characters in the
# prompt string, and the prompt string has not changed, and the current
# cursor position is before the last invisible character in the prompt,
# and the index of the character to move to is past the end of the prompt
# string, then redraw the entire prompt string. We can only do this
# reliably if the terminal supports a `cr' capability.
# This is not an efficiency hack -- there is a problem with redrawing
# portions of the prompt string if they contain terminal escape
# sequences (like drawing the `unbold' sequence without a corresponding
# `bold') that manifests itself on certain terminals.
lendiff = @local_prompt_len
if (current_line == 0 && !@_rl_horizontal_scroll_mode &&
@_rl_term_cr && lendiff > @prompt_visible_length && @_rl_last_c_pos > 0 &&
ofd >= lendiff && @_rl_last_c_pos < prompt_ending_index())
@rl_outstream.write(@_rl_term_cr)
_rl_output_some_chars(@local_prompt,0,lendiff)
if !@rl_byte_oriented
# We take wrap_offset into account here so we can pass correct
# information to _rl_move_cursor_relative.
@_rl_last_c_pos = _rl_col_width(@local_prompt, 0, lendiff) - @wrap_offset
@cpos_adjusted = true
else
@_rl_last_c_pos = lendiff
end
end
o_cpos = @_rl_last_c_pos
# When this function returns, _rl_last_c_pos is correct, and an absolute
# cursor postion in multibyte mode, but a buffer index when not in a
# multibyte locale.
_rl_move_cursor_relative(ofd, old, ostart)
# We need to indicate that the cursor position is correct in the presence
# of invisible characters in the prompt string. Let's see if setting this
# when we make sure we're at the end of the drawn prompt string works.
if (current_line == 0 && !@rl_byte_oriented &&
(@_rl_last_c_pos > 0 || o_cpos > 0) &&
@_rl_last_c_pos == @prompt_physical_chars)
@cpos_adjusted = true
end
# if (len (new) > len (old))
# lendiff == difference in buffer
# col_lendiff == difference on screen
# When not using multibyte characters, these are equal
lendiff = (nls - nfd) - (ols - ofd)
if !@rl_byte_oriented
col_lendiff = _rl_col_width(new, nfd, nls) - _rl_col_width(old, ostart+ofd, ostart+ols)
else
col_lendiff = lendiff
end
# If we are changing the number of invisible characters in a line, and
# the spot of first difference is before the end of the invisible chars,
# lendiff needs to be adjusted.
if (current_line == 0 && !@_rl_horizontal_scroll_mode &&
current_invis_chars != @visible_wrap_offset)
if !@rl_byte_oriented
lendiff += @visible_wrap_offset - current_invis_chars
col_lendiff += @visible_wrap_offset - current_invis_chars
else
lendiff += @visible_wrap_offset - current_invis_chars
col_lendiff = lendiff
end
end
# Insert (diff (len (old), len (new)) ch.
temp = ne - nfd
if !@rl_byte_oriented
col_temp = _rl_col_width(new,nfd,ne)
else
col_temp = temp
end
if (col_lendiff > 0) # XXX - was lendiff
# Non-zero if we're increasing the number of lines.
gl = current_line >= @_rl_vis_botlin && inv_botlin > @_rl_vis_botlin
# If col_lendiff is > 0, implying that the new string takes up more
# screen real estate than the old, but lendiff is < 0, meaning that it
# takes fewer bytes, we need to just output the characters starting from
# the first difference. These will overwrite what is on the display, so
# there's no reason to do a smart update. This can really only happen in
# a multibyte environment.
if lendiff < 0
_rl_output_some_chars(new, nfd, temp)
@_rl_last_c_pos += _rl_col_width(new, nfd, nfd+temp)
# If nfd begins before any invisible characters in the prompt, adjust
# _rl_last_c_pos to account for wrap_offset and set cpos_adjusted to
# let the caller know.
if current_line == 0 && @wrap_offset && nfd <= @prompt_last_invisible
@_rl_last_c_pos -= @wrap_offset
@cpos_adjusted = true
end
return
# Sometimes it is cheaper to print the characters rather than
# use the terminal's capabilities. If we're growing the number
# of lines, make sure we actually cause the new line to wrap
# around on auto-wrapping terminals.
elsif (@_rl_terminal_can_insert && ((2 * col_temp) >= col_lendiff || @_rl_term_IC) && (!@_rl_term_autowrap || !gl))
# If lendiff > prompt_visible_length and _rl_last_c_pos == 0 and
# _rl_horizontal_scroll_mode == 1, inserting the characters with
# _rl_term_IC or _rl_term_ic will screw up the screen because of the
# invisible characters. We need to just draw them.
if (old[ostart+ols,1] != 0.chr && (!@_rl_horizontal_scroll_mode || @_rl_last_c_pos > 0 ||
lendiff <= @prompt_visible_length || current_invis_chars==0))
insert_some_chars(new[nfd..-1], lendiff, col_lendiff)
@_rl_last_c_pos += col_lendiff
elsif ((@rl_byte_oriented) && old[ostart+ols,1] == 0.chr && lendiff > 0)
# At the end of a line the characters do not have to
# be "inserted". They can just be placed on the screen.
# However, this screws up the rest of this block, which
# assumes you've done the insert because you can.
_rl_output_some_chars(new,nfd, lendiff)
@_rl_last_c_pos += col_lendiff
else
_rl_output_some_chars(new,nfd, temp)
@_rl_last_c_pos += col_temp
# If nfd begins before any invisible characters in the prompt, adjust
# _rl_last_c_pos to account for wrap_offset and set cpos_adjusted to
# let the caller know.
if current_line == 0 && @wrap_offset && nfd <= @prompt_last_invisible
@_rl_last_c_pos -= @wrap_offset
@cpos_adjusted = true
end
return
end
# Copy (new) chars to screen from first diff to last match.
temp = nls - nfd
if ((temp - lendiff) > 0)
_rl_output_some_chars(new,(nfd + lendiff),temp - lendiff)
# XXX -- this bears closer inspection. Fixes a redisplay bug
# reported against bash-3.0-alpha by Andreas Schwab involving
# multibyte characters and prompt strings with invisible
# characters, but was previously disabled.
@_rl_last_c_pos += _rl_col_width(new,nfd+lendiff, nfd+lendiff+temp-col_lendiff)
end
else
# cannot insert chars, write to EOL
_rl_output_some_chars(new,nfd, temp)
@_rl_last_c_pos += col_temp
# If we're in a multibyte locale and were before the last invisible
# char in the current line (which implies we just output some invisible
# characters) we need to adjust _rl_last_c_pos, since it represents
# a physical character position.
end
else # Delete characters from line.
# If possible and inexpensive to use terminal deletion, then do so.
if (@_rl_term_dc && (2 * col_temp) >= -col_lendiff)
# If all we're doing is erasing the invisible characters in the
# prompt string, don't bother. It screws up the assumptions
# about what's on the screen.
if (@_rl_horizontal_scroll_mode && @_rl_last_c_pos == 0 &&
-lendiff == @visible_wrap_offset)
col_lendiff = 0
end
if (col_lendiff!=0)
delete_chars(-col_lendiff) # delete (diff) characters
end
# Copy (new) chars to screen from first diff to last match
temp = nls - nfd
if (temp > 0)
# If nfd begins at the prompt, or before the invisible characters in
# the prompt, we need to adjust _rl_last_c_pos in a multibyte locale
# to account for the wrap offset and set cpos_adjusted accordingly.
_rl_output_some_chars(new,nfd, temp)
if !@rl_byte_oriented
@_rl_last_c_pos += _rl_col_width(new,nfd,nfd+temp)
if current_line == 0 && @wrap_offset && nfd <= @prompt_last_invisible
@_rl_last_c_pos -= @wrap_offset
@cpos_adjusted = true
end
else
@_rl_last_c_pos += temp
end
end
# Otherwise, print over the existing material.
else
if (temp > 0)
# If nfd begins at the prompt, or before the invisible characters in
# the prompt, we need to adjust _rl_last_c_pos in a multibyte locale
# to account for the wrap offset and set cpos_adjusted accordingly.
_rl_output_some_chars(new,nfd, temp)
@_rl_last_c_pos += col_temp # XXX
if !@rl_byte_oriented
if current_line == 0 && @wrap_offset && nfd <= @prompt_last_invisible
@_rl_last_c_pos -= @wrap_offset
@cpos_adjusted = true
end
end
end
lendiff = (oe) - (ne)
if !@rl_byte_oriented
col_lendiff = _rl_col_width(old, ostart, ostart+oe) - _rl_col_width(new, 0, ne)
else
col_lendiff = lendiff
end
if (col_lendiff!=0)
if (@_rl_term_autowrap && current_line < inv_botlin)
space_to_eol(col_lendiff)
else
_rl_clear_to_eol(col_lendiff)
end
end
end
end
end
# Basic redisplay algorithm.
def rl_redisplay()
return if !@readline_echoing_p
_rl_wrapped_multicolumn = 0
@rl_display_prompt ||= ""
if (@invisible_line.nil? || @vis_lbreaks.nil?)
init_line_structures(0)
rl_on_new_line()
end
# Draw the line into the buffer.
@cpos_buffer_position = -1
line = @invisible_line
out = inv_botlin = 0
# Mark the line as modified or not. We only do this for history
# lines.
modmark = 0
if (@_rl_mark_modified_lines && current_history() && @rl_undo_list)
line[out,1] = '*'
out += 1
line[out,1] = 0.chr
modmark = 1
end
# If someone thought that the redisplay was handled, but the currently
# visible line has a different modification state than the one about
# to become visible, then correct the caller's misconception.
if (@visible_line[0,1] != @invisible_line[0,1])
@rl_display_fixed = false
end
# If the prompt to be displayed is the `primary' readline prompt (the
# one passed to readline()), use the values we have already expanded.
# If not, use what's already in rl_display_prompt. WRAP_OFFSET is the
# number of non-visible characters in the prompt string.
if (@rl_display_prompt == @rl_prompt || @local_prompt)
if (@local_prompt_prefix && @forced_display)
_rl_output_some_chars(@local_prompt_prefix,0,@local_prompt_prefix.length)
end
if (@local_prompt_len > 0)
temp = @local_prompt_len + out + 2
if (temp >= @line_size)
@line_size = (temp + 1024) - (temp % 1024)
if @visible_line.length >= @line_size
@visible_line = @visible_line[0,@line_size]
else
@visible_line += 0.chr * (@line_size-@visible_line.length)
end
if @invisible_line.length >= @line_size
@invisible_line = @invisible_line[0,@line_size]
else
@invisible_line += 0.chr * (@line_size-@invisible_line.length)
end
if @encoding=='X'
@visible_line.force_encoding('ASCII-8BIT')
@invisible_line.force_encoding('ASCII-8BIT')
end
line = @invisible_line
end
line[out,@local_prompt_len] = @local_prompt
out += @local_prompt_len
end
line[out,1] = 0.chr
@wrap_offset = @local_prompt_len - @prompt_visible_length
else
prompt_this_line = @rl_display_prompt.rindex("\n")
if prompt_this_line.nil?
prompt_this_line = 0
else
prompt_this_line+=1
pmtlen = prompt_this_line # temp var
if (@forced_display)
_rl_output_some_chars(@rl_display_prompt,0,pmtlen)
# Make sure we are at column zero even after a newline,
#regardless of the state of terminal output processing.
if (pmtlen < 2 || @rl_display_prompt[prompt_this_line-2,1] != "\r")
cr()
end
end
end
@prompt_physical_chars = pmtlen = @rl_display_prompt.length - prompt_this_line
temp = pmtlen + out + 2
if (temp >= @line_size)
@line_size = (temp + 1024) - (temp % 1024)
if @visible_line.length >= @line_size
@visible_line = @visible_line[0,@line_size]
else
@visible_line += 0.chr * (@line_size-@visible_line.length)
end
if @invisible_line.length >= @line_size
@invisible_line = @invisible_line[0,@line_size]
else
@invisible_line += 0.chr * (@line_size-@invisible_line.length)
end
if @encoding=='X'
@visible_line.force_encoding('ASCII-8BIT')
@invisible_line.force_encoding('ASCII-8BIT')
end
line = @invisible_line
end
line[out,pmtlen] = @rl_display_prompt[prompt_this_line,pmtlen]
out += pmtlen
line[out,1] = 0.chr
@wrap_offset = @prompt_invis_chars_first_line = 0
end
# inv_lbreaks[i] is where line i starts in the buffer.
@inv_lbreaks[newlines = 0] = 0
lpos = @prompt_physical_chars + modmark
@_rl_wrapped_line = Array.new(@visible_line.length,0)
num = 0
# prompt_invis_chars_first_line is the number of invisible characters in
# the first physical line of the prompt.
# wrap_offset - prompt_invis_chars_first_line is the number of invis
# chars on the second line.
# what if lpos is already >= _rl_screenwidth before we start drawing the
# contents of the command line?
while (lpos >= @_rl_screenwidth)
# fix from Darin Johnson <darin@acuson.com> for prompt string with
# invisible characters that is longer than the screen width. The
# prompt_invis_chars_first_line variable could be made into an array
# saying how many invisible characters there are per line, but that's
# probably too much work for the benefit gained. How many people have
# prompts that exceed two physical lines?
# Additional logic fix from Edward Catmur <ed@catmur.co.uk>
if (!@rl_byte_oriented)
n0 = num
temp = @local_prompt_len
while (num < temp)
z = _rl_col_width(@local_prompt, n0, num)
if (z > @_rl_screenwidth)
num = _rl_find_prev_mbchar(@local_prompt, num, MB_FIND_ANY)
break
elsif (z == @_rl_screenwidth)
break
end
num+=1
end
temp = num
else
temp = ((newlines + 1) * @_rl_screenwidth)
end
# Now account for invisible characters in the current line.
temp += (@local_prompt_prefix.nil? ? ((newlines == 0) ? @prompt_invis_chars_first_line :
((newlines == 1) ? @wrap_offset : 0)) :
((newlines == 0) ? @wrap_offset : 0))
@inv_lbreaks[newlines+=1] = temp
if !@rl_byte_oriented
lpos -= _rl_col_width(@local_prompt, n0, num)
else
lpos -= @_rl_screenwidth
end
end
@prompt_last_screen_line = newlines
# Draw the rest of the line (after the prompt) into invisible_line, keeping
# track of where the cursor is (cpos_buffer_position), the number of the line containing
# the cursor (lb_linenum), the last line number (inv_botlin).
# It maintains an array of line breaks for display (inv_lbreaks).
# This handles expanding tabs for display and displaying meta characters.
lb_linenum = 0
_in = 0
if !@rl_byte_oriented && @rl_end>0
case @encoding
when 'E'
wc = @rl_line_buffer[0,@rl_end].scan(/./me)[0]
wc_bytes = wc ? wc.length : 1
when 'S'
wc = @rl_line_buffer[0,@rl_end].scan(/./ms)[0]
wc_bytes = wc ? wc.length : 1
when 'U'
wc = @rl_line_buffer[0,@rl_end].scan(/./mu)[0]
wc_bytes = wc ? wc.length : 1
when 'X'
wc = @rl_line_buffer[0,@rl_end].force_encoding(@encoding_name)[0]
wc_bytes = wc ? wc.bytesize : 1
end
else
wc_bytes = 1
end
while(_in < @rl_end)
c = @rl_line_buffer[_in,1]
if(c == 0.chr)
@rl_end = _in
break
end
if (!@rl_byte_oriented)
case @encoding
when 'U'
wc_width = wc && wc.unpack('U').first >= 0x1000 ? 2 : 1
when 'X'
wc_width = wc && wc.ord > 0x1000 ? 2 : 1
else
wc_width = wc ? wc.length : 1
end
end
if (out + 8 >= @line_size) # XXX - 8 for \t
@line_size *= 2
if @visible_line.length>=@line_size
@visible_line = @visible_line[0,@line_size]
else
@visible_line += 0.chr * (@line_size-@visible_line.length)
end
if @invisible_line.length>=@line_size
@invisible_line = @invisible_line[0,@line_size]
else
@invisible_line += 0.chr * (@line_size-@invisible_line.length)
end
line = @invisible_line
end
if (_in == @rl_point)
@cpos_buffer_position = out
lb_linenum = newlines
end
if (false && meta_char(c))
if (!@_rl_output_meta_chars && false)
line[out,4] = "\\%03o" % c.ord
if (lpos + 4 >= @_rl_screenwidth)
temp = @_rl_screenwidth - lpos
@inv_lbreaks[newlines+=1] = out + temp
lpos = 4 - temp
else
lpos += 4
end
out += 4
else
line[out,1] = c
out += 1
lpos+=1
if (lpos >= @_rl_screenwidth)
@inv_lbreaks[newlines+=1] = out
@_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
lpos = 0
end
end
elsif (c == "\t")
newout = out + 8 - lpos % 8
temp = newout - out
if (lpos + temp >= @_rl_screenwidth)
temp2 = @_rl_screenwidth - lpos
@inv_lbreaks[newlines+=1] = out + temp2
lpos = temp - temp2
while (out < newout)
line[out,1] = ' '
out += 1
end
else
while (out < newout)
line[out,1] = ' '
out += 1
end
lpos += temp
end
elsif (c == "\n" && !@_rl_horizontal_scroll_mode && @_rl_term_up)
line[out,1] = 0.chr # XXX - sentinel
out += 1
@inv_lbreaks[newlines+=1] = out
lpos = 0
elsif (ctrl_char(c) || c == RUBOUT)
line[out,1] = '^'
out += 1
lpos+=1
if (lpos >= @_rl_screenwidth)
@inv_lbreaks[newlines+=1] = out
@_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
lpos = 0
end
# NOTE: c[0].ord works identically on both 1.8 and 1.9
line[out,1] = ctrl_char(c) ? (c[0].ord|0x40).chr.upcase : '?'
out += 1
lpos+=1
if (lpos >= @_rl_screenwidth)
@inv_lbreaks[newlines+=1] = out
@_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
lpos = 0
end
else
if (!@rl_byte_oriented)
_rl_wrapped_multicolumn = 0
if (@_rl_screenwidth < lpos + wc_width)
for i in lpos ... @_rl_screenwidth
# The space will be removed in update_line()
line[out,1] = ' '
out += 1
_rl_wrapped_multicolumn+=1
lpos+=1
if (lpos >= @_rl_screenwidth)
@inv_lbreaks[newlines+=1] = out
@_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
lpos = 0
end
end
end
if (_in == @rl_point)
@cpos_buffer_position = out
lb_linenum = newlines
end
line[out,wc_bytes] = @rl_line_buffer[_in,wc_bytes]
out += wc_bytes
for i in 0 ... wc_width
lpos+=1
if (lpos >= @_rl_screenwidth)
@inv_lbreaks[newlines+=1] = out
@_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
lpos = 0
end
end
else
line[out,1] = c
out += 1
lpos+=1
if (lpos >= @_rl_screenwidth)
@inv_lbreaks[newlines+=1] = out
@_rl_wrapped_line[newlines] = _rl_wrapped_multicolumn
lpos = 0
end
end
end
if (!@rl_byte_oriented)
_in += wc_bytes
case @encoding
when 'E'
wc = @rl_line_buffer[_in,@rl_end - _in].scan(/./me)[0]
wc_bytes = wc ? wc.length : 1
when 'S'
wc = @rl_line_buffer[_in,@rl_end - _in].scan(/./ms)[0]
wc_bytes = wc ? wc.length : 1
when 'U'
wc = @rl_line_buffer[_in,@rl_end - _in].scan(/./mu)[0]
wc_bytes = wc ? wc.length : 1
when 'X'
wc = @rl_line_buffer[_in,@rl_end - _in].force_encoding(@encoding_name)[0]
wc_bytes = wc ? wc.bytesize : 1
end
else
_in+=1
end
end
line[out,1] = 0.chr
if (@cpos_buffer_position < 0)
@cpos_buffer_position = out
lb_linenum = newlines
end
inv_botlin = newlines
@inv_lbreaks[newlines+1] = out
cursor_linenum = lb_linenum
# CPOS_BUFFER_POSITION == position in buffer where cursor should be placed.
# CURSOR_LINENUM == line number where the cursor should be placed.
# PWP: now is when things get a bit hairy. The visible and invisible
# line buffers are really multiple lines, which would wrap every
# (screenwidth - 1) characters. Go through each in turn, finding
# the changed region and updating it. The line order is top to bottom.
# If we can move the cursor up and down, then use multiple lines,
# otherwise, let long lines display in a single terminal line, and
# horizontally scroll it.
if (!@_rl_horizontal_scroll_mode && @_rl_term_up)
if (!@rl_display_fixed || @forced_display)
@forced_display = false
# If we have more than a screenful of material to display, then
# only display a screenful. We should display the last screen,
# not the first.
if (out >= @_rl_screenchars)
if (!@rl_byte_oriented)
out = _rl_find_prev_mbchar(line, @_rl_screenchars, MB_FIND_ANY)
else
out = @_rl_screenchars - 1
end
end
# The first line is at character position 0 in the buffer. The
# second and subsequent lines start at inv_lbreaks[N], offset by
# OFFSET (which has already been calculated above).
# For each line in the buffer, do the updating display.
linenum = 0
while linenum <= inv_botlin
# This can lead us astray if we execute a program that changes
#the locale from a non-multibyte to a multibyte one.
o_cpos = @_rl_last_c_pos
@cpos_adjusted = false
update_line(@visible_line,vis_pos(linenum), inv_line(linenum), linenum,
vis_llen(linenum), inv_llen(linenum), inv_botlin)
if (linenum == 0 && !@rl_byte_oriented &&
!@cpos_adjusted &&
@_rl_last_c_pos != o_cpos &&
@_rl_last_c_pos > @wrap_offset &&
o_cpos < @prompt_last_invisible)
@_rl_last_c_pos -= @wrap_offset
end
# If this is the line with the prompt, we might need to
# compensate for invisible characters in the new line. Do
# this only if there is not more than one new line (which
# implies that we completely overwrite the old visible line)
# and the new line is shorter than the old. Make sure we are
# at the end of the new line before clearing.
if (linenum == 0 &&
inv_botlin == 0 && @_rl_last_c_pos == out &&
(@wrap_offset > @visible_wrap_offset) &&
(@_rl_last_c_pos < @visible_first_line_len))
if !@rl_byte_oriented
nleft = @_rl_screenwidth - @_rl_last_c_pos
else
nleft = @_rl_screenwidth + @wrap_offset - @_rl_last_c_pos
end
if (nleft!=0)
_rl_clear_to_eol(nleft)
end
end
# Since the new first line is now visible, save its length.
if (linenum == 0)
@visible_first_line_len = (inv_botlin > 0) ? @inv_lbreaks[1] : out - @wrap_offset
end
linenum += 1
end
# We may have deleted some lines. If so, clear the left over
# blank ones at the bottom out.
if (@_rl_vis_botlin > inv_botlin)
while(linenum <= @_rl_vis_botlin)
tt = vis_chars(linenum)
_rl_move_vert(linenum)
_rl_move_cursor_relative(0, tt)
_rl_clear_to_eol((linenum == @_rl_vis_botlin) ? tt.length : @_rl_screenwidth)
linenum += 1
end
end
@_rl_vis_botlin = inv_botlin
# CHANGED_SCREEN_LINE is set to 1 if we have moved to a
# different screen line during this redisplay.
changed_screen_line = @_rl_last_v_pos != cursor_linenum
if (changed_screen_line)
_rl_move_vert(cursor_linenum)
# If we moved up to the line with the prompt using _rl_term_up,
# the physical cursor position on the screen stays the same,
# but the buffer position needs to be adjusted to account
# for invisible characters.
if (@rl_byte_oriented && cursor_linenum == 0 && @wrap_offset!=0)
@_rl_last_c_pos += @wrap_offset
end
end
# We have to reprint the prompt if it contains invisible
# characters, since it's not generally OK to just reprint
# the characters from the current cursor position. But we
# only need to reprint it if the cursor is before the last
# invisible character in the prompt string.
nleft = @prompt_visible_length + @wrap_offset
if (cursor_linenum == 0 && @wrap_offset > 0 && @_rl_last_c_pos > 0 &&
@_rl_last_c_pos < prompt_ending_index() && @local_prompt)
if (@_rl_term_cr)
@rl_outstream.write(@_rl_term_cr)
end
_rl_output_some_chars(@local_prompt,0,nleft)
if !@rl_byte_oriented
@_rl_last_c_pos = _rl_col_width(@local_prompt, 0, nleft) - @wrap_offset
else
@_rl_last_c_pos = nleft
end
end
# Where on that line? And where does that line start
# in the buffer?
pos = @inv_lbreaks[cursor_linenum]
# nleft == number of characters in the line buffer between the
# start of the line and the desired cursor position.
nleft = @cpos_buffer_position - pos
# NLEFT is now a number of characters in a buffer. When in a
# multibyte locale, however, _rl_last_c_pos is an absolute cursor
# position that doesn't take invisible characters in the prompt
# into account. We use a fudge factor to compensate.
# Since _rl_backspace() doesn't know about invisible characters in the
# prompt, and there's no good way to tell it, we compensate for
# those characters here and call _rl_backspace() directly.
if (@wrap_offset!=0 && cursor_linenum == 0 && nleft < @_rl_last_c_pos)
# TX == new physical cursor position in multibyte locale.
if !@rl_byte_oriented
tx = _rl_col_width(@visible_line, pos, pos+nleft) - @visible_wrap_offset
else
tx = nleft
end
if tx >= 0 && @_rl_last_c_pos > tx
_rl_backspace(@_rl_last_c_pos - tx) # XXX
@_rl_last_c_pos = tx
end
end
# We need to note that in a multibyte locale we are dealing with
# _rl_last_c_pos as an absolute cursor position, but moving to a
# point specified by a buffer position (NLEFT) that doesn't take
# invisible characters into account.
if !@rl_byte_oriented
_rl_move_cursor_relative(nleft, @invisible_line,pos)
elsif (nleft != @_rl_last_c_pos)
_rl_move_cursor_relative(nleft, @invisible_line,pos)
end
end
else # Do horizontal scrolling.
# Always at top line.
@_rl_last_v_pos = 0
# Compute where in the buffer the displayed line should start. This
# will be LMARGIN.
# The number of characters that will be displayed before the cursor.
ndisp = @cpos_buffer_position - @wrap_offset
nleft = @prompt_visible_length + @wrap_offset
# Where the new cursor position will be on the screen. This can be
# longer than SCREENWIDTH; if it is, lmargin will be adjusted.
phys_c_pos = @cpos_buffer_position - (@last_lmargin!=0 ? @last_lmargin : @wrap_offset)
t = @_rl_screenwidth / 3
# If the number of characters had already exceeded the screenwidth,
#last_lmargin will be > 0.
# If the number of characters to be displayed is more than the screen
# width, compute the starting offset so that the cursor is about
# two-thirds of the way across the screen.
if (phys_c_pos > @_rl_screenwidth - 2)
lmargin = @cpos_buffer_position - (2 * t)
if (lmargin < 0)
lmargin = 0
end
# If the left margin would be in the middle of a prompt with
# invisible characters, don't display the prompt at all.
if (@wrap_offset!=0 && lmargin > 0 && lmargin < nleft)
lmargin = nleft
end
elsif (ndisp < @_rl_screenwidth - 2) # XXX - was -1
lmargin = 0
elsif (phys_c_pos < 1)
# If we are moving back towards the beginning of the line and
# the last margin is no longer correct, compute a new one.
lmargin = ((@cpos_buffer_position - 1) / t) * t # XXX
if (@wrap_offset!=0 && lmargin > 0 && lmargin < nleft)
lmargin = nleft
end
else
lmargin = @last_lmargin
end
# If the first character on the screen isn't the first character
#in the display line, indicate this with a special character.
if (lmargin > 0)
line[lmargin,1] = '<'
end
# If SCREENWIDTH characters starting at LMARGIN do not encompass
# the whole line, indicate that with a special character at the
# right edge of the screen. If LMARGIN is 0, we need to take the
# wrap offset into account.
t = lmargin + m_offset(lmargin, @wrap_offset) + @_rl_screenwidth
if (t < out)
line[t - 1,1] = '>'
end
if (!@rl_display_fixed || @forced_display || lmargin != @last_lmargin)
@forced_display = false
update_line(@visible_line,@last_lmargin,@invisible_line[lmargin..-1],
0,
@_rl_screenwidth + @visible_wrap_offset,
@_rl_screenwidth + (lmargin ? 0 : @wrap_offset),
0)
# If the visible new line is shorter than the old, but the number
# of invisible characters is greater, and we are at the end of
# the new line, we need to clear to eol.
t = @_rl_last_c_pos - m_offset(lmargin, @wrap_offset)
if ((m_offset(lmargin, @wrap_offset) > @visible_wrap_offset) &&
(@_rl_last_c_pos == out) &&
t < @visible_first_line_len)
nleft = @_rl_screenwidth - t
_rl_clear_to_eol(nleft)
end
@visible_first_line_len = out - lmargin - m_offset(lmargin, @wrap_offset)
if (@visible_first_line_len > @_rl_screenwidth)
@visible_first_line_len = @_rl_screenwidth
end
_rl_move_cursor_relative(@cpos_buffer_position - lmargin, @invisible_line ,lmargin)
@last_lmargin = lmargin
end
end
@rl_outstream.flush
# Swap visible and non-visible lines.
@visible_line,@invisible_line = @invisible_line,@visible_line
@vis_lbreaks,@inv_lbreaks = @inv_lbreaks,@vis_lbreaks
@rl_display_fixed = false
# If we are displaying on a single line, and last_lmargin is > 0, we
# are not displaying any invisible characters, so set visible_wrap_offset
# to 0.
if (@_rl_horizontal_scroll_mode && @last_lmargin!=0)
@visible_wrap_offset = 0
else
@visible_wrap_offset = @wrap_offset
end
end
def rl_line_buffer
@rl_line_buffer.tr(0.chr, '')
end
# Tell the update routines that we have moved onto a new (empty) line.
def rl_on_new_line()
if (@visible_line)
@visible_line[0,1] = 0.chr
end
@_rl_last_c_pos = @_rl_last_v_pos = 0
@_rl_vis_botlin = @last_lmargin = 0
if (@vis_lbreaks)
@vis_lbreaks[0] = @vis_lbreaks[1] = 0
end
@visible_wrap_offset = 0
0
end
def rl_reset_line_state()
rl_on_new_line()
@rl_display_prompt = @rl_prompt ? @rl_prompt : ""
@forced_display = true
0
end
def _rl_vi_initialize_line
rl_unsetstate(RL_STATE_VICMDONCE)
end
# Initialize readline (and terminal if not already).
def rl_initialize()
# If we have never been called before, initialize the
# terminal and data structures.
if (!@rl_initialized)
rl_setstate(RL_STATE_INITIALIZING)
readline_initialize_everything()
rl_unsetstate(RL_STATE_INITIALIZING)
@rl_initialized = true
rl_setstate(RL_STATE_INITIALIZED)
end
# Initalize the current line information.
_rl_init_line_state()
# We aren't done yet. We haven't even gotten started yet!
@rl_done = false
rl_unsetstate(RL_STATE_DONE)
# Tell the history routines what is going on.
_rl_start_using_history()
# Make the display buffer match the state of the line.
rl_reset_line_state()
# No such function typed yet.
@rl_last_func = nil
# Parsing of key-bindings begins in an enabled state.
@_rl_parsing_conditionalized_out = 0
if (@rl_editing_mode == @vi_mode)
_rl_vi_initialize_line()
end
# Each line starts in insert mode (the default).
_rl_set_insert_mode(RL_IM_DEFAULT, 1)
return 0
end
def _rl_strip_prompt(pmt)
return expand_prompt(pmt).first
end
def _rl_col_width(string,start,_end)
return 0 if _end <= start
# Find the first occurance of 0.chr, which marks the end of the string.
# Because newlines are also in the string as 0.chrs (they are tracked
# seperately), we need to ignore any 0.chrs that lie before _end.
index = string[_end...string.length].index(0.chr)
str = index ? string[0,index+_end] : string
width = 0
case @encoding
when 'N'
return (_end - start)
when 'U'
str[start ... _end].scan(/./mu).each {|s| width += s.unpack('U').first >= 0x1000 ? 2 : 1 }
when 'S'
str[start ... _end].scan(/./ms).each {|s| width += s.length }
when 'E'
str[start ... _end].scan(/./me).each {|s| width += s.length }
when 'X'
str[start ... _end].force_encoding(@encoding_name).codepoints.each {|s| width += s > 0x1000 ? 2 : 1 }
end
width
end
# Write COUNT characters from STRING to the output stream.
def _rl_output_some_chars(string,start,count)
case @encoding
when 'X'
@_rl_out_stream.write(string[start, count].force_encoding(@encoding_name))
else
@_rl_out_stream.write(string[start, count])
end
end
# Tell the update routines that we have moved onto a new line with the
# prompt already displayed. Code originally from the version of readline
# distributed with CLISP. rl_expand_prompt must have already been called
# (explicitly or implicitly). This still doesn't work exactly right.
def rl_on_new_line_with_prompt()
# Initialize visible_line and invisible_line to ensure that they can hold
# the already-displayed prompt.
prompt_size = @rl_prompt.length + 1
init_line_structures(prompt_size)
# Make sure the line structures hold the already-displayed prompt for
# redisplay.
lprompt = @local_prompt ? @local_prompt : @rl_prompt
@visible_line[0,lprompt.length] = lprompt
@invisible_line[0,lprompt.length] = lprompt
# If the prompt contains newlines, take the last tail.
prompt_last_line = rl_prompt.rindex("\n")
if prompt_last_line.nil?
prompt_last_line = @rl_prompt
else
prompt_last_line = @rl_prompt[prompt_last_line..-1]
end
l = prompt_last_line.length
if !@rl_byte_oriented
@_rl_last_c_pos = _rl_col_width(prompt_last_line, 0, l)
else
@_rl_last_c_pos = l
end
# Dissect prompt_last_line into screen lines. Note that here we have
# to use the real screenwidth. Readline's notion of screenwidth might be
# one less, see terminal.c.
real_screenwidth = @_rl_screenwidth + (@_rl_term_autowrap ? 0 : 1)
@_rl_last_v_pos = l / real_screenwidth
# If the prompt length is a multiple of real_screenwidth, we don't know
# whether the cursor is at the end of the last line, or already at the
# beginning of the next line. Output a newline just to be safe.
if (l > 0 && (l % real_screenwidth) == 0)
_rl_output_some_chars("\n",0,1)
end
@last_lmargin = 0
newlines = 0
i = 0
while (i <= l)
@_rl_vis_botlin = newlines
@vis_lbreaks[newlines] = i
newlines += 1
i += real_screenwidth
end
@vis_lbreaks[newlines] = l
@visible_wrap_offset = 0
@rl_display_prompt = @rl_prompt # XXX - make sure it's set
return 0
end
def readline_internal_setup()
@_rl_in_stream = @rl_instream
@_rl_out_stream = @rl_outstream
if (@rl_startup_hook)
send(@rl_startup_hook)
end
# If we're not echoing, we still want to at least print a prompt, because
# rl_redisplay will not do it for us. If the calling application has a
# custom redisplay function, though, let that function handle it.
if (!@readline_echoing_p && @rl_redisplay_function == :rl_redisplay)
if (@rl_prompt && !@rl_already_prompted)
nprompt = _rl_strip_prompt(@rl_prompt)
@_rl_out_stream.write(nprompt)
@_rl_out_stream.flush
end
else
if (@rl_prompt && @rl_already_prompted)
rl_on_new_line_with_prompt()
else
rl_on_new_line()
end
send(@rl_redisplay_function)
end
if (@rl_editing_mode == @vi_mode)
rl_vi_insertion_mode(1, 'i')
end
if (@rl_pre_input_hook)
send(@rl_pre_input_hook)
end
end
# Create a default argument.
def _rl_reset_argument()
@rl_numeric_arg = @rl_arg_sign = 1
@rl_explicit_arg = false
@_rl_argcxt = 0
end
# Ring the terminal bell.
def rl_ding()
if @MessageBeep
@MessageBeep.Call(0)
elsif @readline_echoing_p
if @_rl_bell_preference == VISIBLE_BELL
if (@_rl_visible_bell)
@_rl_out_stream.write(@_rl_visible_bell.chr)
else
$stderr.write("\007")
$stderr.flush
end
elsif @_rl_bell_preference == AUDIBLE_BELL
$stderr.write("\007")
$stderr.flush
end
return 0
end
return -1
end
def _rl_search_getchar(cxt)
# Read a key and decide how to proceed.
rl_setstate(RL_STATE_MOREINPUT)
c = cxt.lastc = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
if !@rl_byte_oriented
cxt.mb = ""
c = cxt.lastc = _rl_read_mbstring(cxt.lastc, cxt.mb, MB_LEN_MAX)
end
c
end
def endsrch_char(c)
((ctrl_char(c) || meta_char(c) || (c) == RUBOUT) && ((c) != "\C-G"))
end
def _rl_input_available
IO.select([ $stdin ], nil, [ $stdin ], @_keyboard_input_timeout)
end
# Process just-read character C according to isearch context CXT. Return
# -1 if the caller should just free the context and return, 0 if we should
# break out of the loop, and 1 if we should continue to read characters.
def _rl_isearch_dispatch(cxt, c)
f = nil
if c.class == Fixnum && c < 0
cxt.sflags |= SF_FAILED
cxt.history_pos = cxt.last_found_line
return -1
end
# Translate the keys we do something with to opcodes.
if (c && @_rl_keymap[c])
f = @_rl_keymap[c]
if (f == :rl_reverse_search_history)
cxt.lastc = (cxt.sflags & SF_REVERSE)!=0 ? -1 : -2
elsif (f == :rl_forward_search_history)
cxt.lastc = (cxt.sflags & SF_REVERSE)!=0 ? -2 : -1
elsif (f == :rl_rubout)
cxt.lastc = -3
elsif (c == "\C-G")
cxt.lastc = -4
elsif (c == "\C-W") # XXX
cxt.lastc = -5
elsif (c == "\C-Y") # XXX
cxt.lastc = -6
end
end
# The characters in isearch_terminators (set from the user-settable
# variable isearch-terminators) are used to terminate the search but
# not subsequently execute the character as a command. The default
# value is "\033\012" (ESC and C-J).
if (cxt.lastc.class == String && cxt.search_terminators.include?(cxt.lastc))
# ESC still terminates the search, but if there is pending
#input or if input arrives within 0.1 seconds (on systems
#with select(2)) it is used as a prefix character
#with rl_execute_next. WATCH OUT FOR THIS! This is intended
#to allow the arrow keys to be used like ^F and ^B are used
#to terminate the search and execute the movement command.
#XXX - since _rl_input_available depends on the application-
#settable keyboard timeout value, this could alternatively
#use _rl_input_queued(100000)
if (cxt.lastc == ESC && _rl_input_available())
rl_execute_next(ESC)
end
return (0)
end
if !@rl_byte_oriented
if (cxt.lastc.class == String && (cxt.mb.length == 1) && endsrch_char(cxt.lastc))
# This sets rl_pending_input to c; it will be picked up the next
# time rl_read_key is called.
rl_execute_next(cxt.lastc)
return (0)
end
elsif (cxt.lastc.class == String && endsrch_char(cxt.lastc))
# This sets rl_pending_input to LASTC; it will be picked up the next
# time rl_read_key is called.
rl_execute_next(cxt.lastc)
return (0)
end
# Now dispatch on the character. `Opcodes' affect the search string or
# state. Other characters are added to the string.
case (cxt.lastc)
# search again
when -1
if (cxt.search_string_index == 0)
if (@last_isearch_string)
cxt.search_string_size = 64 + @last_isearch_string_len
cxt.search_string = @last_isearch_string.dup
cxt.search_string_index = @last_isearch_string_len
rl_display_search(cxt.search_string, (cxt.sflags & SF_REVERSE)!=0, -1)
else
return (1)
end
elsif (cxt.sflags & SF_REVERSE)!=0
cxt.sline_index-=1
elsif (cxt.sline_index != cxt.sline_len)
cxt.sline_index+=1
else
rl_ding()
end
# switch directions
when -2
cxt.direction = -cxt.direction
if (cxt.direction < 0)
cxt.sflags |= SF_REVERSE
else
cxt.sflags &= ~SF_REVERSE
end
# delete character from search string.
when -3 # C-H, DEL
# This is tricky. To do this right, we need to keep a
# stack of search positions for the current search, with
# sentinels marking the beginning and end. But this will
# do until we have a real isearch-undo.
if (cxt.search_string_index == 0)
rl_ding()
else
cxt.search_string_index -= 1
cxt.search_string.chop!
end
when -4 # C-G, abort
rl_replace_line(cxt.lines[cxt.save_line], false)
@rl_point = cxt.save_point
@rl_mark = cxt.save_mark
rl_restore_prompt()
rl_clear_message()
return -1
when -5 # C-W
# skip over portion of line we already matched and yank word
wstart = @rl_point + cxt.search_string_index
if (wstart >= @rl_end)
rl_ding()
else
# if not in a word, move to one.
cval = _rl_char_value(@rl_line_buffer, wstart)
if (!_rl_walphabetic(cval))
rl_ding()
else
if !@rl_byte_oriented
n = _rl_find_next_mbchar(@rl_line_buffer, wstart, 1, MB_FIND_NONZERO)
else
n = wstart+1
end
while (n < @rl_end)
cval = _rl_char_value(@rl_line_buffer, n)
break if !_rl_walphabetic(cval)
if !@rl_byte_oriented
n = _rl_find_next_mbchar(@rl_line_buffer, n, 1, MB_FIND_NONZERO)
else
n = n+1
end
end
wlen = n - wstart + 1
if (cxt.search_string_index + wlen + 1 >= cxt.search_string_size)
cxt.search_string_size += wlen + 1
end
cxt.search_string[cxt.search_string_index..-1] = @rl_line_buffer[wstart,wlen]
cxt.search_string_index += wlen
end
end
when -6 # C-Y
# skip over portion of line we already matched and yank rest
wstart = @rl_point + cxt.search_string_index
if (wstart >= @rl_end)
rl_ding()
else
n = @rl_end - wstart + 1
if (cxt.search_string_index + n + 1 >= cxt.search_string_size)
cxt.search_string_size += n + 1
end
cxt.search_string[cxt.search_string_index..-1] = @rl_line_buffer[wstart,n]
end
# Add character to search string and continue search.
else
if (cxt.search_string_index + 2 >= cxt.search_string_size)
cxt.search_string_size += 128
end
if !@rl_byte_oriented
for j in 0 ... cxt.mb.length
cxt.search_string << cxt.mb[j,1]
cxt.search_string_index += 1
end
else
cxt.search_string << c
cxt.search_string_index += 1
end
end
while (cxt.sflags &= ~(SF_FOUND|SF_FAILED))!=0
limit = cxt.sline_len - cxt.search_string_index + 1
# Search the current line.
while ((cxt.sflags & SF_REVERSE)!=0 ? (cxt.sline_index >= 0) : (cxt.sline_index < limit))
if (cxt.search_string[0,cxt.search_string_index] == cxt.sline[cxt.sline_index,cxt.search_string_index])
cxt.sflags |= SF_FOUND
break
else
cxt.sline_index += cxt.direction
end
end
break if (cxt.sflags & SF_FOUND)!=0
# Move to the next line, but skip new copies of the line
# we just found and lines shorter than the string we're
# searching for.
begin
# Move to the next line.
cxt.history_pos += cxt.direction
# At limit for direction?
if ((cxt.sflags & SF_REVERSE)!=0 ? (cxt.history_pos < 0) : (cxt.history_pos == cxt.hlen))
cxt.sflags |= SF_FAILED
break
end
# We will need these later.
cxt.sline = cxt.lines[cxt.history_pos]
cxt.sline_len = cxt.sline.length
end while ((cxt.prev_line_found && cxt.prev_line_found == cxt.lines[cxt.history_pos]) ||
(cxt.search_string_index > cxt.sline_len))
break if (cxt.sflags & SF_FAILED)!=0
# Now set up the line for searching...
cxt.sline_index = (cxt.sflags & SF_REVERSE)!=0 ? cxt.sline_len - cxt.search_string_index : 0
end
if (cxt.sflags & SF_FAILED)!=0
# We cannot find the search string. Ding the bell.
rl_ding()
cxt.history_pos = cxt.last_found_line
return 1
end
# We have found the search string. Just display it. But don't
# actually move there in the history list until the user accepts
# the location.
if (cxt.sflags & SF_FOUND)!=0
cxt.prev_line_found = cxt.lines[cxt.history_pos]
rl_replace_line(cxt.lines[cxt.history_pos], false)
@rl_point = cxt.sline_index
cxt.last_found_line = cxt.history_pos
rl_display_search(cxt.search_string, (cxt.sflags & SF_REVERSE)!=0, (cxt.history_pos == cxt.save_line) ? -1 : cxt.history_pos)
end
1
end
# How to clear things from the "echo-area".
def rl_clear_message()
@rl_display_prompt = @rl_prompt
if (@msg_saved_prompt)
rl_restore_prompt()
@msg_saved_prompt = nil
end
send(@rl_redisplay_function)
0
end
def _rl_isearch_fini(cxt)
# First put back the original state.
@rl_line_buffer = cxt.lines[cxt.save_line].dup
rl_restore_prompt()
# Save the search string for possible later use.
@last_isearch_string = cxt.search_string
@last_isearch_string_len = cxt.search_string_index
cxt.search_string = nil
if (cxt.last_found_line < cxt.save_line)
rl_get_previous_history(cxt.save_line - cxt.last_found_line, 0)
else
rl_get_next_history(cxt.last_found_line - cxt.save_line, 0)
end
# If the string was not found, put point at the end of the last matching
# line. If last_found_line == orig_line, we didn't find any matching
# history lines at all, so put point back in its original position.
if (cxt.sline_index < 0)
if (cxt.last_found_line == cxt.save_line)
cxt.sline_index = cxt.save_point
else
cxt.sline_index = @rl_line_buffer.delete(0.chr).length
end
@rl_mark = cxt.save_mark
end
@rl_point = cxt.sline_index
# Don't worry about where to put the mark here; rl_get_previous_history
# and rl_get_next_history take care of it.
rl_clear_message()
end
def _rl_isearch_cleanup(cxt, r)
if (r >= 0)
_rl_isearch_fini(cxt)
end
@_rl_iscxt = nil
rl_unsetstate(RL_STATE_ISEARCH)
r != 0
end
# Do the command associated with KEY in MAP.
# If the associated command is really a keymap, then read
# another key, and dispatch into that map.
def _rl_dispatch(key, map)
@_rl_dispatching_keymap = map
return _rl_dispatch_subseq(key, map, false)
end
def _rl_dispatch_subseq(key, map, got_subseq)
func = map[key]
if (func)
@rl_executing_keymap = map
@rl_dispatching = true
rl_setstate(RL_STATE_DISPATCHING)
send(map[key],@rl_numeric_arg * @rl_arg_sign, key)
rl_unsetstate(RL_STATE_DISPATCHING)
@rl_dispatching = false
if (@rl_pending_input == 0 && map[key] != :rl_digit_argument)
@rl_last_func = map[key]
end
else
if(map.keys.detect{|x| x =~ /^#{Regexp.escape(key)}/})
key += _rl_subseq_getchar(key)
return _rl_dispatch_subseq(key,map,got_subseq)
elsif(key.length>1 && key[1].ord < 0x7f)
_rl_abort_internal()
return -1
else
@rl_dispatching = true
rl_setstate(RL_STATE_DISPATCHING)
send(:rl_insert,@rl_numeric_arg * @rl_arg_sign, key)
rl_unsetstate(RL_STATE_DISPATCHING)
@rl_dispatching = false
end
end
0
end
# Add KEY to the buffer of characters to be read. Returns 1 if the
# character was stuffed correctly; 0 otherwise.
def rl_stuff_char(key)
return 0 if (ibuffer_space() == 0)
if (key == EOF)
key = NEWLINE
@rl_pending_input = EOF
rl_setstate(RL_STATE_INPUTPENDING)
end
@ibuffer[@push_index] = key
@push_index += 1
if (@push_index >= @ibuffer_len)
@push_index = 0
end
return 1
end
begin
# Cygwin will look like Windows, but we want to treat it like a Posix OS:
raise LoadError, "Cygwin is a Posix OS." if RUBY_PLATFORM =~ /\bcygwin\b/i
raise LoadError, "Not Windows" if RUBY_PLATFORM !~ /mswin|mingw/
if RUBY_VERSION < '1.9.1'
require 'Win32API'
else
require 'dl'
class Win32API
DLL = {}
TYPEMAP = {"0" => DL::TYPE_VOID, "S" => DL::TYPE_VOIDP, "I" => DL::TYPE_LONG}
def initialize(dllname, func, import, export = "0", calltype = :stdcall)
@proto = [import].join.tr("VPpNnLlIi", "0SSI").sub(/^(.)0*$/, '\1')
handle = DLL[dllname] ||= DL.dlopen(dllname)
@func = DL::CFunc.new(handle[func], TYPEMAP[export.tr("VPpNnLlIi", "0SSI")], func, calltype)
end
def call(*args)
import = @proto.split("")
args.each_with_index do |x, i|
args[i], = [x == 0 ? nil : x].pack("p").unpack("l!*") if import[i] == "S"
args[i], = [x].pack("I").unpack("i") if import[i] == "I"
end
ret, = @func.call(args)
return ret || 0
end
alias Call call
end
end
STD_OUTPUT_HANDLE = -11
STD_INPUT_HANDLE = -10
KEY_EVENT = 1
VK_SHIFT = 0x10
VK_MENU = 0x12
VK_LMENU = 0xA4
VK_RMENU = 0xA5
LEFT_CTRL_PRESSED = 0x0008
RIGHT_CTRL_PRESSED = 0x0004
LEFT_ALT_PRESSED = 0x0002
RIGHT_ALT_PRESSED = 0x0001
@getch = Win32API.new("msvcrt", "_getch", [], 'I')
@kbhit = Win32API.new("msvcrt", "_kbhit", [], 'I')
@GetStdHandle = Win32API.new("kernel32","GetStdHandle",['L'],'L')
@SetConsoleCursorPosition = Win32API.new("kernel32","SetConsoleCursorPosition",['L','L'],'L')
@GetConsoleScreenBufferInfo = Win32API.new("kernel32","GetConsoleScreenBufferInfo",['L','P'],'L')
@FillConsoleOutputCharacter = Win32API.new("kernel32","FillConsoleOutputCharacter",['L','L','L','L','P'],'L')
@ReadConsoleInput = Win32API.new( "kernel32", "ReadConsoleInput", ['L', 'P', 'L', 'P'], 'L' )
@MessageBeep = Win32API.new("user32","MessageBeep",['L'],'L')
@GetKeyboardState = Win32API.new("user32","GetKeyboardState",['P'],'L')
@GetKeyState = Win32API.new("user32","GetKeyState",['L'],'L')
@hConsoleHandle = @GetStdHandle.Call(STD_OUTPUT_HANDLE)
@hConsoleInput = @GetStdHandle.Call(STD_INPUT_HANDLE)
@pending_count = 0
@pending_key = nil
begin
case `chcp`.scan(/\d+$/).first.to_i
when 936,949,950,51932,51936,50225
@encoding = "E"
when 932,50220,50221,20222
@encoding = "S"
when 65001
@encoding = "U"
else
@encoding = "N"
end
rescue
@encoding = "N"
end
def rl_getc(stream)
while (@kbhit.Call == 0)
# If there is no input, yield the processor for other threads
sleep(@_keyboard_input_timeout)
end
c = @getch.Call
alt = (@GetKeyState.call(VK_LMENU) & 0x80) != 0
if c==0 || c==0xE0
while (@kbhit.Call == 0)
# If there is no input, yield the processor for other threads
sleep(@_keyboard_input_timeout)
end
r = c.chr + @getch.Call.chr
else
r = c.chr
end
r = "\e"+r if alt
r
end
def rl_gather_tyi()
chars_avail = @kbhit.Call
return 0 if(chars_avail<=0)
k = send(@rl_getc_function,@rl_instream)
rl_stuff_char(k)
return 1
end
rescue LoadError # If we're not on Windows try...
if ENV["LANG"] =~ /\.UTF-8/
@encoding = "U"
elsif ENV["LANG"] =~ /\.EUC/
@encoding = "E"
elsif ENV["LANG"] =~ /\.SHIFT/
@encoding = "S"
else
@encoding = "N"
end
def rl_getc(stream)
begin
c = stream.read(1)
rescue Errno::EINTR
c = rl_getc(stream)
end
return c ? c : EOF
end
def rl_gather_tyi()
result = select([@rl_instream],nil,nil,0.1)
return 0 if result.nil?
k = send(@rl_getc_function,@rl_instream)
rl_stuff_char(k)
return 1
end
end
if (Object.const_defined?('Encoding') and Encoding.respond_to?('default_external'))
@encoding = "X" # ruby 1.9.x or greater
@encoding_name = Encoding.default_external
end
@rl_byte_oriented = @encoding == "N"
# Read a key, including pending input.
def rl_read_key()
@rl_key_sequence_length+=1
if (@rl_pending_input!=0)
c = @rl_pending_input
rl_clear_pending_input()
else
# If the user has an event function, then call it periodically.
if (@rl_event_hook)
while (@rl_event_hook && (c=rl_get_char()).nil?)
send(@rl_event_hook)
if (@rl_done) # XXX - experimental
return ("\n")
end
if (rl_gather_tyi() < 0) # XXX - EIO
@rl_done = true
return ("\n")
end
end
else
if (c=rl_get_char()).nil?
c = send(@rl_getc_function,@rl_instream)
end
end
end
return (c)
end
# Return the amount of space available in the buffer for stuffing
# characters.
def ibuffer_space()
if (@pop_index > @push_index)
return (@pop_index - @push_index - 1)
else
return (@ibuffer_len - (@push_index - @pop_index))
end
end
# Get a key from the buffer of characters to be read.
# Return the key in KEY.
# Result is KEY if there was a key, or 0 if there wasn't.
def rl_get_char()
if (@push_index == @pop_index)
return nil
end
key = @ibuffer[@pop_index]
@pop_index += 1
if (@pop_index >= @ibuffer_len)
@pop_index = 0
end
return key
end
# Stuff KEY into the *front* of the input buffer.
# Returns non-zero if successful, zero if there is
# no space left in the buffer.
def _rl_unget_char(key)
if (ibuffer_space()!=0)
@pop_index-=1
if (@pop_index < 0)
@pop_index = @ibuffer_len - 1
end
@ibuffer[@pop_index] = key
return (1)
end
return (0)
end
def _rl_subseq_getchar(key)
if (key == ESC)
rl_setstate(RL_STATE_METANEXT)
end
rl_setstate(RL_STATE_MOREINPUT)
k = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
if (key == ESC)
rl_unsetstate(RL_STATE_METANEXT)
end
return k
end
# Clear to the end of the line. COUNT is the minimum
# number of character spaces to clear,
def _rl_clear_to_eol(count)
if (@_rl_term_clreol)
@rl_outstream.write(@_rl_term_clreol)
elsif (count!=0)
space_to_eol(count)
end
end
# Clear to the end of the line using spaces. COUNT is the minimum
# number of character spaces to clear,
def space_to_eol(count)
if @hConsoleHandle
csbi = 0.chr * 24
@GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
cursor_pos = csbi[4,4].unpack('L').first
written = 0.chr * 4
@FillConsoleOutputCharacter.Call(@hConsoleHandle,0x20,count,cursor_pos,written)
else
@rl_outstream.write(' ' * count)
end
@_rl_last_c_pos += count
end
def _rl_clear_screen()
if (@_rl_term_clrpag)
@rl_outstream.write(@_rl_term_clrpag)
else
rl_crlf()
end
end
# Move the cursor back.
def _rl_backspace(count)
if (@_rl_term_backspace)
@_rl_out_stream.write(@_rl_term_backspace * count)
else
@_rl_out_stream.write("\b"*count)
end
0
end
# Move to the start of the next line.
def rl_crlf()
if (@_rl_term_cr)
@_rl_out_stream.write(@_rl_term_cr)
end
@_rl_out_stream.write("\n")
return 0
end
# Move to the start of the current line.
def cr()
if (@_rl_term_cr)
@_rl_out_stream.write(@_rl_term_cr)
@_rl_last_c_pos = 0
end
end
def _rl_erase_entire_line()
cr()
_rl_clear_to_eol(0)
cr()
@rl_outstream.flush
end
def _rl_internal_char_cleanup()
# In vi mode, when you exit insert mode, the cursor moves back
# over the previous character. We explicitly check for that here.
if (@rl_editing_mode == @vi_mode && @_rl_keymap == @vi_movement_keymap)
rl_vi_check()
end
if (@rl_num_chars_to_read!=0 && @rl_end >= @rl_num_chars_to_read)
send(@rl_redisplay_function)
@_rl_want_redisplay = false
rl_newline(1, "\n")
end
if (!@rl_done)
send(@rl_redisplay_function)
@_rl_want_redisplay = false
end
# If the application writer has told us to erase the entire line if
# the only character typed was something bound to rl_newline, do so.
if (@rl_erase_empty_line && @rl_done && @rl_last_func == :rl_newline &&
@rl_point == 0 && @rl_end == 0)
_rl_erase_entire_line()
end
end
def readline_internal_charloop()
lastc = -1
eof_found = false
while (!@rl_done)
lk = @_rl_last_command_was_kill
# send(rl_redisplay_function)
# @_rl_want_redisplay = false
if (@rl_pending_input == 0)
# Then initialize the argument and number of keys read.
_rl_reset_argument()
@rl_key_sequence_length = 0
end
rl_setstate(RL_STATE_READCMD)
c = rl_read_key()
rl_unsetstate(RL_STATE_READCMD)
# look at input.c:rl_getc() for the circumstances under which this will
#be returned; punt immediately on read error without converting it to
#a newline.
if (c == READERR)
eof_found = true
break
end
# EOF typed to a non-blank line is a <NL>.
if (c == EOF && @rl_end!=0)
c = NEWLINE
end
# The character _rl_eof_char typed to blank line, and not as the
#previous character is interpreted as EOF.
if (((c == @_rl_eof_char && lastc != c) || c == EOF) && @rl_end==0)
eof_found = true
break
end
lastc = c
if _rl_dispatch(c, @_rl_keymap)== -1
next
end
# If there was no change in _rl_last_command_was_kill, then no kill
#has taken place. Note that if input is pending we are reading
#a prefix command, so nothing has changed yet.
if (@rl_pending_input == 0 && lk == @_rl_last_command_was_kill)
@_rl_last_command_was_kill = false
end
_rl_internal_char_cleanup()
end
eof_found
end
# How to abort things.
def _rl_abort_internal()
rl_ding()
rl_clear_message()
_rl_reset_argument()
rl_clear_pending_input()
rl_unsetstate(RL_STATE_MACRODEF)
@rl_last_func = nil
#throw :readline_top_level
send(@rl_redisplay_function)
@_rl_want_redisplay = false
0
end
def rl_abort(count, key)
_rl_abort_internal()
end
def rl_vi_check()
if (@rl_point!=0 && @rl_point == @rl_end)
@rl_point-=1
end
0
end
def readline_internal_teardown(eof)
# Restore the original of this history line, iff the line that we
# are editing was originally in the history, AND the line has changed.
entry = current_history()
if (entry && @rl_undo_list)
temp = @rl_line_buffer.delete(0.chr).dup
rl_revert_line(1, 0)
entry = replace_history_entry(where_history(), @rl_line_buffer, nil)
entry = nil
@rl_line_buffer = temp+0.chr
temp = nil
end
# At any rate, it is highly likely that this line has an undo list. Get
# rid of it now.
if (@rl_undo_list)
rl_free_undo_list()
end
# Restore normal cursor, if available.
_rl_set_insert_mode(RL_IM_INSERT, 0)
(eof ? nil : @rl_line_buffer.delete(0.chr))
end
# Read a line of input from the global rl_instream, doing output on
# the global rl_outstream.
# If rl_prompt is non-null, then that is our prompt.
def readline_internal()
readline_internal_setup()
eof = readline_internal_charloop()
readline_internal_teardown(eof)
end
# Read a line of input. Prompt with PROMPT. An empty PROMPT means
# none. A return value of NULL means that EOF was encountered.
def readline(prompt)
# If we are at EOF return a NULL string.
if (@rl_pending_input == EOF)
rl_clear_pending_input()
return nil
end
rl_set_prompt(prompt)
rl_initialize()
@readline_echoing_p = true
if (@rl_prep_term_function)
send(@rl_prep_term_function,@_rl_meta_flag)
end
rl_set_signals()
value = readline_internal()
if(@rl_deprep_term_function)
send(@rl_deprep_term_function)
end
rl_clear_signals()
value
end
# Increase the size of RL_LINE_BUFFER until it has enough space to hold
# LEN characters.
def rl_extend_line_buffer(len)
while (len >= @rl_line_buffer.length)
@rl_line_buffer << 0.chr * DEFAULT_BUFFER_SIZE
end
@the_line = @rl_line_buffer
end
# Insert a string of text into the line at point. This is the only
# way that you should do insertion. _rl_insert_char () calls this
# function. Returns the number of characters inserted.
def rl_insert_text(string)
string.delete!(0.chr)
l = string.length
return 0 if (l == 0)
if (@rl_end + l >= @rl_line_buffer.length)
rl_extend_line_buffer(@rl_end + l)
end
@rl_line_buffer[@rl_point,0] = string
# Remember how to undo this if we aren't undoing something.
if (!@_rl_doing_an_undo)
# If possible and desirable, concatenate the undos.
if ((l == 1) &&
@rl_undo_list &&
(@rl_undo_list.what == UNDO_INSERT) &&
(@rl_undo_list.end == @rl_point) &&
(@rl_undo_list.end - @rl_undo_list.start < 20))
@rl_undo_list.end+=1
else
rl_add_undo(UNDO_INSERT, @rl_point, @rl_point + l, nil)
end
end
@rl_point += l
@rl_end += l
if @rl_line_buffer.length <= @rl_end
@rl_line_buffer << 0.chr * (@rl_end - @rl_line_buffer.length + 1)
else
@rl_line_buffer[@rl_end] = "\0"
end
l
end
def alloc_undo_entry(what, start, _end, text)
temp = Struct.new(:what,:start,:end,:text,:next).new
temp.what = what
temp.start = start
temp.end = _end
temp.text = text
temp.next = nil
temp
end
#* Remember how to undo something. Concatenate some undos if that
# seems right.
def rl_add_undo(what, start, _end, text)
temp = alloc_undo_entry(what, start, _end, text)
temp.next = @rl_undo_list
@rl_undo_list = temp
end
# Delete the string between FROM and TO. FROM is inclusive, TO is not.
# Returns the number of characters deleted.
def rl_delete_text(from, to)
# Fix it if the caller is confused.
if (from > to)
from,to = to,from
end
# fix boundaries
if (to > @rl_end)
to = @rl_end
if (from > to)
from = to
end
end
if (from < 0)
from = 0
end
text = rl_copy_text(from, to)
diff = to - from
@rl_line_buffer[from...to] = ''
@rl_line_buffer << 0.chr * diff
# Remember how to undo this delete.
if (!@_rl_doing_an_undo)
rl_add_undo(UNDO_DELETE, from, to, text)
else
text = nil
end
@rl_end -= diff
@rl_line_buffer[@rl_end,1] = 0.chr
return (diff)
end
def rl_copy_text(from, to)
return @rl_line_buffer[from...to]
end
# Fix up point so that it is within the line boundaries after killing
# text. If FIX_MARK_TOO is non-zero, the mark is forced within line
# boundaries also.
def __rl_fix_point(x)
if (x > @rl_end)
@rl_end
elsif (x < 0)
0
else
x
end
end
def _rl_fix_point(fix_mark_too)
@rl_point = __rl_fix_point(@rl_point)
if (fix_mark_too)
@rl_mark = __rl_fix_point(@rl_mark)
end
end
# Begin a group. Subsequent undos are undone as an atomic operation.
def rl_begin_undo_group()
rl_add_undo(UNDO_BEGIN, 0, 0, nil)
@_rl_undo_group_level+=1
0
end
# End an undo group started with rl_begin_undo_group ().
def rl_end_undo_group()
rl_add_undo(UNDO_END, 0, 0, nil)
@_rl_undo_group_level-=1
0
end
def rl_free_undo_list()
replace_history_data(-1, @rl_undo_list, nil)
@rl_undo_list = nil
end
# Replace the contents of the line buffer between START and END with
# TEXT. The operation is undoable. To replace the entire line in an
# undoable mode, use _rl_replace_text(text, 0, rl_end)
def _rl_replace_text(text, start, _end)
rl_begin_undo_group()
rl_delete_text(start, _end + 1)
@rl_point = start
n = rl_insert_text(text)
rl_end_undo_group()
n
end
# Replace the current line buffer contents with TEXT. If CLEAR_UNDO is
# non-zero, we free the current undo list.
def rl_replace_line(text, clear_undo)
len = text.delete(0.chr).length
@rl_line_buffer = text.dup + 0.chr
@rl_end = len
if (clear_undo)
rl_free_undo_list()
end
_rl_fix_point(true)
end
# Replace the DATA in the specified history entries, replacing OLD with
# NEW. WHICH says which one(s) to replace: WHICH == -1 means to replace
# all of the history entries where entry->data == OLD; WHICH == -2 means
# to replace the `newest' history entry where entry->data == OLD; and
# WHICH >= 0 means to replace that particular history entry's data, as
# long as it matches OLD.
def replace_history_data(which,old, new)
new = new.dup if new
if (which < -2 || which >= @history_length || @history_length == 0 || @the_history.nil?)
return
end
if (which >= 0)
entry = @the_history[which]
if (entry && entry.data == old)
entry.data = new
end
return
end
last = -1
for i in 0 ... @history_length
entry = @the_history[i]
if entry.nil?
next
end
if (entry.data == old)
last = i
if (which == -1)
entry.data = new
end
end
end
if (which == -2 && last >= 0)
entry = @the_history[last]
entry.data = new # XXX - we don't check entry->old
end
end
# Move forward COUNT bytes.
def rl_forward_byte(count, key)
if (count < 0)
return (rl_backward_byte(-count, key))
end
if (count > 0)
_end = @rl_point + count
lend = @rl_end > 0 ? @rl_end - ((@rl_editing_mode == @vi_mode)?1:0) : @rl_end
if (_end > lend)
@rl_point = lend
rl_ding()
else
@rl_point = _end
end
end
if (@rl_end < 0)
@rl_end = 0
end
return 0
end
# Move forward COUNT characters.
def rl_forward_char(count, key)
if @rl_byte_oriented
return (rl_forward_byte(count, key))
end
if (count < 0)
return (rl_backward_char(-count, key))
end
if (count > 0)
point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, count, MB_FIND_NONZERO)
if (@rl_end <= point && @rl_editing_mode == @vi_mode)
point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_end, MB_FIND_NONZERO)
end
if (@rl_point == point)
rl_ding()
end
@rl_point = point
if (@rl_end < 0)
@rl_end = 0
end
end
0
end
# Backwards compatibility.
def rl_forward(count, key)
rl_forward_char(count, key)
end
# Move backward COUNT bytes.
def rl_backward_byte(count, key)
if (count < 0)
return (rl_forward_byte(-count, key))
end
if (count > 0)
if (@rl_point < count)
@rl_point = 0
rl_ding()
else
@rl_point -= count
end
end
if (@rl_point < 0)
@rl_point = 0
end
0
end
# Move backward COUNT characters.
def rl_backward_char(count, key)
if @rl_byte_oriented
return (rl_backward_byte(count, key))
end
if (count < 0)
return (rl_forward_char(-count, key))
end
if (count > 0)
point = @rl_point
while (count > 0 && point > 0)
point = _rl_find_prev_mbchar(@rl_line_buffer, point, MB_FIND_NONZERO)
count-=1
end
if (count > 0)
@rl_point = 0
rl_ding()
else
@rl_point = point
end
end
0
end
# Backwards compatibility.
def rl_backward(count, key)
rl_backward_char(count, key)
end
# Move to the beginning of the line.
def rl_beg_of_line(count, key)
@rl_point = 0
0
end
# Move to the end of the line.
def rl_end_of_line(count, key)
@rl_point = @rl_end
0
end
def _rl_char_value(buf,ind)
buf[ind,1]
end
@_rl_allow_pathname_alphabetic_chars = false
@pathname_alphabetic_chars = '/-_=~.#$'
def rl_alphabetic(c)
if c =~ /\w/
return true
end
return !!(@_rl_allow_pathname_alphabetic_chars &&
@pathname_alphabetic_chars[c])
end
def _rl_walphabetic(c)
rl_alphabetic(c)
end
# Move forward a word. We do what Emacs does. Handles multibyte chars.
def rl_forward_word(count, key)
if (count < 0)
return (rl_backward_word(-count, key))
end
while (count>0)
return 0 if (@rl_point == @rl_end)
# If we are not in a word, move forward until we are in one.
# Then, move forward until we hit a non-alphabetic character.
c = _rl_char_value(@rl_line_buffer, @rl_point)
if (!_rl_walphabetic(c))
if !@rl_byte_oriented
@rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
else
@rl_point += 1
end
while (@rl_point < @rl_end)
c = _rl_char_value(@rl_line_buffer, @rl_point)
if (_rl_walphabetic(c))
break
end
if !@rl_byte_oriented
@rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
else
@rl_point += 1
end
end
end
return 0 if (@rl_point == @rl_end)
if !@rl_byte_oriented
@rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
else
@rl_point += 1
end
while (@rl_point < @rl_end)
c = _rl_char_value(@rl_line_buffer, @rl_point)
if (!_rl_walphabetic(c))
break
end
if !@rl_byte_oriented
@rl_point = _rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
else
@rl_point += 1
end
end
count -= 1
end
0
end
# Move backward a word. We do what Emacs does. Handles multibyte chars.
def rl_backward_word(count, key)
if (count < 0)
return (rl_forward_word(-count, key))
end
while (count>0)
return 0 if (@rl_point == 0)
# Like rl_forward_word (), except that we look at the characters
# just before point.
_p = !@rl_byte_oriented ? _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO):(@rl_point-1)
c = _rl_char_value(@rl_line_buffer, _p)
if (!_rl_walphabetic(c))
@rl_point = _p
while (@rl_point > 0)
_p = !@rl_byte_oriented ? _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO):(@rl_point-1)
c = _rl_char_value(@rl_line_buffer, _p)
if (_rl_walphabetic(c))
break
end
@rl_point = _p
end
end
while (@rl_point>0)
_p = !@rl_byte_oriented ? _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO):(@rl_point-1)
c = _rl_char_value(@rl_line_buffer, _p)
if (!_rl_walphabetic(c))
break
else
@rl_point = _p
end
end
count -= 1
end
0
end
# return the `current display line' of the cursor -- the number of lines to
# move up to get to the first screen line of the current readline line.
def _rl_current_display_line()
# Find out whether or not there might be invisible characters in the
# editing buffer.
if (@rl_display_prompt == @rl_prompt)
nleft = @_rl_last_c_pos - @_rl_screenwidth - @rl_visible_prompt_length
else
nleft = @_rl_last_c_pos - @_rl_screenwidth
end
if (nleft > 0)
ret = 1 + nleft / @_rl_screenwidth
else
ret = 0
end
ret
end
# Actually update the display, period.
def rl_forced_update_display()
if (@visible_line)
@visible_line.gsub!(/[^\x00]/,0.chr)
end
rl_on_new_line()
@forced_display=true if !@forced_display
send(@rl_redisplay_function)
0
end
# Clear the current line. Numeric argument to C-l does this.
def rl_refresh_line(ignore1, ignore2)
curr_line = _rl_current_display_line()
_rl_move_vert(curr_line)
_rl_move_cursor_relative(0, @rl_line_buffer) # XXX is this right
_rl_clear_to_eol(0) # arg of 0 means to not use spaces
rl_forced_update_display()
@rl_display_fixed = true
0
end
# C-l typed to a line without quoting clears the screen, and then reprints
# the prompt and the current input line. Given a numeric arg, redraw only
# the current line.
def rl_clear_screen(count, key)
if (@rl_explicit_arg)
rl_refresh_line(count, key)
return 0
end
_rl_clear_screen() # calls termcap function to clear screen
rl_forced_update_display()
@rl_display_fixed = true
0
end
# Restore the _rl_saved_line_for_history if there is one.
def rl_maybe_unsave_line()
if (@_rl_saved_line_for_history)
# Can't call with `1' because rl_undo_list might point to an undo
# list from a history entry, as in rl_replace_from_history() below.
rl_replace_line(@_rl_saved_line_for_history.line, false)
@rl_undo_list = @_rl_saved_line_for_history.data
@_rl_saved_line_for_history = nil
@rl_point = @rl_end # rl_replace_line sets rl_end
else
rl_ding()
end
0
end
# Save the current line in _rl_saved_line_for_history.
def rl_maybe_save_line()
if @_rl_saved_line_for_history.nil?
@_rl_saved_line_for_history = Struct.new(:line,:timestamp,:data).new
@_rl_saved_line_for_history.line = @rl_line_buffer.dup
@_rl_saved_line_for_history.timestamp = nil
@_rl_saved_line_for_history.data = @rl_undo_list
end
0
end
# Returns the magic number which says what history element we are
# looking at now. In this implementation, it returns history_offset.
def where_history()
@history_offset
end
# Make the history entry at WHICH have LINE and DATA. This returns
# the old entry so you can dispose of the data. In the case of an
# invalid WHICH, a NULL pointer is returned.
def replace_history_entry (which, line, data)
if (which < 0 || which >= @history_length)
return nil
end
temp = Struct.new(:line,:timestamp,:data).new
old_value = @the_history[which]
temp.line = line.delete(0.chr)
temp.data = data
temp.timestamp = old_value.timestamp.dup
@the_history[which] = temp
old_value
end
# Perhaps put back the current line if it has changed.
def rl_maybe_replace_line()
temp = current_history()
# If the current line has changed, save the changes.
if (temp && temp.data != @rl_undo_list)
temp = replace_history_entry(where_history(), @rl_line_buffer, @rl_undo_list)
end
0
end
# Back up history_offset to the previous history entry, and return
# a pointer to that entry. If there is no previous entry then return
# a NULL pointer.
def previous_history()
@history_offset!=0 ? @the_history[@history_offset-=1] : nil
end
# Move history_offset forward to the next history entry, and return
# a pointer to that entry. If there is no next entry then return a
# NULL pointer.
def next_history()
(@history_offset == @history_length) ? nil : @the_history[@history_offset+=1]
end
# Get the previous item out of our interactive history, making it the current
# line. If there is no previous history, just ding.
def rl_get_previous_history(count, key)
if (count < 0)
return (rl_get_next_history(-count, key))
end
if (count == 0)
return 0
end
# either not saved by rl_newline or at end of line, so set appropriately.
if (@_rl_history_saved_point == -1 && (@rl_point!=0 || @rl_end!=0))
@_rl_history_saved_point = (@rl_point == @rl_end) ? -1 : @rl_point
end
# If we don't have a line saved, then save this one.
rl_maybe_save_line()
# If the current line has changed, save the changes.
rl_maybe_replace_line()
temp = old_temp = nil
while (count>0)
temp = previous_history()
if temp.nil?
break
end
old_temp = temp
count -= 1
end
# If there was a large argument, and we moved back to the start of the
# history, that is not an error. So use the last value found.
if (temp.nil? && old_temp)
temp = old_temp
end
if temp.nil?
rl_ding()
else
rl_replace_from_history(temp, 0)
_rl_history_set_point()
end
0
end
def _rl_history_set_point ()
@rl_point = (@_rl_history_preserve_point && @_rl_history_saved_point != -1) ?
@_rl_history_saved_point : @rl_end
if (@rl_point > @rl_end)
@rl_point = @rl_end
end
if (@rl_editing_mode == @vi_mode && @_rl_keymap != @vi_insertion_keymap)
@rl_point = 0
end
if (@rl_editing_mode == @emacs_mode)
@rl_mark = (@rl_point == @rl_end ? 0 : @rl_end)
end
end
# Move down to the next history line.
def rl_get_next_history(count, key)
if (count < 0)
return (rl_get_previous_history(-count, key))
end
if (count == 0)
return 0
end
rl_maybe_replace_line()
# either not saved by rl_newline or at end of line, so set appropriately.
if (@_rl_history_saved_point == -1 && (@rl_point!=0 || @rl_end!=0))
@_rl_history_saved_point = (@rl_point == @rl_end) ? -1 : @rl_point
end
temp = nil
while (count>0)
temp = next_history()
if temp.nil?
break
end
count -= 1
end
if temp.nil?
rl_maybe_unsave_line()
else
rl_replace_from_history(temp, 0)
_rl_history_set_point()
end
0
end
def rl_arrow_keys(count, c)
rl_setstate(RL_STATE_MOREINPUT)
ch = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
case (ch.upcase)
when 'A'
rl_get_previous_history(count, ch)
when 'B'
rl_get_next_history(count, ch)
when 'C'
rl_forward_byte(count, ch)
when 'D'
rl_backward_byte(count, ch)
else
rl_ding()
end
0
end
def _rl_any_typein()
return (@push_index != @pop_index)
end
def _rl_insert_typein(c)
string = c
while ((key = rl_get_char()) && @_rl_keymap[key] == :rl_insert)
string << key
end
if (key)
_rl_unget_char(key)
end
rl_insert_text(string)
end
# Insert the character C at the current location, moving point forward.
# If C introduces a multibyte sequence, we read the whole sequence and
# then insert the multibyte char into the line buffer.
def _rl_insert_char(count, c)
return 0 if (count <= 0)
incoming = ''
if @rl_byte_oriented
incoming << c
incoming_length = 1
else
@pending_bytes << c
if _rl_get_char_len(@pending_bytes) == -2
return 1
else
incoming = @pending_bytes
@pending_bytes = ''
incoming_length = incoming.length
end
end
if(count>1)
string = incoming * count
rl_insert_text(string)
string = nil
return 0
end
if @rl_byte_oriented
# We are inserting a single character.
#If there is pending input, then make a string of all of the
#pending characters that are bound to rl_insert, and insert
#them all.
if (_rl_any_typein())
_rl_insert_typein(c)
else
rl_insert_text(c)
end
else
rl_insert_text(incoming)
end
return 0
end
# Overwrite the character at point (or next COUNT characters) with C.
# If C introduces a multibyte character sequence, read the entire sequence
# before starting the overwrite loop.
def _rl_overwrite_char(count, c)
# Read an entire multibyte character sequence to insert COUNT times.
if (count > 0 && !@rl_byte_oriented)
mbkey = ''
_rl_read_mbstring(c, mbkey, MB_LEN_MAX)
end
rl_begin_undo_group()
count.times do
if !@rl_byte_oriented
rl_insert_text(mbkey)
else
_rl_insert_char(1, c)
end
if (@rl_point < @rl_end)
rl_delete(1, c)
end
end
rl_end_undo_group()
return 0
end
def rl_insert(count, c)
((@rl_insert_mode == RL_IM_INSERT) ? _rl_insert_char(count, c) :
_rl_overwrite_char(count, c))
end
# Insert the next typed character verbatim.
def _rl_insert_next(count)
rl_setstate(RL_STATE_MOREINPUT)
c = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
if c.class == Fixnum && c < 0
return -1
end
_rl_insert_char(count, c)
end
def rl_quoted_insert(count, key)
_rl_insert_next(count)
end
# Insert a tab character.
def rl_tab_insert(count, key)
_rl_insert_char(count, "\t")
end
def _rl_vi_save_insert(up)
if (up.nil? || up.what != UNDO_INSERT)
if (@vi_insert_buffer_size >= 1)
@vi_insert_buffer[0] = 0.chr
end
return
end
start = up.start
_end = up.end
len = _end - start + 1
@vi_insert_buffer = @rl_line_buffer[start,len-1]
end
def _rl_vi_done_inserting()
if (@_rl_vi_doing_insert)
# The `C', `s', and `S' commands set this.
rl_end_undo_group()
# Now, the text between rl_undo_list->next->start and
# rl_undo_list->next->end is what was inserted while in insert
# mode. It gets copied to VI_INSERT_BUFFER because it depends
# on absolute indices into the line which may change (though they
# probably will not).
@_rl_vi_doing_insert = 0
_rl_vi_save_insert(@rl_undo_list.next)
@vi_continued_command = 1
else
if ((@_rl_vi_last_key_before_insert == 'i' || @_rl_vi_last_key_before_insert == 'a') && @rl_undo_list)
_rl_vi_save_insert(@rl_undo_list)
# XXX - Other keys probably need to be checked.
elsif (@_rl_vi_last_key_before_insert == 'C')
rl_end_undo_group()
end
while (@_rl_undo_group_level > 0)
rl_end_undo_group()
end
@vi_continued_command = 0
end
end
# Is the command C a VI mode text modification command?
def _rl_vi_textmod_command(c)
return @vi_textmod[c]
end
def _rl_vi_reset_last()
@_rl_vi_last_command = 'i'
@_rl_vi_last_repeat = 1
@_rl_vi_last_arg_sign = 1
@_rl_vi_last_motion = 0
end
def _rl_update_final()
full_lines = false
# If the cursor is the only thing on an otherwise-blank last line,
# compensate so we don't print an extra CRLF.
if (@_rl_vis_botlin && @_rl_last_c_pos == 0 &&
@visible_line[@vis_lbreaks[@_rl_vis_botlin],1] == 0.chr )
@_rl_vis_botlin-=1
full_lines = true
end
_rl_move_vert(@_rl_vis_botlin)
# If we've wrapped lines, remove the final xterm line-wrap flag.
if (full_lines && @_rl_term_autowrap && (vis_llen(@_rl_vis_botlin) == @_rl_screenwidth))
last_line = @visible_line[@vis_lbreaks[@_rl_vis_botlin]..-1]
@cpos_buffer_position = -1 # don't know where we are in buffer
_rl_move_cursor_relative(@_rl_screenwidth - 1, last_line) # XXX
_rl_clear_to_eol(0)
@rl_outstream.write(last_line[@_rl_screenwidth - 1,1])
end
@_rl_vis_botlin = 0
rl_crlf()
@rl_outstream.flush
@rl_display_fixed = true if !@rl_display_fixed
end
# What to do when a NEWLINE is pressed. We accept the whole line.
# KEY is the key that invoked this command. I guess it could have
# meaning in the future.
def rl_newline(count, key)
@rl_done = true
if (@_rl_history_preserve_point)
@_rl_history_saved_point = (@rl_point == @rl_end) ? 1 : @rl_point
end
rl_setstate(RL_STATE_DONE)
if (@rl_editing_mode == @vi_mode)
_rl_vi_done_inserting()
if (_rl_vi_textmod_command(@_rl_vi_last_command).nil?) # XXX
_rl_vi_reset_last()
end
end
# If we've been asked to erase empty lines, suppress the final update,
# since _rl_update_final calls rl_crlf().
if (@rl_erase_empty_line && @rl_point == 0 && @rl_end == 0)
return 0
end
if @readline_echoing_p
_rl_update_final()
end
0
end
# What to do for some uppercase characters, like meta characters,
# and some characters appearing in emacs_ctlx_keymap. This function
# is just a stub, you bind keys to it and the code in _rl_dispatch ()
# is special cased.
def rl_do_lowercase_version(ignore1, ignore2)
0
end
def rl_character_len(c, pos)
if (meta_char(c))
return ((!@_rl_output_meta_chars) ? 4 : 1)
end
if (c == "\t")
return (((pos | 7) + 1) - pos)
end
if (ctrl_char(c) || c == RUBOUT)
return (2)
end
return ((isprint(c)) ? 1 : 2)
end
# This is different from what vi does, so the code's not shared. Emacs
# rubout in overwrite mode has one oddity: it replaces a control
# character that's displayed as two characters (^X) with two spaces.
def _rl_overwrite_rubout(count, key)
if (@rl_point == 0)
rl_ding()
return 1
end
opoint = @rl_point
# L == number of spaces to insert
l = 0
count.times do
rl_backward_char(1, key)
l += rl_character_len(@rl_line_buffer[@rl_point,1], @rl_point) # not exactly right
end
rl_begin_undo_group()
if (count > 1 || @rl_explicit_arg)
rl_kill_text(opoint, @rl_point)
else
rl_delete_text(opoint, @rl_point)
end
# Emacs puts point at the beginning of the sequence of spaces.
if (@rl_point < @rl_end)
opoint = @rl_point
_rl_insert_char(l, ' ')
@rl_point = opoint
end
rl_end_undo_group()
0
end
# Rubout the character behind point.
def rl_rubout(count, key)
if (count < 0)
return (rl_delete(-count, key))
end
if (@rl_point==0)
rl_ding()
return -1
end
if (@rl_insert_mode == RL_IM_OVERWRITE)
return (_rl_overwrite_rubout(count, key))
end
_rl_rubout_char(count, key)
end
# Quick redisplay hack when erasing characters at the end of the line.
def _rl_erase_at_end_of_line(l)
_rl_backspace(l)
@rl_outstream.write(' '*l)
_rl_backspace(l)
@_rl_last_c_pos -= l
@visible_line[@_rl_last_c_pos,l] = 0.chr * l
@rl_display_fixed = true if !@rl_display_fixed
end
def _rl_rubout_char(count, key)
# Duplicated code because this is called from other parts of the library.
if (count < 0)
return (rl_delete(-count, key))
end
if (@rl_point == 0)
rl_ding()
return -1
end
orig_point = @rl_point
if (count > 1 || @rl_explicit_arg)
rl_backward_char(count, key)
rl_kill_text(orig_point, @rl_point)
elsif (@rl_byte_oriented)
c = @rl_line_buffer[@rl_point-=1,1]
rl_delete_text(@rl_point, orig_point)
# The erase-at-end-of-line hack is of questionable merit now.
if (@rl_point == @rl_end && isprint(c) && @_rl_last_c_pos!=0)
l = rl_character_len(c, @rl_point)
_rl_erase_at_end_of_line(l)
end
else
@rl_point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO)
rl_delete_text(@rl_point, orig_point)
end
0
end
# Delete the character under the cursor. Given a numeric argument,
# kill that many characters instead.
def rl_delete(count, key)
if (count < 0)
return (_rl_rubout_char(-count, key))
end
if (@rl_point == @rl_end)
rl_ding()
return -1
end
if (count > 1 || @rl_explicit_arg)
xpoint = @rl_point
rl_forward_byte(count, key)
rl_kill_text(xpoint, @rl_point)
@rl_point = xpoint
else
if !@rl_byte_oriented
xpoint =_rl_find_next_mbchar(@rl_line_buffer, @rl_point, 1, MB_FIND_NONZERO)
else
xpoint = @rl_point + 1
end
rl_delete_text(@rl_point, xpoint)
end
0
end
# Add TEXT to the kill ring, allocating a new kill ring slot as necessary.
# This uses TEXT directly, so the caller must not free it. If APPEND is
# non-zero, and the last command was a kill, the text is appended to the
# current kill ring slot, otherwise prepended.
def _rl_copy_to_kill_ring(text, append)
# First, find the slot to work with.
if (!@_rl_last_command_was_kill)
# Get a new slot.
if @rl_kill_ring.nil?
# If we don't have any defined, then make one.
@rl_kill_ring_length = 1
@rl_kill_ring = Array.new(@rl_kill_ring_length+1)
@rl_kill_ring[slot = 0] = nil
else
# We have to add a new slot on the end, unless we have
# exceeded the max limit for remembering kills.
slot = @rl_kill_ring_length
if (slot == @rl_max_kills)
@rl_kill_ring[0,slot] = @rl_kill_ring[1,slot]
else
slot = @rl_kill_ring_length += 1
end
@rl_kill_ring[slot-=1] = nil
end
else
slot = @rl_kill_ring_length - 1
end
# If the last command was a kill, prepend or append.
if (@_rl_last_command_was_kill && @rl_editing_mode != @vi_mode)
old = @rl_kill_ring[slot]
if (append)
new = old + text
else
new = text + old
end
old = nil
text = nil
@rl_kill_ring[slot] = new
else
@rl_kill_ring[slot] = text
end
@rl_kill_index = slot
0
end
# The way to kill something. This appends or prepends to the last
# kill, if the last command was a kill command. if FROM is less
# than TO, then the text is appended, otherwise prepended. If the
# last command was not a kill command, then a new slot is made for
# this kill.
def rl_kill_text(from, to)
# Is there anything to kill?
if (from == to)
@_rl_last_command_was_kill = true if !@_rl_last_command_was_kill
return 0
end
text = rl_copy_text(from, to)
# Delete the copied text from the line.
rl_delete_text(from, to)
_rl_copy_to_kill_ring(text, from < to)
@_rl_last_command_was_kill = true if !@_rl_last_command_was_kill
0
end
# This does what C-w does in Unix. We can't prevent people from
# using behaviour that they expect.
def rl_unix_word_rubout(count, key)
if (@rl_point == 0)
rl_ding()
else
orig_point = @rl_point
if (count <= 0)
count = 1
end
while (count>0)
while (@rl_point>0 && whitespace(@rl_line_buffer[@rl_point - 1,1]))
@rl_point-=1
end
while (@rl_point>0 && !whitespace(@rl_line_buffer[@rl_point - 1,1]))
@rl_point-=1
end
count -= 1
end
rl_kill_text(orig_point, @rl_point)
if (@rl_editing_mode == @emacs_mode)
@rl_mark = @rl_point
end
end
0
end
# This deletes one filename component in a Unix pathname. That is, it
# deletes backward to directory separator (`/') or whitespace.
def rl_unix_filename_rubout(count, key)
if (@rl_point == 0)
rl_ding()
else
orig_point = @rl_point
if (count <= 0)
count = 1
end
while (count>0)
c = @rl_line_buffer[@rl_point - 1,1]
while (@rl_point>0 && (whitespace(c) || c == '/'))
@rl_point-=1
c = @rl_line_buffer[@rl_point - 1,1]
end
while (@rl_point>0 && !whitespace(c) && c != '/')
@rl_point-=1
c = @rl_line_buffer[@rl_point - 1,1]
end
count -= 1
end
rl_kill_text(orig_point, @rl_point)
if (@rl_editing_mode == @emacs_mode)
@rl_mark = @rl_point
end
end
0
end
# Delete the character under the cursor, unless the insertion
# point is at the end of the line, in which case the character
# behind the cursor is deleted. COUNT is obeyed and may be used
# to delete forward or backward that many characters.
def rl_rubout_or_delete(count, key)
if (@rl_end != 0 && @rl_point == @rl_end)
return (_rl_rubout_char(count, key))
else
return (rl_delete(count, key))
end
end
# Delete all spaces and tabs around point.
def rl_delete_horizontal_space(count, ignore)
start = @rl_point
while (@rl_point!=0 && whitespace(@rl_line_buffer[@rl_point - 1]))
@rl_point-=1
end
start = @rl_point
while (@rl_point < @rl_end && whitespace(@rl_line_buffer[@rl_point]))
@rl_point+=1
end
if (start != @rl_point)
rl_delete_text(start, @rl_point)
@rl_point = start
end
if (@rl_point < 0)
@rl_point = 0
end
0
end
# List the possible completions. See description of rl_complete ().
def rl_possible_completions(ignore, invoking_key)
rl_complete_internal('?')
end
# Like the tcsh editing function delete-char-or-list. The eof character
# is caught before this is invoked, so this really does the same thing as
# delete-char-or-list-or-eof, as long as it's bound to the eof character.
def rl_delete_or_show_completions(count, key)
if (@rl_end != 0 && @rl_point == @rl_end)
return (rl_possible_completions(count, key))
else
return (rl_delete(count, key))
end
end
# Turn the current line into a comment in shell history.
# A K*rn shell style function.
def rl_insert_comment(count, key)
rl_beg_of_line(1, key)
@rl_comment_text = @_rl_comment_begin ? @_rl_comment_begin : '#'
if (!@rl_explicit_arg)
rl_insert_text(@rl_comment_text)
else
@rl_comment_len = @rl_comment_text.length
if @rl_comment_text[0,@rl_comment_len] == @rl_line_buffer[0,@rl_comment_len]
rl_delete_text(@rl_point, @rl_point + @rl_comment_len)
else
rl_insert_text(@rl_comment_text)
end
end
send(@rl_redisplay_function)
rl_newline(1, "\n")
0
end
def alloc_history_entry(string, ts)
temp = Struct.new(:line,:data,:timestamp).new
temp.line = string ? string.delete(0.chr) : string
temp.data = nil
temp.timestamp = ts
return temp
end
def hist_inittime()
t = Time.now.to_i
ts = "X%u" % t
ret = ts.dup
ret[0,1] = @history_comment_char
ret
end
# Place STRING at the end of the history list. The data field
# is set to NULL.
def add_history(string)
if (@history_stifled && (@history_length == @history_max_entries))
# If the history is stifled, and history_length is zero,
# and it equals history_max_entries, we don't save items.
return if (@history_length == 0)
@the_history.shift
else
if @the_history.nil?
@the_history = []
@history_length = 1
else
@history_length+=1
end
end
temp = alloc_history_entry(string, hist_inittime())
@the_history[@history_length] = nil
@the_history[@history_length - 1] = temp
end
def using_history()
@history_offset = @history_length
end
# Set default values for readline word completion. These are the variables
# that application completion functions can change or inspect.
def set_completion_defaults(what_to_do)
# Only the completion entry function can change these.
@rl_filename_completion_desired = false
@rl_filename_quoting_desired = true
@rl_completion_type = what_to_do
@rl_completion_suppress_append = @rl_completion_suppress_quote = false
# The completion entry function may optionally change this.
@rl_completion_mark_symlink_dirs = @_rl_complete_mark_symlink_dirs
end
def _rl_find_completion_word()
_end = @rl_point
found_quote = 0
delimiter = 0.chr
quote_char = 0.chr
brkchars = nil
if @rl_completion_word_break_hook
brkchars = send(@rl_completion_word_break_hook)
end
if brkchars.nil?
brkchars = @rl_completer_word_break_characters
end
if (@rl_completer_quote_characters)
# We have a list of characters which can be used in pairs to
# quote substrings for the completer. Try to find the start
# of an unclosed quoted substring.
# FOUND_QUOTE is set so we know what kind of quotes we found.
scan = 0
pass_next = false
while scan < _end
if !@rl_byte_oriented
scan = _rl_find_next_mbchar(@rl_line_buffer, scan, 1, MB_FIND_ANY)
else
scan += 1
end
if (pass_next)
pass_next = false
next
end
# Shell-like semantics for single quotes -- don't allow backslash
# to quote anything in single quotes, especially not the closing
# quote. If you don't like this, take out the check on the value
# of quote_char.
if (quote_char != "'" && @rl_line_buffer[scan,1] == "\\")
pass_next = true
found_quote |= RL_QF_BACKSLASH
next
end
if (quote_char != 0.chr)
# Ignore everything until the matching close quote char.
if (@rl_line_buffer[scan,1] == quote_char)
# Found matching close. Abandon this substring.
quote_char = 0.chr
@rl_point = _end
end
elsif (@rl_completer_quote_characters.include?(@rl_line_buffer[scan,1]))
# Found start of a quoted substring.
quote_char = @rl_line_buffer[scan,1]
@rl_point = scan + 1
# Shell-like quoting conventions.
if (quote_char == "'")
found_quote |= RL_QF_SINGLE_QUOTE
elsif (quote_char == '"')
found_quote |= RL_QF_DOUBLE_QUOTE
else
found_quote |= RL_QF_OTHER_QUOTE
end
end
end
end
if (@rl_point == _end && quote_char == 0.chr)
# We didn't find an unclosed quoted substring upon which to do
# completion, so use the word break characters to find the
# substring on which to complete.
while (@rl_point = !@rl_byte_oriented ?
_rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_ANY):(@rl_point-1))>0
scan = @rl_line_buffer[@rl_point,1]
if !brkchars.include?(scan)
next
end
# Call the application-specific function to tell us whether
# this word break character is quoted and should be skipped.
if (@rl_char_is_quoted_p && found_quote!=0 &&
send(@rl_char_is_quoted_p,@rl_line_buffer, @rl_point))
next
end
# Convoluted code, but it avoids an n^2 algorithm with calls
# to char_is_quoted.
break
end
end
# If we are at an unquoted word break, then advance past it.
scan = @rl_line_buffer[@rl_point,1]
# If there is an application-specific function to say whether or not
# a character is quoted and we found a quote character, let that
# function decide whether or not a character is a word break, even
# if it is found in rl_completer_word_break_characters. Don't bother
# if we're at the end of the line, though.
if (scan != 0.chr)
if (@rl_char_is_quoted_p)
isbrk = (found_quote == 0 ||
!send(@rl_char_is_quoted_p,@rl_line_buffer, @rl_point)) &&
brkchars.include?(scan)
else
isbrk = brkchars.include?(scan)
end
if (isbrk)
# If the character that caused the word break was a quoting
# character, then remember it as the delimiter.
if (@rl_basic_quote_characters &&
@rl_basic_quote_characters.include?(scan) &&
(_end - @rl_point) > 1)
delimiter = scan
end
# If the character isn't needed to determine something special
# about what kind of completion to perform, then advance past it.
if (@rl_special_prefixes.nil? || !@rl_special_prefixes.include?(scan) )
@rl_point+=1
end
end
end
return [quote_char,found_quote!=0,delimiter]
end
def gen_completion_matches(text, start, _end, our_func, found_quote, quote_char)
@rl_completion_found_quote = found_quote
@rl_completion_quote_character = quote_char
# If the user wants to TRY to complete, but then wants to give
# up and use the default completion function, they set the
# variable rl_attempted_completion_function.
if (@rl_attempted_completion_function)
matches = Readline.send(@rl_attempted_completion_function,text, start, _end)
if (matches || @rl_attempted_completion_over)
@rl_attempted_completion_over = false
return (matches)
end
end
# XXX -- filename dequoting moved into rl_filename_completion_function
matches = rl_completion_matches(text, our_func)
matches
end
# Filter out duplicates in MATCHES. This frees up the strings in
# MATCHES.
def remove_duplicate_matches(matches)
# Sort the items.
# Sort the array without matches[0], since we need it to
# stay in place no matter what.
if matches.length>0
matches[1..-2] = matches[1..-2].sort.uniq
end
matches
end
def postprocess_matches(matchesp, matching_filenames)
matches = matchesp
return 0 if matches.nil?
# It seems to me that in all the cases we handle we would like
# to ignore duplicate possiblilities. Scan for the text to
# insert being identical to the other completions.
if (@rl_ignore_completion_duplicates)
remove_duplicate_matches(matches)
end
# If we are matching filenames, then here is our chance to
# do clever processing by re-examining the list. Call the
# ignore function with the array as a parameter. It can
# munge the array, deleting matches as it desires.
if (@rl_ignore_some_completions_function && matching_filenames)
nmatch = matches.length
send(@rl_ignore_some_completions_function,matches)
if (matches.nil? || matches[0].nil?)
matches = nil
return 0
else
# If we removed some matches, recompute the common prefix.
i = matches.length
if (i > 1 && i < nmatch)
t = matches[0]
compute_lcd_of_matches(matches, i - 1, t)
end
end
end
matchesp = matches
1
end
def insert_all_matches(matches, point, qc)
rl_begin_undo_group()
# remove any opening quote character; make_quoted_replacement will add
# it back.
if (qc && qc.length>0 && point>0 && @rl_line_buffer[point - 1,1] == qc)
point-=1
end
rl_delete_text(point, @rl_point)
@rl_point = point
if (matches[1])
i = 1
while(matches[i])
rp = make_quoted_replacement(matches[i], SINGLE_MATCH, qc)
rl_insert_text(rp)
rl_insert_text(" ")
if (rp != matches[i])
rp = nil
end
i += 1
end
else
rp = make_quoted_replacement(matches[0], SINGLE_MATCH, qc)
rl_insert_text(rp)
rl_insert_text(" ")
if (rp != matches[0])
rp = nil
end
end
rl_end_undo_group()
end
def make_quoted_replacement(match, mtype, qc)
# If we are doing completion on quoted substrings, and any matches
# contain any of the completer_word_break_characters, then auto-
# matically prepend the substring with a quote character (just pick
# the first one from the list of such) if it does not already begin
# with a quote string. FIXME: Need to remove any such automatically
# inserted quote character when it no longer is necessary, such as
# if we change the string we are completing on and the new set of
# matches don't require a quoted substring.
replacement = match
should_quote = match && @rl_completer_quote_characters &&
@rl_filename_completion_desired &&
@rl_filename_quoting_desired
if (should_quote)
should_quote = should_quote && (qc.nil? || qc == 0.chr ||
(@rl_completer_quote_characters &&
@rl_completer_quote_characters.include?(qc)))
end
if (should_quote)
# If there is a single match, see if we need to quote it.
# This also checks whether the common prefix of several
# matches needs to be quoted.
should_quote = @rl_filename_quote_characters ?
!!match[@rl_filename_quote_characters] :
false
do_replace = should_quote ? mtype : NO_MATCH
# Quote the replacement, since we found an embedded
# word break character in a potential match.
if (do_replace != NO_MATCH && @rl_filename_quoting_function)
replacement = send(@rl_filename_quoting_function,match, do_replace, qc)
end
end
replacement
end
def insert_match(match, start, mtype, qc)
oqc = qc
replacement = make_quoted_replacement(match, mtype, qc)
# Now insert the match.
if (replacement)
# Don't double an opening quote character.
if (qc && qc.length>0 && start!=0 && @rl_line_buffer[start - 1,1] == qc &&
replacement[0,1] == qc)
start-=1
# If make_quoted_replacement changed the quoting character, remove
# the opening quote and insert the (fully-quoted) replacement.
elsif (qc && (qc != oqc) && start!=0 && @rl_line_buffer[start - 1,1] == oqc &&
replacement[0,1] != oqc)
start-=1
end
_rl_replace_text(replacement, start, @rl_point - 1)
if (replacement != match)
replacement = nil
end
end
end
# Return the portion of PATHNAME that should be output when listing
# possible completions. If we are hacking filename completion, we
# are only interested in the basename, the portion following the
# final slash. Otherwise, we return what we were passed. Since
# printing empty strings is not very informative, if we're doing
# filename completion, and the basename is the empty string, we look
# for the previous slash and return the portion following that. If
# there's no previous slash, we just return what we were passed.
def printable_part(pathname)
if (!@rl_filename_completion_desired) # don't need to do anything
return (pathname)
end
temp = pathname.rindex('/')
return pathname if temp.nil?
File.basename(pathname)
end
def fnprint(to_print)
printed_len = 0
case @encoding
when 'E'
arr = to_print.scan(/./me)
when 'S'
arr = to_print.scan(/./ms)
when 'U'
arr = to_print.scan(/./mu)
when 'X'
arr = to_print.dup.force_encoding(@encoding_name).chars
else
arr = to_print.scan(/./m)
end
arr.each do |s|
if(ctrl_char(s))
@rl_outstream.write('^'+(s[0].ord|0x40).chr.upcase)
printed_len += 2
elsif s == RUBOUT
@rl_outstream.write('^?')
printed_len += 2
else
@rl_outstream.write(s)
if @encoding=='U'
printed_len += s.unpack('U').first >= 0x1000 ? 2 : 1
elsif @encoding=='X'
printed_len += s.ord >= 0x1000 ? 2 : 1
else
printed_len += s.length
end
end
end
printed_len
end
def _rl_internal_pager(lines)
@rl_outstream.puts "--More--"
@rl_outstream.flush
i = get_y_or_n(1)
_rl_erase_entire_line()
if (i == 0)
return -1
elsif (i == 2)
return (lines - 1)
else
return 0
end
end
def path_isdir(filename)
return File.directory?(filename)
end
# Return the character which best describes FILENAME.
# `@' for symbolic links
# `/' for directories
# `*' for executables
# `=' for sockets
# `|' for FIFOs
# `%' for character special devices
# `#' for block special devices
def stat_char(filename)
return nil if !File.exists?(filename)
return '/' if File.directory?(filename)
return '%' if File.chardev?(filename)
return '#' if File.blockdev?(filename)
return '@' if File.symlink?(filename)
return '=' if File.socket?(filename)
return '|' if File.pipe?(filename)
return '*' if File.executable?(filename)
nil
end
# Output TO_PRINT to rl_outstream. If VISIBLE_STATS is defined and we
# are using it, check for and output a single character for `special'
# filenames. Return the number of characters we output.
def print_filename(to_print, full_pathname)
printed_len = fnprint(to_print)
if (@rl_filename_completion_desired && (@rl_visible_stats || @_rl_complete_mark_directories))
# If to_print != full_pathname, to_print is the basename of the
# path passed. In this case, we try to expand the directory
# name before checking for the stat character.
if (to_print != full_pathname)
if full_pathname.nil? || full_pathname.length==0
dn = '/'
else
dn = File.dirname(full_pathname)
end
s = File.expand_path(dn)
if (@rl_directory_completion_hook)
send(@rl_directory_completion_hook,s)
end
slen = s.length
new_full_pathname = s.dup
if (s[-1,1] == '/' )
slen-=1
else
new_full_pathname[slen,1] = '/'
end
new_full_pathname[slen .. -1] = '/' + to_print
if (@rl_visible_stats)
extension_char = stat_char(new_full_pathname)
else
if (path_isdir(new_full_pathname))
extension_char = '/'
end
end
new_full_pathname = nil
else
s = File.expand_path(full_pathname)
if (@rl_visible_stats)
extension_char = stat_char(s)
else
if (path_isdir(s))
extension_char = '/'
end
end
end
s = nil
if (extension_char)
@rl_outstream.write(extension_char)
printed_len+=1
end
end
printed_len
end
# The user must press "y" or "n". Non-zero return means "y" pressed.
def get_y_or_n(for_pager)
while(true)
rl_setstate(RL_STATE_MOREINPUT)
c = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
if (c == 'y' || c == 'Y' || c == ' ')
return (1)
end
if (c == 'n' || c == 'N' || c == RUBOUT)
return (0)
end
if (c == ABORT_CHAR || (c.class == Fixnum && c < 0))
_rl_abort_internal()
end
if (for_pager && (c == NEWLINE || c == RETURN))
return (2)
end
if (for_pager && (c == 'q' || c == 'Q'))
return (0)
end
rl_ding()
end
end
# Compute width of STRING when displayed on screen by print_filename
def fnwidth(string)
left = string.length + 1
width = pos = 0
while (string[pos] && string[pos,1] != 0.chr)
if (ctrl_char(string[0,1]) || string[0,1] == RUBOUT)
width += 2
pos+=1
else
case @encoding
when 'E'
wc = string[pos,left-pos].scan(/./me)[0]
bytes = wc.length
tempwidth = wc.length
when 'S'
wc = string[pos,left-pos].scan(/./ms)[0]
bytes = wc.length
tempwidth = wc.length
when 'U'
wc = string[pos,left-pos].scan(/./mu)[0]
bytes = wc.length
tempwidth = wc.unpack('U').first >= 0x1000 ? 2 : 1
when 'X'
wc = string[pos,left-pos].force_encoding(@encoding_name)[0]
bytes = wc.bytesize
tempwidth = wc.ord >= 0x1000 ? 2 : 1
else
wc = string[pos,left-pos].scan(/./m)[0]
bytes = wc.length
tempwidth = wc.length
end
clen = bytes
pos += clen
w = tempwidth
width += (w >= 0) ? w : 1
end
end
width
end
# Display MATCHES, a list of matching filenames in argv format. This
# handles the simple case -- a single match -- first. If there is more
# than one match, we compute the number of strings in the list and the
# length of the longest string, which will be needed by the display
# function. If the application wants to handle displaying the list of
# matches itself, it sets RL_COMPLETION_DISPLAY_MATCHES_HOOK to the
# address of a function, and we just call it. If we're handling the
# display ourselves, we just call rl_display_match_list. We also check
# that the list of matches doesn't exceed the user-settable threshold,
# and ask the user if he wants to see the list if there are more matches
# than RL_COMPLETION_QUERY_ITEMS.
def display_matches(matches)
# Move to the last visible line of a possibly-multiple-line command.
_rl_move_vert(@_rl_vis_botlin)
# Handle simple case first. What if there is only one answer?
if matches[1].nil?
temp = printable_part(matches[0])
rl_crlf()
print_filename(temp, matches[0])
rl_crlf()
rl_forced_update_display()
@rl_display_fixed = true
return
end
# There is more than one answer. Find out how many there are,
# and find the maximum printed length of a single entry.
max = 0
i = 1
while(matches[i])
temp = printable_part(matches[i])
len = fnwidth(temp)
if (len > max)
max = len
end
i += 1
end
len = i - 1
# If the caller has defined a display hook, then call that now.
if (@rl_completion_display_matches_hook)
send(@rl_completion_display_matches_hook,matches, len, max)
return
end
# If there are many items, then ask the user if she really wants to
# see them all.
if (@rl_completion_query_items > 0 && len >= @rl_completion_query_items)
rl_crlf()
@rl_outstream.write("Display all #{len} possibilities? (y or n)")
@rl_outstream.flush
if (get_y_or_n(false)==0)
rl_crlf()
rl_forced_update_display()
@rl_display_fixed = true
return
end
end
rl_display_match_list(matches, len, max)
rl_forced_update_display()
@rl_display_fixed = true
end
# Complete the word at or before point.
# WHAT_TO_DO says what to do with the completion.
# `?' means list the possible completions.
# TAB means do standard completion.
# `*' means insert all of the possible completions.
# `!' means to do standard completion, and list all possible completions if
# there is more than one.
# `@' means to do standard completion, and list all possible completions if
# there is more than one and partial completion is not possible.
def rl_complete_internal(what_to_do)
rl_setstate(RL_STATE_COMPLETING)
set_completion_defaults(what_to_do)
saved_line_buffer = @rl_line_buffer ? @rl_line_buffer.delete(0.chr) : nil
our_func = @rl_completion_entry_function ?
@rl_completion_entry_function : :rl_filename_completion_function
# We now look backwards for the start of a filename/variable word.
_end = @rl_point
found_quote = false
delimiter = 0.chr
quote_char = 0.chr
if (@rl_point!=0)
# This (possibly) changes rl_point. If it returns a non-zero char,
# we know we have an open quote.
quote_char,found_quote,delimiter = _rl_find_completion_word()
end
start = @rl_point
@rl_point = _end
text = rl_copy_text(start, _end)
matches = gen_completion_matches(text, start, _end, our_func, found_quote, quote_char)
# nontrivial_lcd is set if the common prefix adds something to the word
# being completed.
nontrivial_lcd = !!(matches && text != matches[0])
text = nil
if matches.nil?
rl_ding()
saved_line_buffer = nil
@completion_changed_buffer = false
rl_unsetstate(RL_STATE_COMPLETING)
return 0
end
# If we are matching filenames, the attempted completion function will
# have set rl_filename_completion_desired to a non-zero value. The basic
# rl_filename_completion_function does this.
i = @rl_filename_completion_desired
if (postprocess_matches(matches, i) == 0)
rl_ding()
saved_line_buffer = nil
@completion_changed_buffer = false
rl_unsetstate(RL_STATE_COMPLETING)
return 0
end
case (what_to_do)
when TAB,'!','@'
# Insert the first match with proper quoting.
if (matches[0])
insert_match(matches[0], start, matches[1] ? MULT_MATCH : SINGLE_MATCH, quote_char)
end
# If there are more matches, ring the bell to indicate.
# If we are in vi mode, Posix.2 says to not ring the bell.
# If the `show-all-if-ambiguous' variable is set, display
# all the matches immediately. Otherwise, if this was the
# only match, and we are hacking files, check the file to
# see if it was a directory. If so, and the `mark-directories'
# variable is set, add a '/' to the name. If not, and we
# are at the end of the line, then add a space.
if (matches[1])
if (what_to_do == '!')
display_matches(matches)
elsif (what_to_do == '@')
if (!nontrivial_lcd)
display_matches(matches)
end
elsif (@rl_editing_mode != @vi_mode)
rl_ding() # There are other matches remaining.
end
else
append_to_match(matches[0], delimiter, quote_char, nontrivial_lcd)
end
when '*'
insert_all_matches(matches, start, quote_char)
when '?'
display_matches(matches)
else
$stderr.write("\r\nreadline: bad value #{what_to_do} for what_to_do in rl_complete\n")
rl_ding()
saved_line_buffer = nil
rl_unsetstate(RL_STATE_COMPLETING)
return 1
end
matches = nil
# Check to see if the line has changed through all of this manipulation.
if (saved_line_buffer)
@completion_changed_buffer = @rl_line_buffer.delete(0.chr) != saved_line_buffer
saved_line_buffer = nil
end
rl_unsetstate(RL_STATE_COMPLETING)
0
end
# Complete the word at or before point. You have supplied the function
# that does the initial simple matching selection algorithm (see
# rl_completion_matches ()). The default is to do filename completion.
def rl_complete(ignore, invoking_key)
if (@rl_inhibit_completion)
return (_rl_insert_char(ignore, invoking_key))
elsif (@rl_last_func == :rl_complete && !@completion_changed_buffer)
return (rl_complete_internal('?'))
elsif (@_rl_complete_show_all)
return (rl_complete_internal('!'))
elsif (@_rl_complete_show_unmodified)
return (rl_complete_internal('@'))
else
return (rl_complete_internal(TAB))
end
end
# Return the history entry which is logically at OFFSET in the history array.
# OFFSET is relative to history_base.
def history_get(offset)
local_index = offset - @history_base
return (local_index >= @history_length || local_index < 0 || @the_history.nil?) ?
nil : @the_history[local_index]
end
def rl_replace_from_history(entry, flags)
# Can't call with `1' because rl_undo_list might point to an undo list
# from a history entry, just like we're setting up here.
rl_replace_line(entry.line, false)
@rl_undo_list = entry.data
@rl_point = @rl_end
@rl_mark = 0
if (@rl_editing_mode == @vi_mode)
@rl_point = 0
@rl_mark = @rl_end
end
end
# Remove history element WHICH from the history. The removed
# element is returned to you so you can free the line, data,
# and containing structure.
def remove_history(which)
if (which < 0 || which >= @history_length || @history_length == 0 || @the_history.nil?)
return nil
end
return_value = @the_history[which]
@the_history.delete_at(which)
@history_length-=1
return_value
end
def block_sigint()
return if @sigint_blocked
@sigint_proc = Signal.trap("INT", "IGNORE")
@sigint_blocked = true
end
def release_sigint()
return if !@sigint_blocked
Signal.trap("INT", @sigint_proc)
@sigint_blocked = false
end
def retry_if_interrupted(&block)
tries = 0
begin
yield block
rescue Errno::EINTR
tries += 1
retry if tries <= 10
end
end
def save_tty_chars()
@_rl_last_tty_chars = @_rl_tty_chars
h = {}
retry_if_interrupted do
h = Hash[*`stty -a`.scan(/(\w+) = ([^;]+);/).flatten]
end
h.each {|k,v| v.gsub!(/\^(.)/){($1[0].ord ^ ((?a..?z).include?($1[0]) ? 0x60 : 0x40)).chr}}
@_rl_tty_chars.t_erase = h['erase']
@_rl_tty_chars.t_kill = h['kill']
@_rl_tty_chars.t_intr = h['intr']
@_rl_tty_chars.t_quit = h['quit']
@_rl_tty_chars.t_start = h['start']
@_rl_tty_chars.t_stop = h['stop']
@_rl_tty_chars.t_eof = h['eof']
@_rl_tty_chars.t_eol = "\n"
@_rl_tty_chars.t_eol2 = h['eol2']
@_rl_tty_chars.t_susp = h['susp']
@_rl_tty_chars.t_dsusp = h['dsusp']
@_rl_tty_chars.t_reprint = h['rprnt']
@_rl_tty_chars.t_flush = h['flush']
@_rl_tty_chars.t_werase = h['werase']
@_rl_tty_chars.t_lnext = h['lnext']
@_rl_tty_chars.t_status = -1
retry_if_interrupted do
@otio = `stty -g`
end
end
def _rl_bind_tty_special_chars(kmap)
kmap[@_rl_tty_chars.t_erase] = :rl_rubout
kmap[@_rl_tty_chars.t_kill] = :rl_unix_line_discard
kmap[@_rl_tty_chars.t_werase] = :rl_unix_word_rubout
kmap[@_rl_tty_chars.t_lnext] = :rl_quoted_insert
end
def prepare_terminal_settings(meta_flag)
retry_if_interrupted do
@readline_echoing_p = (`stty -a`.scan(/-*echo\b/).first == 'echo')
end
# First, the basic settings to put us into character-at-a-time, no-echo
# input mode.
setting = " -echo -icrnl cbreak"
# If this terminal doesn't care how the 8th bit is used, then we can
# use it for the meta-key. If only one of even or odd parity is
# specified, then the terminal is using parity, and we cannot.
retry_if_interrupted do
if (`stty -a`.scan(/-parenb\b/).first == '-parenb')
setting << " pass8"
end
end
setting << " -ixoff"
rl_bind_key(@_rl_tty_chars.t_start, :rl_restart_output)
@_rl_eof_char = @_rl_tty_chars.t_eof
#setting << " -isig"
retry_if_interrupted do
`stty #{setting}`
end
end
def _rl_control_keypad(on)
if on && @_rl_term_ks
@_rl_out_stream.write(@_rl_term_ks)
elsif !on && @_rl_term_ke
@_rl_out_stream.write(@_rl_term_ke)
end
end
# Rebind all of the tty special chars that readline worries about back
# to self-insert. Call this before saving the current terminal special
# chars with save_tty_chars(). This only works on POSIX termios or termio
# systems.
def rl_tty_unset_default_bindings(kmap)
# Don't bother before we've saved the tty special chars at least once.
return if (!rl_isstate(RL_STATE_TTYCSAVED))
kmap[@_rl_tty_chars.t_erase] = :rl_insert
kmap[@_rl_tty_chars.t_kill] = :rl_insert
kmap[@_rl_tty_chars.t_lnext] = :rl_insert
kmap[@_rl_tty_chars.t_werase] = :rl_insert
end
def rl_prep_terminal(meta_flag)
if no_terminal?
@readline_echoing_p = true
return
end
return if (@terminal_prepped)
# Try to keep this function from being INTerrupted.
block_sigint()
if (@_rl_bind_stty_chars)
# If editing in vi mode, make sure we restore the bindings in the
# insertion keymap no matter what keymap we ended up in.
if (@rl_editing_mode == @vi_mode)
rl_tty_unset_default_bindings(@vi_insertion_keymap)
else
rl_tty_unset_default_bindings(@_rl_keymap)
end
end
save_tty_chars()
rl_setstate(RL_STATE_TTYCSAVED)
if (@_rl_bind_stty_chars)
# If editing in vi mode, make sure we set the bindings in the
# insertion keymap no matter what keymap we ended up in.
if (@rl_editing_mode == @vi_mode)
_rl_bind_tty_special_chars(@vi_insertion_keymap)
else
_rl_bind_tty_special_chars(@_rl_keymap)
end
end
prepare_terminal_settings(meta_flag)
if (@_rl_enable_keypad)
_rl_control_keypad(true)
end
@rl_outstream.flush
@terminal_prepped = true
rl_setstate(RL_STATE_TERMPREPPED)
release_sigint()
end
# Restore the terminal's normal settings and modes.
def rl_deprep_terminal()
return if ENV["TERM"].nil?
return if (!@terminal_prepped)
# Try to keep this function from being interrupted.
block_sigint()
if (@_rl_enable_keypad)
_rl_control_keypad(false)
end
@rl_outstream.flush
# restore terminal setting
retry_if_interrupted do
`stty #{@otio}`
end
@terminal_prepped = false
rl_unsetstate(RL_STATE_TERMPREPPED)
release_sigint()
end
# Set the mark at POSITION.
def _rl_set_mark_at_pos(position)
return -1 if (position > @rl_end)
@rl_mark = position
0
end
# A bindable command to set the mark.
def rl_set_mark(count, key)
_rl_set_mark_at_pos(@rl_explicit_arg ? count : @rl_point)
end
# Kill from here to the end of the line. If DIRECTION is negative, kill
# back to the line start instead.
def rl_kill_line (direction, ignore)
if (direction < 0)
return (rl_backward_kill_line(1, ignore))
else
orig_point = @rl_point
rl_end_of_line(1, ignore)
if (orig_point != @rl_point)
rl_kill_text(orig_point, @rl_point)
end
@rl_point = orig_point
if (@rl_editing_mode == @emacs_mode)
@rl_mark = @rl_point
end
end
0
end
# Kill backwards to the start of the line. If DIRECTION is negative, kill
# forwards to the line end instead.
def rl_backward_kill_line(direction, ignore)
if (direction < 0)
return (rl_kill_line(1, ignore))
else
if (@rl_point==0)
rl_ding()
else
orig_point = @rl_point
rl_beg_of_line(1, ignore)
if (@rl_point != orig_point)
rl_kill_text(orig_point, @rl_point)
end
if (@rl_editing_mode == @emacs_mode)
@rl_mark = @rl_point
end
end
end
0
end
# Kill the whole line, no matter where point is.
def rl_kill_full_line(count, ignore)
rl_begin_undo_group()
@rl_point = 0
rl_kill_text(@rl_point, @rl_end)
@rl_mark = 0
rl_end_undo_group()
0
end
# Search backwards through the history looking for a string which is typed
# interactively. Start with the current line.
def rl_reverse_search_history(sign, key)
rl_search_history(-sign, key)
end
# Search forwards through the history looking for a string which is typed
# interactively. Start with the current line.
def rl_forward_search_history(sign, key)
rl_search_history(sign, key)
end
# Search through the history looking for an interactively typed string.
# This is analogous to i-search. We start the search in the current line.
# DIRECTION is which direction to search; >= 0 means forward, < 0 means
# backwards.
def rl_search_history(direction, invoking_key)
rl_setstate(RL_STATE_ISEARCH)
cxt = _rl_isearch_init(direction)
rl_display_search(cxt.search_string, (cxt.sflags & SF_REVERSE)!=0, -1)
# If we are using the callback interface, all we do is set up here and
# return. The key is that we leave RL_STATE_ISEARCH set.
if (rl_isstate(RL_STATE_CALLBACK))
return (0)
end
r = -1
while(true)
_rl_search_getchar(cxt)
# We might want to handle EOF here (c == 0)
r = _rl_isearch_dispatch(cxt, cxt.lastc)
break if (r <= 0)
end
# The searching is over. The user may have found the string that she
# was looking for, or else she may have exited a failing search. If
# LINE_INDEX is -1, then that shows that the string searched for was
# not found. We use this to determine where to place rl_point.
_rl_isearch_cleanup(cxt, r)
end
def _rl_scxt_alloc(type, flags)
cxt = Struct.new(:type,:sflags,:search_string,:search_string_index,:search_string_size,:lines,:allocated_line,
:hlen,:hindex,:save_point,:save_mark,:save_line,:last_found_line,:prev_line_found,:save_undo_list,:history_pos,
:direction,:lastc,:sline,:sline_len,:sline_index,:search_terminators,:mb).new
cxt.type = type
cxt.sflags = flags
cxt.search_string = nil
cxt.search_string_size = cxt.search_string_index = 0
cxt.lines = nil
cxt.allocated_line = nil
cxt.hlen = cxt.hindex = 0
cxt.save_point = @rl_point
cxt.save_mark = @rl_mark
cxt.save_line = where_history()
cxt.last_found_line = cxt.save_line
cxt.prev_line_found = nil
cxt.save_undo_list = nil
cxt.history_pos = 0
cxt.direction = 0
cxt.lastc = 0
cxt.sline = nil
cxt.sline_len = cxt.sline_index = 0
cxt.search_terminators = nil
cxt
end
def history_list()
@the_history
end
def _rl_isearch_init(direction)
cxt = _rl_scxt_alloc(RL_SEARCH_ISEARCH, 0)
if (direction < 0)
cxt.sflags |= SF_REVERSE
end
cxt.search_terminators = @_rl_isearch_terminators ? @_rl_isearch_terminators :
@default_isearch_terminators
# Create an arrary of pointers to the lines that we want to search.
hlist = history_list()
rl_maybe_replace_line()
i = 0
if (hlist)
i += 1 while(hlist[i])
end
# Allocate space for this many lines, +1 for the current input line,
# and remember those lines.
cxt.hlen = i
cxt.lines = []
for i in 0 ... cxt.hlen
cxt.lines[i] = hlist[i].line
end
if (@_rl_saved_line_for_history)
cxt.lines[i] = @_rl_saved_line_for_history.line.dup
else
# Keep track of this so we can free it.
cxt.allocated_line = @rl_line_buffer.dup
cxt.lines << cxt.allocated_line
end
cxt.hlen+=1
# The line where we start the search.
cxt.history_pos = cxt.save_line
rl_save_prompt()
# Initialize search parameters.
cxt.search_string_size = 128
cxt.search_string_index = 0
cxt.search_string = ""
# Normalize DIRECTION into 1 or -1.
cxt.direction = (direction >= 0) ? 1 : -1
cxt.sline = @rl_line_buffer
cxt.sline_len = cxt.sline.delete(0.chr).length
cxt.sline_index = @rl_point
@_rl_iscxt = cxt # save globally
cxt
end
def rl_save_prompt()
@saved_local_prompt = @local_prompt
@saved_local_prefix = @local_prompt_prefix
@saved_prefix_length = @prompt_prefix_length
@saved_local_length = @local_prompt_len
@saved_last_invisible = @prompt_last_invisible
@saved_visible_length = @prompt_visible_length
@saved_invis_chars_first_line = @prompt_invis_chars_first_line
@saved_physical_chars = @prompt_physical_chars
@local_prompt = @local_prompt_prefix = nil
@local_prompt_len = 0
@prompt_last_invisible = @prompt_visible_length = @prompt_prefix_length = 0
@prompt_invis_chars_first_line = @prompt_physical_chars = 0
end
def rl_restore_prompt()
@local_prompt = nil
@local_prompt_prefix = nil
@local_prompt = @saved_local_prompt
@local_prompt_prefix = @saved_local_prefix
@local_prompt_len = @saved_local_length
@prompt_prefix_length = @saved_prefix_length
@prompt_last_invisible = @saved_last_invisible
@prompt_visible_length = @saved_visible_length
@prompt_invis_chars_first_line = @saved_invis_chars_first_line
@prompt_physical_chars = @saved_physical_chars
# can test saved_local_prompt to see if prompt info has been saved.
@saved_local_prompt = @saved_local_prefix = nil
@saved_local_length = 0
@saved_last_invisible = @saved_visible_length = @saved_prefix_length = 0
@saved_invis_chars_first_line = @saved_physical_chars = 0
end
def rl_message(msg_buf)
@rl_display_prompt = msg_buf
if @saved_local_prompt.nil?
rl_save_prompt()
@msg_saved_prompt = true
end
@local_prompt,@prompt_visible_length,@prompt_last_invisible,@prompt_invis_chars_first_line,@prompt_physical_chars =
expand_prompt(msg_buf)
@local_prompt_prefix = nil
@local_prompt_len = @local_prompt ? @local_prompt.length : 0
send(@rl_redisplay_function)
0
end
# Display the current state of the search in the echo-area.
# SEARCH_STRING contains the string that is being searched for,
# DIRECTION is zero for forward, or non-zero for reverse,
# WHERE is the history list number of the current line. If it is
# -1, then this line is the starting one.
def rl_display_search(search_string, reverse_p, where)
message = '('
if (reverse_p)
message << "reverse-"
end
message << "i-search)`"
if (search_string)
message << search_string
end
message << "': "
rl_message(message)
message = nil
send(@rl_redisplay_function)
end
# Transpose the characters at point. If point is at the end of the line,
# then transpose the characters before point.
def rl_transpose_chars(count, key)
return 0 if (count == 0)
if (@rl_point==0 || @rl_end < 2)
rl_ding()
return -1
end
rl_begin_undo_group()
if (@rl_point == @rl_end)
if !@rl_byte_oriented
@rl_point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO)
else
@rl_point -= 1
end
count = 1
end
prev_point = @rl_point
if !@rl_byte_oriented
@rl_point = _rl_find_prev_mbchar(@rl_line_buffer, @rl_point, MB_FIND_NONZERO)
else
@rl_point -= 1
end
char_length = prev_point - @rl_point
dummy = @rl_line_buffer[@rl_point,char_length]
rl_delete_text(@rl_point, @rl_point + char_length)
@rl_point += count
_rl_fix_point(0)
rl_insert_text(dummy)
rl_end_undo_group()
dummy = nil
0
end
# Here is C-u doing what Unix does. You don't *have* to use these
# key-bindings. We have a choice of killing the entire line, or
# killing from where we are to the start of the line. We choose the
# latter, because if you are a Unix weenie, then you haven't backspaced
# into the line at all, and if you aren't, then you know what you are
# doing.
def rl_unix_line_discard(count, key)
if (@rl_point == 0)
rl_ding()
else
rl_kill_text(@rl_point, 0)
@rl_point = 0
if (@rl_editing_mode == @emacs_mode)
@rl_mark = @rl_point
end
end
0
end
# Yank back the last killed text. This ignores arguments.
def rl_yank(count, ignore)
if @rl_kill_ring.nil?
_rl_abort_internal()
return -1
end
_rl_set_mark_at_pos(@rl_point)
rl_insert_text(@rl_kill_ring[@rl_kill_index])
0
end
# If the last command was yank, or yank_pop, and the text just
# before point is identical to the current kill item, then
# delete that text from the line, rotate the index down, and
# yank back some other text.
def rl_yank_pop(count, key)
if (((@rl_last_func != :rl_yank_pop) && (@rl_last_func != :rl_yank)) ||
@rl_kill_ring.nil?)
_rl_abort_internal()
return -1
end
l = @rl_kill_ring[@rl_kill_index].length
n = @rl_point - l
if (n >= 0 && @rl_line_buffer[n,l] == @rl_kill_ring[@rl_kill_index][0,l])
rl_delete_text(n, @rl_point)
@rl_point = n
@rl_kill_index-=1
if (@rl_kill_index < 0)
@rl_kill_index = @rl_kill_ring_length - 1
end
rl_yank(1, 0)
return 0
else
_rl_abort_internal()
return -1
end
end
# Yank the COUNTh argument from the previous history line, skipping
# HISTORY_SKIP lines before looking for the `previous line'.
def rl_yank_nth_arg_internal(count, ignore, history_skip)
pos = where_history()
if (history_skip>0)
history_skip.times { previous_history() }
end
entry = previous_history()
history_set_pos(pos)
if entry.nil?
rl_ding()
return -1
end
arg = history_arg_extract(count, count, entry.line)
if (arg.nil? || arg=='')
rl_ding()
arg = nil
return -1
end
rl_begin_undo_group()
_rl_set_mark_at_pos(@rl_point)
# Vi mode always inserts a space before yanking the argument, and it
# inserts it right *after* rl_point.
if (@rl_editing_mode == @vi_mode)
rl_vi_append_mode(1, ignore)
rl_insert_text(" ")
end
rl_insert_text(arg)
arg = nil
rl_end_undo_group()
return 0
end
# Yank the COUNTth argument from the previous history line.
def rl_yank_nth_arg(count, ignore)
rl_yank_nth_arg_internal(count, ignore, 0)
end
# Yank the last argument from the previous history line. This `knows'
# how rl_yank_nth_arg treats a count of `$'. With an argument, this
# behaves the same as rl_yank_nth_arg.
@history_skip = 0
@explicit_arg_p = false
@count_passed = 1
@direction = 1
@undo_needed = false
def rl_yank_last_arg(count, key)
if (@rl_last_func != :rl_yank_last_arg)
@history_skip = 0
@explicit_arg_p = @rl_explicit_arg
@count_passed = count
@direction = 1
else
if (@undo_needed)
rl_do_undo()
end
if (count < 1)
@direction = -@direction
end
@history_skip += @direction
if (@history_skip < 0)
@history_skip = 0
end
end
if (@explicit_arg_p)
retval = rl_yank_nth_arg_internal(@count_passed, key, @history_skip)
else
retval = rl_yank_nth_arg_internal('$', key, @history_skip)
end
@undo_needed = retval == 0
retval
end
def history_arg_extract(first, last, string)
if first != "$" || last != "$"
fail "RbReadline.history_arg_extract called with currently unsupported args."
end
# Find the last index of an unescaped quote character.
last_unescaped_quote_char = -1
RbReadline::HISTORY_QUOTE_CHARACTERS.each_char do |quote_char|
quote_char = Regexp.escape(quote_char)
if index = string =~ /(?:\\.|[^#{quote_char}\\])#{quote_char} *$/
last_unescaped_quote_char = index if index > last_unescaped_quote_char
end
end
last_unescaped_quote_char += 1 # Because of the regex used above.
# Find the last index of an unescaped word delimiter.
delimiters = RbReadline::HISTORY_WORD_DELIMITERS.chars.to_a.map { |d| Regexp.escape(d) }
unless last_unescaped_delimiter = string =~ /(?:\\.|[^#{delimiters.join}])+? *$/
last_unescaped_delimiter = 0
end
if last_unescaped_quote_char >= last_unescaped_delimiter
quoted_arg = _extract_last_quote(string, string[last_unescaped_quote_char,1])
end
quoted_arg or string[last_unescaped_delimiter...string.length]
end
def _extract_last_quote(string, quote_char)
quote_char = Regexp.escape(quote_char)
string =~ /(#{quote_char}(?:\\.|[^#{quote_char}])+?#{quote_char}) *$/
$1
end
def _rl_char_search_internal(count, dir, smbchar, len)
pos = @rl_point
inc = (dir < 0) ? -1 : 1
while (count!=0)
if ((dir < 0 && pos <= 0) || (dir > 0 && pos >= @rl_end))
rl_ding()
return -1
end
pos = (inc > 0) ? _rl_find_next_mbchar(@rl_line_buffer, pos, 1, MB_FIND_ANY) :
_rl_find_prev_mbchar(@rl_line_buffer, pos, MB_FIND_ANY)
begin
if (_rl_is_mbchar_matched(@rl_line_buffer, pos, @rl_end, smbchar, len)!=0)
count-=1
if (dir < 0)
@rl_point = (dir == BTO) ? pos+1 : pos
else
@rl_point = (dir == FTO) ? pos-1 : pos
end
break
end
prepos = pos
end while ((dir < 0) ? (pos = _rl_find_prev_mbchar(@rl_line_buffer, pos, MB_FIND_ANY)) != prepos :
(pos = _rl_find_next_mbchar(@rl_line_buffer, pos, 1, MB_FIND_ANY)) != prepos)
end
0
end
def _rl_char_search(count, fdir, bdir)
mbchar = ''
mb_len = _rl_read_mbchar(mbchar, MB_LEN_MAX)
if (mbchar.class == Fixnum && c < 0) || mbchar == 0.chr
return -1
end
if (count < 0)
return (_rl_char_search_internal(-count, bdir, mbchar, mb_len))
else
return (_rl_char_search_internal(count, fdir, mbchar, mb_len))
end
end
def rl_char_search(count, key)
_rl_char_search(count, FFIND, BFIND)
end
# Undo the next thing in the list. Return 0 if there
# is nothing to undo, or non-zero if there was.
def trans(i)
((i) == -1 ? @rl_point : ((i) == -2 ? @rl_end : (i)))
end
def rl_do_undo()
start = _end = waiting_for_begin = 0
begin
return 0 if @rl_undo_list.nil?
@_rl_doing_an_undo = true
rl_setstate(RL_STATE_UNDOING)
# To better support vi-mode, a start or end value of -1 means
# rl_point, and a value of -2 means rl_end.
if (@rl_undo_list.what == UNDO_DELETE || @rl_undo_list.what == UNDO_INSERT)
start = trans(@rl_undo_list.start)
_end = trans(@rl_undo_list.end)
end
case (@rl_undo_list.what)
# Undoing deletes means inserting some text.
when UNDO_DELETE
@rl_point = start
rl_insert_text(@rl_undo_list.text)
@rl_undo_list.text = nil
# Undoing inserts means deleting some text.
when UNDO_INSERT
rl_delete_text(start, _end)
@rl_point = start
# Undoing an END means undoing everything 'til we get to a BEGIN.
when UNDO_END
waiting_for_begin+=1
# Undoing a BEGIN means that we are done with this group.
when UNDO_BEGIN
if (waiting_for_begin!=0)
waiting_for_begin-=1
else
rl_ding()
end
end
@_rl_doing_an_undo = false
rl_unsetstate(RL_STATE_UNDOING)
release = @rl_undo_list
@rl_undo_list = @rl_undo_list.next
replace_history_data(-1, release, @rl_undo_list)
release = nil
end while (waiting_for_begin!=0)
1
end
# Do some undoing of things that were done.
def rl_undo_command(count, key)
if (count < 0)
return 0 # Nothing to do.
end
while (count>0)
if (rl_do_undo())
count-=1
else
rl_ding()
break
end
end
0
end
# Delete the word at point, saving the text in the kill ring.
def rl_kill_word(count, key)
if (count < 0)
return (rl_backward_kill_word(-count, key))
else
orig_point = @rl_point
rl_forward_word(count, key)
if (@rl_point != orig_point)
rl_kill_text(orig_point, @rl_point)
end
@rl_point = orig_point
if (@rl_editing_mode == @emacs_mode)
@rl_mark = @rl_point
end
end
0
end
# Rubout the word before point, placing it on the kill ring.
def rl_backward_kill_word(count, ignore)
if (count < 0)
return (rl_kill_word(-count, ignore))
else
orig_point = @rl_point
rl_backward_word(count, ignore)
if (@rl_point != orig_point)
rl_kill_text(orig_point, @rl_point)
end
if (@rl_editing_mode == @emacs_mode)
@rl_mark = @rl_point
end
end
0
end
# Revert the current line to its previous state.
def rl_revert_line(count, key)
if @rl_undo_list.nil?
rl_ding()
else
while (@rl_undo_list)
rl_do_undo()
end
if (@rl_editing_mode == @vi_mode)
@rl_point = @rl_mark = 0 # rl_end should be set correctly
end
end
0
end
def rl_backward_char_search (count, key)
_rl_char_search(count, BFIND, FFIND)
end
def rl_insert_completions(ignore, invoking_key)
rl_complete_internal('*')
end
def _rl_arg_init()
rl_save_prompt()
@_rl_argcxt = 0
rl_setstate(RL_STATE_NUMERICARG)
end
def _rl_arg_getchar()
rl_message("(arg: #{@rl_arg_sign * @rl_numeric_arg}) ")
rl_setstate(RL_STATE_MOREINPUT)
c = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
c
end
# Process C as part of the current numeric argument. Return -1 if the
# argument should be aborted, 0 if we should not read any more chars, and
# 1 if we should continue to read chars.
def _rl_arg_dispatch(cxt, c)
key = c
# If we see a key bound to `universal-argument' after seeing digits,
# it ends the argument but is otherwise ignored.
if (@_rl_keymap[c] == :rl_universal_argument)
if ((cxt & NUM_SAWDIGITS) == 0)
@rl_numeric_arg *= 4
return 1
elsif (rl_isstate(RL_STATE_CALLBACK))
@_rl_argcxt |= NUM_READONE
return 0 # XXX
else
rl_setstate(RL_STATE_MOREINPUT)
key = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
rl_restore_prompt()
rl_clear_message()
rl_unsetstate(RL_STATE_NUMERICARG)
if key.class == Fixnum && key < 0
return -1
end
return (_rl_dispatch(key, @_rl_keymap))
end
end
#c = (c[0].ord & ~0x80).chr
r = c[1,1]
if (r>='0' && r<='9')
r = r.to_i
@rl_numeric_arg = @rl_explicit_arg ? (@rl_numeric_arg * 10) + r : r
@rl_explicit_arg = 1
@_rl_argcxt |= NUM_SAWDIGITS
elsif (c == '-' && !@rl_explicit_arg)
@rl_numeric_arg = 1
@_rl_argcxt |= NUM_SAWMINUS
@rl_arg_sign = -1
else
# Make M-- command equivalent to M--1 command.
if ((@_rl_argcxt & NUM_SAWMINUS)!=0 && @rl_numeric_arg == 1 && !@rl_explicit_arg)
@rl_explicit_arg = 1
end
rl_restore_prompt()
rl_clear_message()
rl_unsetstate(RL_STATE_NUMERICARG)
r = _rl_dispatch(key, @_rl_keymap)
if (rl_isstate(RL_STATE_CALLBACK))
# At worst, this will cause an extra redisplay. Otherwise,
# we have to wait until the next character comes in.
if (!@rl_done)
send(@rl_redisplay_function)
end
r = 0
end
return r
end
1
end
def _rl_arg_overflow()
if (@rl_numeric_arg > 1000000)
@_rl_argcxt = 0
@rl_explicit_arg = @rl_numeric_arg = 0
rl_ding()
rl_restore_prompt()
rl_clear_message()
rl_unsetstate(RL_STATE_NUMERICARG)
return 1
end
0
end
# Handle C-u style numeric args, as well as M--, and M-digits.
def rl_digit_loop()
while (true)
return 1 if _rl_arg_overflow()!=0
c = _rl_arg_getchar()
if (c >= "\xFE")
_rl_abort_internal()
return -1
end
r = _rl_arg_dispatch(@_rl_argcxt, c)
break if (r <= 0 || !rl_isstate(RL_STATE_NUMERICARG))
end
return r
end
# Start a numeric argument with initial value KEY
def rl_digit_argument(ignore, key)
_rl_arg_init()
if (rl_isstate(RL_STATE_CALLBACK))
_rl_arg_dispatch(@_rl_argcxt, key)
rl_message("(arg: #{@rl_arg_sign * @rl_numeric_arg}) ")
return 0
else
rl_execute_next(key)
return (rl_digit_loop())
end
end
# Make C be the next command to be executed.
def rl_execute_next(c)
@rl_pending_input = c
rl_setstate(RL_STATE_INPUTPENDING)
0
end
# Meta-< goes to the start of the history.
def rl_beginning_of_history(count, key)
rl_get_previous_history(1 + where_history(), key)
end
# Meta-> goes to the end of the history. (The current line).
def rl_end_of_history(count, key)
rl_maybe_replace_line()
using_history()
rl_maybe_unsave_line()
0
end
# Uppercase the word at point.
def rl_upcase_word(count, key)
rl_change_case(count, UpCase)
end
# Lowercase the word at point.
def rl_downcase_word(count, key)
rl_change_case(count, DownCase)
end
# Upcase the first letter, downcase the rest.
def rl_capitalize_word(count, key)
rl_change_case(count, CapCase)
end
# Save an undo entry for the text from START to END.
def rl_modifying(start, _end)
if (start > _end)
start,_end = _end,start
end
if (start != _end)
temp = rl_copy_text(start, _end)
rl_begin_undo_group()
rl_add_undo(UNDO_DELETE, start, _end, temp)
rl_add_undo(UNDO_INSERT, start, _end, nil)
rl_end_undo_group()
end
0
end
# The meaty function.
# Change the case of COUNT words, performing OP on them.
# OP is one of UpCase, DownCase, or CapCase.
# If a negative argument is given, leave point where it started,
# otherwise, leave it where it moves to.
def rl_change_case(count, op)
start = @rl_point
rl_forward_word(count, 0)
_end = @rl_point
if (op != UpCase && op != DownCase && op != CapCase)
rl_ding()
return -1
end
if (count < 0)
start,_end = _end,start
end
# We are going to modify some text, so let's prepare to undo it.
rl_modifying(start, _end)
inword = false
while (start < _end)
c = _rl_char_value(@rl_line_buffer, start)
# This assumes that the upper and lower case versions are the same width.
if !@rl_byte_oriented
_next = _rl_find_next_mbchar(@rl_line_buffer, start, 1, MB_FIND_NONZERO)
else
_next = start + 1
end
if (!_rl_walphabetic(c))
inword = false
start = _next
next
end
if (op == CapCase)
nop = inword ? DownCase : UpCase
inword = true
else
nop = op
end
if (isascii(c))
nc = (nop == UpCase) ? c.upcase : c.downcase
@rl_line_buffer[start] = nc
end
start = _next
end
@rl_point = _end
0
end
def isascii(c)
int_val = c[0].to_i # 1.8 + 1.9 compat.
return (int_val < 128 && int_val > 0)
end
# Search non-interactively through the history list. DIR < 0 means to
# search backwards through the history of previous commands; otherwise
# the search is for commands subsequent to the current position in the
# history list. PCHAR is the character to use for prompting when reading
# the search string; if not specified (0), it defaults to `:'.
def noninc_search(dir, pchar)
cxt = _rl_nsearch_init(dir, pchar)
if (rl_isstate(RL_STATE_CALLBACK))
return (0)
end
# Read the search string.
r = 0
while (true)
c = _rl_search_getchar(cxt)
if (c == 0.chr)
break
end
r = _rl_nsearch_dispatch(cxt, c)
if (r < 0)
return 1
elsif (r == 0)
break
end
end
r = _rl_nsearch_dosearch(cxt)
(r >= 0) ? _rl_nsearch_cleanup(cxt, r) : (r != 1)
end
# Search forward through the history list for a string. If the vi-mode
# code calls this, KEY will be `?'.
def rl_noninc_forward_search(count, key)
noninc_search(1, (key == '?') ? '?' : nil)
end
# Reverse search the history list for a string. If the vi-mode code
# calls this, KEY will be `/'.
def rl_noninc_reverse_search(count, key)
noninc_search(-1, (key == '/') ? '/' : nil)
end
# Make the data from the history entry ENTRY be the contents of the
# current line. This doesn't do anything with rl_point; the caller
# must set it.
def make_history_line_current(entry)
_rl_replace_text(entry.line, 0, @rl_end)
_rl_fix_point(1)
if (@rl_editing_mode == @vi_mode)
# POSIX.2 says that the `U' command doesn't affect the copy of any
# command lines to the edit line. We're going to implement that by
# making the undo list start after the matching line is copied to the
# current editing buffer.
rl_free_undo_list()
end
if (@_rl_saved_line_for_history)
@_rl_saved_line_for_history = nil
end
end
# Make the current history item be the one at POS, an absolute index.
# Returns zero if POS is out of range, else non-zero.
def history_set_pos(pos)
if (pos > @history_length || pos < 0 || @the_history.nil?)
return (0)
end
@history_offset = pos
1
end
# Do an anchored search for string through the history in DIRECTION.
def history_search_prefix (string, direction)
history_search_internal(string, direction, ANCHORED_SEARCH)
end
# Search for STRING in the history list. DIR is < 0 for searching
# backwards. POS is an absolute index into the history list at
# which point to begin searching.
def history_search_pos(string, dir, pos)
old = where_history()
history_set_pos(pos)
if (history_search(string, dir) == -1)
history_set_pos(old)
return (-1)
end
ret = where_history()
history_set_pos(old)
ret
end
# Search the history list for STRING starting at absolute history position
# POS. If STRING begins with `^', the search must match STRING at the
# beginning of a history line, otherwise a full substring match is performed
# for STRING. DIR < 0 means to search backwards through the history list,
# DIR >= 0 means to search forward.
def noninc_search_from_pos(string, pos, dir)
return 1 if (pos < 0)
old = where_history()
return -1 if (history_set_pos(pos) == 0)
rl_setstate(RL_STATE_SEARCH)
if (string[0,1] == '^')
ret = history_search_prefix(string + 1, dir)
else
ret = history_search(string, dir)
end
rl_unsetstate(RL_STATE_SEARCH)
if (ret != -1)
ret = where_history()
end
history_set_pos(old)
ret
end
# Search for a line in the history containing STRING. If DIR is < 0, the
# search is backwards through previous entries, else through subsequent
# entries. Returns 1 if the search was successful, 0 otherwise.
def noninc_dosearch(string, dir)
if (string.nil? || string == '' || @noninc_history_pos < 0)
rl_ding()
return 0
end
pos = noninc_search_from_pos(string, @noninc_history_pos + dir, dir)
if (pos == -1)
# Search failed, current history position unchanged.
rl_maybe_unsave_line()
rl_clear_message()
@rl_point = 0
rl_ding()
return 0
end
@noninc_history_pos = pos
oldpos = where_history()
history_set_pos(@noninc_history_pos)
entry = current_history()
if (@rl_editing_mode != @vi_mode)
history_set_pos(oldpos)
end
make_history_line_current(entry)
@rl_point = 0
@rl_mark = @rl_end
rl_clear_message()
1
end
def _rl_make_prompt_for_search(pchar)
rl_save_prompt()
# We've saved the prompt, and can do anything with the various prompt
# strings we need before they're restored. We want the unexpanded
# portion of the prompt string after any final newline.
_p = @rl_prompt ? @rl_prompt.rindex("\n") : nil
if _p.nil?
len = (@rl_prompt && @rl_prompt.length>0 ) ? @rl_prompt.length : 0
if (len>0)
pmt = @rl_prompt.dup
else
pmt = ''
end
pmt << pchar
else
_p+=1
pmt = @rl_prompt[_p..-1]
pmt << pchar
end
# will be overwritten by expand_prompt, called from rl_message
@prompt_physical_chars = @saved_physical_chars + 1
pmt
end
def _rl_nsearch_init(dir, pchar)
cxt = _rl_scxt_alloc(RL_SEARCH_NSEARCH, 0)
if (dir < 0)
cxt.sflags |= SF_REVERSE # not strictly needed
end
cxt.direction = dir
cxt.history_pos = cxt.save_line
rl_maybe_save_line()
# Clear the undo list, since reading the search string should create its
# own undo list, and the whole list will end up being freed when we
# finish reading the search string.
@rl_undo_list = nil
# Use the line buffer to read the search string.
@rl_line_buffer[0,1] = 0.chr
@rl_end = @rl_point = 0
_p = _rl_make_prompt_for_search(pchar ? pchar : ':')
rl_message(_p)
_p = nil
rl_setstate(RL_STATE_NSEARCH)
@_rl_nscxt = cxt
cxt
end
def _rl_nsearch_cleanup(cxt, r)
cxt = nil
@_rl_nscxt = nil
rl_unsetstate(RL_STATE_NSEARCH)
r != 1
end
def _rl_nsearch_abort(cxt)
rl_maybe_unsave_line()
rl_clear_message()
@rl_point = cxt.save_point
@rl_mark = cxt.save_mark
rl_restore_prompt()
rl_unsetstate(RL_STATE_NSEARCH)
end
# Process just-read character C according to search context CXT. Return -1
# if the caller should abort the search, 0 if we should break out of the
# loop, and 1 if we should continue to read characters.
def _rl_nsearch_dispatch(cxt, c)
case (c)
when "\C-W"
rl_unix_word_rubout(1, c)
when "\C-W"
rl_unix_line_discard(1, c)
when RETURN,NEWLINE
return 0
when "\C-H",RUBOUT
if (@rl_point == 0)
_rl_nsearch_abort(cxt)
return -1
end
_rl_rubout_char(1, c)
when "\C-C","\C-G"
rl_ding()
_rl_nsearch_abort(cxt)
return -1
else
if !@rl_byte_oriented
rl_insert_text(cxt.mb)
else
_rl_insert_char(1, c)
end
end
send(@rl_redisplay_function)
1
end
# Perform one search according to CXT, using NONINC_SEARCH_STRING. Return
# -1 if the search should be aborted, any other value means to clean up
# using _rl_nsearch_cleanup (). Returns 1 if the search was successful,
# 0 otherwise.
def _rl_nsearch_dosearch(cxt)
@rl_mark = cxt.save_mark
# If rl_point == 0, we want to re-use the previous search string and
# start from the saved history position. If there's no previous search
# string, punt.
if (@rl_point == 0)
if @noninc_search_string.nil?
rl_ding()
rl_restore_prompt()
rl_unsetstate(RL_STATE_NSEARCH)
return -1
end
else
# We want to start the search from the current history position.
@noninc_history_pos = cxt.save_line
@noninc_search_string = @rl_line_buffer.dup
# If we don't want the subsequent undo list generated by the search
#matching a history line to include the contents of the search string,
#we need to clear rl_line_buffer here. For now, we just clear the
#undo list generated by reading the search string. (If the search
#fails, the old undo list will be restored by rl_maybe_unsave_line.)
rl_free_undo_list()
end
rl_restore_prompt()
noninc_dosearch(@noninc_search_string, cxt.direction)
end
# Transpose the words at point. If point is at the end of the line,
# transpose the two words before point.
def rl_transpose_words(count, key)
orig_point = @rl_point
return if (count==0)
# Find the two words.
rl_forward_word(count, key)
w2_end = @rl_point
rl_backward_word(1, key)
w2_beg = @rl_point
rl_backward_word(count, key)
w1_beg = @rl_point
rl_forward_word(1, key)
w1_end = @rl_point
# Do some check to make sure that there really are two words.
if ((w1_beg == w2_beg) || (w2_beg < w1_end))
rl_ding()
@rl_point = orig_point
return -1
end
# Get the text of the words.
word1 = rl_copy_text(w1_beg, w1_end)
word2 = rl_copy_text(w2_beg, w2_end)
# We are about to do many insertions and deletions. Remember them
# as one operation.
rl_begin_undo_group()
# Do the stuff at word2 first, so that we don't have to worry
# about word1 moving.
@rl_point = w2_beg
rl_delete_text(w2_beg, w2_end)
rl_insert_text(word1)
@rl_point = w1_beg
rl_delete_text(w1_beg, w1_end)
rl_insert_text(word2)
# This is exactly correct since the text before this point has not
# changed in length.
@rl_point = w2_end
# I think that does it.
rl_end_undo_group()
word1 = nil
word2 = nil
0
end
# Re-read the current keybindings file.
def rl_re_read_init_file(count, ignore)
r = rl_read_init_file(nil)
rl_set_keymap_from_edit_mode()
r
end
# Exchange the position of mark and point.
def rl_exchange_point_and_mark(count, key)
if (@rl_mark > @rl_end)
@rl_mark = -1
end
if (@rl_mark == -1)
rl_ding()
return -1
else
@rl_point, @rl_mark = @rl_mark, @rl_point
end
0
end
# A convenience function for displaying a list of strings in
# columnar format on readline's output stream. MATCHES is the list
# of strings, in argv format, LEN is the number of strings in MATCHES,
# and MAX is the length of the longest string in MATCHES.
def rl_display_match_list(matches, len, max)
# How many items of MAX length can we fit in the screen window?
max += 2
limit = @_rl_screenwidth / max
if (limit != 1 && (limit * max == @_rl_screenwidth))
limit-=1
end
# Avoid a possible floating exception. If max > _rl_screenwidth,
# limit will be 0 and a divide-by-zero fault will result.
if (limit == 0)
limit = 1
end
# How many iterations of the printing loop?
count = (len + (limit - 1)) / limit
# Watch out for special case. If LEN is less than LIMIT, then
# just do the inner printing loop.
# 0 < len <= limit implies count = 1.
# Sort the items if they are not already sorted.
if (!@rl_ignore_completion_duplicates)
matches[1,len] = matches[1,len].sort
end
rl_crlf()
lines = 0
if (!@_rl_print_completions_horizontally)
# Print the sorted items, up-and-down alphabetically, like ls.
for i in 1 .. count
l = i
for j in 0 ... limit
if (l > len || matches[l].nil?)
break
else
temp = printable_part(matches[l])
printed_len = print_filename(temp, matches[l])
if (j + 1 < limit)
@rl_outstream.write(' '*(max - printed_len))
end
end
l += count
end
rl_crlf()
lines+=1
if (@_rl_page_completions && lines >= (@_rl_screenheight - 1) && i < count)
lines = _rl_internal_pager(lines)
return if (lines < 0)
end
end
else
# Print the sorted items, across alphabetically, like ls -x.
i = 1
while(matches[i])
temp = printable_part(matches[i])
printed_len = print_filename(temp, matches[i])
# Have we reached the end of this line?
if (matches[i+1])
if ((limit > 1) && (i % limit) == 0)
rl_crlf()
lines+=1
if (@_rl_page_completions && lines >= @_rl_screenheight - 1)
lines = _rl_internal_pager(lines)
return if (lines < 0)
end
else
@rl_outstream.write(' '*(max - printed_len))
end
end
i += 1
end
rl_crlf()
end
end
# Append any necessary closing quote and a separator character to the
# just-inserted match. If the user has specified that directories
# should be marked by a trailing `/', append one of those instead. The
# default trailing character is a space. Returns the number of characters
# appended. If NONTRIVIAL_MATCH is set, we test for a symlink (if the OS
# has them) and don't add a suffix for a symlink to a directory. A
# nontrivial match is one that actually adds to the word being completed.
# The variable rl_completion_mark_symlink_dirs controls this behavior
# (it's initially set to the what the user has chosen, indicated by the
# value of _rl_complete_mark_symlink_dirs, but may be modified by an
# application's completion function).
def append_to_match(text, delimiter, quote_char, nontrivial_match)
temp_string = 0.chr * 4
temp_string_index = 0
if (quote_char && @rl_point>0 && !@rl_completion_suppress_quote &&
@rl_line_buffer[@rl_point - 1,1] != quote_char)
temp_string[temp_string_index] = quote_char
temp_string_index += 1
end
if (delimiter != 0.chr)
temp_string[temp_string_index] = delimiter
temp_string_index += 1
elsif (!@rl_completion_suppress_append && @rl_completion_append_character)
temp_string[temp_string_index] = @rl_completion_append_character
temp_string_index += 1
end
temp_string[temp_string_index] = 0.chr
temp_string_index += 1
if (@rl_filename_completion_desired)
filename = File.expand_path(text)
s = (nontrivial_match && !@rl_completion_mark_symlink_dirs) ?
File.lstat(filename) : File.stat(filename)
if s.directory?
if @_rl_complete_mark_directories
# This is clumsy. Avoid putting in a double slash if point
# is at the end of the line and the previous character is a
# slash.
if (@rl_point>0 && @rl_line_buffer[@rl_point,1] == 0.chr && @rl_line_buffer[@rl_point - 1,1] == '/' )
elsif (@rl_line_buffer[@rl_point,1] != '/')
rl_insert_text('/')
end
end
# Don't add anything if the filename is a symlink and resolves to a
# directory.
elsif s.symlink? && File.stat(filename).directory?
else
if (@rl_point == @rl_end && temp_string_index>0)
rl_insert_text(temp_string)
end
end
filename = nil
else
if (@rl_point == @rl_end && temp_string_index>0)
rl_insert_text(temp_string)
end
end
temp_string_index
end
# Stifle the history list, remembering only MAX number of lines.
def stifle_history(max)
max = 0 if (max < 0)
if (@history_length > max)
@the_history.slice!(0,(@history_length - max))
@history_length = max
end
@history_stifled = true
@max_input_history = @history_max_entries = max
end
# Stop stifling the history. This returns the previous maximum
# number of history entries. The value is positive if the history
# was stifled, negative if it wasn't.
def unstifle_history()
if (@history_stifled)
@history_stifled = false
return (@history_max_entries)
else
return (-@history_max_entries)
end
end
def history_is_stifled()
return (@history_stifled)
end
def clear_history()
@the_history = nil
@history_offset = @history_length = 0
end
# Insert COUNT characters from STRING to the output stream at column COL.
def insert_some_chars(string, count, col)
if @hConsoleHandle
_rl_output_some_chars(string,0,count)
else
# DEBUGGING
if (@rl_byte_oriented)
if (count != col)
$stderr.write("readline: debug: insert_some_chars: count (#{count}) != col (#{col})\n");
end
end
# If IC is defined, then we do not have to "enter" insert mode.
#if (@_rl_term_IC)
# buffer = tgoto(@_rl_term_IC, 0, col)
# @_rl_out_stream.write(buffer)
# _rl_output_some_chars(string,0,count)
#else
# If we have to turn on insert-mode, then do so.
if (@_rl_term_im)
@_rl_out_stream.write(@_rl_term_im)
end
# If there is a special command for inserting characters, then
# use that first to open up the space.
if (@_rl_term_ic)
@_rl_out_stream.write(@_rl_term_ic * count)
end
# Print the text.
_rl_output_some_chars(string,0, count)
# If there is a string to turn off insert mode, we had best use
# it now.
if (@_rl_term_ei)
@_rl_out_stream.write(@_rl_term_ei)
end
#end
end
end
# Delete COUNT characters from the display line.
def delete_chars(count)
return if (count > @_rl_screenwidth) # XXX
if @hConsoleHandle.nil?
#if (@_rl_term_DC)
# buffer = tgoto(_rl_term_DC, count, count);
# @_rl_out_stream.write(buffer * count)
#else
if (@_rl_term_dc)
@_rl_out_stream.write(@_rl_term_dc * count)
end
#end
end
end
# adjust pointed byte and find mbstate of the point of string.
# adjusted point will be point <= adjusted_point, and returns
# differences of the byte(adjusted_point - point).
# if point is invalied (point < 0 || more than string length),
# it returns -1
def _rl_adjust_point(string, point)
length = string.length
return -1 if (point < 0)
return -1 if (length < point)
pos = 0
case @encoding
when 'E'
x = string.scan(/./me)
i, len = 0, x.length
while (pos < point && i < len)
pos += x[i].length
i += 1
end
when 'S'
x = string.scan(/./ms)
i, len = 0, x.length
while (pos < point && i < len)
pos += x[i].length
i += 1
end
when 'U'
x = string.scan(/./mu)
i, len = 0, x.length
while (pos < point && i < len)
pos += x[i].length
i += 1
end
when 'X'
enc = string.encoding
str = string.force_encoding(@encoding_name)
len = str.length
if point <= length / 2
# count byte size from head
i = 0
while (pos < point && i < len)
pos += str[i].bytesize
i += 1
end
else
# count byte size from tail
pos = str.bytesize
i = len - 1
while (pos > point && i >= 0)
pos -= str[i].bytesize
i -= 1
end
pos += str[i + 1].bytesize if pos < point
end
string.force_encoding(enc)
else
pos = point
end
pos - point
end
# Find next `count' characters started byte point of the specified seed.
# If flags is MB_FIND_NONZERO, we look for non-zero-width multibyte
# characters.
def _rl_find_next_mbchar(string, seed, count, flags)
if @encoding == 'N'
return (seed + count)
end
seed = 0 if seed < 0
return seed if count <= 0
point = seed + _rl_adjust_point(string,seed)
if (seed < point)
count -= 1
end
str = (flags == MB_FIND_NONZERO) ? string.sub(/\x00+$/,'') : string
case @encoding
when 'E'
point += str[point..-1].scan(/./me)[0,count].to_s.length
when 'S'
point += str[point..-1].scan(/./ms)[0,count].to_s.length
when 'U'
point += str[point..-1].scan(/./mu)[0,count].to_s.length
when 'X'
point += str[point..-1].force_encoding(@encoding_name)[0,count].bytesize
else
point += count
point = str.length if point >= str.length
end
point
end
# Find previous character started byte point of the specified seed.
# Returned point will be point <= seed. If flags is MB_FIND_NONZERO,
# we look for non-zero-width multibyte characters.
def _rl_find_prev_mbchar(string, seed, flags)
if @encoding == 'N'
return ((seed == 0) ? seed : seed - 1)
end
length = string.length
if seed < 0
return 0
elsif length < seed
return length
end
case @encoding
when 'E'
string[0,seed].scan(/./me)[0..-2].to_s.length
when 'S'
string[0,seed].scan(/./ms)[0..-2].to_s.length
when 'U'
string[0,seed].scan(/./mu)[0..-2].to_s.length
when 'X'
string[0,seed].force_encoding(@encoding_name)[0..-2].bytesize
end
end
# compare the specified two characters. If the characters matched,
# return true. Otherwise return false.
def _rl_compare_chars(buf1, pos1, buf2, pos2)
return false if buf1[pos1].nil? || buf2[pos2].nil?
case @encoding
when 'E'
buf1[pos1..-1].scan(/./me)[0] == buf2[pos2..-1].scan(/./me)[0]
when 'S'
buf1[pos1..-1].scan(/./ms)[0] == buf2[pos2..-1].scan(/./ms)[0]
when 'U'
buf1[pos1..-1].scan(/./mu)[0] == buf2[pos2..-1].scan(/./mu)[0]
when 'X'
buf1[pos1..-1].force_encoding(@encoding_name)[0] == buf2[pos2..-1].force_encoding(@encoding_name)[0]
else
buf1[pos1] == buf2[pos2]
end
end
# return the number of bytes parsed from the multibyte sequence starting
# at src, if a non-L'\0' wide character was recognized. It returns 0,
# if a L'\0' wide character was recognized. It returns (size_t)(-1),
# if an invalid multibyte sequence was encountered. It returns (size_t)(-2)
# if it couldn't parse a complete multibyte character.
def _rl_get_char_len(src)
return 0 if src[0,1] == 0.chr || src.length==0
case @encoding
when 'E'
len = src.scan(/./me)[0].to_s.length
when 'S'
len = src.scan(/./ms)[0].to_s.length
when 'U'
len = src.scan(/./mu)[0].to_s.length
when 'X'
src = src.dup.force_encoding(@encoding_name)
len = src.valid_encoding? ? src[0].bytesize : 0
else
len = 1
end
len==0 ? -2 : len
end
# read multibyte char
def _rl_read_mbchar(mbchar, size)
mb_len = 0
while (mb_len < size)
rl_setstate(RL_STATE_MOREINPUT)
c = rl_read_key()
rl_unsetstate(RL_STATE_MOREINPUT)
break if c.class == Fixnum && c < 0
mbchar << c
mb_len += 1
case @encoding
when 'E'
break unless mbchar.scan(/./me).empty?
when 'S'
break unless mbchar.scan(/./ms).empty?
when 'U'
break unless mbchar.scan(/./mu).empty?
when 'X'
break if mbchar.dup.force_encoding(@encoding_name).valid_encoding?
end
end
mb_len
end
# Read a multibyte-character string whose first character is FIRST into
# the buffer MB of length MLEN. Returns the last character read, which
# may be FIRST. Used by the search functions, among others. Very similar
# to _rl_read_mbchar.
def _rl_read_mbstring(first, mb, mlen)
c = first
(0...mlen).each do
mb << c
if _rl_get_char_len(mb) == -2
# Read more for multibyte character
rl_setstate(RL_STATE_MOREINPUT)
c = rl_read_key()
break if c.class == Fixnum && c < 0
rl_unsetstate(RL_STATE_MOREINPUT)
else
break
end
end
c
end
def _rl_is_mbchar_matched(string, seed, _end, mbchar, length)
return 0 if ((_end - seed) < length)
for i in 0 ... length
if (string[seed + i] != mbchar[i])
return 0
end
end
1
end
# Redraw the last line of a multi-line prompt that may possibly contain
# terminal escape sequences. Called with the cursor at column 0 of the
# line to draw the prompt on.
def redraw_prompt(t)
oldp = @rl_display_prompt
rl_save_prompt()
@rl_display_prompt = t
@local_prompt,@prompt_visible_length,@prompt_last_invisible,@prompt_invis_chars_first_line,@prompt_physical_chars =
expand_prompt(t)
@local_prompt_prefix = nil
@local_prompt_len = @local_prompt ? @local_prompt.length : 0
rl_forced_update_display()
@rl_display_prompt = oldp
rl_restore_prompt()
end
# Redisplay the current line after a SIGWINCH is received.
def _rl_redisplay_after_sigwinch()
# Clear the current line and put the cursor at column 0. Make sure
# the right thing happens if we have wrapped to a new screen line.
if @_rl_term_cr
@rl_outstream.write(@_rl_term_cr)
@_rl_last_c_pos = 0
if @_rl_term_clreol
@rl_outstream.write(@_rl_term_clreol)
else
space_to_eol(@_rl_screenwidth)
@rl_outstream.write(@_rl_term_cr)
end
if @_rl_last_v_pos > 0
_rl_move_vert(0)
end
else
rl_crlf()
end
# Redraw only the last line of a multi-line prompt.
t = @rl_display_prompt.index("\n")
if t
redraw_prompt(@rl_display_prompt[(t+1)..-1])
else
rl_forced_update_display()
end
end
def rl_resize_terminal()
if @readline_echoing_p
_rl_get_screen_size(@rl_instream.fileno, 1)
if @rl_redisplay_function != :rl_redisplay
rl_forced_update_display()
else
_rl_redisplay_after_sigwinch()
end
end
end
def rl_sigwinch_handler(sig)
rl_setstate(RL_STATE_SIGHANDLER)
rl_resize_terminal()
rl_unsetstate(RL_STATE_SIGHANDLER)
end
module_function :rl_attempted_completion_function,:rl_deprep_term_function,
:rl_event_hook,:rl_attempted_completion_over,:rl_basic_quote_characters,
:rl_basic_word_break_characters,:rl_completer_quote_characters,
:rl_completer_word_break_characters,:rl_completion_append_character,
:rl_filename_quote_characters,:rl_instream,:rl_library_version,:rl_outstream,
:rl_readline_name,
:rl_attempted_completion_function=,:rl_deprep_term_function=,
:rl_event_hook=,:rl_attempted_completion_over=,:rl_basic_quote_characters=,
:rl_basic_word_break_characters=,:rl_completer_quote_characters=,
:rl_completer_word_break_characters=,:rl_completion_append_character=,
:rl_filename_quote_characters=,:rl_instream=,:rl_library_version=,:rl_outstream=,
:rl_readline_name=,:history_length,:history_base,:rl_point
def no_terminal?
term = ENV["TERM"]
term.nil? || (term == 'dumb') || (RUBY_PLATFORM =~ /mswin|mingw/)
end
private :no_terminal?
end