def _process_incremental_search_keyevent(self, keyinfo): keytuple = keyinfo.tuple() log(u"IncrementalSearchPromptMode %s %s"%(keyinfo, keytuple)) if keyinfo.keyname == u'backspace': self.subsearch_query = self.subsearch_query[:-1] if len(self.subsearch_query) > 0: self.line=self.subsearch_fun(self.subsearch_query) else: self._bell() self.line = "" #empty query means no search result elif keyinfo.keyname in [u'return', u'escape']: self._bell() self.prompt = self.subsearch_oldprompt self.process_keyevent_queue = self.process_keyevent_queue[:-1] self._history.history_cursor = len(self._history.history) if keyinfo.keyname == u'escape': self.l_buffer.set_line(self.subsearch_old_line) return False elif keyinfo.keyname: pass elif keytuple == self.subsearch_init_event: self._history.history_cursor += self.subsearch_direction self.line = self.subsearch_fun(self.subsearch_query) elif keyinfo.control == False and keyinfo.meta == False : self.subsearch_query += keyinfo.char self.line=self.subsearch_fun(self.subsearch_query) else: pass self.prompt = self.subsearch_prompt%self.subsearch_query self.l_buffer.set_line(self.line)
def forward_search_history(self, e): # (C-s) u"""Search forward starting at the current line and moving down through the the history as necessary. This is an incremental search.""" log("fwd_search_history") self._init_incremental_search(self._history.forward_search_history, e) self.finalize()
def reverse_search_history(self, e): # (C-r) '''Search backward starting at the current line and moving up through the history as necessary. This is an incremental search.''' log("rev_search_history") self._init_incremental_search(self._history.reverse_search_history, e) self.l_buffer.selection_mark = -1 self.finalize()
def clear_range(self, pos_range): u'''Clears range that may span to multiple lines pos_range is (x1,y1, x2,y2) including y2 >= y1 x2 == -1 means end of line ''' log("clearRange(%s)" % str(pos_range)) (x1,y1, x2,y2) = pos_range if y2 < y1: return elif y2 == y1 and x2>=0: self.pos(x1,y1) self._write(' '*(x2-x1+1)) else: start = y1 end = y2+1 if x1 > 0: self.pos(x1,y1) self._write("\033[0K") start = y1+1 if 0 <= x2 < w-1: self.pos(x2,y2) self._write("\033[1K") end = y2 for line in range(start, end): self.pos(0,line) self._write("\033[2K") self.position = None self.pos(x1,y1)
def _process_non_incremental_search_keyevent(self, keyinfo): keytuple = keyinfo.tuple() log(u"SearchPromptMode %s %s" % (keyinfo, keytuple)) history = self._history if keyinfo.keyname == u"backspace": self.non_inc_query = self.non_inc_query[:-1] elif keyinfo.keyname in [u"return", u"escape"]: if self.non_inc_query: if self.non_inc_direction == -1: res = history.reverse_search_history(self.non_inc_query) else: res = history.forward_search_history(self.non_inc_query) self._bell() self.prompt = self.non_inc_oldprompt self.process_keyevent_queue = self.process_keyevent_queue[:-1] self._history.history_cursor = len(self._history.history) if keyinfo.keyname == u"escape": self.l_buffer = self.non_inc_oldline else: self.l_buffer.set_line(res) return False elif keyinfo.keyname: pass elif keyinfo.control == False and keyinfo.meta == False: self.non_inc_query += keyinfo.char else: pass self.prompt = self.non_inc_oldprompt + u":" + self.non_inc_query
def reverse_search_history(self, searchfor, startpos=None): if startpos is None: startpos = self.history_cursor origpos = startpos result = lineobj.ReadLineTextBuffer("") for idx, line in list(enumerate(self.history))[startpos:0:-1]: if searchfor in line: startpos = idx break #If we get a new search without change in search term it means #someone pushed ctrl-r and we should find the next match if self.last_search_for == searchfor and startpos > 0: startpos -= 1 for idx, line in list(enumerate(self.history))[startpos:0:-1]: if searchfor in line: startpos = idx break if self.history: result = self.history[startpos].get_line_text() else: result = "" self.history_cursor = startpos self.last_search_for = searchfor log("reverse_search_history: old:%d new:%d result:%r"%(origpos, self.history_cursor, result)) return result
def size(self): log("size()") if self.windowSize: log("# returned cached value %s" % str(self.windowSize)) return self.windowSize self._querySize() return self.windowSize
def test_complete (self): import rlcompleter logger.sock_silent = False log("-" * 50) r = EmacsModeTest() completerobj = rlcompleter.Completer() def _nop(val, word): return word completerobj._callable_postfix = _nop r.completer = completerobj.complete r._bind_key("tab", r.complete) r.input(u'"exi(ksdjksjd)"') r.input(u'Control-a') r.input(u'Right') r.input(u'Right') r.input(u'Right') r.input(u'Tab') self.assert_line(r, u"exit(ksdjksjd)", 4) r.input(u'Escape') r.input(u'"exi"') r.input(u'Control-a') r.input(u'Right') r.input(u'Right') r.input(u'Right') r.input(u'Tab') self.assert_line(r, u"exit", 4)
def _setPos(self, x, y): if self.positionIsSynchronized and self.position == (x, y): return self._write("\033[%d;%dH"%(y+1,x+1)) self.position = (x,y) self.positionIsSynchronized = True log("_setPos%s" % str(self.position))
def install_readline(hook): log("Installing ansi_console") global _old_raw_input if _old_raw_input is not None: return # don't run _setup twice try: f_in = sys.stdin.fileno() f_out = sys.stdout.fileno() except (AttributeError, ValueError): return if not os.isatty(f_in) or not os.isatty(f_out): return if '__pypy__' in sys.builtin_module_names: # PyPy def _old_raw_input(prompt=''): # sys.__raw_input__() is only called when stdin and stdout are # as expected and are ttys. If it is the case, then get_reader() # should not really fail in _wrapper.raw_input(). If it still # does, then we will just cancel the redirection and call again # the built-in raw_input(). try: del sys.__raw_input__ except AttributeError: pass return raw_input(prompt) sys.__raw_input__ = hook else: # this is not really what readline.c does. Better than nothing I guess import __builtin__ _old_raw_input = __builtin__.raw_input __builtin__.raw_input = hook
def get_current_history_length(self): '''Return the number of lines currently in the history. (This is different from get_history_length(), which returns the maximum number of lines that will be written to a history file.)''' value = len(self.history) log("get_current_history_length:%d"%value) return value
def _init_incremental_search(self, searchfun, init_event): u"""Initialize search prompt """ log("init_incremental_search") self.subsearch_query = u"" self.subsearch_fun = searchfun self.subsearch_old_line = self.l_buffer.get_line_text() queue = self.process_keyevent_queue queue.append(self._process_incremental_search_keyevent) self.subsearch_oldprompt = self.prompt if self.previous_func != self.reverse_search_history and self.previous_func != self.forward_search_history: self.subsearch_query = self.l_buffer[0:Point].get_line_text() if self.subsearch_fun == self.reverse_search_history: self.subsearch_prompt = u"reverse-i-search%d`%s': " else: self.subsearch_prompt = u"forward-i-search%d`%s': " self.prompt = self.subsearch_prompt % (self._history.history_cursor, "") if self.subsearch_query: self.line = self._process_incremental_search_keyevent(init_event) else: self.line = u""
def cursor(self, visible=None, size=None): log("cursor(visible='%s')" % visible) if visible is not None: if visible: self._write("\033[?25h") else: self._write("\033[?25l")
def _readline_from_keyboard(self): c = self.console while 1: self._update_line() event = c.getkeypress() if self.next_meta: self.next_meta = False control, meta, shift, code = event.keyinfo event.keyinfo = (control, True, shift, code) # Process exit keys. Only exit on empty line if event.keyinfo in self.exit_dispatch: if lineobj.EndOfLine(self.l_buffer) == 0: raise EOFError dispatch_func = self.key_dispatch.get(event.keyinfo, self.self_insert) log("readline from keyboard:%s" % (event.keyinfo,)) r = None if dispatch_func: r = dispatch_func(event) self.l_buffer.push_undo() self.previous_func = dispatch_func if r: self._update_line() break
def paste(self,e): '''Paste windows clipboard. Assume single line strip other lines and end of line markers and trailing spaces''' #(Control-v) if self.enable_win32_clipboard: txt=clipboard.get_clipboard_text_and_convert(False) txt=txt.split("\n")[0].strip("\r").strip("\n") log("paste: >%s<"%map(ord,txt)) self.insert_text(txt)
def _bind_key(self, key, func): '''setup the mapping from key to call the function.''' if type(func) != type(self._bind_key): print "Trying to bind non method to keystroke:%s,%s"%(key,func) raise PyreadlineError("Trying to bind non method to keystroke:%s,%s,%s,%s"%(key,func,type(func),type(self._bind_key))) keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple() log(">>>%s -> %s<<<"%(keyinfo,func.__name__)) self.key_dispatch[keyinfo] = func
def forward_search_history(self, e): # (C-s) '''Search forward starting at the current line and moving down through the the history as necessary. This is an incremental search.''' log("fwd_search_history") self._init_incremental_search(self._history.forward_search_history, e) self.l_buffer.selection_mark = -1 self.finalize()
def _readline_from_keyboard(self): c = self.console def nop(e): pass while 1: self._update_line() lbuf = self.l_buffer log_sock("point:%d mark:%d selection_mark:%d" % (lbuf.point, lbuf.mark, lbuf.selection_mark)) try: event = c.getkeypress() log_sock(u">>%s" % event) except KeyboardInterrupt: from pyreadline.keysyms.common import KeyPress from pyreadline.console.event import Event event = Event(0, 0) event.char = "c" event.keyinfo = KeyPress("c", shift=False, control=True, meta=False, keyname=None) log_sock("KBDIRQ") if self.allow_ctrl_c: now = time.time() if (now - self.ctrl_c_timeout) < self.ctrl_c_tap_time_interval: raise else: self.ctrl_c_timeout = now pass else: raise if self.next_meta: self.next_meta = False control, meta, shift, code = event.keyinfo event.keyinfo = (control, True, shift, code) # Process exit keys. Only exit on empty line keyinfo = event.keyinfo.tuple() if keyinfo in self.exit_dispatch: if lineobj.EndOfLine(self.l_buffer) == 0: raise EOFError if len(keyinfo[-1]) > 1: default = nop else: default = self.self_insert dispatch_func = self.key_dispatch.get(keyinfo, default) log("readline from keyboard:%s,%s" % (keyinfo, dispatch_func)) log_sock((u"%s|%s" % (ensure_unicode(format(keyinfo)), dispatch_func.__name__)), "bound_function") r = None if dispatch_func: r = dispatch_func(event) self._keylog(dispatch_func, self.l_buffer) self.l_buffer.push_undo() self.previous_func = dispatch_func if r: self._update_line() break
def write_plain(self, text, attr=None): '''write text at current cursor position.''' log('write("%s", %s)' %(text,attr)) if attr is None: attr = self.attr n = c_int(0) self.SetConsoleTextAttribute(self.hout, attr) self.WriteConsoleW(self.hout, ensure_unicode(chunk), len(chunk), byref(junk), None) return len(text)
def write_plain(self, text, attr=None): u'''write text at current cursor position.''' log(u'write("%s", %s)' %(text, attr)) if attr is None: attr = self.attr n = c_int(0) self.SetConsoleTextAttribute(self.hout, attr) self.WriteConsoleA(self.hout, text, len(text), byref(n), None) return len(text)
def write_scrolling(self, text, attr=None): '''write text at current cursor position while watching for scrolling. If the window scrolls because you are at the bottom of the screen buffer, all positions that you are storing will be shifted by the scroll amount. For example, I remember the cursor position of the prompt so that I can redraw the line but if the window scrolls, the remembered position is off. This variant of write tries to keep track of the cursor position so that it will know when the screen buffer is scrolled. It returns the number of lines that the buffer scrolled. ''' x, y = self.pos() w, h = self.size() scroll = 0 # the result # split the string into ordinary characters and funny characters chunks = self.motion_char_re.split(text) for chunk in chunks: log('C:'+chunk) n = self.write_color(chunk, attr) if len(chunk) == 1: # the funny characters will be alone if chunk[0] == '\n': # newline x = 0 y += 1 elif chunk[0] == '\r': # carriage return x = 0 elif chunk[0] == '\t': # tab x = 8*(int(x/8)+1) if x > w: # newline x -= w y += 1 elif chunk[0] == '\007': # bell pass elif chunk[0] == '\010': x -= 1 if x < 0: y -= 1 # backed up 1 line else: # ordinary character x += 1 if x == w: # wrap x = 0 y += 1 if y == h: # scroll scroll += 1 y = h - 1 else: # chunk of ordinary characters x += n l = int(x / w) # lines we advanced x = x % w # new x value y += l if y >= h: # scroll scroll += y - h + 1 y = h - 1 return scroll
def apply_attrs(self, attrs): if attrs is None: attrs = 0 if attrs == self.saveattr: return attrs_str = self.get_attrs_str(attrs) log("new attribute string is: %s", attrs_str) self._write("\033[%sm" % attrs_str) self.saveattr = attrs
def _querySize(self): try: w = self._readIntFromCmd("tput cols") h = self._readIntFromCmd("tput lines") self.windowSize = (w,h) log("size() # = " + str(self.windowSize)) except: print "Get terminal size failed, exitting" sys.exit(1)
def write(self, s): if s == "\r\n": log("write('\\r\\n')") # reset window size each time we enter new line self.windowSize = None else: log("write('%s')" % s) self.position = None return self._write(s)
def write_color(self, text, attr=None): text = ensure_unicode(text) n,res= self.ansiwriter.write_color(text,attr) junk = c_int(0) for attr,chunk in res: log(unicode(attr)) log(unicode(chunk)) self.SetConsoleTextAttribute(self.hout, attr.winattr) self.WriteConsoleW(self.hout, chunk, len(chunk), byref(junk), None) return n
def write_color(self, text, attr=None): text = ensure_unicode(text) n, res = self.ansiwriter.write_color(text, attr) junk = DWORD(0) for attr, chunk in res: log(u"console.attr:%s" % unicode(attr)) log(u"console.chunk:%s" % unicode(chunk)) self.SetConsoleTextAttribute(self.hout, attr.winattr) for short_chunk in split_block(chunk): self.WriteConsoleW(self.hout, short_chunk, len(short_chunk), byref(junk), None) return n
def write_plain(self, text, attr=None): u"""write text at current cursor position.""" text = ensure_unicode(text) log(u'write("%s", %s)' % (text, attr)) if attr is None: attr = self.attr junk = DWORD(0) self.SetConsoleTextAttribute(self.hout, attr) for short_chunk in split_block(chunk): self.WriteConsoleW(self.hout, ensure_unicode(short_chunk), len(short_chunk), byref(junk), None) return len(text)
def __init__(self, newbuffer=0): '''Initialize the Console object. newbuffer=1 will allocate a new buffer so the old content will be restored on exit. ''' self.serial = 0 self.attr = System.Console.ForegroundColor self.saveattr = winattr[str(System.Console.ForegroundColor).lower()] self.savebg = System.Console.BackgroundColor log('initial attr=%s' % self.attr)
def input (self, keytext): if keytext[0:1] == u'"' and keytext[-1:] == u'"': lst_key = [u'"%s"' % c for c in keytext[1:-1]] else: lst_key = [keytext] for key in lst_key: keyinfo, event = keytext_to_keyinfo_and_event (key) dispatch_func = self.key_dispatch.get(keyinfo.tuple(),self.self_insert) self.tested_commands[dispatch_func.__name__]=dispatch_func log(u"keydisp: %s %s"%( key,dispatch_func.__name__)) dispatch_func (event) self.previous_func=dispatch_func
def getKey(self, timeout=365*24*3600): if not self.sequence: self._updateSequence(timeout) if not self.sequence: return None log("Key Sequence len=%d" % len(self.sequence)) res = convertSequence(self.sequence, offset=self.offset) self.offset = res[1] if self.offset == len(self.sequence): self._resetSequence() return res[0]
def getkeypress(self): """Return next key press event from the queue, ignoring others.""" while 1: e = self.get() if e.type == "KeyPress" and e.keycode not in key_modifiers: log("console.getkeypress %s" % e) if e.keyinfo.keyname == "next": self.scroll_window(12) elif e.keyinfo.keyname == "prior": self.scroll_window(-12) else: return e elif (e.type == "KeyRelease") and ( e.keyinfo == KeyPress("S", False, True, False, "S") ): log("getKeypress:%s,%s,%s" % (e.keyinfo, e.keycode, e.type)) return e
def paste_mulitline_code(self,e): """Paste windows clipboard as multiline code. Removes any empty lines in the code""" 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 self.finalize()
def _process_incremental_search_keyevent(self, keyinfo): log("_process_incremental_search_keyevent") keytuple = keyinfo.tuple() #dispatch_func = self.key_dispatch.get(keytuple, default) revtuples = [] fwdtuples = [] for ktuple, func in self.key_dispatch.iteritems(): if func == self.reverse_search_history: revtuples.append(ktuple) elif func == self.forward_search_history: fwdtuples.append(ktuple) log(u"IncrementalSearchPromptMode %s %s" % (keyinfo, keytuple)) if keyinfo.keyname == u'backspace': self.subsearch_query = self.subsearch_query[:-1] if len(self.subsearch_query) > 0: self.line = self.subsearch_fun(self.subsearch_query) else: self._bell() self.line = "" # empty query means no search result elif keyinfo.keyname in [u'return', u'escape']: self._bell() self.prompt = self.subsearch_oldprompt self.process_keyevent_queue = self.process_keyevent_queue[:-1] self._history.history_cursor = len(self._history.history) if keyinfo.keyname == u'escape': self.l_buffer.set_line(self.subsearch_old_line) return True elif keyinfo.keyname: pass elif keytuple in revtuples: self.subsearch_fun = self._history.reverse_search_history self.subsearch_prompt = u"reverse-i-search%d`%s': " self.line = self.subsearch_fun(self.subsearch_query) elif keytuple in fwdtuples: self.subsearch_fun = self._history.forward_search_history self.subsearch_prompt = u"forward-i-search%d`%s': " self.line = self.subsearch_fun(self.subsearch_query) elif keyinfo.control == False and keyinfo.meta == False: self.subsearch_query += keyinfo.char self.line = self.subsearch_fun(self.subsearch_query) else: pass self.prompt = self.subsearch_prompt % (self._history.history_cursor, self.subsearch_query) self.l_buffer.set_line(self.line)
def _init_digit_argument(self, keyinfo): """Initialize search prompt """ c = self.console line = self.l_buffer.get_line_text() self._digit_argument_oldprompt = self.prompt queue = self.process_keyevent_queue queue = self.process_keyevent_queue queue.append(self._process_digit_argument_keyevent) if keyinfo.char == "-": self.argument = -1 elif keyinfo.char in "0123456789": self.argument = int(keyinfo.char) log("<%s> %s" % (self.argument, type(self.argument))) self.prompt = "(arg: %s) " % self.argument log("arg-init %s %s" % (self.argument, keyinfo.char))
def __init__(self, newbuffer=0): '''Initialize the Console object. newbuffer=1 will allocate a new buffer so the old content will be restored on exit. ''' #Do I need the following line? It causes a console to be created whenever #readline is imported into a pythonw application which seems wrong. Things #seem to work without it... #self.AllocConsole() if newbuffer: self.hout = self.CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, 0, None, 1, None) self.SetConsoleActiveScreenBuffer(self.hout) else: self.hout = self.GetStdHandle(STD_OUTPUT_HANDLE) self.hin = self.GetStdHandle(STD_INPUT_HANDLE) self.inmode = DWORD(0) self.GetConsoleMode(self.hin, byref(self.inmode)) self.SetConsoleMode(self.hin, 0xf) info = CONSOLE_SCREEN_BUFFER_INFO() self.GetConsoleScreenBufferInfo(self.hout, byref(info)) self.attr = info.wAttributes self.saveattr = info.wAttributes # remember the initial colors self.defaultstate = AnsiState() self.defaultstate.winattr = info.wAttributes self.ansiwriter = AnsiWriter(self.defaultstate) background = self.attr & 0xf0 for escape in self.escape_to_color: if self.escape_to_color[escape] is not None: self.escape_to_color[escape] |= background log('initial attr=%x' % self.attr) self.softspace = 0 # this is for using it as a file-like object self.serial = 0 self.pythondll = \ CDLL('python%s%s' % (sys.version[0], sys.version[2])) self.pythondll.PyMem_Malloc.restype = c_size_t self.pythondll.PyMem_Malloc.argtypes = [c_size_t] self.inputHookPtr = \ c_void_p.from_address(addressof(self.pythondll.PyOS_InputHook)).value setattr(Console, 'PyMem_Malloc', self.pythondll.PyMem_Malloc)
def write_plain(self, text, attr=None): """Write text at current cursor position.""" text = ensure_unicode(text) log('write("%s", %s)' % (text, attr)) if attr is None: attr = self.attr junk = DWORD(0) self.SetConsoleTextAttribute(self.hout, attr) for short_chunk in split_block(chunk): self.WriteConsoleW( self.hout, ensure_unicode(short_chunk), len(short_chunk), byref(junk), None, ) return len(text)
def pos(self, x=None, y=None): log(u"ansi_console.pos(%s, %s)" % (x, y)) if x is not None and y is not None: self._setPos(x, y) return (x, y) else: (oldX, oldY) = self._queryPos() if x is not None or y is not None: needToUpdate = True else: needToUpdate = False if x is None: x = oldX if y is None: y = oldY if needToUpdate: self._setPos(x, y) return (x, y)
def readline(self, prompt=""): """Prompt the user for a line of text. Clear the console as necessary, redraw according to user actions and report exceptions as they're raised. """ # handle startup_hook if self.first_prompt: self.first_prompt = False if self.startup_hook: try: self.startup_hook() except: print("startup hook failed") traceback.print_exc() c = self.console self.l_buffer.reset_line() self.prompt = prompt self._print_prompt() if self.pre_input_hook: try: self.pre_input_hook() except: print("pre_input_hook failed") traceback.print_exc() self.pre_input_hook = None 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._update_line() self.paste_line_buffer = self.paste_line_buffer[1:] c.write("\r\n") else: self._readline_from_keyboard() c.write("\r\n") self.add_history(self.l_buffer.copy()) log("returning(%s)" % self.l_buffer.get_line_text()) return self.l_buffer.get_line_text() + "\n"
def _readline_from_keyboard_poll(self): c = self.console def nop(e): pass try: event = c.getkeypress() except KeyboardInterrupt: event = self.handle_ctrl_c() self._update_line() return False if self.next_meta: self.next_meta = False control, meta, shift, code = event.keyinfo event.keyinfo = (control, True, shift, code) #Process exit keys. Only exit on empty line keyinfo = event.keyinfo.tuple() if keyinfo in self.exit_dispatch: if lineobj.EndOfLine(self.l_buffer) == 0: raise EOFError if len(keyinfo[-1]) > 1: default = nop else: default = self.self_insert dispatch_func = self.key_dispatch.get(keyinfo, default) log("readline from keyboard:%s,%s" % (keyinfo, dispatch_func)) log_sock("%s|%s" % (format(keyinfo), dispatch_func.__name__), "bound_function") r = None if dispatch_func: r = dispatch_func(event) self._keylog(dispatch_func, self.l_buffer) self.l_buffer.push_undo() self.previous_func = dispatch_func self._update_line() if r: return True return False
def process_keyevent(self, keyinfo): def nop(e): pass keytuple = keyinfo.tuple() 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
def _init_incremental_search(self, searchfun, init_event): log('init_incremental_search') self.subsearch_query = '' self.subsearch_fun = searchfun self.subsearch_old_line = self.l_buffer.get_line_text() queue = self.process_keyevent_queue queue.append(self._process_incremental_search_keyevent) self.subsearch_oldprompt = self.prompt if self.previous_func != self.reverse_search_history and self.previous_func != self.forward_search_history: self.subsearch_query = self.l_buffer[0:Point].get_line_text() if self.subsearch_fun == self.reverse_search_history: self.subsearch_prompt = "reverse-i-search%d`%s': " else: self.subsearch_prompt = "forward-i-search%d`%s': " self.prompt = self.subsearch_prompt % (self._history.history_cursor, '') if self.subsearch_query: self.line = self._process_incremental_search_keyevent(init_event) else: self.line = ''
def _process_incremental_search_keyevent(self, keyinfo): log('_process_incremental_search_keyevent') keytuple = keyinfo.tuple() revtuples = [] fwdtuples = [] for ktuple, func in self.key_dispatch.iteritems(): if func == self.reverse_search_history: revtuples.append(ktuple) elif func == self.forward_search_history: fwdtuples.append(ktuple) log('IncrementalSearchPromptMode %s %s' % (keyinfo, keytuple)) if keyinfo.keyname == 'backspace': self.subsearch_query = self.subsearch_query[:-1] if len(self.subsearch_query) > 0: self.line = self.subsearch_fun(self.subsearch_query) else: self._bell() self.line = '' else: if keyinfo.keyname in ('return', 'escape'): self._bell() self.prompt = self.subsearch_oldprompt self.process_keyevent_queue = self.process_keyevent_queue[:-1] self._history.history_cursor = len(self._history.history) if keyinfo.keyname == 'escape': self.l_buffer.set_line(self.subsearch_old_line) return True if keyinfo.keyname: pass elif keytuple in revtuples: self.subsearch_fun = self._history.reverse_search_history self.subsearch_prompt = "reverse-i-search%d`%s': " self.line = self.subsearch_fun(self.subsearch_query) elif keytuple in fwdtuples: self.subsearch_fun = self._history.forward_search_history self.subsearch_prompt = "forward-i-search%d`%s': " self.line = self.subsearch_fun(self.subsearch_query) elif keyinfo.control == False and keyinfo.meta == False: self.subsearch_query += keyinfo.char self.line = self.subsearch_fun(self.subsearch_query) self.prompt = self.subsearch_prompt % (self._history.history_cursor, self.subsearch_query) self.l_buffer.set_line(self.line)
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 = ensure_str(''.join(buf[self.begidx:self.endidx])) log('complete text="%s"' % ensure_unicode(text)) i = 0 while 1: try: r = self.completer(ensure_unicode(text), i) except IndexError: break i += 1 if r is None: break elif r and r not in completions: completions.append(r) else: pass log('text completions=<%s>' % list(map(ensure_unicode, completions))) if (self.complete_filesystem == "on") and 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 = ensure_str(''.join(buf[self.begidx:self.endidx])) log('file complete text="%s"' % ensure_unicode(text)) completions = list( map(ensure_unicode, glob.glob(os.path.expanduser(text) + '*'.encode('ascii')))) 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>' % list(map(ensure_unicode, completions))) return completions
def readline(self, prompt=''): '''Try to act like GNU readline.''' # handle startup_hook self.ctrl_c_timeout = time.time() self.l_buffer.selection_mark = -1 if self.first_prompt: self.first_prompt = False if self.startup_hook: try: self.startup_hook() except: print 'startup hook failed' traceback.print_exc() c = self.console self.l_buffer.reset_line() self.prompt = prompt self._print_prompt() if self.pre_input_hook: try: self.pre_input_hook() except: print 'pre_input_hook failed' traceback.print_exc() self.pre_input_hook = None 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._update_line() self.paste_line_buffer = self.paste_line_buffer[1:] c.write('\r\n') else: self._readline_from_keyboard() c.write('\r\n') self.add_history(self.l_buffer.copy()) log('returning(%s)' % self.l_buffer.get_line_text()) return self.l_buffer.get_line_text() + '\n'
def readline(self, prompt=""): """Callback that returns after every line is sent by the user. Returns ------- `get_line_buffer` with a newline after. Notes ----- Updates 'ctrl_c_timeout'. """ self.readline_setup(prompt) self._readline_from_keyboard() self.console.write("\r\n") line = self.get_line_buffer() if line != "": log("Returning. Line Buffer: (%s) " % line) return line + "\n"
def handle_ctrl_c(self): from pyreadline.keysyms.common import KeyPress from pyreadline.console.event import Event log("KBDIRQ") event = Event(0, 0) event.char = "c" event.keyinfo = KeyPress( "c", shift=False, control=True, meta=False, keyname=None ) if self.allow_ctrl_c: now = time.time() if (now - self.ctrl_c_timeout) < self.ctrl_c_tap_time_interval: log("Raise KeyboardInterrupt") raise KeyboardInterrupt else: self.ctrl_c_timeout = now else: raise KeyboardInterrupt return event
def _bind_key(self, key, func): """Setup the mapping from key to call the function. Parameters ---------- func : function Must be callable. Raises ------ ReadlineError """ if not callable(func): print("Trying to bind non method to keystroke:%s,%s" % (key, func)) raise ReadlineError( "Trying to bind non method to keystroke:%s,%s,%s,%s" % (key, func, type(func), type(self._bind_key)) ) keyinfo = make_KeyPress_from_keydescr(key.lower()).tuple() log(">>>%s -> %s<<<" % (keyinfo, func.__name__), logging.DEBUG) self.key_dispatch[keyinfo] = func
def scroll_window(self, lines): '''Scroll the window by the indicated number of lines.''' info = CONSOLE_SCREEN_BUFFER_INFO() self.GetConsoleScreenBufferInfo(self.hout, byref(info)) rect = info.srWindow log('sw: rtop=%d rbot=%d' % (rect.Top, rect.Bottom)) top = rect.Top + lines bot = rect.Bottom + lines h = bot - top maxbot = info.dwSize.Y - 1 if top < 0: top = 0 bot = h if bot > maxbot: bot = maxbot top = bot - h nrect = SMALL_RECT() nrect.Top = top nrect.Bottom = bot nrect.Left = rect.Left nrect.Right = rect.Right log('sn: top=%d bot=%d' % (top, bot)) r = self.SetConsoleWindowInfo(self.hout, True, byref(nrect)) log('r=%d' % r)
def write_color(self, text, attr=None): """write text at current cursor position and interpret color escapes. return the number of characters written. """ log('write_color("%s", %s)' % (text, attr)) chunks = self.terminal_escape.split(text) log("chunks=%s" % repr(chunks)) bg = self.savebg n = 0 # count the characters we actually write, omitting the escapes if attr is None: # use attribute from initial console attr = self.attr try: fg = self.trtable[(0x000F & attr)] bg = self.trtable[(0x00F0 & attr) >> 4] except TypeError: fg = attr for chunk in chunks: m = self.escape_parts.match(chunk) if m: log(m.group(1)) attr = ansicolor.get(m.group(1), self.attr) n += len(chunk) System.Console.ForegroundColor = fg System.Console.BackgroundColor = bg System.Console.Write(chunk) return n
def _process_keyevent(self, keyinfo): log('_process_keyevent <%s>' % keyinfo) def nop(e): pass if self.next_meta: self.next_meta = False keyinfo.meta = True keytuple = keyinfo.tuple() if self._insert_verbatim: self.insert_text(keyinfo) self._insert_verbatim = False self.argument = 0 return False if keytuple in self.exit_dispatch: pars = ( self.l_buffer, lineobj.EndOfLine(self.l_buffer)) log('exit_dispatch:<%s, %s>' % pars) if lineobj.EndOfLine(self.l_buffer) == 0: raise EOFError if keyinfo.keyname or keyinfo.control or keyinfo.meta: default = nop else: default = self.self_insert dispatch_func = self.key_dispatch.get(keytuple, default) log('readline from keyboard:<%s,%s>' % (keytuple, dispatch_func)) r = None if dispatch_func: r = dispatch_func(keyinfo) self._keylog(dispatch_func, self.l_buffer) self.l_buffer.push_undo() self.previous_func = dispatch_func return r
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 = ensure_str(''.join(buf[self.begidx:self.endidx])) log('complete text="%s"' % text) i = 0 while 1: try: r = ensure_unicode(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 = ensure_str(''.join(buf[self.begidx:self.endidx])) log('file complete text="%s"' % text) completions = map(ensure_unicode, glob.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 debug_output(self, on, filename="pyreadline_debug_log.txt"): """Initialize the loggers used through the repository. Parameters ---------- on : str One of 'on' or 'on_nologfile'. If is any other value, the global logger instance will be stopped. filename : str (pathlike) Path of the logfile """ if on in ["on", "on_nologfile"]: self.debug = True if on == "on": logger.start_file_log(filename) logger.start_socket_log() logger.log("STARTING LOG") elif on == "on_nologfile": logger.start_socket_log() logger.log("STARTING LOG") else: logger.log("STOPING LOG") logger.stop_file_log() logger.stop_socket_log()
def readline(self, prompt=""): """Try to act like GNU readline.""" # handle startup_hook if self.first_prompt: self.first_prompt = False if self.startup_hook: try: self.startup_hook() except: print "startup hook failed" traceback.print_exc() c = self.console self.l_buffer.reset_line() self.prompt = prompt self._print_prompt() if self.pre_input_hook: try: self.pre_input_hook() except: print "pre_input_hook failed" traceback.print_exc() self.pre_input_hook = None 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._update_line() self.paste_line_buffer = self.paste_line_buffer[1:] c.write("\r\n") else: self._readline_from_keyboard() c.write("\r\n") self.add_history(self.l_buffer.copy()) log("returning(%s)" % self.l_buffer.get_line_text()) return self.l_buffer.get_line_text() + "\n"
def parse_and_bind(self, string): """Parse and execute single line of a readline init file.""" log('parse_and_bind("%s")' % string, logging.DEBUG) if string.startswith("#"): return try: if string.startswith("set"): m = re.compile(r"set\s+([-a-zA-Z0-9]+)\s+(.+)\s*$").match(string) if m: var_name = m.group(1) val = m.group(2) try: setattr(self.mode, var_name.replace("-", "_"), val) except AttributeError: log('unknown var="%s" val="%s"' % (var_name, val)) else: log('bad set "%s"' % string) return m = re.compile(r"\s*(.+)\s*:\s*([-a-zA-Z]+)\s*$").match(string) if m: key = m.group(1) func_name = m.group(2) py_name = func_name.replace("-", "_") try: func = getattr(self.mode, py_name) except AttributeError: log('unknown func key="%s" func="%s"' % (key, func_name)) if self.debug: print( 'pyreadline parse_and_bind error, unknown function to bind: "%s"' % func_name ) return self.mode._bind_key(key, func) except Exception: log("error") raise
def reverse_search_history(self, searchfor, startpos=None): if startpos is None: startpos = self.history_cursor origpos = startpos result = lineobj.ReadLineTextBuffer("") find_pos = -1 for idx, line in list(enumerate(self.history))[startpos:0:-1]: find_pos = line.find(searchfor) if find_pos >= 0: startpos = idx break #If we get a new search without change in search term it means #someone pushed ctrl-r and we should find the next match if self.last_search_for == searchfor and startpos > 0: startpos -= 1 for idx, line in list(enumerate(self.history))[startpos:0:-1]: find_pos = line.find(searchfor) if find_pos >= 0: startpos = idx break if self.history: result = self.history[startpos] if find_pos >= 0: result = copy.copy(result) result.point = find_pos else: result = "" self.history_cursor = startpos self.last_search_for = searchfor log("reverse_search_history: old:%d new:%d result:%r" % (origpos, self.history_cursor, result)) return result
def test_complete(self): import rlcompleter logger.sock_silent = False log("-" * 50) r = EmacsModeTest() r.completer = rlcompleter.Completer().complete r._bind_key("tab", r.complete) r.input(u'"exi(ksdjksjd)"') r.input(u'Control-a') r.input(u'Right') r.input(u'Right') r.input(u'Right') r.input(u'Tab') self.assert_line(r, "exit(ksdjksjd)", 4) r.input(u'Escape') r.input(u'"exi"') r.input(u'Control-a') r.input(u'Right') r.input(u'Right') r.input(u'Right') r.input(u'Tab') self.assert_line(r, "exit", 4)
def size(self): log("size()") if self.windowSize: log("# returned cached value %s" % str(self.windowSize)) return self.windowSize w = self._readIntFromCmd("tput cols") h = self._readIntFromCmd("tput lines") self.windowSize = (w, h) log("size() # = " + str(self.windowSize)) return (w, h)
def debug_output(on, filename='pyreadline_debug_log.txt'): if on in ('on', 'on_nologfile'): self.debug = True if on == 'on': logger.start_file_log(filename) logger.start_socket_log() logger.log('STARTING LOG') elif on == 'on_nologfile': logger.start_socket_log() logger.log('STARTING LOG') else: logger.log('STOPING LOG') logger.stop_file_log() logger.stop_socket_log()
def debug_output(on, filename=u"pyreadline_debug_log.txt"): #Not implemented yet if on in [u"on", u"on_nologfile"]: self.debug=True if on == "on": logger.start_file_log(filename) logger.start_socket_log() logger.log(u"STARTING LOG") elif on == u"on_nologfile": logger.start_socket_log() logger.log(u"STARTING LOG") else: logger.log(u"STOPING LOG") logger.stop_file_log() logger.stop_socket_log()
def _process_digit_argument_keyevent(self, keyinfo): log('DigitArgumentMode.keyinfo %s' % keyinfo) keytuple = keyinfo.tuple() log('DigitArgumentMode.keytuple %s %s' % (keyinfo, keytuple)) if keyinfo.keyname in ('return', ): self.prompt = self._digit_argument_oldprompt self.process_keyevent_queue = self.process_keyevent_queue[:-1] return True if keyinfo.keyname: pass elif keyinfo.char in '0123456789' and keyinfo.control == False and keyinfo.meta == False: log('arg %s %s' % (self.argument, keyinfo.char)) self.argument = self.argument * 10 + int(keyinfo.char) else: self.prompt = self._digit_argument_oldprompt raise LeaveModeTryNext self.prompt = '(arg: %s) ' % self.argument