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]
11 #from wxd.d_wx import wx
12 #from wxd.d_stc import stc
22 from buffer import Buffer
26 from pseudo
import PseudoFileIn
27 from pseudo
import PseudoFileOut
28 from pseudo
import PseudoFileErr
29 from version
import VERSION
37 sys
.ps3
= '<-- ' # Input prompt.
39 NAVKEYS
= (wx
.WXK_END
, wx
.WXK_LEFT
, wx
.WXK_RIGHT
,
40 wx
.WXK_UP
, wx
.WXK_DOWN
, wx
.WXK_PRIOR
, wx
.WXK_NEXT
)
43 class ShellFrame(frame
.Frame
):
44 """Frame containing the shell component."""
47 revision
= __revision__
49 def __init__(self
, parent
=None, id=-1, title
='PyShell',
50 pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
51 style
=wx
.DEFAULT_FRAME_STYLE
, locals=None,
52 InterpClass
=None, *args
, **kwds
):
53 """Create ShellFrame instance."""
54 frame
.Frame
.__init
__(self
, parent
, id, title
, pos
, size
, style
)
55 intro
= 'PyShell %s - The Flakiest Python Shell' % VERSION
56 intro
+= '\nSponsored by Orbtech - ' + \
57 'Your source for Python programming expertise.'
58 self
.SetStatusText(intro
.replace('\n', ', '))
59 self
.shell
= Shell(parent
=self
, id=-1, introText
=intro
,
60 locals=locals, InterpClass
=InterpClass
,
62 # Override the shell so that status messages go to the status bar.
63 self
.shell
.setStatusText
= self
.SetStatusText
65 def OnClose(self
, event
):
66 """Event handler for closing."""
67 # This isn't working the way I want, but I'll leave it for now.
68 if self
.shell
.waiting
:
75 def OnAbout(self
, event
):
76 """Display an About window."""
77 title
= 'About PyShell'
78 text
= 'PyShell %s\n\n' % VERSION
+ \
79 'Yet another Python shell, only flakier.\n\n' + \
80 'Half-baked by Patrick K. O\'Brien,\n' + \
81 'the other half is still in the oven.\n\n' + \
82 'Shell Revision: %s\n' % self
.shell
.revision
+ \
83 'Interpreter Revision: %s\n\n' % self
.shell
.interp
.revision
+ \
84 'Python Version: %s\n' % sys
.version
.split()[0] + \
85 'wxPython Version: %s\n' % wx
.VERSION_STRING
+ \
86 'Platform: %s\n' % sys
.platform
87 dialog
= wx
.MessageDialog(self
, text
, title
,
88 wx
.OK | wx
.ICON_INFORMATION
)
94 """Simplified interface to all shell-related functionality.
96 This is a semi-transparent facade, in that all attributes of other
97 are accessible, even though only some are visible to the user."""
99 name
= 'Shell Interface'
100 revision
= __revision__
102 def __init__(self
, other
):
103 """Create a ShellFacade instance."""
109 Home Go to the beginning of the command or line.
110 Shift+Home Select to the beginning of the command or line.
111 Shift+End Select to the end of the line.
112 End Go to the end of the line.
113 Ctrl+C Copy selected text, removing prompts.
114 Ctrl+Shift+C Copy selected text, retaining prompts.
115 Ctrl+X Cut selected text.
116 Ctrl+V Paste from clipboard.
117 Ctrl+Shift+V Paste and run multiple commands from clipboard.
118 Ctrl+Up Arrow Retrieve Previous History item.
119 Alt+P Retrieve Previous History item.
120 Ctrl+Down Arrow Retrieve Next History item.
121 Alt+N Retrieve Next History item.
122 Shift+Up Arrow Insert Previous History item.
123 Shift+Down Arrow Insert Next History item.
124 F8 Command-completion of History item.
125 (Type a few characters of a previous command and press F8.)
126 Ctrl+Enter Insert new line into multiline command.
127 Ctrl+] Increase font size.
128 Ctrl+[ Decrease font size.
129 Ctrl+= Default font size.
133 """Display some useful information about how to use the shell."""
134 self
.write(self
.helpText
)
136 def __getattr__(self
, name
):
137 if hasattr(self
.other
, name
):
138 return getattr(self
.other
, name
)
140 raise AttributeError, name
142 def __setattr__(self
, name
, value
):
143 if self
.__dict
__.has_key(name
):
144 self
.__dict
__[name
] = value
145 elif hasattr(self
.other
, name
):
146 setattr(self
.other
, name
, value
)
148 raise AttributeError, name
150 def _getAttributeNames(self
):
151 """Return list of magic attributes to extend introspection."""
157 'autoCompleteCaseInsensitive',
158 'autoCompleteIncludeDouble',
159 'autoCompleteIncludeMagic',
160 'autoCompleteIncludeSingle',
177 class Shell(editwindow
.EditWindow
):
178 """Shell based on StyledTextCtrl."""
181 revision
= __revision__
183 def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
,
184 size
=wx
.DefaultSize
, style
=wx
.CLIP_CHILDREN
,
185 introText
='', locals=None, InterpClass
=None, *args
, **kwds
):
186 """Create Shell instance."""
187 editwindow
.EditWindow
.__init
__(self
, parent
, id, pos
, size
, style
)
191 # Grab these so they can be restored by self.redirect* methods.
192 self
.stdin
= sys
.stdin
193 self
.stdout
= sys
.stdout
194 self
.stderr
= sys
.stderr
195 # Import a default interpreter class if one isn't provided.
196 if InterpClass
== None:
197 from interpreter
import Interpreter
199 Interpreter
= InterpClass
200 # Create a replacement for stdin.
201 self
.reader
= PseudoFileIn(self
.readline
, self
.readlines
)
202 self
.reader
.input = ''
203 self
.reader
.isreading
= False
204 # Set up the interpreter.
205 self
.interp
= Interpreter(locals=locals,
206 rawin
=self
.raw_input,
208 stdout
=PseudoFileOut(self
.writeOut
),
209 stderr
=PseudoFileErr(self
.writeErr
),
212 self
.buffer = Buffer()
213 # Find out for which keycodes the interpreter will autocomplete.
214 self
.autoCompleteKeys
= self
.interp
.getAutoCompleteKeys()
215 # Keep track of the last non-continuation prompt positions.
216 self
.promptPosStart
= 0
217 self
.promptPosEnd
= 0
218 # Keep track of multi-line commands.
220 # Create the command history. Commands are added into the
221 # front of the list (ie. at index 0) as they are entered.
222 # self.historyIndex is the current position in the history; it
223 # gets incremented as you retrieve the previous command,
224 # decremented as you retrieve the next, and reset when you hit
225 # Enter. self.historyIndex == -1 means you're on the current
226 # command, not in the history.
228 self
.historyIndex
= -1
229 # Assign handlers for keyboard events.
230 wx
.EVT_CHAR(self
, self
.OnChar
)
231 wx
.EVT_KEY_DOWN(self
, self
.OnKeyDown
)
232 # Assign handler for idle time.
234 wx
.EVT_IDLE(self
, self
.OnIdle
)
235 # Display the introductory banner information.
236 self
.showIntro(introText
)
237 # Assign some pseudo keywords to the interpreter's namespace.
238 self
.setBuiltinKeywords()
239 # Add 'shell' to the interpreter's local namespace.
241 # Do this last so the user has complete control over their
242 # environment. They can override anything they want.
243 self
.execStartupScript(self
.interp
.startupScript
)
244 wx
.CallAfter(self
.ScrollToLine
, 0)
250 """Set focus to the shell."""
253 def OnIdle(self
, event
):
254 """Free the CPU to do other things."""
259 def showIntro(self
, text
=''):
260 """Display introductory text in the shell."""
262 if not text
.endswith(os
.linesep
):
266 self
.write(self
.interp
.introText
)
267 except AttributeError:
270 def setBuiltinKeywords(self
):
271 """Create pseudo keywords as part of builtins.
273 This sets `close`, `exit` and `quit` to a helpful string.
276 __builtin__
.close
= __builtin__
.exit
= __builtin__
.quit
= \
277 'Click on the close button to leave the application.'
280 """Quit the application."""
282 # XXX Good enough for now but later we want to send a close event.
284 # In the close event handler we can make sure they want to
285 # quit. Other applications, like PythonCard, may choose to
286 # hide rather than quit so we should just post the event and
287 # let the surrounding app decide what it wants to do.
288 self
.write('Click on the close button to leave the application.')
290 def setLocalShell(self
):
291 """Add 'shell' to locals as reference to ShellFacade instance."""
292 self
.interp
.locals['shell'] = ShellFacade(other
=self
)
294 def execStartupScript(self
, startupScript
):
295 """Execute the user's PYTHONSTARTUP script if they have one."""
296 if startupScript
and os
.path
.isfile(startupScript
):
297 text
= 'Startup script executed: ' + startupScript
298 self
.push('print %r; execfile(%r)' % (text
, startupScript
))
303 """Display information about Py."""
307 Py Shell Revision: %s
308 Py Interpreter Revision: %s
312 (__author__
, VERSION
, self
.revision
, self
.interp
.revision
,
313 sys
.version
.split()[0], wx
.VERSION_STRING
, sys
.platform
)
314 self
.write(text
.strip())
316 def OnChar(self
, event
):
317 """Keypress event handler.
319 Only receives an event if OnKeyDown calls event.Skip() for the
320 corresponding event."""
322 # Prevent modification of previously submitted
323 # commands/responses.
324 if not self
.CanEdit():
326 key
= event
.KeyCode()
327 currpos
= self
.GetCurrentPos()
328 stoppos
= self
.promptPosEnd
329 # Return (Enter) needs to be ignored in this handler.
330 if key
== wx
.WXK_RETURN
:
332 elif key
in self
.autoCompleteKeys
:
333 # Usually the dot (period) key activates auto completion.
334 # Get the command between the prompt and the cursor. Add
335 # the autocomplete character to the end of the command.
336 if self
.AutoCompActive():
337 self
.AutoCompCancel()
338 command
= self
.GetTextRange(stoppos
, currpos
) + chr(key
)
340 if self
.autoComplete
:
341 self
.autoCompleteShow(command
)
342 elif key
== ord('('):
343 # The left paren activates a call tip and cancels an
344 # active auto completion.
345 if self
.AutoCompActive():
346 self
.AutoCompCancel()
347 # Get the command between the prompt and the cursor. Add
348 # the '(' to the end of the command.
349 self
.ReplaceSelection('')
350 command
= self
.GetTextRange(stoppos
, currpos
) + '('
352 self
.autoCallTipShow(command
)
354 # Allow the normal event handling to take place.
357 def OnKeyDown(self
, event
):
358 """Key down event handler."""
360 key
= event
.KeyCode()
361 # If the auto-complete window is up let it do its thing.
362 if self
.AutoCompActive():
365 # Prevent modification of previously submitted
366 # commands/responses.
367 controlDown
= event
.ControlDown()
368 altDown
= event
.AltDown()
369 shiftDown
= event
.ShiftDown()
370 currpos
= self
.GetCurrentPos()
371 endpos
= self
.GetTextLength()
372 selecting
= self
.GetSelectionStart() != self
.GetSelectionEnd()
373 # Return (Enter) is used to submit a command to the
375 if not controlDown
and key
== wx
.WXK_RETURN
:
376 if self
.CallTipActive():
379 # Ctrl+Return (Cntrl+Enter) is used to insert a line break.
380 elif controlDown
and key
== wx
.WXK_RETURN
:
381 if self
.CallTipActive():
383 if currpos
== endpos
:
386 self
.insertLineBreak()
387 # Let Ctrl-Alt-* get handled normally.
388 elif controlDown
and altDown
:
390 # Clear the current, unexecuted command.
391 elif key
== wx
.WXK_ESCAPE
:
392 if self
.CallTipActive():
396 # Increase font size.
397 elif controlDown
and key
in (ord(']'),):
398 dispatcher
.send(signal
='FontIncrease')
399 # Decrease font size.
400 elif controlDown
and key
in (ord('['),):
401 dispatcher
.send(signal
='FontDecrease')
403 elif controlDown
and key
in (ord('='),):
404 dispatcher
.send(signal
='FontDefault')
405 # Cut to the clipboard.
406 elif (controlDown
and key
in (ord('X'), ord('x'))) \
407 or (shiftDown
and key
== wx
.WXK_DELETE
):
409 # Copy to the clipboard.
410 elif controlDown
and not shiftDown \
411 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
413 # Copy to the clipboard, including prompts.
414 elif controlDown
and shiftDown \
415 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
416 self
.CopyWithPrompts()
417 # Copy to the clipboard, including prefixed prompts.
418 elif altDown
and not controlDown \
419 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
420 self
.CopyWithPromptsPrefixed()
421 # Home needs to be aware of the prompt.
422 elif key
== wx
.WXK_HOME
:
423 home
= self
.promptPosEnd
425 self
.SetCurrentPos(home
)
426 if not selecting
and not shiftDown
:
428 self
.EnsureCaretVisible()
432 # The following handlers modify text, so we need to see if
433 # there is a selection that includes text prior to the prompt.
435 # Don't modify a selection with text prior to the prompt.
436 elif selecting
and key
not in NAVKEYS
and not self
.CanEdit():
438 # Paste from the clipboard.
439 elif (controlDown
and not shiftDown
and key
in (ord('V'), ord('v'))) \
440 or (shiftDown
and not controlDown
and key
== wx
.WXK_INSERT
):
442 # Paste from the clipboard, run commands.
443 elif controlDown
and shiftDown
and key
in (ord('V'), ord('v')):
445 # Replace with the previous command from the history buffer.
446 elif (controlDown
and key
== wx
.WXK_UP
) \
447 or (altDown
and key
in (ord('P'), ord('p'))):
448 self
.OnHistoryReplace(step
=+1)
449 # Replace with the next command from the history buffer.
450 elif (controlDown
and key
== wx
.WXK_DOWN
) \
451 or (altDown
and key
in (ord('N'), ord('n'))):
452 self
.OnHistoryReplace(step
=-1)
453 # Insert the previous command from the history buffer.
454 elif (shiftDown
and key
== wx
.WXK_UP
) and self
.CanEdit():
455 self
.OnHistoryInsert(step
=+1)
456 # Insert the next command from the history buffer.
457 elif (shiftDown
and key
== wx
.WXK_DOWN
) and self
.CanEdit():
458 self
.OnHistoryInsert(step
=-1)
459 # Search up the history for the text in front of the cursor.
460 elif key
== wx
.WXK_F8
:
461 self
.OnHistorySearch()
462 # Don't backspace over the latest non-continuation prompt.
463 elif key
== wx
.WXK_BACK
:
464 if selecting
and self
.CanEdit():
466 elif currpos
> self
.promptPosEnd
:
468 # Only allow these keys after the latest prompt.
469 elif key
in (wx
.WXK_TAB
, wx
.WXK_DELETE
):
472 # Don't toggle between insert mode and overwrite mode.
473 elif key
== wx
.WXK_INSERT
:
475 # Don't allow line deletion.
476 elif controlDown
and key
in (ord('L'), ord('l')):
478 # Don't allow line transposition.
479 elif controlDown
and key
in (ord('T'), ord('t')):
481 # Basic navigation keys should work anywhere.
484 # Protect the readonly portion of the shell.
485 elif not self
.CanEdit():
490 def clearCommand(self
):
491 """Delete the current, unexecuted command."""
492 startpos
= self
.promptPosEnd
493 endpos
= self
.GetTextLength()
494 self
.SetSelection(startpos
, endpos
)
495 self
.ReplaceSelection('')
498 def OnHistoryReplace(self
, step
):
499 """Replace with the previous/next command from the history buffer."""
501 self
.replaceFromHistory(step
)
503 def replaceFromHistory(self
, step
):
504 """Replace selection with command from the history buffer."""
506 self
.ReplaceSelection('')
507 newindex
= self
.historyIndex
+ step
508 if -1 <= newindex
<= len(self
.history
):
509 self
.historyIndex
= newindex
510 if 0 <= newindex
<= len(self
.history
)-1:
511 command
= self
.history
[self
.historyIndex
]
512 command
= command
.replace('\n', os
.linesep
+ ps2
)
513 self
.ReplaceSelection(command
)
515 def OnHistoryInsert(self
, step
):
516 """Insert the previous/next command from the history buffer."""
517 if not self
.CanEdit():
519 startpos
= self
.GetCurrentPos()
520 self
.replaceFromHistory(step
)
521 endpos
= self
.GetCurrentPos()
522 self
.SetSelection(endpos
, startpos
)
524 def OnHistorySearch(self
):
525 """Search up the history buffer for the text in front of the cursor."""
526 if not self
.CanEdit():
528 startpos
= self
.GetCurrentPos()
529 # The text up to the cursor is what we search for.
530 numCharsAfterCursor
= self
.GetTextLength() - startpos
531 searchText
= self
.getCommand(rstrip
=False)
532 if numCharsAfterCursor
> 0:
533 searchText
= searchText
[:-numCharsAfterCursor
]
536 # Search upwards from the current history position and loop
537 # back to the beginning if we don't find anything.
538 if (self
.historyIndex
<= -1) \
539 or (self
.historyIndex
>= len(self
.history
)-2):
540 searchOrder
= range(len(self
.history
))
542 searchOrder
= range(self
.historyIndex
+1, len(self
.history
)) + \
543 range(self
.historyIndex
)
544 for i
in searchOrder
:
545 command
= self
.history
[i
]
546 if command
[:len(searchText
)] == searchText
:
547 # Replace the current selection with the one we found.
548 self
.ReplaceSelection(command
[len(searchText
):])
549 endpos
= self
.GetCurrentPos()
550 self
.SetSelection(endpos
, startpos
)
551 # We've now warped into middle of the history.
552 self
.historyIndex
= i
555 def setStatusText(self
, text
):
556 """Display status information."""
558 # This method will likely be replaced by the enclosing app to
559 # do something more interesting, like write to a status bar.
562 def insertLineBreak(self
):
563 """Insert a new line break."""
565 self
.write(os
.linesep
)
569 def processLine(self
):
570 """Process the line of text at which the user hit Enter."""
572 # The user hit ENTER and we need to decide what to do. They
573 # could be sitting on any line in the shell.
575 thepos
= self
.GetCurrentPos()
576 startpos
= self
.promptPosEnd
577 endpos
= self
.GetTextLength()
579 # If they hit RETURN inside the current command, execute the
582 self
.SetCurrentPos(endpos
)
583 self
.interp
.more
= False
584 command
= self
.GetTextRange(startpos
, endpos
)
585 lines
= command
.split(os
.linesep
+ ps2
)
586 lines
= [line
.rstrip() for line
in lines
]
587 command
= '\n'.join(lines
)
588 if self
.reader
.isreading
:
590 # Match the behavior of the standard Python shell
591 # when the user hits return without entering a
594 self
.reader
.input = command
595 self
.write(os
.linesep
)
598 # Or replace the current command with the other command.
600 # If the line contains a command (even an invalid one).
601 if self
.getCommand(rstrip
=False):
602 command
= self
.getMultilineCommand()
605 # Otherwise, put the cursor back where we started.
607 self
.SetCurrentPos(thepos
)
608 self
.SetAnchor(thepos
)
610 def getMultilineCommand(self
, rstrip
=True):
611 """Extract a multi-line command from the editor.
613 The command may not necessarily be valid Python syntax."""
614 # XXX Need to extract real prompts here. Need to keep track of
615 # the prompt every time a command is issued.
620 # This is a total hack job, but it works.
621 text
= self
.GetCurLine()[0]
622 line
= self
.GetCurrentLine()
623 while text
[:ps2size
] == ps2
and line
> 0:
626 text
= self
.GetCurLine()[0]
627 if text
[:ps1size
] == ps1
:
628 line
= self
.GetCurrentLine()
630 startpos
= self
.GetCurrentPos() + ps1size
633 while self
.GetCurLine()[0][:ps2size
] == ps2
:
636 stoppos
= self
.GetCurrentPos()
637 command
= self
.GetTextRange(startpos
, stoppos
)
638 command
= command
.replace(os
.linesep
+ ps2
, '\n')
639 command
= command
.rstrip()
640 command
= command
.replace('\n', os
.linesep
+ ps2
)
644 command
= command
.rstrip()
647 def getCommand(self
, text
=None, rstrip
=True):
648 """Extract a command from text which may include a shell prompt.
650 The command may not necessarily be valid Python syntax."""
652 text
= self
.GetCurLine()[0]
653 # Strip the prompt off the front leaving just the command.
654 command
= self
.lstripPrompt(text
)
656 command
= '' # Real commands have prompts.
658 command
= command
.rstrip()
661 def lstripPrompt(self
, text
):
662 """Return text without a leading prompt."""
667 # Strip the prompt off the front of text.
668 if text
[:ps1size
] == ps1
:
669 text
= text
[ps1size
:]
670 elif text
[:ps2size
] == ps2
:
671 text
= text
[ps2size
:]
674 def push(self
, command
):
675 """Send command to the interpreter for execution."""
676 self
.write(os
.linesep
)
677 busy
= wx
.BusyCursor()
679 self
.more
= self
.interp
.push(command
)
683 self
.addHistory(command
.rstrip())
686 def addHistory(self
, command
):
687 """Add command to the command history."""
688 # Reset the history position.
689 self
.historyIndex
= -1
690 # Insert this command into the history, unless it's a blank
691 # line or the same as the last command.
693 and (len(self
.history
) == 0 or command
!= self
.history
[0]):
694 self
.history
.insert(0, command
)
696 def write(self
, text
):
697 """Display text in the shell.
699 Replace line endings with OS-specific endings."""
700 text
= self
.fixLineEndings(text
)
702 self
.EnsureCaretVisible()
704 def fixLineEndings(self
, text
):
705 """Return text with line endings replaced by OS-specific endings."""
706 lines
= text
.split('\r\n')
707 for l
in range(len(lines
)):
708 chunks
= lines
[l
].split('\r')
709 for c
in range(len(chunks
)):
710 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n'))
711 lines
[l
] = os
.linesep
.join(chunks
)
712 text
= os
.linesep
.join(lines
)
716 """Display proper prompt for the context: ps1, ps2 or ps3.
718 If this is a continuation line, autoindent as necessary."""
719 isreading
= self
.reader
.isreading
722 prompt
= str(sys
.ps3
)
724 prompt
= str(sys
.ps2
)
726 prompt
= str(sys
.ps1
)
727 pos
= self
.GetCurLine()[1]
732 self
.write(os
.linesep
)
734 self
.promptPosStart
= self
.GetCurrentPos()
738 self
.promptPosEnd
= self
.GetCurrentPos()
739 # Keep the undo feature from undoing previous responses.
740 self
.EmptyUndoBuffer()
741 # XXX Add some autoindent magic here if more.
743 self
.write(' '*4) # Temporary hack indentation.
744 self
.EnsureCaretVisible()
745 self
.ScrollToColumn(0)
748 """Replacement for stdin.readline()."""
751 reader
.isreading
= True
754 while not reader
.input:
759 reader
.isreading
= False
760 input = str(input) # In case of Unicode.
764 """Replacement for stdin.readlines()."""
766 while lines
[-1:] != ['\n']:
767 lines
.append(self
.readline())
770 def raw_input(self
, prompt
=''):
771 """Return string based on user input."""
774 return self
.readline()
776 def ask(self
, prompt
='Please enter your response:'):
777 """Get response from the user using a dialog box."""
778 dialog
= wx
.TextEntryDialog(None, prompt
,
779 'Input Dialog (Raw)', '')
781 if dialog
.ShowModal() == wx
.ID_OK
:
782 text
= dialog
.GetValue()
789 """Halt execution pending a response from the user."""
790 self
.ask('Press enter to continue:')
793 """Delete all text from the shell."""
796 def run(self
, command
, prompt
=True, verbose
=True):
797 """Execute command as if it was typed in directly.
798 >>> shell.run('print "this"')
803 # Go to the very bottom of the text.
804 endpos
= self
.GetTextLength()
805 self
.SetCurrentPos(endpos
)
806 command
= command
.rstrip()
807 if prompt
: self
.prompt()
808 if verbose
: self
.write(command
)
811 def runfile(self
, filename
):
812 """Execute all commands in file as if they were typed into the
814 file = open(filename
)
817 for command
in file.readlines():
818 if command
[:6] == 'shell.':
819 # Run shell methods silently.
820 self
.run(command
, prompt
=False, verbose
=False)
822 self
.run(command
, prompt
=False, verbose
=True)
826 def autoCompleteShow(self
, command
):
827 """Display auto-completion popup list."""
828 list = self
.interp
.getAutoCompleteList(command
,
829 includeMagic
=self
.autoCompleteIncludeMagic
,
830 includeSingle
=self
.autoCompleteIncludeSingle
,
831 includeDouble
=self
.autoCompleteIncludeDouble
)
833 options
= ' '.join(list)
835 self
.AutoCompShow(offset
, options
)
837 def autoCallTipShow(self
, command
):
838 """Display argument spec and docstring in a popup window."""
839 if self
.CallTipActive():
841 (name
, argspec
, tip
) = self
.interp
.getCallTip(command
)
843 dispatcher
.send(signal
='Shell.calltip', sender
=self
, calltip
=tip
)
844 if not self
.autoCallTip
:
847 startpos
= self
.GetCurrentPos()
848 self
.write(argspec
+ ')')
849 endpos
= self
.GetCurrentPos()
850 self
.SetSelection(endpos
, startpos
)
852 curpos
= self
.GetCurrentPos()
853 tippos
= curpos
- (len(name
) + 1)
854 fallback
= curpos
- self
.GetColumn(curpos
)
855 # In case there isn't enough room, only go back to the
857 tippos
= max(tippos
, fallback
)
858 self
.CallTipShow(tippos
, tip
)
860 def writeOut(self
, text
):
861 """Replacement for stdout."""
864 def writeErr(self
, text
):
865 """Replacement for stderr."""
868 def redirectStdin(self
, redirect
=True):
869 """If redirect is true then sys.stdin will come from the shell."""
871 sys
.stdin
= self
.reader
873 sys
.stdin
= self
.stdin
875 def redirectStdout(self
, redirect
=True):
876 """If redirect is true then sys.stdout will go to the shell."""
878 sys
.stdout
= PseudoFileOut(self
.writeOut
)
880 sys
.stdout
= self
.stdout
882 def redirectStderr(self
, redirect
=True):
883 """If redirect is true then sys.stderr will go to the shell."""
885 sys
.stderr
= PseudoFileErr(self
.writeErr
)
887 sys
.stderr
= self
.stderr
890 """Return true if text is selected and can be cut."""
891 if self
.GetSelectionStart() != self
.GetSelectionEnd() \
892 and self
.GetSelectionStart() >= self
.promptPosEnd \
893 and self
.GetSelectionEnd() >= self
.promptPosEnd
:
899 """Return true if a paste should succeed."""
900 if self
.CanEdit() and editwindow
.EditWindow
.CanPaste(self
):
906 """Return true if editing should succeed."""
907 if self
.GetSelectionStart() != self
.GetSelectionEnd():
908 if self
.GetSelectionStart() >= self
.promptPosEnd \
909 and self
.GetSelectionEnd() >= self
.promptPosEnd
:
914 return self
.GetCurrentPos() >= self
.promptPosEnd
917 """Remove selection and place it on the clipboard."""
918 if self
.CanCut() and self
.CanCopy():
919 if self
.AutoCompActive():
920 self
.AutoCompCancel()
921 if self
.CallTipActive():
924 self
.ReplaceSelection('')
927 """Copy selection and place it on the clipboard."""
931 command
= self
.GetSelectedText()
932 command
= command
.replace(os
.linesep
+ ps2
, os
.linesep
)
933 command
= command
.replace(os
.linesep
+ ps1
, os
.linesep
)
934 command
= self
.lstripPrompt(text
=command
)
935 data
= wx
.TextDataObject(command
)
938 def CopyWithPrompts(self
):
939 """Copy selection, including prompts, and place it on the clipboard."""
941 command
= self
.GetSelectedText()
942 data
= wx
.TextDataObject(command
)
945 def CopyWithPromptsPrefixed(self
):
946 """Copy selection, including prompts prefixed with four
947 spaces, and place it on the clipboard."""
949 command
= self
.GetSelectedText()
951 command
= spaces
+ command
.replace(os
.linesep
,
953 data
= wx
.TextDataObject(command
)
956 def _clip(self
, data
):
957 if wx
.TheClipboard
.Open():
958 wx
.TheClipboard
.UsePrimarySelection(False)
959 wx
.TheClipboard
.SetData(data
)
960 wx
.TheClipboard
.Flush()
961 wx
.TheClipboard
.Close()
964 """Replace selection with clipboard contents."""
965 if self
.CanPaste() and wx
.TheClipboard
.Open():
967 if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)):
968 data
= wx
.TextDataObject()
969 if wx
.TheClipboard
.GetData(data
):
970 self
.ReplaceSelection('')
971 command
= data
.GetText()
972 command
= command
.rstrip()
973 command
= self
.fixLineEndings(command
)
974 command
= self
.lstripPrompt(text
=command
)
975 command
= command
.replace(os
.linesep
+ ps2
, '\n')
976 command
= command
.replace(os
.linesep
, '\n')
977 command
= command
.replace('\n', os
.linesep
+ ps2
)
979 wx
.TheClipboard
.Close()
981 def PasteAndRun(self
):
982 """Replace selection with clipboard contents, run commands."""
983 if wx
.TheClipboard
.Open():
986 if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)):
987 data
= wx
.TextDataObject()
988 if wx
.TheClipboard
.GetData(data
):
989 endpos
= self
.GetTextLength()
990 self
.SetCurrentPos(endpos
)
991 startpos
= self
.promptPosEnd
992 self
.SetSelection(startpos
, endpos
)
993 self
.ReplaceSelection('')
994 text
= data
.GetText()
996 text
= self
.fixLineEndings(text
)
997 text
= self
.lstripPrompt(text
)
998 text
= text
.replace(os
.linesep
+ ps1
, '\n')
999 text
= text
.replace(os
.linesep
+ ps2
, '\n')
1000 text
= text
.replace(os
.linesep
, '\n')
1001 lines
= text
.split('\n')
1005 if line
.strip() == ps2
.strip():
1006 # If we are pasting from something like a
1007 # web page that drops the trailing space
1008 # from the ps2 prompt of a blank line.
1010 if line
.strip() != '' and line
.lstrip() == line
:
1013 # Add the previous command to the list.
1014 commands
.append(command
)
1015 # Start a new command, which may be multiline.
1018 # Multiline command. Add to the command.
1021 commands
.append(command
)
1022 for command
in commands
:
1023 command
= command
.replace('\n', os
.linesep
+ ps2
)
1026 wx
.TheClipboard
.Close()
1028 def wrap(self
, wrap
=True):
1029 """Sets whether text is word wrapped."""
1031 self
.SetWrapMode(wrap
)
1032 except AttributeError:
1033 return 'Wrapping is not available in this version.'
1035 def zoom(self
, points
=0):
1036 """Set the zoom level.
1038 This number of points is added to the size of all fonts. It
1039 may be positive to magnify or negative to reduce."""
1040 self
.SetZoom(points
)