1 """Shell is an interactive text control in which a user types in
2 commands to be sent to the interpreter. This particular shell is
3 based on wxPython's wxStyledTextCtrl.
5 Sponsored by Orbtech - Your source for Python programming expertise."""
7 __author__
= "Patrick K. O'Brien <pobrien@orbtech.com>"
9 __revision__
= "$Revision$"[11:-2]
19 from buffer import Buffer
23 from pseudo
import PseudoFileIn
24 from pseudo
import PseudoFileOut
25 from pseudo
import PseudoFileErr
26 from version
import VERSION
28 sys
.ps3
= '<-- ' # Input prompt.
30 NAVKEYS
= (wx
.WXK_END
, wx
.WXK_LEFT
, wx
.WXK_RIGHT
,
31 wx
.WXK_UP
, wx
.WXK_DOWN
, wx
.WXK_PRIOR
, wx
.WXK_NEXT
)
34 class ShellFrame(frame
.Frame
):
35 """Frame containing the shell component."""
38 revision
= __revision__
40 def __init__(self
, parent
=None, id=-1, title
='PyShell',
41 pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
42 style
=wx
.DEFAULT_FRAME_STYLE
, locals=None,
43 InterpClass
=None, *args
, **kwds
):
44 """Create ShellFrame instance."""
45 frame
.Frame
.__init
__(self
, parent
, id, title
, pos
, size
, style
)
46 intro
= 'PyShell %s - The Flakiest Python Shell' % VERSION
47 intro
+= '\nSponsored by Orbtech - ' + \
48 'Your source for Python programming expertise.'
49 self
.SetStatusText(intro
.replace('\n', ', '))
50 self
.shell
= Shell(parent
=self
, id=-1, introText
=intro
,
51 locals=locals, InterpClass
=InterpClass
,
53 # Override the shell so that status messages go to the status bar.
54 self
.shell
.setStatusText
= self
.SetStatusText
56 def OnClose(self
, event
):
57 """Event handler for closing."""
58 # This isn't working the way I want, but I'll leave it for now.
59 if self
.shell
.waiting
:
66 def OnAbout(self
, event
):
67 """Display an About window."""
68 title
= 'About PyShell'
69 text
= 'PyShell %s\n\n' % VERSION
+ \
70 'Yet another Python shell, only flakier.\n\n' + \
71 'Half-baked by Patrick K. O\'Brien,\n' + \
72 'the other half is still in the oven.\n\n' + \
73 'Shell Revision: %s\n' % self
.shell
.revision
+ \
74 'Interpreter Revision: %s\n\n' % self
.shell
.interp
.revision
+ \
75 'Platform: %s\n' % sys
.platform
+ \
76 'Python Version: %s\n' % sys
.version
.split()[0] + \
77 'wxPython Version: %s\n' % wx
.VERSION_STRING
+ \
78 ('\t(%s)\n' % ", ".join(wx
.PlatformInfo
[1:]))
79 dialog
= wx
.MessageDialog(self
, text
, title
,
80 wx
.OK | wx
.ICON_INFORMATION
)
86 """Simplified interface to all shell-related functionality.
88 This is a semi-transparent facade, in that all attributes of other
89 are accessible, even though only some are visible to the user."""
91 name
= 'Shell Interface'
92 revision
= __revision__
94 def __init__(self
, other
):
95 """Create a ShellFacade instance."""
101 Home Go to the beginning of the command or line.
102 Shift+Home Select to the beginning of the command or line.
103 Shift+End Select to the end of the line.
104 End Go to the end of the line.
105 Ctrl+C Copy selected text, removing prompts.
106 Ctrl+Shift+C Copy selected text, retaining prompts.
107 Ctrl+X Cut selected text.
108 Ctrl+V Paste from clipboard.
109 Ctrl+Shift+V Paste and run multiple commands from clipboard.
110 Ctrl+Up Arrow Retrieve Previous History item.
111 Alt+P Retrieve Previous History item.
112 Ctrl+Down Arrow Retrieve Next History item.
113 Alt+N Retrieve Next History item.
114 Shift+Up Arrow Insert Previous History item.
115 Shift+Down Arrow Insert Next History item.
116 F8 Command-completion of History item.
117 (Type a few characters of a previous command and press F8.)
118 Ctrl+Enter Insert new line into multiline command.
119 Ctrl+] Increase font size.
120 Ctrl+[ Decrease font size.
121 Ctrl+= Default font size.
125 """Display some useful information about how to use the shell."""
126 self
.write(self
.helpText
)
128 def __getattr__(self
, name
):
129 if hasattr(self
.other
, name
):
130 return getattr(self
.other
, name
)
132 raise AttributeError, name
134 def __setattr__(self
, name
, value
):
135 if self
.__dict
__.has_key(name
):
136 self
.__dict
__[name
] = value
137 elif hasattr(self
.other
, name
):
138 setattr(self
.other
, name
, value
)
140 raise AttributeError, name
142 def _getAttributeNames(self
):
143 """Return list of magic attributes to extend introspection."""
149 'autoCompleteAutoHide',
150 'autoCompleteCaseInsensitive',
151 'autoCompleteIncludeDouble',
152 'autoCompleteIncludeMagic',
153 'autoCompleteIncludeSingle',
170 class Shell(editwindow
.EditWindow
):
171 """Shell based on StyledTextCtrl."""
174 revision
= __revision__
176 def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
,
177 size
=wx
.DefaultSize
, style
=wx
.CLIP_CHILDREN
,
178 introText
='', locals=None, InterpClass
=None, *args
, **kwds
):
179 """Create Shell instance."""
180 editwindow
.EditWindow
.__init
__(self
, parent
, id, pos
, size
, style
)
184 locals = __main__
.__dict
__
185 # Grab these so they can be restored by self.redirect* methods.
186 self
.stdin
= sys
.stdin
187 self
.stdout
= sys
.stdout
188 self
.stderr
= sys
.stderr
189 # Import a default interpreter class if one isn't provided.
190 if InterpClass
== None:
191 from interpreter
import Interpreter
193 Interpreter
= InterpClass
194 # Create a replacement for stdin.
195 self
.reader
= PseudoFileIn(self
.readline
, self
.readlines
)
196 self
.reader
.input = ''
197 self
.reader
.isreading
= False
198 # Set up the interpreter.
199 self
.interp
= Interpreter(locals=locals,
200 rawin
=self
.raw_input,
202 stdout
=PseudoFileOut(self
.writeOut
),
203 stderr
=PseudoFileErr(self
.writeErr
),
206 self
.buffer = Buffer()
207 # Find out for which keycodes the interpreter will autocomplete.
208 self
.autoCompleteKeys
= self
.interp
.getAutoCompleteKeys()
209 # Keep track of the last non-continuation prompt positions.
210 self
.promptPosStart
= 0
211 self
.promptPosEnd
= 0
212 # Keep track of multi-line commands.
214 # Create the command history. Commands are added into the
215 # front of the list (ie. at index 0) as they are entered.
216 # self.historyIndex is the current position in the history; it
217 # gets incremented as you retrieve the previous command,
218 # decremented as you retrieve the next, and reset when you hit
219 # Enter. self.historyIndex == -1 means you're on the current
220 # command, not in the history.
222 self
.historyIndex
= -1
223 # Assign handlers for keyboard events.
224 wx
.EVT_CHAR(self
, self
.OnChar
)
225 wx
.EVT_KEY_DOWN(self
, self
.OnKeyDown
)
226 # Assign handler for idle time.
228 wx
.EVT_IDLE(self
, self
.OnIdle
)
229 # Display the introductory banner information.
230 self
.showIntro(introText
)
231 # Assign some pseudo keywords to the interpreter's namespace.
232 self
.setBuiltinKeywords()
233 # Add 'shell' to the interpreter's local namespace.
235 # Do this last so the user has complete control over their
236 # environment. They can override anything they want.
237 self
.execStartupScript(self
.interp
.startupScript
)
238 wx
.CallAfter(self
.ScrollToLine
, 0)
244 """Set focus to the shell."""
247 def OnIdle(self
, event
):
248 """Free the CPU to do other things."""
253 def showIntro(self
, text
=''):
254 """Display introductory text in the shell."""
256 if not text
.endswith(os
.linesep
):
260 self
.write(self
.interp
.introText
)
261 except AttributeError:
264 def setBuiltinKeywords(self
):
265 """Create pseudo keywords as part of builtins.
267 This sets `close`, `exit` and `quit` to a helpful string.
270 __builtin__
.close
= __builtin__
.exit
= __builtin__
.quit
= \
271 'Click on the close button to leave the application.'
274 """Quit the application."""
276 # XXX Good enough for now but later we want to send a close event.
278 # In the close event handler we can make sure they want to
279 # quit. Other applications, like PythonCard, may choose to
280 # hide rather than quit so we should just post the event and
281 # let the surrounding app decide what it wants to do.
282 self
.write('Click on the close button to leave the application.')
284 def setLocalShell(self
):
285 """Add 'shell' to locals as reference to ShellFacade instance."""
286 self
.interp
.locals['shell'] = ShellFacade(other
=self
)
288 def execStartupScript(self
, startupScript
):
289 """Execute the user's PYTHONSTARTUP script if they have one."""
290 if startupScript
and os
.path
.isfile(startupScript
):
291 text
= 'Startup script executed: ' + startupScript
292 self
.push('print %r; execfile(%r)' % (text
, startupScript
))
297 """Display information about Py."""
301 Py Shell Revision: %s
302 Py Interpreter Revision: %s
306 (__author__
, VERSION
, self
.revision
, self
.interp
.revision
,
307 sys
.version
.split()[0], wx
.VERSION_STRING
, sys
.platform
)
308 self
.write(text
.strip())
310 def OnChar(self
, event
):
311 """Keypress event handler.
313 Only receives an event if OnKeyDown calls event.Skip() for the
314 corresponding event."""
316 # Prevent modification of previously submitted
317 # commands/responses.
318 if not self
.CanEdit():
320 key
= event
.KeyCode()
321 currpos
= self
.GetCurrentPos()
322 stoppos
= self
.promptPosEnd
323 # Return (Enter) needs to be ignored in this handler.
324 if key
== wx
.WXK_RETURN
:
326 elif key
in self
.autoCompleteKeys
:
327 # Usually the dot (period) key activates auto completion.
328 # Get the command between the prompt and the cursor. Add
329 # the autocomplete character to the end of the command.
330 if self
.AutoCompActive():
331 self
.AutoCompCancel()
332 command
= self
.GetTextRange(stoppos
, currpos
) + chr(key
)
334 if self
.autoComplete
:
335 self
.autoCompleteShow(command
)
336 elif key
== ord('('):
337 # The left paren activates a call tip and cancels an
338 # active auto completion.
339 if self
.AutoCompActive():
340 self
.AutoCompCancel()
341 # Get the command between the prompt and the cursor. Add
342 # the '(' to the end of the command.
343 self
.ReplaceSelection('')
344 command
= self
.GetTextRange(stoppos
, currpos
) + '('
346 self
.autoCallTipShow(command
)
348 # Allow the normal event handling to take place.
351 def OnKeyDown(self
, event
):
352 """Key down event handler."""
354 key
= event
.KeyCode()
355 # If the auto-complete window is up let it do its thing.
356 if self
.AutoCompActive():
359 # Prevent modification of previously submitted
360 # commands/responses.
361 controlDown
= event
.ControlDown()
362 altDown
= event
.AltDown()
363 shiftDown
= event
.ShiftDown()
364 currpos
= self
.GetCurrentPos()
365 endpos
= self
.GetTextLength()
366 selecting
= self
.GetSelectionStart() != self
.GetSelectionEnd()
367 # Return (Enter) is used to submit a command to the
369 if not controlDown
and key
== wx
.WXK_RETURN
:
370 if self
.CallTipActive():
373 # Ctrl+Return (Cntrl+Enter) is used to insert a line break.
374 elif controlDown
and key
== wx
.WXK_RETURN
:
375 if self
.CallTipActive():
377 if currpos
== endpos
:
380 self
.insertLineBreak()
381 # Let Ctrl-Alt-* get handled normally.
382 elif controlDown
and altDown
:
384 # Clear the current, unexecuted command.
385 elif key
== wx
.WXK_ESCAPE
:
386 if self
.CallTipActive():
390 # Increase font size.
391 elif controlDown
and key
in (ord(']'),):
392 dispatcher
.send(signal
='FontIncrease')
393 # Decrease font size.
394 elif controlDown
and key
in (ord('['),):
395 dispatcher
.send(signal
='FontDecrease')
397 elif controlDown
and key
in (ord('='),):
398 dispatcher
.send(signal
='FontDefault')
399 # Cut to the clipboard.
400 elif (controlDown
and key
in (ord('X'), ord('x'))) \
401 or (shiftDown
and key
== wx
.WXK_DELETE
):
403 # Copy to the clipboard.
404 elif controlDown
and not shiftDown \
405 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
407 # Copy to the clipboard, including prompts.
408 elif controlDown
and shiftDown \
409 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
410 self
.CopyWithPrompts()
411 # Copy to the clipboard, including prefixed prompts.
412 elif altDown
and not controlDown \
413 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
414 self
.CopyWithPromptsPrefixed()
415 # Home needs to be aware of the prompt.
416 elif key
== wx
.WXK_HOME
:
417 home
= self
.promptPosEnd
419 self
.SetCurrentPos(home
)
420 if not selecting
and not shiftDown
:
422 self
.EnsureCaretVisible()
426 # The following handlers modify text, so we need to see if
427 # there is a selection that includes text prior to the prompt.
429 # Don't modify a selection with text prior to the prompt.
430 elif selecting
and key
not in NAVKEYS
and not self
.CanEdit():
432 # Paste from the clipboard.
433 elif (controlDown
and not shiftDown
and key
in (ord('V'), ord('v'))) \
434 or (shiftDown
and not controlDown
and key
== wx
.WXK_INSERT
):
436 # Paste from the clipboard, run commands.
437 elif controlDown
and shiftDown
and key
in (ord('V'), ord('v')):
439 # Replace with the previous command from the history buffer.
440 elif (controlDown
and key
== wx
.WXK_UP
) \
441 or (altDown
and key
in (ord('P'), ord('p'))):
442 self
.OnHistoryReplace(step
=+1)
443 # Replace with the next command from the history buffer.
444 elif (controlDown
and key
== wx
.WXK_DOWN
) \
445 or (altDown
and key
in (ord('N'), ord('n'))):
446 self
.OnHistoryReplace(step
=-1)
447 # Insert the previous command from the history buffer.
448 elif (shiftDown
and key
== wx
.WXK_UP
) and self
.CanEdit():
449 self
.OnHistoryInsert(step
=+1)
450 # Insert the next command from the history buffer.
451 elif (shiftDown
and key
== wx
.WXK_DOWN
) and self
.CanEdit():
452 self
.OnHistoryInsert(step
=-1)
453 # Search up the history for the text in front of the cursor.
454 elif key
== wx
.WXK_F8
:
455 self
.OnHistorySearch()
456 # Don't backspace over the latest non-continuation prompt.
457 elif key
== wx
.WXK_BACK
:
458 if selecting
and self
.CanEdit():
460 elif currpos
> self
.promptPosEnd
:
462 # Only allow these keys after the latest prompt.
463 elif key
in (wx
.WXK_TAB
, wx
.WXK_DELETE
):
466 # Don't toggle between insert mode and overwrite mode.
467 elif key
== wx
.WXK_INSERT
:
469 # Don't allow line deletion.
470 elif controlDown
and key
in (ord('L'), ord('l')):
472 # Don't allow line transposition.
473 elif controlDown
and key
in (ord('T'), ord('t')):
475 # Basic navigation keys should work anywhere.
478 # Protect the readonly portion of the shell.
479 elif not self
.CanEdit():
484 def clearCommand(self
):
485 """Delete the current, unexecuted command."""
486 startpos
= self
.promptPosEnd
487 endpos
= self
.GetTextLength()
488 self
.SetSelection(startpos
, endpos
)
489 self
.ReplaceSelection('')
492 def OnHistoryReplace(self
, step
):
493 """Replace with the previous/next command from the history buffer."""
495 self
.replaceFromHistory(step
)
497 def replaceFromHistory(self
, step
):
498 """Replace selection with command from the history buffer."""
500 self
.ReplaceSelection('')
501 newindex
= self
.historyIndex
+ step
502 if -1 <= newindex
<= len(self
.history
):
503 self
.historyIndex
= newindex
504 if 0 <= newindex
<= len(self
.history
)-1:
505 command
= self
.history
[self
.historyIndex
]
506 command
= command
.replace('\n', os
.linesep
+ ps2
)
507 self
.ReplaceSelection(command
)
509 def OnHistoryInsert(self
, step
):
510 """Insert the previous/next command from the history buffer."""
511 if not self
.CanEdit():
513 startpos
= self
.GetCurrentPos()
514 self
.replaceFromHistory(step
)
515 endpos
= self
.GetCurrentPos()
516 self
.SetSelection(endpos
, startpos
)
518 def OnHistorySearch(self
):
519 """Search up the history buffer for the text in front of the cursor."""
520 if not self
.CanEdit():
522 startpos
= self
.GetCurrentPos()
523 # The text up to the cursor is what we search for.
524 numCharsAfterCursor
= self
.GetTextLength() - startpos
525 searchText
= self
.getCommand(rstrip
=False)
526 if numCharsAfterCursor
> 0:
527 searchText
= searchText
[:-numCharsAfterCursor
]
530 # Search upwards from the current history position and loop
531 # back to the beginning if we don't find anything.
532 if (self
.historyIndex
<= -1) \
533 or (self
.historyIndex
>= len(self
.history
)-2):
534 searchOrder
= range(len(self
.history
))
536 searchOrder
= range(self
.historyIndex
+1, len(self
.history
)) + \
537 range(self
.historyIndex
)
538 for i
in searchOrder
:
539 command
= self
.history
[i
]
540 if command
[:len(searchText
)] == searchText
:
541 # Replace the current selection with the one we found.
542 self
.ReplaceSelection(command
[len(searchText
):])
543 endpos
= self
.GetCurrentPos()
544 self
.SetSelection(endpos
, startpos
)
545 # We've now warped into middle of the history.
546 self
.historyIndex
= i
549 def setStatusText(self
, text
):
550 """Display status information."""
552 # This method will likely be replaced by the enclosing app to
553 # do something more interesting, like write to a status bar.
556 def insertLineBreak(self
):
557 """Insert a new line break."""
559 self
.write(os
.linesep
)
563 def processLine(self
):
564 """Process the line of text at which the user hit Enter."""
566 # The user hit ENTER and we need to decide what to do. They
567 # could be sitting on any line in the shell.
569 thepos
= self
.GetCurrentPos()
570 startpos
= self
.promptPosEnd
571 endpos
= self
.GetTextLength()
573 # If they hit RETURN inside the current command, execute the
576 self
.SetCurrentPos(endpos
)
577 self
.interp
.more
= False
578 command
= self
.GetTextRange(startpos
, endpos
)
579 lines
= command
.split(os
.linesep
+ ps2
)
580 lines
= [line
.rstrip() for line
in lines
]
581 command
= '\n'.join(lines
)
582 if self
.reader
.isreading
:
584 # Match the behavior of the standard Python shell
585 # when the user hits return without entering a
588 self
.reader
.input = command
589 self
.write(os
.linesep
)
592 # Or replace the current command with the other command.
594 # If the line contains a command (even an invalid one).
595 if self
.getCommand(rstrip
=False):
596 command
= self
.getMultilineCommand()
599 # Otherwise, put the cursor back where we started.
601 self
.SetCurrentPos(thepos
)
602 self
.SetAnchor(thepos
)
604 def getMultilineCommand(self
, rstrip
=True):
605 """Extract a multi-line command from the editor.
607 The command may not necessarily be valid Python syntax."""
608 # XXX Need to extract real prompts here. Need to keep track of
609 # the prompt every time a command is issued.
614 # This is a total hack job, but it works.
615 text
= self
.GetCurLine()[0]
616 line
= self
.GetCurrentLine()
617 while text
[:ps2size
] == ps2
and line
> 0:
620 text
= self
.GetCurLine()[0]
621 if text
[:ps1size
] == ps1
:
622 line
= self
.GetCurrentLine()
624 startpos
= self
.GetCurrentPos() + ps1size
627 while self
.GetCurLine()[0][:ps2size
] == ps2
:
630 stoppos
= self
.GetCurrentPos()
631 command
= self
.GetTextRange(startpos
, stoppos
)
632 command
= command
.replace(os
.linesep
+ ps2
, '\n')
633 command
= command
.rstrip()
634 command
= command
.replace('\n', os
.linesep
+ ps2
)
638 command
= command
.rstrip()
641 def getCommand(self
, text
=None, rstrip
=True):
642 """Extract a command from text which may include a shell prompt.
644 The command may not necessarily be valid Python syntax."""
646 text
= self
.GetCurLine()[0]
647 # Strip the prompt off the front leaving just the command.
648 command
= self
.lstripPrompt(text
)
650 command
= '' # Real commands have prompts.
652 command
= command
.rstrip()
655 def lstripPrompt(self
, text
):
656 """Return text without a leading prompt."""
661 # Strip the prompt off the front of text.
662 if text
[:ps1size
] == ps1
:
663 text
= text
[ps1size
:]
664 elif text
[:ps2size
] == ps2
:
665 text
= text
[ps2size
:]
668 def push(self
, command
):
669 """Send command to the interpreter for execution."""
670 self
.write(os
.linesep
)
671 busy
= wx
.BusyCursor()
673 self
.more
= self
.interp
.push(command
)
677 self
.addHistory(command
.rstrip())
680 def addHistory(self
, command
):
681 """Add command to the command history."""
682 # Reset the history position.
683 self
.historyIndex
= -1
684 # Insert this command into the history, unless it's a blank
685 # line or the same as the last command.
687 and (len(self
.history
) == 0 or command
!= self
.history
[0]):
688 self
.history
.insert(0, command
)
690 def write(self
, text
):
691 """Display text in the shell.
693 Replace line endings with OS-specific endings."""
694 text
= self
.fixLineEndings(text
)
696 self
.EnsureCaretVisible()
698 def fixLineEndings(self
, text
):
699 """Return text with line endings replaced by OS-specific endings."""
700 lines
= text
.split('\r\n')
701 for l
in range(len(lines
)):
702 chunks
= lines
[l
].split('\r')
703 for c
in range(len(chunks
)):
704 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n'))
705 lines
[l
] = os
.linesep
.join(chunks
)
706 text
= os
.linesep
.join(lines
)
710 """Display proper prompt for the context: ps1, ps2 or ps3.
712 If this is a continuation line, autoindent as necessary."""
713 isreading
= self
.reader
.isreading
716 prompt
= str(sys
.ps3
)
718 prompt
= str(sys
.ps2
)
720 prompt
= str(sys
.ps1
)
721 pos
= self
.GetCurLine()[1]
726 self
.write(os
.linesep
)
728 self
.promptPosStart
= self
.GetCurrentPos()
732 self
.promptPosEnd
= self
.GetCurrentPos()
733 # Keep the undo feature from undoing previous responses.
734 self
.EmptyUndoBuffer()
735 # XXX Add some autoindent magic here if more.
737 self
.write(' '*4) # Temporary hack indentation.
738 self
.EnsureCaretVisible()
739 self
.ScrollToColumn(0)
742 """Replacement for stdin.readline()."""
745 reader
.isreading
= True
748 while not reader
.input:
753 reader
.isreading
= False
754 input = str(input) # In case of Unicode.
758 """Replacement for stdin.readlines()."""
760 while lines
[-1:] != ['\n']:
761 lines
.append(self
.readline())
764 def raw_input(self
, prompt
=''):
765 """Return string based on user input."""
768 return self
.readline()
770 def ask(self
, prompt
='Please enter your response:'):
771 """Get response from the user using a dialog box."""
772 dialog
= wx
.TextEntryDialog(None, prompt
,
773 'Input Dialog (Raw)', '')
775 if dialog
.ShowModal() == wx
.ID_OK
:
776 text
= dialog
.GetValue()
783 """Halt execution pending a response from the user."""
784 self
.ask('Press enter to continue:')
787 """Delete all text from the shell."""
790 def run(self
, command
, prompt
=True, verbose
=True):
791 """Execute command as if it was typed in directly.
792 >>> shell.run('print "this"')
797 # Go to the very bottom of the text.
798 endpos
= self
.GetTextLength()
799 self
.SetCurrentPos(endpos
)
800 command
= command
.rstrip()
801 if prompt
: self
.prompt()
802 if verbose
: self
.write(command
)
805 def runfile(self
, filename
):
806 """Execute all commands in file as if they were typed into the
808 file = open(filename
)
811 for command
in file.readlines():
812 if command
[:6] == 'shell.':
813 # Run shell methods silently.
814 self
.run(command
, prompt
=False, verbose
=False)
816 self
.run(command
, prompt
=False, verbose
=True)
820 def autoCompleteShow(self
, command
):
821 """Display auto-completion popup list."""
822 self
.AutoCompSetAutoHide(self
.autoCompleteAutoHide
)
823 self
.AutoCompSetIgnoreCase(self
.autoCompleteCaseInsensitive
)
824 list = self
.interp
.getAutoCompleteList(command
,
825 includeMagic
=self
.autoCompleteIncludeMagic
,
826 includeSingle
=self
.autoCompleteIncludeSingle
,
827 includeDouble
=self
.autoCompleteIncludeDouble
)
829 options
= ' '.join(list)
831 self
.AutoCompShow(offset
, options
)
833 def autoCallTipShow(self
, command
):
834 """Display argument spec and docstring in a popup window."""
835 if self
.CallTipActive():
837 (name
, argspec
, tip
) = self
.interp
.getCallTip(command
)
839 dispatcher
.send(signal
='Shell.calltip', sender
=self
, calltip
=tip
)
840 if not self
.autoCallTip
:
843 startpos
= self
.GetCurrentPos()
844 self
.write(argspec
+ ')')
845 endpos
= self
.GetCurrentPos()
846 self
.SetSelection(endpos
, startpos
)
848 curpos
= self
.GetCurrentPos()
849 tippos
= curpos
- (len(name
) + 1)
850 fallback
= curpos
- self
.GetColumn(curpos
)
851 # In case there isn't enough room, only go back to the
853 tippos
= max(tippos
, fallback
)
854 self
.CallTipShow(tippos
, tip
)
856 def writeOut(self
, text
):
857 """Replacement for stdout."""
860 def writeErr(self
, text
):
861 """Replacement for stderr."""
864 def redirectStdin(self
, redirect
=True):
865 """If redirect is true then sys.stdin will come from the shell."""
867 sys
.stdin
= self
.reader
869 sys
.stdin
= self
.stdin
871 def redirectStdout(self
, redirect
=True):
872 """If redirect is true then sys.stdout will go to the shell."""
874 sys
.stdout
= PseudoFileOut(self
.writeOut
)
876 sys
.stdout
= self
.stdout
878 def redirectStderr(self
, redirect
=True):
879 """If redirect is true then sys.stderr will go to the shell."""
881 sys
.stderr
= PseudoFileErr(self
.writeErr
)
883 sys
.stderr
= self
.stderr
886 """Return true if text is selected and can be cut."""
887 if self
.GetSelectionStart() != self
.GetSelectionEnd() \
888 and self
.GetSelectionStart() >= self
.promptPosEnd \
889 and self
.GetSelectionEnd() >= self
.promptPosEnd
:
895 """Return true if a paste should succeed."""
896 if self
.CanEdit() and editwindow
.EditWindow
.CanPaste(self
):
902 """Return true if editing should succeed."""
903 if self
.GetSelectionStart() != self
.GetSelectionEnd():
904 if self
.GetSelectionStart() >= self
.promptPosEnd \
905 and self
.GetSelectionEnd() >= self
.promptPosEnd
:
910 return self
.GetCurrentPos() >= self
.promptPosEnd
913 """Remove selection and place it on the clipboard."""
914 if self
.CanCut() and self
.CanCopy():
915 if self
.AutoCompActive():
916 self
.AutoCompCancel()
917 if self
.CallTipActive():
920 self
.ReplaceSelection('')
923 """Copy selection and place it on the clipboard."""
927 command
= self
.GetSelectedText()
928 command
= command
.replace(os
.linesep
+ ps2
, os
.linesep
)
929 command
= command
.replace(os
.linesep
+ ps1
, os
.linesep
)
930 command
= self
.lstripPrompt(text
=command
)
931 data
= wx
.TextDataObject(command
)
934 def CopyWithPrompts(self
):
935 """Copy selection, including prompts, and place it on the clipboard."""
937 command
= self
.GetSelectedText()
938 data
= wx
.TextDataObject(command
)
941 def CopyWithPromptsPrefixed(self
):
942 """Copy selection, including prompts prefixed with four
943 spaces, and place it on the clipboard."""
945 command
= self
.GetSelectedText()
947 command
= spaces
+ command
.replace(os
.linesep
,
949 data
= wx
.TextDataObject(command
)
952 def _clip(self
, data
):
953 if wx
.TheClipboard
.Open():
954 wx
.TheClipboard
.UsePrimarySelection(False)
955 wx
.TheClipboard
.SetData(data
)
956 wx
.TheClipboard
.Flush()
957 wx
.TheClipboard
.Close()
960 """Replace selection with clipboard contents."""
961 if self
.CanPaste() and wx
.TheClipboard
.Open():
963 if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)):
964 data
= wx
.TextDataObject()
965 if wx
.TheClipboard
.GetData(data
):
966 self
.ReplaceSelection('')
967 command
= data
.GetText()
968 command
= command
.rstrip()
969 command
= self
.fixLineEndings(command
)
970 command
= self
.lstripPrompt(text
=command
)
971 command
= command
.replace(os
.linesep
+ ps2
, '\n')
972 command
= command
.replace(os
.linesep
, '\n')
973 command
= command
.replace('\n', os
.linesep
+ ps2
)
975 wx
.TheClipboard
.Close()
977 def PasteAndRun(self
):
978 """Replace selection with clipboard contents, run commands."""
979 if wx
.TheClipboard
.Open():
982 if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)):
983 data
= wx
.TextDataObject()
984 if wx
.TheClipboard
.GetData(data
):
985 endpos
= self
.GetTextLength()
986 self
.SetCurrentPos(endpos
)
987 startpos
= self
.promptPosEnd
988 self
.SetSelection(startpos
, endpos
)
989 self
.ReplaceSelection('')
990 text
= data
.GetText()
992 text
= self
.fixLineEndings(text
)
993 text
= self
.lstripPrompt(text
)
994 text
= text
.replace(os
.linesep
+ ps1
, '\n')
995 text
= text
.replace(os
.linesep
+ ps2
, '\n')
996 text
= text
.replace(os
.linesep
, '\n')
997 lines
= text
.split('\n')
1001 if line
.strip() == ps2
.strip():
1002 # If we are pasting from something like a
1003 # web page that drops the trailing space
1004 # from the ps2 prompt of a blank line.
1006 if line
.strip() != '' and line
.lstrip() == line
:
1009 # Add the previous command to the list.
1010 commands
.append(command
)
1011 # Start a new command, which may be multiline.
1014 # Multiline command. Add to the command.
1017 commands
.append(command
)
1018 for command
in commands
:
1019 command
= command
.replace('\n', os
.linesep
+ ps2
)
1022 wx
.TheClipboard
.Close()
1024 def wrap(self
, wrap
=True):
1025 """Sets whether text is word wrapped."""
1027 self
.SetWrapMode(wrap
)
1028 except AttributeError:
1029 return 'Wrapping is not available in this version.'
1031 def zoom(self
, points
=0):
1032 """Set the zoom level.
1034 This number of points is added to the size of all fonts. It
1035 may be positive to magnify or negative to reduce."""
1036 self
.SetZoom(points
)