diff --git a/doc/ChangeLog b/doc/ChangeLog index 06e97ce..aadbabf 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,5 +1,6 @@ 2006-03-16 Jörgen Stenarson * Added vi patch + * Added visible selection mode to emacs mode 2006-03-16 Jörgen Stenarson * Refactored emacs mode diff --git a/pyreadline/configuration/pyreadlineconfig.ini b/pyreadline/configuration/pyreadlineconfig.ini index 1fcf303..0c5cc67 100644 --- a/pyreadline/configuration/pyreadlineconfig.ini +++ b/pyreadline/configuration/pyreadlineconfig.ini @@ -4,16 +4,21 @@ set_mode("emacs") #will cause following bind_keys to bind to emacs mode bind_exit_key("Control-d") bind_exit_key("Control-z") - #Commands for moving bind_key("Home", "beginning_of_line") bind_key("End", "end_of_line") -bind_key("Left", "backward_char") +bind_key("Left", "backward_char") bind_key("Control-b", "backward_char") bind_key("Right", "forward_char") bind_key("Control-f", "forward_char") bind_key("Alt-f", "forward_word") +bind_key("Control-Right", "forward_word") +bind_key("Shift-Right", "forward_char_extend_selection") +bind_key("Shift-Left", "backward_char_extend_selection") +bind_key("Shift-Control-Right", "forward_word_extend_selection") +bind_key("Shift-Control-Left", "backward_word_extend_selection") bind_key("Alt-b", "backward_word") +bind_key("Control-Left", "backward_word") bind_key("Clear", "clear_screen") bind_key("Control-l", "clear_screen") bind_key("Control-a", "beginning_of_line") @@ -21,7 +26,7 @@ bind_key("Control-e", "end_of_line") #bind_key("Control-l", "redraw_current_line") #Commands for Manipulating the History -bind_key("Return", "accept_line") +bind_key("Return", "accept_line") bind_key("Control-p", "previous_history") bind_key("Control-n", "next_history") bind_key("Up", "history_search_backward") @@ -37,12 +42,14 @@ bind_key("Alt-n", "non_incremental_forward_search_history") bind_key("Delete", "delete_char") bind_key("Control-d", "delete_char") bind_key("BackSpace", "backward_delete_char") -bind_key("Control-Shift-v", "quoted_insert") +bind_key("Control-BackSpace", "backward_delete_word") +#bind_key("Control-Shift-v", "quoted_insert") bind_key("Control-space", "self_insert") #Killing and Yanking bind_key("Control-k", "kill_line") bind_key("Control-shift-k", "kill_whole_line") +bind_key("Escape", "kill_whole_line") bind_key("Meta-d", "kill_word") bind_key("Control-w", "unix_word_rubout") bind_key("Meta-Delete", "backward_kill_word") @@ -50,23 +57,25 @@ bind_key("Meta-Delete", "backward_kill_word") #Copy paste bind_key("Control-m", "set_mark") bind_key("Control-q", "copy_region_to_clipboard") +bind_key("Control-x", "cut_selection_to_clipboard") +bind_key("Control-Shift-x", "copy_selection_to_clipboard") bind_key("Control-v", "paste") bind_key("Alt-v", "ipython_paste") bind_key("Control-y", "paste") bind_key("Control-z", "undo") bind_key("Control-_", "undo") +bind_key('Control-Shift-v', "paste_mulitline_code") #Unbinding keys: #un_bind_key("Home") - #Other bell_style("none") #modes: none, audible, visible(not implemented) show_all_if_ambiguous("on") mark_directories("on") -completer_delims(" \t\n\"\\'`@$><=;|&{(") +completer_delims(" \t\n\"\\'`@$><=;|&{(?") debug_output("off") + history_filename("~/.pythonhistory") history_length(200) #value of -1 means no limit - diff --git a/pyreadline/lineeditor/lineobj.py b/pyreadline/lineeditor/lineobj.py index e3bfc89..1da1556 100644 --- a/pyreadline/lineeditor/lineobj.py +++ b/pyreadline/lineeditor/lineobj.py @@ -17,8 +17,6 @@ def quote_char(c): if ord(c)>0: return c - - ############## Line positioner ######################## class LinePositioner(object): @@ -358,44 +356,109 @@ class ReadLineTextBuffer(TextLine): def __init__(self,txtstr,point=None,mark=None): super(ReadLineTextBuffer,self).__init__(txtstr,point,mark) self.enable_win32_clipboard=True + self.selection_mark=-1 + self.enable_selection=True def insert_text(self,char): + self.delete_selection() + self.selection_mark=-1 self._insert_text(char) - + ######### Movement def beginning_of_line(self): + self.selection_mark=-1 self.point=StartOfLine def end_of_line(self): + self.selection_mark=-1 self.point=EndOfLine def forward_char(self): + self.selection_mark=-1 self.point=NextChar def backward_char(self): + self.selection_mark=-1 self.point=PrevChar def forward_word(self): + self.selection_mark=-1 self.point=NextWordStart def backward_word(self): + self.selection_mark=-1 + self.point=PrevWordStart + +######### Movement select + def beginning_of_line_extend_selection(self): + if self.enable_selection and self.selection_mark<0: + self.selection_mark=self.point + self.point=StartOfLine + + def end_of_line_extend_selection(self): + if self.enable_selection and self.selection_mark<0: + self.selection_mark=self.point + self.point=EndOfLine + + def forward_char_extend_selection(self): + if self.enable_selection and self.selection_mark<0: + self.selection_mark=self.point + self.point=NextChar + + def backward_char_extend_selection(self): + if self.enable_selection and self.selection_mark<0: + self.selection_mark=self.point + self.point=PrevChar + + def forward_word_extend_selection(self): + if self.enable_selection and self.selection_mark<0: + self.selection_mark=self.point + self.point=NextWordStart + + def backward_word_extend_selection(self): + if self.enable_selection and self.selection_mark<0: + self.selection_mark=self.point self.point=PrevWordStart ######### delete + def delete_selection(self): + if self.enable_selection and self.selection_mark>0: + if self.selection_mark0: + self.backward_char() + self.delete_char() + self.selection_mark=-1 + + def backward_delete_word(self): + if not self.delete_selection(): + del self[PrevWordEnd:Point] + self.selection_mark=-1 def delete_current_word(self): - del self[CurrentWord] + if not self.delete_selection(): + del self[CurrentWord] + self.selection_mark=-1 def delete_horizontal_space(self): - pass + if not self.delete_selection(): + pass + self.selection_mark=-1 ######### Case def upcase_word(self): @@ -488,6 +551,22 @@ class ReadLineTextBuffer(TextLine): toclipboard="".join(self.line_buffer[begin:end]) clipboard.SetClipboardText(str(toclipboard)) + def copy_selection_to_clipboard(self): # () + '''Copy the text in the region to the windows clipboard.''' + if self.enable_win32_clipboard and self.enable_selection and self.selection_mark>0: + selection_mark=min(self.selection_mark,len(self.line_buffer)) + cursor=min(self.point,len(self.line_buffer)) + if self.selection_mark==-1: + return + begin=min(cursor,selection_mark) + end=max(cursor,selection_mark) + toclipboard="".join(self.line_buffer[begin:end]) + clipboard.SetClipboardText(str(toclipboard)) + + + def cut_selection_to_clipboard(self): # () + self.copy_selection_to_clipboard() + self.delete_selection() ############## Paste diff --git a/pyreadline/modes/basemode.py b/pyreadline/modes/basemode.py index 4cfb6b0..3330e2c 100644 --- a/pyreadline/modes/basemode.py +++ b/pyreadline/modes/basemode.py @@ -6,12 +6,13 @@ # 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 os,re 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 +import pyreadline.clipboard as clipboard class BaseMode(object): mode="base" @@ -41,11 +42,12 @@ class BaseMode(object): next_meta=property(*_gs("next_meta")) first_prompt=property(*_gs("first_prompt")) prompt=property(*_gs("prompt")) + paste_line_buffer=property(*_gs("paste_line_buffer")) + 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")) @@ -54,9 +56,11 @@ class BaseMode(object): _update_prompt_pos=property(_g("_update_prompt_pos")) _update_line=property(_g("_update_line")) enable_win32_clipboard=property(_g("enable_win32_clipboard")) + enable_ipython_paste_list_of_lists=property(_g("enable_ipython_paste_list_of_lists")) + enable_ipython_paste_for_paths=property(_g("enable_ipython_paste_for_paths")) _bell=property(_g("_bell")) _history=property(_g("_history")) - + def _readline_from_keyboard(self): raise NotImplementedError @@ -229,6 +233,37 @@ class BaseMode(object): composed of letters and digits.''' self.l_buffer.backward_word() + + + def beginning_of_line_extend_selection(self, e): # + '''Move to the start of the current line. ''' + self.l_buffer.beginning_of_line_extend_selection() + + def end_of_line_extend_selection(self, e): # + '''Move to the end of the line. ''' + self.l_buffer.end_of_line_extend_selection() + + def forward_char_extend_selection(self, e): # + '''Move forward a character. ''' + self.l_buffer.forward_char_extend_selection() + + def backward_char_extend_selection(self, e): # + '''Move back a character. ''' + self.l_buffer.backward_char_extend_selection() + + def forward_word_extend_selection(self, e): # + '''Move forward to the end of the next word. Words are composed of + letters and digits.''' + self.l_buffer.forward_word_extend_selection() + + def backward_word_extend_selection(self, e): # + '''Move back to the start of the current or previous word. Words are + composed of letters and digits.''' + self.l_buffer.backward_word_extend_selection() + + + + 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.''' @@ -257,8 +292,63 @@ class BaseMode(object): to kill the characters instead of deleting them.''' self.l_buffer.backward_delete_char() + def backward_delete_word(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_word() + 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) + +# Paste from clipboard + + def paste(self,e): + '''Paste windows clipboard''' + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert(False) + self.insert_text(txt) + + def paste_mulitline_code(self,e): + '''Paste windows clipboard''' + reg=re.compile("\r?\n") + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert(False) + t=reg.split(txt) + t=[row for row in t if row.strip()!=""] #remove empty lines + if t!=[""]: + self.insert_text(t[0]) + self.add_history(self.l_buffer.copy()) + self.paste_line_buffer=t[1:] + log("multi: %s"%self.paste_line_buffer) + return True + else: + return False + + def ipython_paste(self,e): + '''Paste windows clipboard. If enable_ipython_paste_list_of_lists is + True then try to convert tabseparated data to repr of list of lists or + repr of array''' + if self.enable_win32_clipboard: + txt=clipboard.get_clipboard_text_and_convert( + self.enable_ipython_paste_list_of_lists) + if self.enable_ipython_paste_for_paths: + if len(txt)<300 and ("\t" not in txt) and ("\n" not in txt): + txt=txt.replace("\\","/").replace(" ",r"\ ") + self.insert_text(txt) + + + def copy_region_to_clipboard(self, e): # () + '''Copy the text in the region to the windows clipboard.''' + self.l_buffer.copy_region_to_clipboard() + + def copy_selection_to_clipboard(self, e): # () + '''Copy the text in the region to the windows clipboard.''' + self.l_buffer.copy_selection_to_clipboard() + + def cut_selection_to_clipboard(self, e): # () + '''Copy the text in the region to the windows clipboard.''' + self.l_buffer.cut_selection_to_clipboard() + diff --git a/pyreadline/modes/emacs.py b/pyreadline/modes/emacs.py index aead4f4..eadeeb1 100644 --- a/pyreadline/modes/emacs.py +++ b/pyreadline/modes/emacs.py @@ -52,6 +52,7 @@ class EmacsMode(basemode.BaseMode): def readline(self, prompt=''): '''Try to act like GNU readline.''' # handle startup_hook + self.l_buffer.selection_mark=-1 if self.first_prompt: self.first_prompt = False if self.startup_hook: @@ -76,7 +77,7 @@ class EmacsMode(basemode.BaseMode): log("in readline: %s"%self.paste_line_buffer) if len(self.paste_line_buffer)>0: - self.l_buffer=lineobj.ReadlineTextBuffer(self.paste_line_buffer[0]) + self.l_buffer=lineobj.ReadLineTextBuffer(self.paste_line_buffer[0]) self._update_line() self.paste_line_buffer=self.paste_line_buffer[1:] c.write('\r\n') @@ -307,18 +308,6 @@ class EmacsMode(basemode.BaseMode): yanked right away. By default, this command is unbound.''' pass - def copy_region_to_clipboard(self, e): # () - '''Copy the text in the region to the windows clipboard.''' - if self.enable_win32_clipboard: - mark=min(self.l_buffer.mark,len(self.l_buffer.line_buffer)) - cursor=min(self.l_buffer.point,len(self.l_buffer.line_buffer)) - if self.l_buffer.mark==-1: - return - begin=min(cursor,mark) - end=max(cursor,mark) - toclipboard="".join(self.l_buffer.line_buffer[begin:end]) - clipboard.SetClipboardText(str(toclipboard)) - def copy_backward_word(self, e): # () '''Copy the word before point to the kill buffer. The word boundaries are the same as backward-word. By default, this command @@ -331,39 +320,6 @@ class EmacsMode(basemode.BaseMode): unbound.''' pass - def paste(self,e): - '''Paste windows clipboard''' - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert(False) - self.insert_text(txt) - - def paste_mulitline_code(self,e): - '''Paste windows clipboard''' - reg=re.compile("\r?\n") - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert(False) - t=reg.split(txt) - t=[row for row in t if row.strip()!=""] #remove empty lines - if t!=[""]: - self.insert_text(t[0]) - self.add_history(self.l_buffer.copy()) - self.paste_line_buffer=t[1:] - log("multi: %s"%self.paste_line_buffer) - return True - else: - return False - - def ipython_paste(self,e): - '''Paste windows clipboard. If enable_ipython_paste_list_of_lists is - True then try to convert tabseparated data to repr of list of lists or - repr of array''' - if self.enable_win32_clipboard: - txt=clipboard.get_clipboard_text_and_convert( - self.enable_ipython_paste_list_of_lists) - if self.enable_ipython_paste_for_paths: - if len(txt)<300 and ("\t" not in txt) and ("\n" not in txt): - txt=txt.replace("\\","/").replace(" ",r"\ ") - self.insert_text(txt) def yank(self, e): # (C-y) '''Yank the top of the kill ring into the buffer at point. ''' @@ -506,6 +462,7 @@ class EmacsMode(basemode.BaseMode): def _bind_key(self, key, func): '''setup the mapping from key to call the function.''' +# print key,func keyinfo = key_text_to_keyinfo(key) # print key,keyinfo,func.__name__ self.key_dispatch[keyinfo] = func @@ -518,7 +475,6 @@ class EmacsMode(basemode.BaseMode): def init_editing_mode(self, e): # (C-e) '''When in vi command mode, this causes a switch to emacs editing mode.''' - self._bind_exit_key('Control-d') self._bind_exit_key('Control-z') diff --git a/pyreadline/modes/vi.py b/pyreadline/modes/vi.py index 03afbac..75653d1 100644 --- a/pyreadline/modes/vi.py +++ b/pyreadline/modes/vi.py @@ -95,7 +95,6 @@ class ViMode(basemode.BaseMode): def init_editing_mode(self, e): # (M-C-j) '''Initialize vi editingmode''' - print "initing vi" self.show_all_if_ambiguous = 'on' self.key_dispatch = {} self.__vi_insert_mode = None diff --git a/pyreadline/rlmain.py b/pyreadline/rlmain.py index bd781c5..8bc3305 100644 --- a/pyreadline/rlmain.py +++ b/pyreadline/rlmain.py @@ -53,6 +53,7 @@ class Readline(object): self.size = self.console.size() self.prompt_color = None self.command_color = None + self.selection_color =0x00f0 self.key_dispatch = {} self.previous_func = None self.first_prompt = True @@ -89,7 +90,6 @@ class Readline(object): self.paste_line_buffer=[] - #Below is for refactoring, raise errors when using old style attributes #that should be refactored out def _g(x): @@ -306,7 +306,17 @@ class Readline(object): c=self.console c.pos(*self.prompt_end_pos) ltext = self.l_buffer.quoted_text() - n = c.write_scrolling(ltext, self.command_color) + if self.l_buffer.enable_selection and self.l_buffer.selection_mark>0: + start=len(self.l_buffer[:self.l_buffer.selection_mark].quoted_text()) + stop=len(self.l_buffer[:self.l_buffer.point].quoted_text()) + if start>stop: + stop,start=start,stop + n = c.write_scrolling(ltext[:start], self.command_color) + n = c.write_scrolling(ltext[start:stop], self.selection_color) + n = c.write_scrolling(ltext[stop:], self.command_color) + else: + n = c.write_scrolling(ltext, self.command_color) + self._update_prompt_pos(n) self._clear_after() self._set_cursor() @@ -321,8 +331,9 @@ class Readline(object): def setmode(name): self.mode=modes[name] def bind_key(key,name): - if hasattr(self,name): - modes[mode]._bind_key(key,getattr(self,name)) + if hasattr(modes[mode],name): + #print key,name + modes[mode]._bind_key(key,getattr(modes[mode],name)) def un_bind_key(key): keyinfo = key_text_to_keyinfo(key) if keyinfo in modes[mode].key_dispatch: