pyreadline-refactor: improved support for ironpython. Seems quite ok now. Also another bug fix in history search

This commit is contained in:
jstenar
2006-11-03 22:39:12 +00:00
parent 6dc285c0db
commit ccc2c2b175
11 changed files with 223 additions and 79 deletions
+9 -5
View File
@@ -1,15 +1,19 @@
import sys
success=False
in_ironpython=sys.version.startswith("IronPython")
try:
from clipboard import GetClipboardText,SetClipboardText
success=True
except ImportError:
if in_ironpython:
try:
from ironpython_clipboard import GetClipboardText,SetClipboardText
success=True
except ImportError:
pass
else:
try:
from clipboard import GetClipboardText,SetClipboardText
success=True
except ImportError:
pass
+9 -6
View File
@@ -1,15 +1,18 @@
import glob
import glob,sys
success=False
in_ironpython=sys.version.startswith("IronPython")
try:
from console import *
success=True
except ImportError:
if in_ironpython:
try:
from ironpython_console import *
success=True
except ImportError:
raise
else:
try:
from console import *
success=True
except ImportError:
pass
+17 -6
View File
@@ -209,6 +209,14 @@ class Console(object):
self.SetConsoleMode(self.hin, self.inmode)
self.FreeConsole()
def _get_top_bot(self):
info = CONSOLE_SCREEN_BUFFER_INFO()
self.GetConsoleScreenBufferInfo(self.hout, byref(info))
rect = info.srWindow
top = rect.Top
bot = rect.Bottom
return top,bot
def fixcoord(self, x, y):
'''Return a long with x and y packed inside, also handle negative x and y.'''
if x < 0 or y < 0:
@@ -279,7 +287,6 @@ class Console(object):
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:
@@ -344,12 +351,9 @@ class Console(object):
return n
def write_color(self, text, attr=None):
log_sock(text)
log_sock("%s"%attr)
n,res= self.ansiwriter.write_color(text,attr)
junk = c_int(0)
for attr,chunk in res:
log_sock("%s:%s"%(attr,chunk))
log(str(attr))
log(str(chunk))
self.SetConsoleTextAttribute(self.hout, attr.winattr)
@@ -409,8 +413,17 @@ class Console(object):
self.WriteConsoleOutputCharacterA(self.hout, text, len(text), pos, byref(n))
self.FillConsoleOutputAttribute(self.hout, attr, n, pos, byref(n))
def clear_to_end_of_window(self):
top,bot=self._get_top_bot()
pos=self.pos()
w,h=self.size()
self.rectangle( (pos[0],pos[1],w,pos[1]+1))
if pos[1]<bot:
self.rectangle((0,pos[1]+1,w,bot+1))
def rectangle(self, rect, attr=None, fill=' '):
'''Fill Rectangle.'''
log_sock("rect:%s"%[rect])
x0, y0, x1, y1 = rect
n = c_int(0)
if attr is None:
@@ -445,7 +458,6 @@ class Console(object):
bot = rect.Bottom + lines
h = bot - top
maxbot = info.dwSize.Y-1
log('sw: lines=%d mb=%d top=%d bot=%d' % (lines,maxbot,top,bot))
if top < 0:
top = 0
bot = h
@@ -491,7 +503,6 @@ class Console(object):
return e
elif e.type == 'KeyRelease' and e.keyinfo==(True, False, False, 83):
log("getKeypress:%s,%s,%s"%(e.keyinfo,e.keycode,e.type))
# log_sock(str(e))
return e
def getchar(self):
+102 -17
View File
@@ -9,6 +9,21 @@
'''Cursor control and color for the .NET console.
'''
#
# Ironpython requires a patch to work do:
#
# In file PythonCommandLine.cs patch line:
# class PythonCommandLine
# {
# to:
# public class PythonCommandLine
# {
#
#
#
# primitive debug printing that won't interfere with the screen
import clr
@@ -23,14 +38,14 @@ import os
import System
from event import Event
from pyreadline.logger import log
from pyreadline.logger import log,log_sock
#print "Codepage",System.Console.InputEncoding.CodePage
from pyreadline.keysyms import make_keysym, make_keyinfo,make_KeyPress
from pyreadline.keysyms import make_keysym, make_keyinfo,make_KeyPress,make_KeyPress_from_keydescr
from pyreadline.console.ansi import AnsiState
color=System.ConsoleColor
ansicolor={
ansicolor={"0;30": color.Black,
"0;31": color.DarkRed,
"0;32": color.DarkGreen,
"0;33": color.DarkYellow,
@@ -38,6 +53,7 @@ ansicolor={
"0;35": color.DarkMagenta,
"0;36": color.DarkCyan,
"0;37": color.DarkGray,
"1;30": color.Gray,
"1;31": color.Red,
"1;32": color.Green,
"1;33": color.Yellow,
@@ -47,6 +63,15 @@ ansicolor={
"1;37": color.White
}
winattr={"black":0,"darkgray":0+8,
"darkred":4,"red":4+8,
"darkgreen":2,"green":2+8,
"darkyellow":6,"yellow":6+8,
"darkblue":1,"blue":1+8,
"darkmagenta":5, "magenta":5+8,
"darkcyan":3,"cyan":3+8,
"gray":7,"white":7+8}
class Console(object):
'''Console driver for Windows.
@@ -60,15 +85,25 @@ class Console(object):
'''
self.serial=0
self.attr = System.Console.ForegroundColor
self.saveattr = System.Console.ForegroundColor
self.saveattr = winattr[str(System.Console.ForegroundColor).lower()]
self.savebg=System.Console.BackgroundColor
log('initial attr=%s' % self.attr)
log_sock("%s"%self.saveattr)
def _get(self):
top=System.Console.WindowTop
log_sock("WindowTop:%s"%top,"console")
return top
def _set(self,value):
top=System.Console.WindowTop
log_sock("Set WindowTop:old:%s,new:%s"%(top,value),"console")
WindowTop=property(_get,_set)
del _get,_set
def __del__(self):
'''Cleanup the console when finished.'''
# I don't think this ever gets called
self.SetConsoleTextAttribute(self.hout, self.saveattr)
self.SetConsoleMode(self.hin, self.inmode)
self.FreeConsole()
pass
def pos(self, x=None, y=None):
'''Move or query the window cursor.'''
@@ -153,6 +188,11 @@ class Console(object):
y = h - 1
return scroll
trtable={0:color.Black,4:color.DarkRed,2:color.DarkGreen,6:color.DarkYellow,
1:color.DarkBlue,5:color.DarkMagenta,3:color.DarkCyan,7:color.Gray,
8:color.DarkGray,4+8:color.Red,2+8:color.Green,6+8:color.Yellow,
1+8:color.Blue,5+8:color.Magenta,3+8:color.Cyan,7+8:color.White}
def write_color(self, text, attr=None):
'''write text at current cursor position and interpret color escapes.
@@ -161,17 +201,24 @@ class Console(object):
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)
log('attr=%s' % attr)
System.Console.ForegroundColor=attr
System.Console.ForegroundColor=fg
System.Console.BackgroundColor=bg
#self.WriteConsoleA(self.hout, chunk, len(chunk), byref(junk), None)
System.Console.Write(chunk)
return n
@@ -215,9 +262,21 @@ class Console(object):
self.pos(x,y)
self.write_color(text,attr)
def clear_to_end_of_window(self):
oldtop=self.WindowTop
lastline=self.WindowTop+System.Console.WindowHeight
pos=self.pos()
w,h=self.size()
length=w-pos[0]+min((lastline-pos[1]-1),5)*w-1
self.write_color(length*" ")
self.pos(*pos)
self.WindowTop=oldtop
def rectangle(self, rect, attr=None, fill=' '):
'''Fill Rectangle.'''
pass
oldtop=self.WindowTop
oldpos=self.pos()
#raise NotImplementedError
x0, y0, x1, y1 = rect
if attr is None:
@@ -229,6 +288,7 @@ class Console(object):
for y in range(y0, y1):
System.Console.SetCursorPosition(x0,y)
self.write_color(rowfill,attr)
self.pos(*oldpos)
def scroll(self, rect, dx, dy, attr=None, fill=' '):
'''Scroll a rectangle.'''
@@ -237,23 +297,28 @@ class Console(object):
def scroll_window(self, lines):
'''Scroll the window by the indicated number of lines.'''
top=System.Console.WindowTop+lines
top=self.WindowTop+lines
if top<0:
top=0
if top+System.Console.WindowHeight>System.Console.BufferHeight:
top=System.Console.BufferHeight
System.Console.WindowTop=top
self.WindowTop=top
def getkeypress(self):
'''Return next key press event from the queue, ignoring others.'''
ck=System.ConsoleKey
while 1:
e = System.Console.ReadKey(True)
try:
e = System.Console.ReadKey(True)
except KeyboardInterrupt:
return CTRL_C_EVENT
if e.Key == System.ConsoleKey.PageDown: #PageDown
self.scroll_window(12)
elif e.Key == System.ConsoleKey.PageUp:#PageUp
self.scroll_window(-12)
elif str(e.KeyChar)=="\000":#Drop deadkeys
log_sock("Deadkey: %s"%e)
return event(self,e)
pass
else:
return event(self,e)
@@ -268,10 +333,17 @@ class Console(object):
def size(self, width=None, height=None):
'''Set/get window size.'''
sc=System.Console
if width is not None and height is not None:
sc.BufferWidth,sc.BufferHeight=width,height
else:
return sc.BufferWidth,sc.BufferHeight
if width is not None and height is not None:
sc.WindowWidth,sc.WindowHeight=width,height
else:
return sc.WindowWidth,sc.WindowHeight
return sc.WindowWidth-1,sc.WindowHeight-1
def cursor(self, visible=True, size=None):
'''Set cursor on or off.'''
@@ -298,12 +370,25 @@ class event(Event):
self.char = str(input.KeyChar)
self.keycode = input.Key
self.state = input.Modifiers
log_sock("%s,%s,%s"%(input.Modifiers,input.Key,input.KeyChar),"console")
self.type="KeyRelease"
self.keysym = make_keysym(self.keycode)
self.keyinfo = make_KeyPress(self.char, self.state, self.keycode)
def make_event_from_keydescr(keydescr):
def input():
return 1
input.KeyChar="a"
input.Key=System.ConsoleKey.A
input.Modifiers=System.ConsoleModifiers.Shift
input.next_serial=input
e=event(input,input)
del input.next_serial
keyinfo=make_KeyPress_from_keydescr(keydescr)
e.keyinfo=keyinfo
return e
CTRL_C_EVENT=make_event_from_keydescr("Control-c")
def install_readline(hook):
class IronPythonWrapper(IronPythonConsole.IConsole):
@@ -343,4 +428,4 @@ if __name__ == '__main__':
print e.Key,chr(e.KeyChar),ord(e.KeyChar),e.Modifiers
del c
System.Console.Clear()
System.Console.Clear()
+6 -2
View File
@@ -8,7 +8,7 @@
#*****************************************************************************
import System
from common import validkey,KeyPress,make_KeyPress_from_keydescr
from pyreadline.logger import log_sock
c32=System.ConsoleKey
Shift=System.ConsoleModifiers.Shift
Control=System.ConsoleModifiers.Control
@@ -196,7 +196,11 @@ def make_KeyPress(char,state,keycode):
control=bool(int(state)&int(Control))
meta=bool(int(state)&int(Alt))
keyname=code2sym_map.get(keycode,"").lower()
if control:
log_sock("make key %s %s %s %s"%(shift,control,meta,keycode),"keysyms")
if control and meta: #equivalent to altgr so clear flags
control=False
meta=False
elif control:
char=str(keycode)
return KeyPress(char,shift,control,meta,keyname)
+34 -28
View File
@@ -58,7 +58,7 @@ class LineHistory(object):
if filename is None:
filename=self.history_filename
try:
for line in open(filename, 'rt'):
for line in open(filename, 'r'):
self.add_history(lineobj.ReadLineTextBuffer(line.rstrip()))
except IOError:
self.history = []
@@ -179,33 +179,38 @@ class LineHistory(object):
return self._non_i_search(1,current)
def _search(self, direction, partial):
if (self.lastcommand != self.history_search_forward and
self.lastcommand != self.history_search_backward):
self.query = ''.join(partial[0:partial.point].get_line_text())
hcstart=max(self.history_cursor,0)
hc = self.history_cursor + direction
while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
h = self.history[hc]
if not self.query:
self.history_cursor = hc
result=lineobj.ReadLineTextBuffer(h,point=len(h.get_line_text()))
return result
elif h.get_line_text().startswith(self.query) and h != partial.get_line_text():
self.history_cursor = hc
result=lineobj.ReadLineTextBuffer(h,point=partial.point)
return result
hc += direction
else:
if len(self.history)==0:
pass
elif hc>=len(self.history) and not self.query:
self.history_cursor=len(self.history)
return lineobj.ReadLineTextBuffer("",point=0)
elif self.history[hcstart].get_line_text().startswith(self.query) and self.query:
return lineobj.ReadLineTextBuffer(self.history[hcstart],point=partial.point)
else:
return lineobj.ReadLineTextBuffer(partial,point=partial.point)
return lineobj.ReadLineTextBuffer(self.query,point=min(len(self.query),partial.point))
try:
if (self.lastcommand != self.history_search_forward and
self.lastcommand != self.history_search_backward):
self.query = ''.join(partial[0:partial.point].get_line_text())
hcstart=max(self.history_cursor,0)
log_sock("hcstart %s"%hcstart)
hc = self.history_cursor + direction
while (direction < 0 and hc >= 0) or (direction > 0 and hc < len(self.history)):
h = self.history[hc]
if not self.query:
self.history_cursor = hc
result=lineobj.ReadLineTextBuffer(h,point=len(h.get_line_text()))
return result
elif h.get_line_text().startswith(self.query) and h != partial.get_line_text():
self.history_cursor = hc
result=lineobj.ReadLineTextBuffer(h,point=partial.point)
return result
hc += direction
else:
if len(self.history)==0:
pass
elif hc>=len(self.history) and not self.query:
self.history_cursor=len(self.history)
return lineobj.ReadLineTextBuffer("",point=0)
elif self.history[max(min(hcstart,len(self.history)-1),0)].get_line_text().startswith(self.query) and self.query:
return lineobj.ReadLineTextBuffer(self.history[max(min(hcstart,len(self.history)-1),0)],point=partial.point)
else:
return lineobj.ReadLineTextBuffer(partial,point=partial.point)
return lineobj.ReadLineTextBuffer(self.query,point=min(len(self.query),partial.point))
except IndexError:
log_sock("hcstart:%s %s"%(hcstart,len(self.history)))
raise
def history_search_forward(self,partial): # ()
'''Search forward through the history for the string of characters
@@ -218,6 +223,7 @@ class LineHistory(object):
'''Search backward through the history for the string of characters
between the start of the current line and the point. This is a
non-incremental search. By default, this command is unbound.'''
q= self._search(-1,partial)
return q
+2 -3
View File
@@ -26,10 +26,10 @@ host="localhost"
port=8081
logsocket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
show_event=["keypress","bound_function","bind_key"]
show_event=["keypress","bound_function","bind_key","console"]
show_event=["bound_function"]
sock_silent=False
sock_silent=True
def log_sock(s,event_type=None):
if sock_silent:
@@ -43,4 +43,3 @@ def log_sock(s,event_type=None):
pass
log_sock("Starting pyreadline")
+4 -1
View File
@@ -6,7 +6,7 @@
# Distributed under the terms of the BSD License. The full license is in
# the file COPYING, distributed as part of this software.
#*****************************************************************************
import os,re,math,glob
import os,re,math,glob,sys
import pyreadline.logger as logger
from pyreadline.logger import log
from pyreadline.keysyms.common import make_KeyPress_from_keydescr
@@ -14,6 +14,7 @@ import pyreadline.lineeditor.lineobj as lineobj
import pyreadline.lineeditor.history as history
import pyreadline.clipboard as clipboard
from pyreadline.error import ReadlineError,GetSetError
in_ironpython=sys.version.startswith("IronPython")
class BaseMode(object):
mode="base"
@@ -173,6 +174,8 @@ class BaseMode(object):
if i < len(completions):
self.console.write(completions[i].ljust(wmax+1))
self.console.write('\n')
if in_ironpython:
self.prompt=sys.ps1
self._print_prompt()
def complete(self, e): # (TAB)
+11 -3
View File
@@ -6,7 +6,7 @@
# 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,sys
import pyreadline.logger as logger
from pyreadline.logger import log,log_sock
from pyreadline.lineeditor.lineobj import Point
@@ -21,6 +21,8 @@ def format(keyinfo):
k=keyinfo+(ord(keyinfo[-1]),)
return "(%s,%s,%s,%s,%x)"%k
in_ironpython=sys.version.startswith("IronPython")
class EmacsMode(basemode.BaseMode):
mode="emacs"
@@ -28,7 +30,7 @@ class EmacsMode(basemode.BaseMode):
super(EmacsMode,self).__init__(rlobj)
self._keylog=(lambda x,y: None)
self.previous_func=None
self.prompt=""
self.prompt=">>>"
def __repr__(self):
return "<EmacsMode>"
@@ -105,7 +107,13 @@ class EmacsMode(basemode.BaseMode):
self.paste_line_buffer=self.paste_line_buffer[1:]
c.write('\r\n')
else:
self._readline_from_keyboard()
try:
self._readline_from_keyboard()
except EOFError:
if in_ironpython:
return None
else:
raise
c.write('\r\n')
self.add_history(self.l_buffer.copy())
+16 -8
View File
@@ -29,6 +29,14 @@ import release
from modes import editingmodes
in_ironpython=sys.version.startswith("IronPython")
if in_ironpython:#ironpython does not provide a prompt string to readline
import System
default_prompt=">>> "
else:
default_prompt=""
def quote_char(c):
if ord(c)>0:
return c
@@ -47,7 +55,7 @@ class Readline(object):
self.size = self.console.size()
self.prompt_color = None
self.command_color = None
self.selection_color =0x00f0
self.selection_color = self.console.saveattr<<4
self.key_dispatch = {}
self.previous_func = None
self.first_prompt = True
@@ -270,7 +278,7 @@ class Readline(object):
c = self.console
x, y = c.pos()
w, h = c.size()
c.rectangle((x, y, w, y+1))
c.rectangle((x, y, w+1, y+1))
c.rectangle((0, y+1, w, min(y+3,h)))
def _set_cursor(self):
@@ -278,15 +286,15 @@ class Readline(object):
xc, yc = self.prompt_end_pos
w, h = c.size()
xc += self.l_buffer.visible_line_width()
while(xc > w):
while(xc >= w):
xc -= w
yc += 1
c.pos(xc, yc)
def _print_prompt(self):
c = self.console
log('prompt="%s"' % repr(self.prompt))
x, y = c.pos()
n = c.write_scrolling(self.prompt, self.prompt_color)
self.prompt_begin_pos = (x, y - n)
self.prompt_end_pos = c.pos()
@@ -313,9 +321,11 @@ class Readline(object):
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()
if hasattr(c,"clear_to_end_of_window"): #Work around function for ironpython due
c.clear_to_end_of_window() #to System.Console's lack of FillFunction
else:
self._clear_after()
self._set_cursor()
def readline(self, prompt=''):
@@ -329,9 +339,7 @@ class Readline(object):
self.mode=modes[name]
def bind_key(key,name):
log("bind %s %s"%(key,name))
log_sock("bindkey: %s %s"%(key,name),"bind_key")
if hasattr(modes[mode],name):
# print "can bind",key,name
modes[mode]._bind_key(key,getattr(modes[mode],name))
else:
print "Trying to bind unknown command '%s' to key '%s'"%(name,key)
+13
View File
@@ -304,6 +304,19 @@ class TestsHistory (unittest.TestCase):
r.input ('Up')
self.assert_line(r,'ako',3)
def test_history_3 (self):
r = EmacsModeTest ()
r.add_history ('aaaa')
r.add_history ('aaba')
r.add_history ('aaca')
r.add_history ('akca')
r.add_history ('bbb')
r.add_history ('ako')
self.assert_line(r,'',0)
r.input ('k')
r.input ('Up')
self.assert_line(r,'k',1)
def assert_line(self,r,line,cursor):