pyreadline-refactor: Added vi-mode patch, a lot of work remains.

This commit is contained in:
jstenar
2006-03-21 18:26:41 +00:00
parent c649fb8235
commit 2130c2787c
10 changed files with 1521 additions and 387 deletions
+3
View File
@@ -1,3 +1,6 @@
2006-03-16 Jörgen Stenarson <jorgen.stenarson -at- bostream.nu>
* Added vi patch
2006-03-16 Jörgen Stenarson <jorgen.stenarson -at- bostream.nu>
* Refactored emacs mode
+5 -2
View File
@@ -522,11 +522,14 @@ class Console(object):
else:
return (info.dwSize.X, info.dwSize.Y)
def cursor(self, visible):
def cursor(self, visible=None, size=None):
'''Set cursor on or off.'''
info = CONSOLE_CURSOR_INFO()
if self.GetConsoleCursorInfo(self.hout, byref(info)):
info.bVisible = visible
if visible is not None:
info.bVisible = visible
if size is not None:
info.dwSize = size
self.SetConsoleCursorInfo(self.hout, byref(info))
def bell(self):
+16 -6
View File
@@ -151,6 +151,7 @@ class TextLine(object):
self._point=0
self.mark=-1
self.undo_stack=[]
self.overwrite=False
if isinstance(txtstr,TextLine): #copy
if point is None:
self.point=txtstr.point
@@ -243,9 +244,15 @@ class TextLine(object):
self.point = 0
def _insert_text(self, text):
for c in text:
self.line_buffer.insert(self.point, c)
self.point += 1
if self.overwrite:
for c in text:
#if self.point:
self.line_buffer[self.point]= c
self.point += 1
else:
for c in text:
self.line_buffer.insert(self.point, c)
self.point += 1
def __getitem__(self,key):
#Check if key is LineSlice, convert to regular slice
@@ -318,11 +325,14 @@ class TextLine(object):
value=TextLine(value).line_buffer
self.line_buffer=prev+value+rest
def __len__(self):
return len(self.line_buffer)
def upper(self):
self.line_buffer=self.line_buffer.upper()
self.line_buffer=[x.upper() for x in self.line_buffer]
def lower(self):
self.line_buffer=self.line_buffer.lower()
self.line_buffer=[x.lower() for x in self.line_buffer]
def startswith(self,txt):
return self.get_line_text().startswith(txt)
@@ -523,4 +533,4 @@ if __name__=="__main__":
[]
print '%-15s "%s"'%(name,show_pos(q,pos,"^"))
l=TextLine("kjjk")
+3 -3
View File
@@ -1,5 +1,5 @@
__all__=["emacs","notemacs"]
import emacs,notemacs
editingmodes=[emacs.EmacsMode,notemacs.NotEmacsMode]
__all__=["emacs","notemacs","vi"]
import emacs,notemacs,vi
editingmodes=[emacs.EmacsMode,notemacs.NotEmacsMode,vi.ViMode]
#add check to ensure all modes have unique mode names
+264
View File
@@ -0,0 +1,264 @@
# -*- coding: utf-8 -*-
#*****************************************************************************
# Copyright (C) 2003-2006 Gary Bishop.
# Copyright (C) 2006 Jorgen Stenarson. <jorgen.stenarson@bostream.nu>
#
# 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 pyreadline.logger as logger
from pyreadline.logger import log
from pyreadline.keysyms import key_text_to_keyinfo
import pyreadline.lineeditor.lineobj as lineobj
import pyreadline.lineeditor.history as history
class BaseMode(object):
mode="base"
def __init__(self,rlobj):
self.rlobj=rlobj
self.exit_dispatch = {}
self.key_dispatch = {}
self.startup_hook=None
self.pre_input_hook=None
def __repr__(self):
return "<BaseMode>"
def _gs(x):
def g(self):
return getattr(self.rlobj,x)
def s(self,q):
setattr(self.rlobj,x,q)
return g,s
def _g(x):
def g(self):
return getattr(self.rlobj,x)
return g
l_buffer=property(*_gs("l_buffer"))
next_meta=property(*_gs("next_meta"))
first_prompt=property(*_gs("first_prompt"))
prompt=property(*_gs("prompt"))
console=property(_g("console"))
insert_text=property(_g("insert_text"))
_print_prompt=property(_g("_print_prompt"))
_update_line=property(_g("_update_line"))
paste_line_buffer=property(_g("paste_line_buffer"))
add_history=property(_g("add_history"))
_bell=property(_g("_bell"))
_clear_after=property(_g("_clear_after"))
_set_cursor=property(_g("_set_cursor"))
_print_prompt=property(_g("_print_prompt"))
_update_prompt_pos=property(_g("_update_prompt_pos"))
_update_line=property(_g("_update_line"))
enable_win32_clipboard=property(_g("enable_win32_clipboard"))
_bell=property(_g("_bell"))
_history=property(_g("_history"))
def _readline_from_keyboard(self):
raise NotImplementedError
def readline(self, prompt=''):
raise NotImplementedError
#Create key bindings:
def _bind_key(self, key, func):
'''setup the mapping from key to call the function.'''
keyinfo = key_text_to_keyinfo(key)
# print key,keyinfo,func.__name__
self.key_dispatch[keyinfo] = func
def _bind_exit_key(self, key):
'''setup the mapping from key to call the function.'''
keyinfo = key_text_to_keyinfo(key)
self.exit_dispatch[keyinfo] = None
def init_editing_mode(self, e): # (C-e)
'''When in vi command mode, this causes a switch to emacs editing
mode.'''
raise NotImplementedError
#completion commands
def _get_completions(self):
'''Return a list of possible completions for the string ending at the point.
Also set begidx and endidx in the process.'''
completions = []
self.begidx = self.l_buffer.point
self.endidx = self.l_buffer.point
buf=self.l_buffer.line_buffer
if self.completer:
# get the string to complete
while self.begidx > 0:
self.begidx -= 1
if buf[self.begidx] in self.completer_delims:
self.begidx += 1
break
text = ''.join(buf[self.begidx:self.endidx])
log('complete text="%s"' % text)
i = 0
while 1:
try:
r = self.completer(text, i)
except:
break
i += 1
if r and r not in completions:
completions.append(r)
else:
break
log('text completions=%s' % completions)
if not completions:
# get the filename to complete
while self.begidx > 0:
self.begidx -= 1
if buf[self.begidx] in ' \t\n':
self.begidx += 1
break
text = ''.join(buf[self.begidx:self.endidx])
log('file complete text="%s"' % text)
completions = glob(os.path.expanduser(text) + '*')
if self.mark_directories == 'on':
mc = []
for f in completions:
if os.path.isdir(f):
mc.append(f + os.sep)
else:
mc.append(f)
completions = mc
log('fnames=%s' % completions)
return completions
def _display_completions(self, completions):
if not completions:
return
self.console.write('\n')
wmax = max(map(len, completions))
w, h = self.console.size()
cols = max(1, int((w-1) / (wmax+1)))
rows = int(math.ceil(float(len(completions)) / cols))
for row in range(rows):
s = ''
for col in range(cols):
i = col*rows + row
if i < len(completions):
self.console.write(completions[i].ljust(wmax+1))
self.console.write('\n')
self._print_prompt()
def complete(self, e): # (TAB)
'''Attempt to perform completion on the text before point. The
actual completion performed is application-specific. The default is
filename completion.'''
completions = self._get_completions()
if completions:
cprefix = commonprefix(completions)
rep = [ c for c in cprefix ]
self.l_buffer[self.begidx:self.endidx] = rep
self.l_buffer.point += len(rep) - (self.endidx - self.begidx)
if len(completions) > 1:
if self.show_all_if_ambiguous == 'on':
self._display_completions(completions)
else:
self._bell()
else:
self._bell()
def possible_completions(self, e): # (M-?)
'''List the possible completions of the text before point. '''
completions = self._get_completions()
self._display_completions(completions)
def insert_completions(self, e): # (M-*)
'''Insert all completions of the text before point that would have
been generated by possible-completions.'''
completions = self._get_completions()
b = self.begidx
e = self.endidx
for comp in completions:
rep = [ c for c in comp ]
rep.append(' ')
self.l_buffer[b:e] = rep
b += len(rep)
e = b
self.line_cursor = b
def menu_complete(self, e): # ()
'''Similar to complete, but replaces the word to be completed with a
single match from the list of possible completions. Repeated
execution of menu-complete steps through the list of possible
completions, inserting each match in turn. At the end of the list of
completions, the bell is rung (subject to the setting of bell-style)
and the original text is restored. An argument of n moves n
positions forward in the list of matches; a negative argument may be
used to move backward through the list. This command is intended to
be bound to TAB, but is unbound by default.'''
pass
### Methods below here are bindable emacs functions
def beginning_of_line(self, e): # (C-a)
'''Move to the start of the current line. '''
self.l_buffer.beginning_of_line()
def end_of_line(self, e): # (C-e)
'''Move to the end of the line. '''
self.l_buffer.end_of_line()
def forward_char(self, e): # (C-f)
'''Move forward a character. '''
self.l_buffer.forward_char()
def backward_char(self, e): # (C-b)
'''Move back a character. '''
self.l_buffer.backward_char()
def forward_word(self, e): # (M-f)
'''Move forward to the end of the next word. Words are composed of
letters and digits.'''
self.l_buffer.forward_word()
def backward_word(self, e): # (M-b)
'''Move back to the start of the current or previous word. Words are
composed of letters and digits.'''
self.l_buffer.backward_word()
def clear_screen(self, e): # (C-l)
'''Clear the screen and redraw the current line, leaving the current
line at the top of the screen.'''
self.console.page()
def redraw_current_line(self, e): # ()
'''Refresh the current line. By default, this is unbound.'''
pass
def accept_line(self, e): # (Newline or Return)
'''Accept the line regardless of where the cursor is. If this line
is non-empty, it may be added to the history list for future recall
with add_history(). If this line is a modified history line, the
history line is restored to its original state.'''
return True
def delete_char(self, e): # (C-d)
'''Delete the character at point. If point is at the beginning of
the line, there are no characters in the line, and the last
character typed was not bound to delete-char, then return EOF.'''
self.l_buffer.delete_char()
def backward_delete_char(self, e): # (Rubout)
'''Delete the character behind the cursor. A numeric argument means
to kill the characters instead of deleting them.'''
self.l_buffer.backward_delete_char()
def self_insert(self, e): # (a, b, A, 1, !, ...)
'''Insert yourself. '''
if ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys.
self.insert_text(e.char)
+4 -216
View File
@@ -12,51 +12,16 @@ from pyreadline.logger import log
from pyreadline.keysyms import key_text_to_keyinfo
import pyreadline.lineeditor.lineobj as lineobj
import pyreadline.lineeditor.history as history
import basemode
class EmacsMode(object):
class EmacsMode(basemode.BaseMode):
mode="emacs"
def __init__(self,rlobj):
self.rlobj=rlobj
self.exit_dispatch = {}
self.key_dispatch = {}
self.startup_hook=None
self.pre_input_hook=None
super(EmacsMode,self).__init__(rlobj)
def __repr__(self):
return "<EmacsMode>"
def _gs(x):
def g(self):
return getattr(self.rlobj,x)
def s(self,q):
setattr(self.rlobj,x,q)
return g,s
def _g(x):
def g(self):
return getattr(self.rlobj,x)
return g
l_buffer=property(*_gs("l_buffer"))
next_meta=property(*_gs("next_meta"))
first_prompt=property(*_gs("first_prompt"))
prompt=property(*_gs("prompt"))
console=property(_g("console"))
insert_text=property(_g("insert_text"))
_print_prompt=property(_g("_print_prompt"))
_update_line=property(_g("_update_line"))
paste_line_buffer=property(_g("paste_line_buffer"))
add_history=property(_g("add_history"))
_bell=property(_g("_bell"))
_clear_after=property(_g("_clear_after"))
_set_cursor=property(_g("_set_cursor"))
_print_prompt=property(_g("_print_prompt"))
_update_prompt_pos=property(_g("_update_prompt_pos"))
_update_line=property(_g("_update_line"))
enable_win32_clipboard=property(_g("enable_win32_clipboard"))
_bell=property(_g("_bell"))
_history=property(_g("_history"))
def _readline_from_keyboard(self):
c=self.console
while 1:
@@ -124,50 +89,6 @@ class EmacsMode(object):
log('returning(%s)' % self.l_buffer.get_line_text())
return self.l_buffer.get_line_text() + '\n'
### Methods below here are bindable emacs functions
def beginning_of_line(self, e): # (C-a)
'''Move to the start of the current line. '''
self.l_buffer.beginning_of_line()
def end_of_line(self, e): # (C-e)
'''Move to the end of the line. '''
self.l_buffer.end_of_line()
def forward_char(self, e): # (C-f)
'''Move forward a character. '''
self.l_buffer.forward_char()
def backward_char(self, e): # (C-b)
'''Move back a character. '''
self.l_buffer.backward_char()
def forward_word(self, e): # (M-f)
'''Move forward to the end of the next word. Words are composed of
letters and digits.'''
self.l_buffer.forward_word()
def backward_word(self, e): # (M-b)
'''Move back to the start of the current or previous word. Words are
composed of letters and digits.'''
self.l_buffer.backward_word()
def clear_screen(self, e): # (C-l)
'''Clear the screen and redraw the current line, leaving the current
line at the top of the screen.'''
self.console.page()
def redraw_current_line(self, e): # ()
'''Refresh the current line. By default, this is unbound.'''
pass
def accept_line(self, e): # (Newline or Return)
'''Accept the line regardless of where the cursor is. If this line
is non-empty, it may be added to the history list for future recall
with add_history(). If this line is a modified history line, the
history line is restored to its original state.'''
return True
######### History commands
def previous_history(self, e): # (C-p)
'''Move back through the history list, fetching the previous command. '''
@@ -283,17 +204,6 @@ class EmacsMode(object):
the history list, inserting the last argument of each line in turn.'''
pass
def delete_char(self, e): # (C-d)
'''Delete the character at point. If point is at the beginning of
the line, there are no characters in the line, and the last
character typed was not bound to delete-char, then return EOF.'''
self.l_buffer.delete_char()
def backward_delete_char(self, e): # (Rubout)
'''Delete the character behind the cursor. A numeric argument means
to kill the characters instead of deleting them.'''
self.l_buffer.backward_delete_char()
def forward_backward_delete_char(self, e): # ()
'''Delete the character under the cursor, unless the cursor is at
the end of the line, in which case the character behind the cursor
@@ -311,11 +221,6 @@ class EmacsMode(object):
ws = ' ' * (self.tabstop - (self.line_cursor%self.tabstop))
self.insert_text(ws)
def self_insert(self, e): # (a, b, A, 1, !, ...)
'''Insert yourself. '''
if ord(e.char)!=0: #don't insert null character in buffer, can happen with dead keys.
self.insert_text(e.char)
def transpose_chars(self, e): # (C-t)
'''Drag the character before the cursor forward over the character
at the cursor, moving the cursor forward as well. If the insertion
@@ -489,123 +394,6 @@ class EmacsMode(object):
default, this is not bound to a key.'''
pass
def _get_completions(self):
'''Return a list of possible completions for the string ending at the point.
Also set begidx and endidx in the process.'''
completions = []
self.begidx = self.l_buffer.point
self.endidx = self.l_buffer.point
buf=self.l_buffer.line_buffer
if self.completer:
# get the string to complete
while self.begidx > 0:
self.begidx -= 1
if buf[self.begidx] in self.completer_delims:
self.begidx += 1
break
text = ''.join(buf[self.begidx:self.endidx])
log('complete text="%s"' % text)
i = 0
while 1:
try:
r = self.completer(text, i)
except:
break
i += 1
if r and r not in completions:
completions.append(r)
else:
break
log('text completions=%s' % completions)
if not completions:
# get the filename to complete
while self.begidx > 0:
self.begidx -= 1
if buf[self.begidx] in ' \t\n':
self.begidx += 1
break
text = ''.join(buf[self.begidx:self.endidx])
log('file complete text="%s"' % text)
completions = glob(os.path.expanduser(text) + '*')
if self.mark_directories == 'on':
mc = []
for f in completions:
if os.path.isdir(f):
mc.append(f + os.sep)
else:
mc.append(f)
completions = mc
log('fnames=%s' % completions)
return completions
def _display_completions(self, completions):
if not completions:
return
self.console.write('\n')
wmax = max(map(len, completions))
w, h = self.console.size()
cols = max(1, int((w-1) / (wmax+1)))
rows = int(math.ceil(float(len(completions)) / cols))
for row in range(rows):
s = ''
for col in range(cols):
i = col*rows + row
if i < len(completions):
self.console.write(completions[i].ljust(wmax+1))
self.console.write('\n')
self._print_prompt()
def complete(self, e): # (TAB)
'''Attempt to perform completion on the text before point. The
actual completion performed is application-specific. The default is
filename completion.'''
completions = self._get_completions()
if completions:
cprefix = commonprefix(completions)
rep = [ c for c in cprefix ]
self.l_buffer[self.begidx:self.endidx] = rep
self.l_buffer.point += len(rep) - (self.endidx - self.begidx)
if len(completions) > 1:
if self.show_all_if_ambiguous == 'on':
self._display_completions(completions)
else:
self._bell()
else:
self._bell()
def possible_completions(self, e): # (M-?)
'''List the possible completions of the text before point. '''
completions = self._get_completions()
self._display_completions(completions)
def insert_completions(self, e): # (M-*)
'''Insert all completions of the text before point that would have
been generated by possible-completions.'''
completions = self._get_completions()
b = self.begidx
e = self.endidx
for comp in completions:
rep = [ c for c in comp ]
rep.append(' ')
self.l_buffer[b:e] = rep
b += len(rep)
e = b
self.line_cursor = b
def menu_complete(self, e): # ()
'''Similar to complete, but replaces the word to be completed with a
single match from the list of possible completions. Repeated
execution of menu-complete steps through the list of possible
completions, inserting each match in turn. At the end of the list of
completions, the bell is rung (subject to the setting of bell-style)
and the original text is restored. An argument of n moves n
positions forward in the list of matches; a negative argument may be
used to move backward through the list. This command is intended to
be bound to TAB, but is unbound by default.'''
pass
def delete_char_or_list(self, e): # ()
'''Deletes the character under the cursor if not at the beginning or
end of the line (like delete-char). If at the end of the line,
+4 -158
View File
@@ -12,53 +12,16 @@ from pyreadline.logger import log
from pyreadline.keysyms import key_text_to_keyinfo
import pyreadline.lineeditor.lineobj as lineobj
import pyreadline.lineeditor.history as history
import basemode
class NotEmacsMode(object):
class NotEmacsMode(basemode.BaseMode):
mode="notemacs"
def __init__(self,rlobj):
self.rlobj=rlobj
self.exit_dispatch = {}
self.key_dispatch = {}
self.startup_hook=None
self.pre_input_hook=None
super(NotEmacsMode,self).__init__(rlobj)
def __repr__(self):
return "<NotEmacsMode>"
def _gs(x):
def g(self):
return getattr(self.rlobj,x)
def s(self,q):
setattr(self.rlobj,x,q)
return g,s
def _g(x):
def g(self):
return getattr(self.rlobj,x)
return g
l_buffer=property(*_gs("l_buffer"))
next_meta=property(*_gs("next_meta"))
first_prompt=property(*_gs("first_prompt"))
prompt=property(*_gs("prompt"))
console=property(_g("console"))
insert_text=property(_g("insert_text"))
_print_prompt=property(_g("_print_prompt"))
_update_line=property(_g("_update_line"))
paste_line_buffer=property(_g("paste_line_buffer"))
add_history=property(_g("add_history"))
_bell=property(_g("_bell"))
_clear_after=property(_g("_clear_after"))
_set_cursor=property(_g("_set_cursor"))
_print_prompt=property(_g("_print_prompt"))
_update_prompt_pos=property(_g("_update_prompt_pos"))
_update_line=property(_g("_update_line"))
enable_win32_clipboard=property(_g("enable_win32_clipboard"))
_bell=property(_g("_bell"))
def _readline_from_keyboard(self):
c=self.console
while 1:
@@ -491,123 +454,6 @@ class NotEmacsMode(object):
default, this is not bound to a key.'''
pass
def _get_completions(self):
'''Return a list of possible completions for the string ending at the point.
Also set begidx and endidx in the process.'''
completions = []
self.begidx = self.l_buffer.point
self.endidx = self.l_buffer.point
buf=self.l_buffer.line_buffer
if self.completer:
# get the string to complete
while self.begidx > 0:
self.begidx -= 1
if buf[self.begidx] in self.completer_delims:
self.begidx += 1
break
text = ''.join(buf[self.begidx:self.endidx])
log('complete text="%s"' % text)
i = 0
while 1:
try:
r = self.completer(text, i)
except:
break
i += 1
if r and r not in completions:
completions.append(r)
else:
break
log('text completions=%s' % completions)
if not completions:
# get the filename to complete
while self.begidx > 0:
self.begidx -= 1
if buf[self.begidx] in ' \t\n':
self.begidx += 1
break
text = ''.join(buf[self.begidx:self.endidx])
log('file complete text="%s"' % text)
completions = glob(os.path.expanduser(text) + '*')
if self.mark_directories == 'on':
mc = []
for f in completions:
if os.path.isdir(f):
mc.append(f + os.sep)
else:
mc.append(f)
completions = mc
log('fnames=%s' % completions)
return completions
def _display_completions(self, completions):
if not completions:
return
self.console.write('\n')
wmax = max(map(len, completions))
w, h = self.console.size()
cols = max(1, int((w-1) / (wmax+1)))
rows = int(math.ceil(float(len(completions)) / cols))
for row in range(rows):
s = ''
for col in range(cols):
i = col*rows + row
if i < len(completions):
self.console.write(completions[i].ljust(wmax+1))
self.console.write('\n')
self._print_prompt()
def complete(self, e): # (TAB)
'''Attempt to perform completion on the text before point. The
actual completion performed is application-specific. The default is
filename completion.'''
completions = self._get_completions()
if completions:
cprefix = commonprefix(completions)
rep = [ c for c in cprefix ]
self.l_buffer[self.begidx:self.endidx] = rep
self.l_buffer.point += len(rep) - (self.endidx - self.begidx)
if len(completions) > 1:
if self.show_all_if_ambiguous == 'on':
self._display_completions(completions)
else:
self._bell()
else:
self._bell()
def possible_completions(self, e): # (M-?)
'''List the possible completions of the text before point. '''
completions = self._get_completions()
self._display_completions(completions)
def insert_completions(self, e): # (M-*)
'''Insert all completions of the text before point that would have
been generated by possible-completions.'''
completions = self._get_completions()
b = self.begidx
e = self.endidx
for comp in completions:
rep = [ c for c in comp ]
rep.append(' ')
self.l_buffer[b:e] = rep
b += len(rep)
e = b
self.line_cursor = b
def menu_complete(self, e): # ()
'''Similar to complete, but replaces the word to be completed with a
single match from the list of possible completions. Repeated
execution of menu-complete steps through the list of possible
completions, inserting each match in turn. At the end of the list of
completions, the bell is rung (subject to the setting of bell-style)
and the original text is restored. An argument of n moves n
positions forward in the list of matches; a negative argument may be
used to move backward through the list. This command is intended to
be bound to TAB, but is unbound by default.'''
pass
def delete_char_or_list(self, e): # ()
'''Deletes the character under the cursor if not at the beginning or
end of the line (like delete-char). If at the end of the line,
File diff suppressed because it is too large Load Diff
+1
View File
@@ -20,6 +20,7 @@ name = 'pyreadline'
# because bdist_rpm does not accept dashes (an RPM) convention, and
# bdist_deb does not accept underscores (a Debian convention).
branch = 'refactor'
version = 'refactor'
+6 -2
View File
@@ -26,6 +26,7 @@ from keysyms import key_text_to_keyinfo
import pyreadline.lineeditor.lineobj as lineobj
import pyreadline.lineeditor.history as history
import release
from modes import editingmodes
@@ -59,9 +60,10 @@ class Readline(object):
self.tabstop = 4
self.editingmodes=[mode(self) for mode in editingmodes]
for mode in self.editingmodes:
mode.init_editing_mode(None)
self.mode=self.editingmodes[0]
self.mode.init_editing_mode(None)
self.begidx = 0
self.endidx = 0
@@ -351,7 +353,9 @@ class Readline(object):
def debug_output(on,filename="pyreadline_debug_log.txt"): #Not implemented yet
logger.start_log(on,filename)
logger.log("STARTING LOG")
loc={"mode":mode,
# print release.branch
loc={"branch":release.branch,
"mode":mode,
"modes":modes,
"set_mode":setmode,
"bind_key":bind_key,