Updated to new PyCrust
git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@12049 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
This commit is contained in:
@@ -53,7 +53,8 @@ class FillingTree(wxTreeCtrl):
|
|||||||
objtype = type(object)
|
objtype = type(object)
|
||||||
if objtype is types.DictType:
|
if objtype is types.DictType:
|
||||||
dict = object
|
dict = object
|
||||||
elif objtype in (types.InstanceType, types.ModuleType):
|
elif (objtype in (types.InstanceType, types.ModuleType)) \
|
||||||
|
or hasattr(object, '__class__'):
|
||||||
for key in introspect.getAttributeNames(object):
|
for key in introspect.getAttributeNames(object):
|
||||||
# Believe it or not, some attributes can disappear, such as
|
# Believe it or not, some attributes can disappear, such as
|
||||||
# the exc_traceback attribute of the sys module. So this is
|
# the exc_traceback attribute of the sys module. So this is
|
||||||
@@ -170,10 +171,16 @@ if wxPlatform == '__WXMSW__':
|
|||||||
'helv' : 'Lucida Console',
|
'helv' : 'Lucida Console',
|
||||||
'lucida' : 'Lucida Console',
|
'lucida' : 'Lucida Console',
|
||||||
'other' : 'Comic Sans MS',
|
'other' : 'Comic Sans MS',
|
||||||
'size' : 8,
|
'size' : 10,
|
||||||
'lnsize' : 7,
|
'lnsize' : 9,
|
||||||
'backcol': '#FFFFFF',
|
'backcol': '#FFFFFF',
|
||||||
}
|
}
|
||||||
|
# Versions of wxPython prior to 2.3.2 had a sizing bug on Win platform.
|
||||||
|
# The font was 2 points too large. So we need to reduce the font size.
|
||||||
|
if ((wxMAJOR_VERSION, wxMINOR_VERSION) == (2, 3) and wxRELEASE_NUMBER < 2) \
|
||||||
|
or (wxMAJOR_VERSION <= 2 and wxMINOR_VERSION <= 2):
|
||||||
|
faces['size'] -= 2
|
||||||
|
faces['lnsize'] -= 2
|
||||||
else: # GTK
|
else: # GTK
|
||||||
faces = { 'times' : 'Times',
|
faces = { 'times' : 'Times',
|
||||||
'mono' : 'Courier',
|
'mono' : 'Courier',
|
||||||
@@ -299,3 +306,4 @@ class App(wxApp):
|
|||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -48,16 +48,17 @@ class Interpreter(InteractiveInterpreter):
|
|||||||
"""Send command to the interpreter to be executed.
|
"""Send command to the interpreter to be executed.
|
||||||
|
|
||||||
Because this may be called recursively, we append a new list
|
Because this may be called recursively, we append a new list
|
||||||
onto the commandBuffer list and then append commands into
|
onto the commandBuffer list and then append commands into that.
|
||||||
that. If the passed in command is part of a multi-line command
|
If the passed in command is part of a multi-line command we keep
|
||||||
we keep appending the pieces to the last list in commandBuffer
|
appending the pieces to the last list in commandBuffer until we
|
||||||
until we have a complete command, then, finally, we delete
|
have a complete command. If not, we delete that last list."""
|
||||||
that last list."""
|
if not self.more:
|
||||||
|
try: del self.commandBuffer[-1]
|
||||||
|
except IndexError: pass
|
||||||
if not self.more: self.commandBuffer.append([])
|
if not self.more: self.commandBuffer.append([])
|
||||||
self.commandBuffer[-1].append(command)
|
self.commandBuffer[-1].append(command)
|
||||||
source = '\n'.join(self.commandBuffer[-1])
|
source = '\n'.join(self.commandBuffer[-1])
|
||||||
self.more = self.runsource(source)
|
self.more = self.runsource(source)
|
||||||
if not self.more: del self.commandBuffer[-1]
|
|
||||||
return self.more
|
return self.more
|
||||||
|
|
||||||
def runsource(self, source):
|
def runsource(self, source):
|
||||||
@@ -102,3 +103,4 @@ class InterpreterAlaCarte(Interpreter):
|
|||||||
sys.ps1 = ps1
|
sys.ps1 = ps1
|
||||||
sys.ps2 = ps2
|
sys.ps2 = ps2
|
||||||
|
|
||||||
|
|
@@ -49,11 +49,18 @@ def getAllAttributeNames(object):
|
|||||||
Recursively walk through a class and all base classes.
|
Recursively walk through a class and all base classes.
|
||||||
"""
|
"""
|
||||||
attributes = []
|
attributes = []
|
||||||
|
# Wake up sleepy objects - a hack for ZODB objects in "ghost" state.
|
||||||
|
wakeupcall = dir(object)
|
||||||
|
del wakeupcall
|
||||||
# Get attributes available through the normal convention.
|
# Get attributes available through the normal convention.
|
||||||
attributes += dir(object)
|
attributes += dir(object)
|
||||||
# For a class instance, get the attributes for the class.
|
# For a class instance, get the attributes for the class.
|
||||||
if hasattr(object, '__class__'):
|
if hasattr(object, '__class__'):
|
||||||
attributes += getAllAttributeNames(object.__class__)
|
# Break a circular reference. This happens with extension classes.
|
||||||
|
if object.__class__ is object:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
attributes += getAllAttributeNames(object.__class__)
|
||||||
# Also get attributes from any and all parent classes.
|
# Also get attributes from any and all parent classes.
|
||||||
if hasattr(object, '__bases__'):
|
if hasattr(object, '__bases__'):
|
||||||
for base in object.__bases__:
|
for base in object.__bases__:
|
||||||
@@ -74,13 +81,13 @@ def getCallTip(command='', locals=None):
|
|||||||
dropSelf = 0
|
dropSelf = 0
|
||||||
if hasattr(object, '__name__'): # Make sure this is a useable object.
|
if hasattr(object, '__name__'): # Make sure this is a useable object.
|
||||||
# Switch to the object that has the information we need.
|
# Switch to the object that has the information we need.
|
||||||
if inspect.ismethod(object):
|
if inspect.ismethod(object) or hasattr(object, 'im_func'):
|
||||||
# Get the function from the object otherwise inspec.getargspec()
|
# Get the function from the object otherwise inspect.getargspec()
|
||||||
# complains that the object isn't a Python function.
|
# complains that the object isn't a Python function.
|
||||||
object = object.im_func
|
object = object.im_func
|
||||||
dropSelf = 1
|
dropSelf = 1
|
||||||
elif inspect.isclass(object):
|
elif inspect.isclass(object):
|
||||||
# Get the __init__ method for the class.
|
# Get the __init__ method function for the class.
|
||||||
try:
|
try:
|
||||||
object = object.__init__.im_func
|
object = object.__init__.im_func
|
||||||
dropSelf = 1
|
dropSelf = 1
|
||||||
@@ -92,10 +99,11 @@ def getCallTip(command='', locals=None):
|
|||||||
dropSelf = 1
|
dropSelf = 1
|
||||||
break
|
break
|
||||||
name = object.__name__
|
name = object.__name__
|
||||||
|
tip1 = ''
|
||||||
if inspect.isbuiltin(object):
|
if inspect.isbuiltin(object):
|
||||||
# Builtin functions don't have an argspec that we can get.
|
# Builtin functions don't have an argspec that we can get.
|
||||||
tip1 = ''
|
pass
|
||||||
else:
|
elif inspect.isfunction(object):
|
||||||
# tip1 is a string like: "getCallTip(command='', locals=None)"
|
# tip1 is a string like: "getCallTip(command='', locals=None)"
|
||||||
argspec = apply(inspect.formatargspec, inspect.getargspec(object))
|
argspec = apply(inspect.formatargspec, inspect.getargspec(object))
|
||||||
if dropSelf:
|
if dropSelf:
|
||||||
@@ -129,7 +137,7 @@ def getRoot(command, terminator=None):
|
|||||||
|
|
||||||
The command would normally terminate with a "(" or ".". Anything after
|
The command would normally terminate with a "(" or ".". Anything after
|
||||||
the terminator will be dropped, allowing you to get back to the root.
|
the terminator will be dropped, allowing you to get back to the root.
|
||||||
Return only the root portion that can be eval()'d without side effect.
|
Return only the root portion that can be eval()'d without side effects.
|
||||||
"""
|
"""
|
||||||
root = ''
|
root = ''
|
||||||
validChars = "._" + string.uppercase + string.lowercase + string.digits
|
validChars = "._" + string.uppercase + string.lowercase + string.digits
|
||||||
@@ -151,7 +159,7 @@ def getRoot(command, terminator=None):
|
|||||||
# Detect situations where we are in the middle of a string.
|
# Detect situations where we are in the middle of a string.
|
||||||
# This code catches the simplest case, but needs to catch others.
|
# This code catches the simplest case, but needs to catch others.
|
||||||
if command[i-1] in ("'", '"'):
|
if command[i-1] in ("'", '"'):
|
||||||
# Were in the middle of a string so we aren't dealing with an
|
# We're in the middle of a string so we aren't dealing with an
|
||||||
# object and it would be misleading to return anything here.
|
# object and it would be misleading to return anything here.
|
||||||
root = ''
|
root = ''
|
||||||
else:
|
else:
|
||||||
@@ -159,3 +167,4 @@ def getRoot(command, terminator=None):
|
|||||||
root = command[i:]
|
root = command[i:]
|
||||||
return root
|
return root
|
||||||
|
|
||||||
|
|
@@ -25,10 +25,16 @@ if wxPlatform == '__WXMSW__':
|
|||||||
'helv' : 'Lucida Console',
|
'helv' : 'Lucida Console',
|
||||||
'lucida' : 'Lucida Console',
|
'lucida' : 'Lucida Console',
|
||||||
'other' : 'Comic Sans MS',
|
'other' : 'Comic Sans MS',
|
||||||
'size' : 8,
|
'size' : 10,
|
||||||
'lnsize' : 7,
|
'lnsize' : 9,
|
||||||
'backcol': '#FFFFFF',
|
'backcol': '#FFFFFF',
|
||||||
}
|
}
|
||||||
|
# Versions of wxPython prior to 2.3.2 had a sizing bug on Win platform.
|
||||||
|
# The font was 2 points too large. So we need to reduce the font size.
|
||||||
|
if ((wxMAJOR_VERSION, wxMINOR_VERSION) == (2, 3) and wxRELEASE_NUMBER < 2) \
|
||||||
|
or (wxMAJOR_VERSION <= 2 and wxMINOR_VERSION <= 2):
|
||||||
|
faces['size'] -= 2
|
||||||
|
faces['lnsize'] -= 2
|
||||||
else: # GTK
|
else: # GTK
|
||||||
faces = { 'times' : 'Times',
|
faces = { 'times' : 'Times',
|
||||||
'mono' : 'Courier',
|
'mono' : 'Courier',
|
||||||
@@ -40,6 +46,62 @@ else: # GTK
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class ShellFacade:
|
||||||
|
"""Simplified interface to all shell-related functionality.
|
||||||
|
|
||||||
|
This is a semi-transparent facade, in that all attributes of other are
|
||||||
|
still accessible, even though only some are visible to the user."""
|
||||||
|
|
||||||
|
name = 'PyCrust Shell Interface'
|
||||||
|
revision = __version__
|
||||||
|
|
||||||
|
def __init__(self, other):
|
||||||
|
"""Create a ShellFacade instance."""
|
||||||
|
methods = ['ask',
|
||||||
|
'clear',
|
||||||
|
'pause',
|
||||||
|
'prompt',
|
||||||
|
'quit',
|
||||||
|
'redirectStderr',
|
||||||
|
'redirectStdin',
|
||||||
|
'redirectStdout',
|
||||||
|
'run',
|
||||||
|
'runfile',
|
||||||
|
]
|
||||||
|
for method in methods:
|
||||||
|
self.__dict__[method] = getattr(other, method)
|
||||||
|
d = self.__dict__
|
||||||
|
d['other'] = other
|
||||||
|
d['help'] = 'There is no help available, yet.'
|
||||||
|
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if hasattr(self.other, name):
|
||||||
|
return getattr(self.other, name)
|
||||||
|
else:
|
||||||
|
raise AttributeError, name
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
if self.__dict__.has_key(name):
|
||||||
|
self.__dict__[name] = value
|
||||||
|
elif hasattr(self.other, name):
|
||||||
|
return setattr(self.other, name, value)
|
||||||
|
else:
|
||||||
|
raise AttributeError, name
|
||||||
|
|
||||||
|
def _getAttributeNames(self):
|
||||||
|
"""Return list of magic attributes to extend introspection."""
|
||||||
|
list = ['autoCallTip',
|
||||||
|
'autoComplete',
|
||||||
|
'autoCompleteCaseInsensitive',
|
||||||
|
'autoCompleteIncludeDouble',
|
||||||
|
'autoCompleteIncludeMagic',
|
||||||
|
'autoCompleteIncludeSingle',
|
||||||
|
]
|
||||||
|
list.sort()
|
||||||
|
return list
|
||||||
|
|
||||||
|
|
||||||
class Shell(wxStyledTextCtrl):
|
class Shell(wxStyledTextCtrl):
|
||||||
"""PyCrust Shell based on wxStyledTextCtrl."""
|
"""PyCrust Shell based on wxStyledTextCtrl."""
|
||||||
|
|
||||||
@@ -78,26 +140,18 @@ class Shell(wxStyledTextCtrl):
|
|||||||
*args, **kwds)
|
*args, **kwds)
|
||||||
# Keep track of the most recent prompt starting and ending positions.
|
# Keep track of the most recent prompt starting and ending positions.
|
||||||
self.promptPos = [0, 0]
|
self.promptPos = [0, 0]
|
||||||
|
# Keep track of the most recent non-continuation prompt.
|
||||||
|
self.prompt1Pos = [0, 0]
|
||||||
# Keep track of multi-line commands.
|
# Keep track of multi-line commands.
|
||||||
self.more = 0
|
self.more = 0
|
||||||
# Create the command history. Commands are added into the front of
|
# Create the command history. Commands are added into the front of
|
||||||
# the list (ie. at index 0) as they are entered. self.historyPos is
|
# the list (ie. at index 0) as they are entered. self.historyIndex
|
||||||
# the current position in the history; it gets incremented as you
|
# is the current position in the history; it gets incremented as you
|
||||||
# retrieve the previous command, decremented as you retrieve the next,
|
# retrieve the previous command, decremented as you retrieve the
|
||||||
# and reset when you hit Enter. self.historyPos == -1 means you're on
|
# next, and reset when you hit Enter. self.historyIndex == -1 means
|
||||||
# the current command, not in the history. self.tempCommand is
|
# you're on the current command, not in the history.
|
||||||
# storage space for whatever was on the last line when you first hit
|
|
||||||
# "Retrieve-Previous", so that the final "Retrieve-Next" will restore
|
|
||||||
# whatever was originally there. self.lastCommandRecalled remembers
|
|
||||||
# the index of the last command to be recalled from the history, so
|
|
||||||
# you can repeat a group of commands by going up-up-up-enter to find
|
|
||||||
# the first one in the group then down-enter-down-enter to recall each
|
|
||||||
# subsequent command. Also useful for multiline commands, in lieu of
|
|
||||||
# a proper implementation of those.
|
|
||||||
self.history = []
|
self.history = []
|
||||||
self.historyPos = -1
|
self.historyIndex = -1
|
||||||
self.tempCommand = ''
|
|
||||||
self.lastCommandRecalled = -1
|
|
||||||
# Assign handlers for keyboard events.
|
# Assign handlers for keyboard events.
|
||||||
EVT_KEY_DOWN(self, self.OnKeyDown)
|
EVT_KEY_DOWN(self, self.OnKeyDown)
|
||||||
EVT_CHAR(self, self.OnChar)
|
EVT_CHAR(self, self.OnChar)
|
||||||
@@ -176,15 +230,15 @@ class Shell(wxStyledTextCtrl):
|
|||||||
|
|
||||||
# XXX Good enough for now but later we want to send a close event.
|
# XXX Good enough for now but later we want to send a close event.
|
||||||
|
|
||||||
# In the close event handler we can prompt to make sure they want to quit.
|
# In the close event handler we can make sure they want to quit.
|
||||||
# Other applications, like PythonCard, may choose to hide rather than
|
# Other applications, like PythonCard, may choose to hide rather than
|
||||||
# quit so we should just post the event and let the surrounding app
|
# quit so we should just post the event and let the surrounding app
|
||||||
# decide what it wants to do.
|
# decide what it wants to do.
|
||||||
self.write('Click on the close button to leave the application.')
|
self.write('Click on the close button to leave the application.')
|
||||||
|
|
||||||
def setLocalShell(self):
|
def setLocalShell(self):
|
||||||
"""Add 'shell' to locals."""
|
"""Add 'shell' to locals as reference to ShellFacade instance."""
|
||||||
self.interp.locals['shell'] = self
|
self.interp.locals['shell'] = ShellFacade(other=self)
|
||||||
|
|
||||||
def execStartupScript(self, startupScript):
|
def execStartupScript(self, startupScript):
|
||||||
"""Execute the user's PYTHONSTARTUP script if they have one."""
|
"""Execute the user's PYTHONSTARTUP script if they have one."""
|
||||||
@@ -225,90 +279,57 @@ class Shell(wxStyledTextCtrl):
|
|||||||
self.StyleSetSpec(wxSTC_P_COMMENTBLOCK, "fore:#7F7F7F")
|
self.StyleSetSpec(wxSTC_P_COMMENTBLOCK, "fore:#7F7F7F")
|
||||||
self.StyleSetSpec(wxSTC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces)
|
self.StyleSetSpec(wxSTC_P_STRINGEOL, "fore:#000000,face:%(mono)s,back:#E0C0E0,eolfilled" % faces)
|
||||||
|
|
||||||
|
def OnChar(self, event):
|
||||||
|
"""Keypress event handler.
|
||||||
|
|
||||||
|
Prevents modification of previously submitted commands/responses."""
|
||||||
|
key = event.KeyCode()
|
||||||
|
currpos = self.GetCurrentPos()
|
||||||
|
if currpos < self.prompt1Pos[1]:
|
||||||
|
wxBell()
|
||||||
|
return
|
||||||
|
stoppos = self.promptPos[1]
|
||||||
|
if key == ord('.'):
|
||||||
|
# The dot or period key activates auto completion.
|
||||||
|
# Get the command between the prompt and the cursor.
|
||||||
|
# Add a dot to the end of the command.
|
||||||
|
command = self.GetTextRange(stoppos, currpos) + '.'
|
||||||
|
self.write('.')
|
||||||
|
if self.autoComplete: self.autoCompleteShow(command)
|
||||||
|
elif key == ord('('):
|
||||||
|
# The left paren activates a call tip and cancels
|
||||||
|
# an active auto completion.
|
||||||
|
if self.AutoCompActive(): self.AutoCompCancel()
|
||||||
|
# Get the command between the prompt and the cursor.
|
||||||
|
# Add the '(' to the end of the command.
|
||||||
|
command = self.GetTextRange(stoppos, currpos) + '('
|
||||||
|
self.write('(')
|
||||||
|
if self.autoCallTip: self.autoCallTipShow(command)
|
||||||
|
else:
|
||||||
|
# Allow the normal event handling to take place.
|
||||||
|
event.Skip()
|
||||||
|
|
||||||
def OnKeyDown(self, event):
|
def OnKeyDown(self, event):
|
||||||
"""Key down event handler.
|
"""Key down event handler.
|
||||||
|
|
||||||
The main goal here is to not allow modifications to previous
|
Prevents modification of previously submitted commands/responses."""
|
||||||
lines of text."""
|
|
||||||
key = event.KeyCode()
|
key = event.KeyCode()
|
||||||
currpos = self.GetCurrentPos()
|
currpos = self.GetCurrentPos()
|
||||||
stoppos = self.promptPos[1]
|
stoppos = self.promptPos[1]
|
||||||
# If the auto-complete window is up let it do its thing.
|
# If the auto-complete window is up let it do its thing.
|
||||||
if self.AutoCompActive():
|
if self.AutoCompActive():
|
||||||
event.Skip()
|
event.Skip()
|
||||||
# Control+UpArrow steps up through the history.
|
# Retrieve the previous command from the history buffer.
|
||||||
elif key == WXK_UP and event.ControlDown() \
|
elif (event.ControlDown() and key == WXK_UP) \
|
||||||
and self.historyPos < len(self.history) - 1:
|
or (event.AltDown() and key in (ord('P'), ord('p'))):
|
||||||
# Move to the end of the buffer.
|
self.OnHistoryRetrieve(step=+1)
|
||||||
endpos = self.GetTextLength()
|
# Retrieve the next command from the history buffer.
|
||||||
self.SetCurrentPos(endpos)
|
elif (event.ControlDown() and key == WXK_DOWN) \
|
||||||
# The first Control+Up stores the current command;
|
or (event.AltDown() and key in (ord('N'), ord('n'))):
|
||||||
# Control+Down brings it back.
|
self.OnHistoryRetrieve(step=-1)
|
||||||
if self.historyPos == -1:
|
# Search up the history for the text in front of the cursor.
|
||||||
self.tempCommand = self.getCommand()
|
elif key == WXK_F8:
|
||||||
# Now replace the current line with the next one from the history.
|
self.OnHistorySearch()
|
||||||
self.historyPos = self.historyPos + 1
|
|
||||||
self.SetSelection(stoppos, endpos)
|
|
||||||
self.ReplaceSelection(self.history[self.historyPos])
|
|
||||||
# Control+DownArrow steps down through the history.
|
|
||||||
elif key == WXK_DOWN and event.ControlDown():
|
|
||||||
# Move to the end of the buffer.
|
|
||||||
endpos = self.GetTextLength()
|
|
||||||
self.SetCurrentPos(endpos)
|
|
||||||
# Are we at the bottom end of the history?
|
|
||||||
if self.historyPos == -1:
|
|
||||||
# Do we have a lastCommandRecalled stored?
|
|
||||||
if self.lastCommandRecalled >= 0:
|
|
||||||
# Replace the current line with the command after the
|
|
||||||
# last-recalled command (you'd think there should be a +1
|
|
||||||
# here but there isn't because the history was shuffled up
|
|
||||||
# by 1 after the previous command was recalled).
|
|
||||||
self.SetSelection(stoppos, endpos)
|
|
||||||
self.ReplaceSelection(self.history[self.lastCommandRecalled])
|
|
||||||
# We've now warped into middle of the history.
|
|
||||||
self.historyPos = self.lastCommandRecalled
|
|
||||||
self.lastCommandRecalled = -1
|
|
||||||
else:
|
|
||||||
# Fetch either the previous line from the history, or the saved
|
|
||||||
# command if we're back at the start.
|
|
||||||
self.historyPos = self.historyPos - 1
|
|
||||||
if self.historyPos == -1:
|
|
||||||
newText = self.tempCommand
|
|
||||||
else:
|
|
||||||
newText = self.history[self.historyPos]
|
|
||||||
# Replace the current line with the new text.
|
|
||||||
self.SetSelection(stoppos, endpos)
|
|
||||||
self.ReplaceSelection(newText)
|
|
||||||
# F8 on the last line does a search up the history for the text in
|
|
||||||
# front of the cursor.
|
|
||||||
elif key == WXK_F8 and self.GetCurrentLine() == self.GetLineCount()-1:
|
|
||||||
tempCommand = self.getCommand()
|
|
||||||
# The first F8 saves the current command, just like Control+Up.
|
|
||||||
if self.historyPos == -1:
|
|
||||||
self.tempCommand = tempCommand
|
|
||||||
# The text up to the cursor is what we search for.
|
|
||||||
searchText = tempCommand
|
|
||||||
numCharsAfterCursor = self.GetTextLength() - self.GetCurrentPos()
|
|
||||||
if numCharsAfterCursor > 0:
|
|
||||||
searchText = searchText[:-numCharsAfterCursor]
|
|
||||||
# Search upwards from the current history position and loop back
|
|
||||||
# to the beginning if we don't find anything.
|
|
||||||
for i in range(self.historyPos+1, len(self.history)) + \
|
|
||||||
range(self.historyPos):
|
|
||||||
command = self.history[i]
|
|
||||||
if command[:len(searchText)] == searchText:
|
|
||||||
# Replace the current line with the one we've found.
|
|
||||||
endpos = self.GetTextLength()
|
|
||||||
self.SetSelection(stoppos, endpos)
|
|
||||||
self.ReplaceSelection(command)
|
|
||||||
# Put the cursor back at the end of the search text.
|
|
||||||
pos = self.GetTextLength() - len(command) + len(searchText)
|
|
||||||
self.SetCurrentPos(pos)
|
|
||||||
self.SetAnchor(pos)
|
|
||||||
# We've now warped into middle of the history.
|
|
||||||
self.historyPos = i
|
|
||||||
self.lastCommandRecalled = -1
|
|
||||||
break
|
|
||||||
# Return is used to submit a command to the interpreter.
|
# Return is used to submit a command to the interpreter.
|
||||||
elif key == WXK_RETURN:
|
elif key == WXK_RETURN:
|
||||||
if self.CallTipActive: self.CallTipCancel()
|
if self.CallTipActive: self.CallTipCancel()
|
||||||
@@ -316,8 +337,12 @@ class Shell(wxStyledTextCtrl):
|
|||||||
# Home needs to be aware of the prompt.
|
# Home needs to be aware of the prompt.
|
||||||
elif key == WXK_HOME:
|
elif key == WXK_HOME:
|
||||||
if currpos >= stoppos:
|
if currpos >= stoppos:
|
||||||
self.SetCurrentPos(stoppos)
|
if event.ShiftDown():
|
||||||
self.SetAnchor(stoppos)
|
# Select text from current position to end of prompt.
|
||||||
|
self.SetSelection(self.GetCurrentPos(), stoppos)
|
||||||
|
else:
|
||||||
|
self.SetCurrentPos(stoppos)
|
||||||
|
self.SetAnchor(stoppos)
|
||||||
else:
|
else:
|
||||||
event.Skip()
|
event.Skip()
|
||||||
# Basic navigation keys should work anywhere.
|
# Basic navigation keys should work anywhere.
|
||||||
@@ -326,11 +351,11 @@ class Shell(wxStyledTextCtrl):
|
|||||||
event.Skip()
|
event.Skip()
|
||||||
# Don't backspace over the latest prompt.
|
# Don't backspace over the latest prompt.
|
||||||
elif key == WXK_BACK:
|
elif key == WXK_BACK:
|
||||||
if currpos > stoppos:
|
if currpos > self.prompt1Pos[1]:
|
||||||
event.Skip()
|
event.Skip()
|
||||||
# Only allow these keys after the latest prompt.
|
# Only allow these keys after the latest prompt.
|
||||||
elif key in (WXK_TAB, WXK_DELETE):
|
elif key in (WXK_TAB, WXK_DELETE):
|
||||||
if currpos >= stoppos:
|
if currpos >= self.prompt1Pos[1]:
|
||||||
event.Skip()
|
event.Skip()
|
||||||
# Don't toggle between insert mode and overwrite mode.
|
# Don't toggle between insert mode and overwrite mode.
|
||||||
elif key == WXK_INSERT:
|
elif key == WXK_INSERT:
|
||||||
@@ -338,36 +363,58 @@ class Shell(wxStyledTextCtrl):
|
|||||||
else:
|
else:
|
||||||
event.Skip()
|
event.Skip()
|
||||||
|
|
||||||
def OnChar(self, event):
|
def OnHistoryRetrieve(self, step):
|
||||||
"""Keypress event handler.
|
"""Retrieve the previous/next command from the history buffer."""
|
||||||
|
startpos = self.GetCurrentPos()
|
||||||
The main goal here is to not allow modifications to previous
|
if startpos < self.prompt1Pos[1]:
|
||||||
lines of text."""
|
wxBell()
|
||||||
key = event.KeyCode()
|
return
|
||||||
currpos = self.GetCurrentPos()
|
newindex = self.historyIndex + step
|
||||||
stoppos = self.promptPos[1]
|
if not (-1 <= newindex < len(self.history)):
|
||||||
if currpos >= stoppos:
|
wxBell()
|
||||||
if key == 46:
|
return
|
||||||
# "." The dot or period key activates auto completion.
|
self.historyIndex = newindex
|
||||||
# Get the command between the prompt and the cursor.
|
if newindex == -1:
|
||||||
# Add a dot to the end of the command.
|
self.ReplaceSelection('')
|
||||||
command = self.GetTextRange(stoppos, currpos) + '.'
|
|
||||||
self.write('.')
|
|
||||||
if self.autoComplete: self.autoCompleteShow(command)
|
|
||||||
elif key == 40:
|
|
||||||
# "(" The left paren activates a call tip and cancels
|
|
||||||
# an active auto completion.
|
|
||||||
if self.AutoCompActive(): self.AutoCompCancel()
|
|
||||||
# Get the command between the prompt and the cursor.
|
|
||||||
# Add the '(' to the end of the command.
|
|
||||||
command = self.GetTextRange(stoppos, currpos) + '('
|
|
||||||
self.write('(')
|
|
||||||
if self.autoCallTip: self.autoCallTipShow(command)
|
|
||||||
else:
|
|
||||||
# Allow the normal event handling to take place.
|
|
||||||
event.Skip()
|
|
||||||
else:
|
else:
|
||||||
pass
|
self.ReplaceSelection('')
|
||||||
|
command = self.history[self.historyIndex]
|
||||||
|
command = command.replace('\n', os.linesep + sys.ps2)
|
||||||
|
self.ReplaceSelection(command)
|
||||||
|
endpos = self.GetCurrentPos()
|
||||||
|
self.SetSelection(endpos, startpos)
|
||||||
|
|
||||||
|
def OnHistorySearch(self):
|
||||||
|
"""Search up the history buffer for the text in front of the cursor."""
|
||||||
|
startpos = self.GetCurrentPos()
|
||||||
|
if startpos < self.prompt1Pos[1]:
|
||||||
|
wxBell()
|
||||||
|
return
|
||||||
|
# The text up to the cursor is what we search for.
|
||||||
|
numCharsAfterCursor = self.GetTextLength() - startpos
|
||||||
|
searchText = self.getCommand(rstrip=0)
|
||||||
|
if numCharsAfterCursor > 0:
|
||||||
|
searchText = searchText[:-numCharsAfterCursor]
|
||||||
|
if not searchText:
|
||||||
|
return
|
||||||
|
# Search upwards from the current history position and loop back
|
||||||
|
# to the beginning if we don't find anything.
|
||||||
|
if (self.historyIndex <= -1) \
|
||||||
|
or (self.historyIndex >= len(self.history)-2):
|
||||||
|
searchOrder = range(len(self.history))
|
||||||
|
else:
|
||||||
|
searchOrder = range(self.historyIndex+1, len(self.history)) + \
|
||||||
|
range(self.historyIndex)
|
||||||
|
for i in searchOrder:
|
||||||
|
command = self.history[i]
|
||||||
|
if command[:len(searchText)] == searchText:
|
||||||
|
# Replace the current selection with the one we've found.
|
||||||
|
self.ReplaceSelection(command[len(searchText):])
|
||||||
|
endpos = self.GetCurrentPos()
|
||||||
|
self.SetSelection(endpos, startpos)
|
||||||
|
# We've now warped into middle of the history.
|
||||||
|
self.historyIndex = i
|
||||||
|
break
|
||||||
|
|
||||||
def setStatusText(self, text):
|
def setStatusText(self, text):
|
||||||
"""Display status information."""
|
"""Display status information."""
|
||||||
@@ -382,32 +429,36 @@ class Shell(wxStyledTextCtrl):
|
|||||||
# The user hit ENTER and we need to decide what to do. They could be
|
# The user hit ENTER and we need to decide what to do. They could be
|
||||||
# sitting on any line in the shell.
|
# sitting on any line in the shell.
|
||||||
|
|
||||||
# Grab information about the current line.
|
|
||||||
thepos = self.GetCurrentPos()
|
thepos = self.GetCurrentPos()
|
||||||
theline = self.GetCurrentLine()
|
|
||||||
command = self.getCommand()
|
|
||||||
# Go to the very bottom of the text.
|
|
||||||
endpos = self.GetTextLength()
|
endpos = self.GetTextLength()
|
||||||
self.SetCurrentPos(endpos)
|
# If they hit RETURN at the very bottom, execute the command.
|
||||||
endline = self.GetCurrentLine()
|
if thepos == endpos:
|
||||||
# If they hit RETURN on the last line, execute the command.
|
self.interp.more = 0
|
||||||
if theline == endline:
|
if self.getCommand():
|
||||||
|
command = self.GetTextRange(self.prompt1Pos[1], endpos)
|
||||||
|
else:
|
||||||
|
command = self.GetTextRange(self.prompt1Pos[1], \
|
||||||
|
self.promptPos[1])
|
||||||
|
command = command.replace(os.linesep + sys.ps2, '\n')
|
||||||
self.push(command)
|
self.push(command)
|
||||||
# Otherwise, replace the last line with the new line.
|
# Otherwise, replace the last command with the new command.
|
||||||
else:
|
elif thepos < self.prompt1Pos[0]:
|
||||||
|
theline = self.GetCurrentLine()
|
||||||
|
command = self.getCommand()
|
||||||
# If the new line contains a command (even an invalid one).
|
# If the new line contains a command (even an invalid one).
|
||||||
if command:
|
if command:
|
||||||
startpos = self.PositionFromLine(endline)
|
self.SetCurrentPos(endpos)
|
||||||
|
startpos = self.prompt1Pos[1]
|
||||||
self.SetSelection(startpos, endpos)
|
self.SetSelection(startpos, endpos)
|
||||||
self.ReplaceSelection('')
|
self.ReplaceSelection('')
|
||||||
self.prompt()
|
|
||||||
self.write(command)
|
self.write(command)
|
||||||
|
self.more = 0
|
||||||
# Otherwise, put the cursor back where we started.
|
# Otherwise, put the cursor back where we started.
|
||||||
else:
|
else:
|
||||||
self.SetCurrentPos(thepos)
|
self.SetCurrentPos(thepos)
|
||||||
self.SetAnchor(thepos)
|
self.SetAnchor(thepos)
|
||||||
|
|
||||||
def getCommand(self, text=None):
|
def getCommand(self, text=None, rstrip=1):
|
||||||
"""Extract a command from text which may include a shell prompt.
|
"""Extract a command from text which may include a shell prompt.
|
||||||
|
|
||||||
The command may not necessarily be valid Python syntax."""
|
The command may not necessarily be valid Python syntax."""
|
||||||
@@ -419,7 +470,8 @@ class Shell(wxStyledTextCtrl):
|
|||||||
ps1size = len(ps1)
|
ps1size = len(ps1)
|
||||||
ps2 = str(sys.ps2)
|
ps2 = str(sys.ps2)
|
||||||
ps2size = len(ps2)
|
ps2size = len(ps2)
|
||||||
text = text.rstrip()
|
if rstrip:
|
||||||
|
text = text.rstrip()
|
||||||
# Strip the prompt off the front of text leaving just the command.
|
# Strip the prompt off the front of text leaving just the command.
|
||||||
if text[:ps1size] == ps1:
|
if text[:ps1size] == ps1:
|
||||||
command = text[ps1size:]
|
command = text[ps1size:]
|
||||||
@@ -431,9 +483,10 @@ class Shell(wxStyledTextCtrl):
|
|||||||
|
|
||||||
def push(self, command):
|
def push(self, command):
|
||||||
"""Send command to the interpreter for execution."""
|
"""Send command to the interpreter for execution."""
|
||||||
self.addHistory(command)
|
|
||||||
self.write(os.linesep)
|
self.write(os.linesep)
|
||||||
self.more = self.interp.push(command)
|
self.more = self.interp.push(command)
|
||||||
|
if not self.more:
|
||||||
|
self.addHistory(command.rstrip())
|
||||||
self.prompt()
|
self.prompt()
|
||||||
# Keep the undo feature from undoing previous responses. The only
|
# Keep the undo feature from undoing previous responses. The only
|
||||||
# thing that can be undone is stuff typed after the prompt, before
|
# thing that can be undone is stuff typed after the prompt, before
|
||||||
@@ -442,12 +495,8 @@ class Shell(wxStyledTextCtrl):
|
|||||||
|
|
||||||
def addHistory(self, command):
|
def addHistory(self, command):
|
||||||
"""Add command to the command history."""
|
"""Add command to the command history."""
|
||||||
# Store the last-recalled command; see the main comment for
|
|
||||||
# self.lastCommandRecalled.
|
|
||||||
if command != '':
|
|
||||||
self.lastCommandRecalled = self.historyPos
|
|
||||||
# Reset the history position.
|
# Reset the history position.
|
||||||
self.historyPos = -1
|
self.historyIndex = -1
|
||||||
# Insert this command into the history, unless it's a blank
|
# Insert this command into the history, unless it's a blank
|
||||||
# line or the same as the last command.
|
# line or the same as the last command.
|
||||||
if command != '' \
|
if command != '' \
|
||||||
@@ -480,11 +529,13 @@ class Shell(wxStyledTextCtrl):
|
|||||||
pos = self.GetCurLine()[1]
|
pos = self.GetCurLine()[1]
|
||||||
if pos > 0: self.write(os.linesep)
|
if pos > 0: self.write(os.linesep)
|
||||||
self.promptPos[0] = self.GetCurrentPos()
|
self.promptPos[0] = self.GetCurrentPos()
|
||||||
|
if not self.more: self.prompt1Pos[0] = self.GetCurrentPos()
|
||||||
self.write(prompt)
|
self.write(prompt)
|
||||||
self.promptPos[1] = self.GetCurrentPos()
|
self.promptPos[1] = self.GetCurrentPos()
|
||||||
|
if not self.more: self.prompt1Pos[1] = self.GetCurrentPos()
|
||||||
# XXX Add some autoindent magic here if more.
|
# XXX Add some autoindent magic here if more.
|
||||||
if self.more:
|
if self.more:
|
||||||
self.write('\t') # Temporary hack indentation.
|
self.write(' '*4) # Temporary hack indentation.
|
||||||
self.EnsureCaretVisible()
|
self.EnsureCaretVisible()
|
||||||
self.ScrollToColumn(0)
|
self.ScrollToColumn(0)
|
||||||
|
|
||||||
@@ -533,6 +584,9 @@ class Shell(wxStyledTextCtrl):
|
|||||||
this
|
this
|
||||||
>>>
|
>>>
|
||||||
"""
|
"""
|
||||||
|
# Go to the very bottom of the text.
|
||||||
|
endpos = self.GetTextLength()
|
||||||
|
self.SetCurrentPos(endpos)
|
||||||
command = command.rstrip()
|
command = command.rstrip()
|
||||||
if prompt: self.prompt()
|
if prompt: self.prompt()
|
||||||
if verbose: self.write(command)
|
if verbose: self.write(command)
|
||||||
@@ -813,3 +867,4 @@ class ShellFrame(wxFrame, ShellMenu):
|
|||||||
self.createMenus()
|
self.createMenus()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@@ -7,4 +7,4 @@ __cvsid__ = "$Id$"
|
|||||||
__date__ = "July 1, 2001"
|
__date__ = "July 1, 2001"
|
||||||
__version__ = "$Revision$"[11:-2]
|
__version__ = "$Revision$"[11:-2]
|
||||||
|
|
||||||
VERSION = '0.6'
|
VERSION = '0.7'
|
||||||
|
Reference in New Issue
Block a user