# -*- coding: utf-8 -*- # ***************************************************************************** # Copyright (C) 2003-2006 Gary Bishop. # Copyright (C) 2006-2020 Michael Graz. # Copyright (C) 2006-2020 Jorgen Stenarson. # Copyright (C) 2020 Bassem Girgis. # # Distributed under the terms of the BSD License. The full license is in # the file COPYING, distributed as part of this software. # ***************************************************************************** import os import pyreadline3.lineeditor.lineobj as lineobj from pyreadline3.logger import log from . import basemode class ViMode(basemode.BaseMode): mode = "vi" def __init__(self, rlobj): super().__init__(rlobj) self.__vi_insert_mode = None def __repr__(self): return "" def process_keyevent(self, keyinfo): def nop(e): pass keytuple = keyinfo.tuple() # Process exit keys. Only exit on empty line if keytuple in self.exit_dispatch: if lineobj.EndOfLine(self.l_buffer) == 0: raise EOFError dispatch_func = self.key_dispatch.get(keytuple, self.vi_key) log("readline from keyboard:%s->%s" % (keytuple, dispatch_func)) r = None if dispatch_func: r = dispatch_func(keyinfo) self.l_buffer.push_undo() self.previous_func = dispatch_func if r: self._update_line() return True return False # Methods below here are bindable emacs functions def init_editing_mode(self, e): # (M-C-j) """Initialize vi editingmode""" self.show_all_if_ambiguous = "on" self.key_dispatch = {} self.__vi_insert_mode = None self._vi_command = None self._vi_command_edit = None self._vi_key_find_char = None self._vi_key_find_direction = True self._vi_yank_buffer = None self._vi_multiplier1 = "" self._vi_multiplier2 = "" self._vi_undo_stack = [] self._vi_undo_cursor = -1 self._vi_current = None self._vi_search_text = "" self._vi_search_position = 0 self.vi_save_line() self.vi_set_insert_mode(True) # make ' ' to ~ self insert for c in range(ord(" "), 127): self._bind_key("%s" % chr(c), self.vi_key) self._bind_key("BackSpace", self.vi_backspace) self._bind_key("Escape", self.vi_escape) self._bind_key("Return", self.vi_accept_line) self._bind_key("Left", self.backward_char) self._bind_key("Right", self.forward_char) self._bind_key("Home", self.beginning_of_line) self._bind_key("End", self.end_of_line) self._bind_key("Delete", self.delete_char) self._bind_key("Control-d", self.vi_eof) self._bind_key("Control-z", self.vi_eof) self._bind_key("Control-r", self.vi_redo) self._bind_key("Up", self.vi_arrow_up) self._bind_key("Control-p", self.vi_up) self._bind_key("Down", self.vi_arrow_down) self._bind_key("Control-n", self.vi_down) self._bind_key("Tab", self.vi_complete) # self._bind_key('Control-e', self.emacs) def vi_key(self, e): if not self._vi_command: self._vi_command = ViCommand(self) elif self._vi_command.is_end: if self._vi_command.is_edit: self._vi_command_edit = self._vi_command self._vi_command = ViCommand(self) self._vi_command.add_char(e.char) def vi_error(self): self._bell() def vi_get_is_insert_mode(self): return self.__vi_insert_mode vi_is_insert_mode = property(vi_get_is_insert_mode) def vi_escape(self, e): if self.vi_is_insert_mode: if self._vi_command: self._vi_command.add_char(e.char) else: self._vi_command = ViCommand(self) self.vi_set_insert_mode(False) self.l_buffer.point = lineobj.PrevChar elif self._vi_command and self._vi_command.is_replace_one: self._vi_command.add_char(e.char) else: self.vi_error() def vi_backspace(self, e): if self._vi_command: self._vi_command.add_char(e.char) else: self._vi_do_backspace(self._vi_command) def _vi_do_backspace(self, vi_cmd): if self.vi_is_insert_mode or (self._vi_command and self._vi_command.is_search): if self.l_buffer.point > 0: self.l_buffer.point -= 1 if self.l_buffer.overwrite: try: prev = self._vi_undo_stack[self._vi_undo_cursor][1][ self.l_buffer.point ] self.l_buffer.line_buffer[self.l_buffer.point] = prev except IndexError: del self.l_buffer.line_buffer[self.l_buffer.point] else: self.vi_save_line() del self.l_buffer.line_buffer[self.l_buffer.point] def vi_accept_line(self, e): if self._vi_command and self._vi_command.is_search: self._vi_command.do_search() return False self._vi_command = None self.vi_set_insert_mode(True) self._vi_undo_stack = [] self._vi_undo_cursor = -1 self._vi_current = None if self.l_buffer.line_buffer: self.add_history(self.l_buffer.copy()) return self.accept_line(e) def vi_eof(self, e): raise EOFError def vi_set_insert_mode(self, value): if self.__vi_insert_mode == value: return self.__vi_insert_mode = value if value: self.vi_save_line() self.cursor_size = 25 else: self.cursor_size = 100 def vi_undo_restart(self): tpl_undo = ( self.l_buffer.point, self.l_buffer.line_buffer[:], ) self._vi_undo_stack = [tpl_undo] self._vi_undo_cursor = 0 def vi_save_line(self): if self._vi_undo_stack and self._vi_undo_cursor >= 0: del self._vi_undo_stack[self._vi_undo_cursor + 1 :] # tpl_undo = (self.l_buffer.point, self.l_buffer[:], ) tpl_undo = ( self.l_buffer.point, self.l_buffer.line_buffer[:], ) if ( not self._vi_undo_stack or self._vi_undo_stack[self._vi_undo_cursor][1] != tpl_undo[1] ): self._vi_undo_stack.append(tpl_undo) self._vi_undo_cursor += 1 def vi_undo_prepare(self): if self._vi_undo_cursor == len(self._vi_undo_stack) - 1: self.vi_save_line() def vi_undo(self, do_pop=True): self.vi_undo_prepare() if not self._vi_undo_stack or self._vi_undo_cursor <= 0: self.vi_error() return self._vi_undo_cursor -= 1 self.vi_undo_assign() def vi_undo_all(self): self.vi_undo_prepare() if self._vi_undo_cursor > 0: self._vi_undo_cursor = 0 self.vi_undo_assign() else: self.vi_error() def vi_undo_assign(self): tpl_undo = self._vi_undo_stack[self._vi_undo_cursor] self.l_buffer.line_buffer = tpl_undo[1][:] self.l_buffer.point = tpl_undo[0] def vi_redo(self, e): if self._vi_undo_cursor >= len(self._vi_undo_stack) - 1: self.vi_error() return self._vi_undo_cursor += 1 self.vi_undo_assign() def vi_search(self, rng): for i in rng: line_history = self._history.history[i] pos = line_history.get_line_text().find(self._vi_search_text) if pos >= 0: self._vi_search_position = i self._history.history_cursor = i self.l_buffer.line_buffer = list(line_history.line_buffer) self.l_buffer.point = pos self.vi_undo_restart() return True self._bell() return False def vi_search_first(self): text = "".join(self.l_buffer.line_buffer[1:]) if text: self._vi_search_text = text self._vi_search_position = len(self._history.history) - 1 elif self._vi_search_text: self._vi_search_position -= 1 else: self.vi_error() self.vi_undo() return if not self.vi_search(list(range(self._vi_search_position, -1, -1))): # Here: search text not found self.vi_undo() def vi_search_again_backward(self): self.vi_search(list(range(self._vi_search_position - 1, -1, -1))) def vi_search_again_forward(self): self.vi_search( list(range(self._vi_search_position + 1, len(self._history.history))) ) def vi_up(self, e): if self._history.history_cursor == len(self._history.history): self._vi_current = self.l_buffer.line_buffer[:] # self._history.previous_history (e) self._history.previous_history(self.l_buffer) if self.vi_is_insert_mode: self.end_of_line(e) else: self.beginning_of_line(e) self.vi_undo_restart() def vi_down(self, e): if self._history.history_cursor >= len(self._history.history): self.vi_error() return if self._history.history_cursor < len(self._history.history) - 1: # self._history.next_history (e) self._history.next_history(self.l_buffer) if self.vi_is_insert_mode: self.end_of_line(e) else: self.beginning_of_line(e) self.vi_undo_restart() elif self._vi_current is not None: self._history.history_cursor = len(self._history.history) self.l_buffer.line_buffer = self._vi_current self.end_of_line(e) if not self.vi_is_insert_mode and self.l_buffer.point > 0: self.l_buffer.point -= 1 self._vi_current = None else: self.vi_error() return def vi_arrow_up(self, e): self.vi_set_insert_mode(True) self.vi_up(e) self.vi_save_line() def vi_arrow_down(self, e): self.vi_set_insert_mode(True) self.vi_down(e) self.vi_save_line() def vi_complete(self, e): text = self.l_buffer.get_line_text() if text and not text.isspace(): return self.complete(e) else: return self.vi_key(e) # vi input states # sequence of possible states are in the order below _VI_BEGIN = "vi_begin" _VI_MULTI1 = "vi_multi1" _VI_ACTION = "vi_action" _VI_MULTI2 = "vi_multi2" _VI_MOTION = "vi_motion" _VI_MOTION_ARGUMENT = "vi_motion_argument" _VI_REPLACE_ONE = "vi_replace_one" _VI_TEXT = "vi_text" _VI_SEARCH = "vi_search" _VI_END = "vi_end" # vi helper class class ViCommand: def __init__(self, readline): self.readline = readline self.lst_char = [] self.state = _VI_BEGIN self.action = self.movement self.motion = None self.motion_argument = None self.text = None self.pos_motion = None self.is_edit = False self.is_overwrite = False self.is_error = False self.is_star = False self.delete_left = 0 self.delete_right = 0 self.readline._vi_multiplier1 = "" self.readline._vi_multiplier2 = "" self.set_override_multiplier(0) self.skip_multipler = False self.tabstop = 4 self.dct_fcn = { ord("$"): self.key_dollar, ord("^"): self.key_hat, ord(";"): self.key_semicolon, ord(","): self.key_comma, ord("%"): self.key_percent, ord("."): self.key_dot, ord("/"): self.key_slash, ord("*"): self.key_star, ord("|"): self.key_bar, ord("~"): self.key_tilde, 8: self.key_backspace, } def add_char(self, char): self.lst_char.append(char) if self.state == _VI_BEGIN and self.readline.vi_is_insert_mode: self.readline.vi_save_line() self.state = _VI_TEXT if self.state == _VI_SEARCH: if char == "\x08": # backspace self.key_backspace(char) else: self.set_text(char) return if self.state == _VI_TEXT: if char == "\x1b": # escape self.escape(char) elif char == "\x09": # tab ts = self.tabstop ws = " " * (ts - (self.readline.l_buffer.point % ts)) self.set_text(ws) elif char == "\x08": # backspace self.key_backspace(char) else: self.set_text(char) return if self.state == _VI_MOTION_ARGUMENT: self.set_motion_argument(char) return if self.state == _VI_REPLACE_ONE: self.replace_one(char) return try: fcn_instance = self.dct_fcn[ord(char)] except BaseException: fcn_instance = getattr(self, "key_%s" % char, None) if fcn_instance: fcn_instance(char) return if char.isdigit(): self.key_digit(char) return # Here: could not process key self.error() def set_text(self, text): if self.text is None: self.text = text else: self.text += text self.set_buffer(text) def set_buffer(self, text): for char in text: if not self.char_isprint(char): continue # self.readline.l_buffer.insert_text(char) # continue # #overwrite in l_buffer obj if self.is_overwrite: if self.readline.l_buffer.point < len( self.readline.l_buffer.line_buffer ): # self.readline.l_buffer[self.l_buffer.point]=char self.readline.l_buffer.line_buffer[self.readline.l_buffer.point] = ( char ) else: # self.readline.l_buffer.insert_text(char) self.readline.l_buffer.line_buffer.append(char) else: # self.readline.l_buffer.insert_text(char) self.readline.l_buffer.line_buffer.insert( self.readline.l_buffer.point, char ) self.readline.l_buffer.point += 1 def replace_one(self, char): if char == "\x1b": # escape self.end() return self.is_edit = True self.readline.vi_save_line() times = self.get_multiplier() cursor = self.readline.l_buffer.point self.readline.l_buffer.line_buffer[cursor : cursor + times] = char * times if times > 1: self.readline.l_buffer.point += times - 1 self.end() def char_isprint(self, char): return ord(char) >= ord(" ") and ord(char) <= ord("~") def key_dollar(self, char): self.motion = self.motion_end_in_line self.delete_right = 1 self.state = _VI_MOTION self.apply() def key_hat(self, char): self.motion = self.motion_beginning_of_line self.state = _VI_MOTION self.apply() def key_0(self, char): if self.state in [_VI_BEGIN, _VI_ACTION]: self.key_hat(char) else: self.key_digit(char) def key_digit(self, char): if self.state in [_VI_BEGIN, _VI_MULTI1]: self.readline._vi_multiplier1 += char self.readline._vi_multiplier2 = "" self.state = _VI_MULTI1 elif self.state in [_VI_ACTION, _VI_MULTI2]: self.readline._vi_multiplier2 += char self.state = _VI_MULTI2 def key_w(self, char): if self.action == self.change: self.key_e(char) return self.motion = self.motion_word_short self.state = _VI_MOTION self.apply() def key_W(self, char): if self.action == self.change: self.key_E(char) return self.motion = self.motion_word_long self.state = _VI_MOTION self.apply() def key_e(self, char): self.motion = self.motion_end_short self.state = _VI_MOTION self.delete_right = 1 self.apply() def key_E(self, char): self.motion = self.motion_end_long self.state = _VI_MOTION self.delete_right = 1 self.apply() def key_b(self, char): self.motion = self.motion_back_short self.state = _VI_MOTION self.apply() def key_B(self, char): self.motion = self.motion_back_long self.state = _VI_MOTION self.apply() def key_f(self, char): self.readline._vi_key_find_direction = True self.motion = self.motion_find_char_forward self.delete_right = 1 self.state = _VI_MOTION_ARGUMENT def key_F(self, char): self.readline._vi_key_find_direction = False self.motion = self.motion_find_char_backward self.delete_left = 1 self.state = _VI_MOTION_ARGUMENT def key_t(self, char): self.motion = self.motion_to_char_forward self.delete_right = 1 self.state = _VI_MOTION_ARGUMENT def key_T(self, char): self.motion = self.motion_to_char_backward self.state = _VI_MOTION_ARGUMENT def key_j(self, char): self.readline.vi_down(ViEvent(char)) self.state = _VI_END def key_k(self, char): self.readline.vi_up(ViEvent(char)) self.state = _VI_END def key_semicolon(self, char): if self.readline._vi_key_find_char is None: self.error() return if self.readline._vi_key_find_direction: self.motion = self.motion_find_char_forward else: self.motion = self.motion_find_char_backward self.set_motion_argument(self.readline._vi_key_find_char) def key_comma(self, char): if self.readline._vi_key_find_char is None: self.error() return if self.readline._vi_key_find_direction: self.motion = self.motion_find_char_backward else: self.motion = self.motion_find_char_forward self.set_motion_argument(self.readline._vi_key_find_char) def key_percent(self, char): """find matching <([{}])>""" self.motion = self.motion_matching self.delete_right = 1 self.state = _VI_MOTION self.apply() def key_dot(self, char): vi_cmd_edit = self.readline._vi_command_edit if not vi_cmd_edit: return if vi_cmd_edit.is_star: self.key_star(char) return if self.has_multiplier(): count = self.get_multiplier() else: count = 0 # Create the ViCommand object after getting multiplier from self # Side effect of the ViCommand creation is resetting of global # multipliers vi_cmd = ViCommand(self.readline) if count >= 1: vi_cmd.set_override_multiplier(count) vi_cmd_edit.set_override_multiplier(count) elif vi_cmd_edit.override_multiplier: vi_cmd.set_override_multiplier(vi_cmd_edit.override_multiplier) for char in vi_cmd_edit.lst_char: vi_cmd.add_char(char) if vi_cmd_edit.is_overwrite and self.readline.l_buffer.point > 0: self.readline.l_buffer.point -= 1 self.readline.vi_set_insert_mode(False) self.end() def key_slash(self, char): self.readline.vi_save_line() self.readline.l_buffer.line_buffer = ["/"] self.readline.l_buffer.point = 1 self.state = _VI_SEARCH def key_star(self, char): self.is_star = True self.is_edit = True self.readline.vi_save_line() completions = self.readline._get_completions() if completions: text = " ".join(completions) + " " self.readline.l_buffer.line_buffer[ self.readline.begidx : self.readline.endidx + 1 ] = list(text) prefix_len = self.readline.endidx - self.readline.begidx self.readline.l_buffer.point += len(text) - prefix_len self.readline.vi_set_insert_mode(True) else: self.error() self.state = _VI_TEXT def key_bar(self, char): self.motion = self.motion_column self.state = _VI_MOTION self.apply() def key_tilde(self, char): self.is_edit = True self.readline.vi_save_line() for i in range(self.get_multiplier()): try: c = self.readline.l_buffer.line_buffer[self.readline.l_buffer.point] if c.isupper(): self.readline.l_buffer.line_buffer[self.readline.l_buffer.point] = ( c.lower() ) elif c.islower(): self.readline.l_buffer.line_buffer[self.readline.l_buffer.point] = ( c.upper() ) self.readline.l_buffer.point += 1 except IndexError: break self.end() def key_h(self, char): self.motion = self.motion_left self.state = _VI_MOTION self.apply() def key_backspace(self, char): if self.state in [_VI_TEXT, _VI_SEARCH]: if self.text and len(self.text): self.text = self.text[:-1] try: # Remove backspaces for potential dot command self.lst_char.pop() self.lst_char.pop() except IndexError: pass else: self.key_h(char) self.readline._vi_do_backspace(self) if self.state == _VI_SEARCH and not (self.readline.l_buffer.line_buffer): self.state = _VI_BEGIN def key_l(self, char): self.motion = self.motion_right self.state = _VI_MOTION self.apply() def key_i(self, char): self.is_edit = True self.state = _VI_TEXT self.readline.vi_set_insert_mode(True) def key_I(self, char): self.is_edit = True self.state = _VI_TEXT self.readline.vi_set_insert_mode(True) self.readline.l_buffer.point = 0 def key_a(self, char): self.is_edit = True self.state = _VI_TEXT self.readline.vi_set_insert_mode(True) if len(self.readline.l_buffer.line_buffer): self.readline.l_buffer.point += 1 def key_A(self, char): self.is_edit = True self.state = _VI_TEXT self.readline.vi_set_insert_mode(True) self.readline.l_buffer.point = len(self.readline.l_buffer.line_buffer) def key_d(self, char): self.is_edit = True self.state = _VI_ACTION self.action = self.delete def key_D(self, char): self.is_edit = True self.state = _VI_ACTION self.action = self.delete_end_of_line self.apply() def key_x(self, char): self.is_edit = True self.state = _VI_ACTION self.action = self.delete_char self.apply() def key_X(self, char): self.is_edit = True self.state = _VI_ACTION self.action = self.delete_prev_char self.apply() def key_s(self, char): self.is_edit = True i1 = self.readline.l_buffer.point i2 = self.readline.l_buffer.point + self.get_multiplier() self.skip_multipler = True self.readline.vi_set_insert_mode(True) del self.readline.l_buffer.line_buffer[i1:i2] self.state = _VI_TEXT def key_S(self, char): self.is_edit = True self.readline.vi_set_insert_mode(True) self.readline.l_buffer.line_buffer = [] self.readline.l_buffer.point = 0 self.state = _VI_TEXT def key_c(self, char): self.is_edit = True self.state = _VI_ACTION self.action = self.change def key_C(self, char): self.is_edit = True self.readline.vi_set_insert_mode(True) del self.readline.l_buffer.line_buffer[self.readline.l_buffer.point :] self.state = _VI_TEXT def key_r(self, char): self.state = _VI_REPLACE_ONE def key_R(self, char): self.is_edit = True self.is_overwrite = True self.readline.l_buffer.overwrite = True self.readline.vi_set_insert_mode(True) self.state = _VI_TEXT def key_y(self, char): self._state = _VI_ACTION self.action = self.yank def key_Y(self, char): self.readline._vi_yank_buffer = self.readline.l_buffer.get_line_text() self.end() def key_p(self, char): if not self.readline._vi_yank_buffer: return self.is_edit = True self.readline.vi_save_line() self.readline.l_buffer.point += 1 self.readline.l_buffer.insert_text( self.readline._vi_yank_buffer * self.get_multiplier() ) self.readline.l_buffer.point -= 1 self.state = _VI_END def key_P(self, char): if not self.readline._vi_yank_buffer: return self.is_edit = True self.readline.vi_save_line() self.readline.l_buffer.insert_text( self.readline._vi_yank_buffer * self.get_multiplier() ) self.readline.l_buffer.point -= 1 self.state = _VI_END def key_u(self, char): self.readline.vi_undo() self.state = _VI_END def key_U(self, char): self.readline.vi_undo_all() self.state = _VI_END def key_v(self, char): editor = ViExternalEditor(self.readline.l_buffer.line_buffer) self.readline.l_buffer.line_buffer = list(editor.result) self.readline.l_buffer.point = 0 self.is_edit = True self.state = _VI_END def error(self): self.readline._bell() self.is_error = True def state_is_end(self): return self.state == _VI_END is_end = property(state_is_end) def state_is_search(self): return self.state == _VI_SEARCH is_search = property(state_is_search) def state_is_replace_one(self): return self.state == _VI_REPLACE_ONE is_replace_one = property(state_is_replace_one) def do_search(self): self.readline.vi_search_first() self.state = _VI_END def key_n(self, char): self.readline.vi_search_again_backward() self.state = _VI_END def key_N(self, char): self.readline.vi_search_again_forward() self.state = _VI_END def motion_beginning_of_line(self, line, index=0, count=1, **kw): return 0 def motion_end_in_line(self, line, index=0, count=1, **kw): return max(0, len(self.readline.l_buffer.line_buffer) - 1) def motion_word_short(self, line, index=0, count=1, **kw): return vi_pos_word_short(line, index, count) def motion_word_long(self, line, index=0, count=1, **kw): return vi_pos_word_long(line, index, count) def motion_end_short(self, line, index=0, count=1, **kw): return vi_pos_end_short(line, index, count) def motion_end_long(self, line, index=0, count=1, **kw): return vi_pos_end_long(line, index, count) def motion_back_short(self, line, index=0, count=1, **kw): return vi_pos_back_short(line, index, count) def motion_back_long(self, line, index=0, count=1, **kw): return vi_pos_back_long(line, index, count) def motion_find_char_forward(self, line, index=0, count=1, char=None): self.readline._vi_key_find_char = char return vi_pos_find_char_forward(line, char, index, count) def motion_find_char_backward(self, line, index=0, count=1, char=None): self.readline._vi_key_find_char = char return vi_pos_find_char_backward(line, char, index, count) def motion_to_char_forward(self, line, index=0, count=1, char=None): return vi_pos_to_char_forward(line, char, index, count) def motion_to_char_backward(self, line, index=0, count=1, char=None): return vi_pos_to_char_backward(line, char, index, count) def motion_left(self, line, index=0, count=1, char=None): return max(0, index - count) def motion_right(self, line, index=0, count=1, char=None): return min(len(line), index + count) def motion_matching(self, line, index=0, count=1, char=None): return vi_pos_matching(line, index) def motion_column(self, line, index=0, count=1, char=None): return max(0, count - 1) def has_multiplier(self): return ( self.override_multiplier or self.readline._vi_multiplier1 or self.readline._vi_multiplier2 ) def get_multiplier(self): if self.override_multiplier: return int(self.override_multiplier) if self.readline._vi_multiplier1 == "": m1 = 1 else: m1 = int(self.readline._vi_multiplier1) if self.readline._vi_multiplier2 == "": m2 = 1 else: m2 = int(self.readline._vi_multiplier2) return m1 * m2 def set_override_multiplier(self, count): self.override_multiplier = count def apply(self): if self.motion: self.pos_motion = self.motion( self.readline.l_buffer.line_buffer, self.readline.l_buffer.point, self.get_multiplier(), char=self.motion_argument, ) if self.pos_motion < 0: self.error() return self.action() if self.state != _VI_TEXT: self.end() def movement(self): if self.pos_motion <= len(self.readline.l_buffer.line_buffer): self.readline.l_buffer.point = self.pos_motion else: self.readline.l_buffer.point = len(self.readline.l_buffer.line_buffer) - 1 def yank(self): if self.pos_motion > self.readline.l_buffer.point: s = self.readline.l_buffer.line_buffer[ self.readline.l_buffer.point : self.pos_motion + self.delete_right ] else: index = max(0, self.pos_motion - self.delete_left) s = self.readline.l_buffer.line_buffer[ index : self.readline.l_buffer.point + self.delete_right ] self.readline._vi_yank_buffer = s def delete(self): self.readline.vi_save_line() self.yank() # point=lineobj.Point(self.readline.l_buffer) # pm=self.pos_motion # del self.readline.l_buffer[point:pm] # return if self.pos_motion > self.readline.l_buffer.point: del self.readline.l_buffer.line_buffer[ self.readline.l_buffer.point : self.pos_motion + self.delete_right ] if self.readline.l_buffer.point > len(self.readline.l_buffer.line_buffer): self.readline.l_buffer.point = len(self.readline.l_buffer.line_buffer) else: index = max(0, self.pos_motion - self.delete_left) del self.readline.l_buffer.line_buffer[ index : self.readline.l_buffer.point + self.delete_right ] self.readline.l_buffer.point = index def delete_end_of_line(self): self.readline.vi_save_line() # del self.readline.l_buffer [self.readline.l_buffer.point : ] line_text = self.readline.l_buffer.get_line_text() line_text = line_text[: self.readline.l_buffer.point] self.readline.l_buffer.set_line(line_text) if self.readline.l_buffer.point > 0: self.readline.l_buffer.point -= 1 def delete_char(self): # point=lineobj.Point(self.readline.l_buffer) # del self.readline.l_buffer[point:point+self.get_multiplier ()] # return self.pos_motion = self.readline.l_buffer.point + self.get_multiplier() self.delete() end = max(0, len(self.readline.l_buffer) - 1) if self.readline.l_buffer.point > end: self.readline.l_buffer.point = end def delete_prev_char(self): self.pos_motion = self.readline.l_buffer.point - self.get_multiplier() self.delete() def change(self): self.readline.vi_set_insert_mode(True) self.delete() self.skip_multipler = True self.state = _VI_TEXT def escape(self, char): if self.state == _VI_TEXT: if not self.skip_multipler: times = self.get_multiplier() if times > 1 and self.text: extra = self.text * (times - 1) self.set_buffer(extra) self.state = _VI_END def set_motion_argument(self, char): self.motion_argument = char self.apply() def end(self): self.state = _VI_END if self.readline.l_buffer.point >= len(self.readline.l_buffer.line_buffer): self.readline.l_buffer.point = max( 0, len(self.readline.l_buffer.line_buffer) - 1 ) class ViExternalEditor: def __init__(self, line): if isinstance(line, type([])): line = "".join(line) file_tmp = self.get_tempfile() fp_tmp = self.file_open(file_tmp, "w") fp_tmp.write(line) fp_tmp.close() self.run_editor(file_tmp) fp_tmp = self.file_open(file_tmp, "r") self.result = fp_tmp.read() fp_tmp.close() self.file_remove(file_tmp) def get_tempfile(self): import tempfile return tempfile.mktemp(prefix="readline-", suffix=".py") def file_open(self, filename, mode): return open(filename, mode) def file_remove(self, filename): os.remove(filename) def get_editor(self): try: return os.environ["EDITOR"] except KeyError: return "notepad" # ouch def run_editor(self, filename): cmd = "%s %s" % ( self.get_editor(), filename, ) self.run_command(cmd) def run_command(self, command): os.system(command) class ViEvent: def __init__(self, char): self.char = char # vi standalone functions def vi_is_word(char): log( "xx vi_is_word: type(%s), %s" % ( type(char), char, ) ) return char.isalpha() or char.isdigit() or char == "_" def vi_is_space(char): return char.isspace() def vi_is_word_or_space(char): return vi_is_word(char) or vi_is_space(char) def vi_pos_word_short(line, index=0, count=1): try: for i in range(count): in_word = vi_is_word(line[index]) if not in_word: while not vi_is_word(line[index]): index += 1 else: while vi_is_word(line[index]): index += 1 while vi_is_space(line[index]): index += 1 return index except IndexError: return len(line) def vi_pos_word_long(line, index=0, count=1): try: for i in range(count): in_space = vi_is_space(line[index]) if not in_space: while not vi_is_space(line[index]): index += 1 while vi_is_space(line[index]): index += 1 return index except IndexError: return len(line) def vi_pos_end_short(line, index=0, count=1): try: for i in range(count): index += 1 while vi_is_space(line[index]): index += 1 in_word = vi_is_word(line[index]) if not in_word: while not vi_is_word_or_space(line[index]): index += 1 else: while vi_is_word(line[index]): index += 1 return index - 1 except IndexError: return max(0, len(line) - 1) def vi_pos_end_long(line, index=0, count=1): try: for i in range(count): index += 1 while vi_is_space(line[index]): index += 1 while not vi_is_space(line[index]): index += 1 return index - 1 except IndexError: return max(0, len(line) - 1) class vi_list(list): """This is a list that cannot have a negative index""" def __getitem__(self, key): try: if int(key) < 0: raise IndexError except ValueError: pass return list.__getitem__(self, key) def vi_pos_back_short(line, index=0, count=1): line = vi_list(line) try: for i in range(count): index -= 1 while vi_is_space(line[index]): index -= 1 in_word = vi_is_word(line[index]) if in_word: while vi_is_word(line[index]): index -= 1 else: while not vi_is_word_or_space(line[index]): index -= 1 return index + 1 except IndexError: return 0 def vi_pos_back_long(line, index=0, count=1): line = vi_list(line) try: for i in range(count): index -= 1 while vi_is_space(line[index]): index -= 1 while not vi_is_space(line[index]): index -= 1 return index + 1 except IndexError: return 0 def vi_pos_find_char_forward(line, char, index=0, count=1): try: for i in range(count): index += 1 while line[index] != char: index += 1 return index except IndexError: return -1 def vi_pos_find_char_backward(line, char, index=0, count=1): try: for i in range(count): index -= 1 while True: if index < 0: return -1 if line[index] == char: break index -= 1 return index except IndexError: return -1 def vi_pos_to_char_forward(line, char, index=0, count=1): index = vi_pos_find_char_forward(line, char, index, count) if index > 0: return index - 1 return index def vi_pos_to_char_backward(line, char, index=0, count=1): index = vi_pos_find_char_backward(line, char, index, count) if index >= 0: return index + 1 return index _vi_dct_matching = { "<": (">", +1), ">": ("<", -1), "(": (")", +1), ")": ("(", -1), "[": ("]", +1), "]": ("[", -1), "{": ("}", +1), "}": ("{", -1), } def vi_pos_matching(line, index=0): """find matching <([{}])>""" anchor = None target = None delta = 1 count = 0 try: while True: if anchor is None: # first find anchor try: target, delta = _vi_dct_matching[line[index]] anchor = line[index] count = 1 except KeyError: index += 1 continue else: # Here the anchor has been found # Need to get corresponding target if index < 0: return -1 if line[index] == anchor: count += 1 elif line[index] == target: count -= 1 if count == 0: return index index += delta except IndexError: return -1