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 'Python Version: %s\n' % sys
.version
.split()[0] + \
76 'wxPython Version: %s\n' % wx
.VERSION_STRING
+ \
77 'Platform: %s\n' % sys
.platform
78 dialog
= wx
.MessageDialog(self
, text
, title
,
79 wx
.OK | wx
.ICON_INFORMATION
)
85 """Simplified interface to all shell-related functionality.
87 This is a semi-transparent facade, in that all attributes of other
88 are accessible, even though only some are visible to the user."""
90 name
= 'Shell Interface'
91 revision
= __revision__
93 def __init__(self
, other
):
94 """Create a ShellFacade instance."""
100 Home Go to the beginning of the command or line.
101 Shift+Home Select to the beginning of the command or line.
102 Shift+End Select to the end of the line.
103 End Go to the end of the line.
104 Ctrl+C Copy selected text, removing prompts.
105 Ctrl+Shift+C Copy selected text, retaining prompts.
106 Ctrl+X Cut selected text.
107 Ctrl+V Paste from clipboard.
108 Ctrl+Shift+V Paste and run multiple commands from clipboard.
109 Ctrl+Up Arrow Retrieve Previous History item.
110 Alt+P Retrieve Previous History item.
111 Ctrl+Down Arrow Retrieve Next History item.
112 Alt+N Retrieve Next History item.
113 Shift+Up Arrow Insert Previous History item.
114 Shift+Down Arrow Insert Next History item.
115 F8 Command-completion of History item.
116 (Type a few characters of a previous command and press F8.)
117 Ctrl+Enter Insert new line into multiline command.
118 Ctrl+] Increase font size.
119 Ctrl+[ Decrease font size.
120 Ctrl+= Default font size.
124 """Display some useful information about how to use the shell."""
125 self
.write(self
.helpText
)
127 def __getattr__(self
, name
):
128 if hasattr(self
.other
, name
):
129 return getattr(self
.other
, name
)
131 raise AttributeError, name
133 def __setattr__(self
, name
, value
):
134 if self
.__dict
__.has_key(name
):
135 self
.__dict
__[name
] = value
136 elif hasattr(self
.other
, name
):
137 setattr(self
.other
, name
, value
)
139 raise AttributeError, name
141 def _getAttributeNames(self
):
142 """Return list of magic attributes to extend introspection."""
148 'autoCompleteAutoHide',
149 'autoCompleteCaseInsensitive',
150 'autoCompleteIncludeDouble',
151 'autoCompleteIncludeMagic',
152 'autoCompleteIncludeSingle',
169 class Shell(editwindow
.EditWindow
):
170 """Shell based on StyledTextCtrl."""
173 revision
= __revision__
175 def __init__(self
, parent
, id=-1, pos
=wx
.DefaultPosition
,
176 size
=wx
.DefaultSize
, style
=wx
.CLIP_CHILDREN
,
177 introText
='', locals=None, InterpClass
=None, *args
, **kwds
):
178 """Create Shell instance."""
179 editwindow
.EditWindow
.__init
__(self
, parent
, id, pos
, size
, style
)
183 # Grab these so they can be restored by self.redirect* methods.
184 self
.stdin
= sys
.stdin
185 self
.stdout
= sys
.stdout
186 self
.stderr
= sys
.stderr
187 # Import a default interpreter class if one isn't provided.
188 if InterpClass
== None:
189 from interpreter
import Interpreter
191 Interpreter
= InterpClass
192 # Create a replacement for stdin.
193 self
.reader
= PseudoFileIn(self
.readline
, self
.readlines
)
194 self
.reader
.input = ''
195 self
.reader
.isreading
= False
196 # Set up the interpreter.
197 self
.interp
= Interpreter(locals=locals,
198 rawin
=self
.raw_input,
200 stdout
=PseudoFileOut(self
.writeOut
),
201 stderr
=PseudoFileErr(self
.writeErr
),
204 self
.buffer = Buffer()
205 # Find out for which keycodes the interpreter will autocomplete.
206 self
.autoCompleteKeys
= self
.interp
.getAutoCompleteKeys()
207 # Keep track of the last non-continuation prompt positions.
208 self
.promptPosStart
= 0
209 self
.promptPosEnd
= 0
210 # Keep track of multi-line commands.
212 # Create the command history. Commands are added into the
213 # front of the list (ie. at index 0) as they are entered.
214 # self.historyIndex is the current position in the history; it
215 # gets incremented as you retrieve the previous command,
216 # decremented as you retrieve the next, and reset when you hit
217 # Enter. self.historyIndex == -1 means you're on the current
218 # command, not in the history.
220 self
.historyIndex
= -1
221 # Assign handlers for keyboard events.
222 wx
.EVT_CHAR(self
, self
.OnChar
)
223 wx
.EVT_KEY_DOWN(self
, self
.OnKeyDown
)
224 # Assign handler for idle time.
226 wx
.EVT_IDLE(self
, self
.OnIdle
)
227 # Display the introductory banner information.
228 self
.showIntro(introText
)
229 # Assign some pseudo keywords to the interpreter's namespace.
230 self
.setBuiltinKeywords()
231 # Add 'shell' to the interpreter's local namespace.
233 # Do this last so the user has complete control over their
234 # environment. They can override anything they want.
235 self
.execStartupScript(self
.interp
.startupScript
)
236 wx
.CallAfter(self
.ScrollToLine
, 0)
242 """Set focus to the shell."""
245 def OnIdle(self
, event
):
246 """Free the CPU to do other things."""
251 def showIntro(self
, text
=''):
252 """Display introductory text in the shell."""
254 if not text
.endswith(os
.linesep
):
258 self
.write(self
.interp
.introText
)
259 except AttributeError:
262 def setBuiltinKeywords(self
):
263 """Create pseudo keywords as part of builtins.
265 This sets `close`, `exit` and `quit` to a helpful string.
268 __builtin__
.close
= __builtin__
.exit
= __builtin__
.quit
= \
269 'Click on the close button to leave the application.'
272 """Quit the application."""
274 # XXX Good enough for now but later we want to send a close event.
276 # In the close event handler we can make sure they want to
277 # quit. Other applications, like PythonCard, may choose to
278 # hide rather than quit so we should just post the event and
279 # let the surrounding app decide what it wants to do.
280 self
.write('Click on the close button to leave the application.')
282 def setLocalShell(self
):
283 """Add 'shell' to locals as reference to ShellFacade instance."""
284 self
.interp
.locals['shell'] = ShellFacade(other
=self
)
286 def execStartupScript(self
, startupScript
):
287 """Execute the user's PYTHONSTARTUP script if they have one."""
288 if startupScript
and os
.path
.isfile(startupScript
):
289 text
= 'Startup script executed: ' + startupScript
290 self
.push('print %r; execfile(%r)' % (text
, startupScript
))
295 """Display information about Py."""
299 Py Shell Revision: %s
300 Py Interpreter Revision: %s
304 (__author__
, VERSION
, self
.revision
, self
.interp
.revision
,
305 sys
.version
.split()[0], wx
.VERSION_STRING
, sys
.platform
)
306 self
.write(text
.strip())
308 def OnChar(self
, event
):
309 """Keypress event handler.
311 Only receives an event if OnKeyDown calls event.Skip() for the
312 corresponding event."""
314 # Prevent modification of previously submitted
315 # commands/responses.
316 if not self
.CanEdit():
318 key
= event
.KeyCode()
319 currpos
= self
.GetCurrentPos()
320 stoppos
= self
.promptPosEnd
321 # Return (Enter) needs to be ignored in this handler.
322 if key
== wx
.WXK_RETURN
:
324 elif key
in self
.autoCompleteKeys
:
325 # Usually the dot (period) key activates auto completion.
326 # Get the command between the prompt and the cursor. Add
327 # the autocomplete character to the end of the command.
328 if self
.AutoCompActive():
329 self
.AutoCompCancel()
330 command
= self
.GetTextRange(stoppos
, currpos
) + chr(key
)
332 if self
.autoComplete
:
333 self
.autoCompleteShow(command
)
334 elif key
== ord('('):
335 # The left paren activates a call tip and cancels an
336 # active auto completion.
337 if self
.AutoCompActive():
338 self
.AutoCompCancel()
339 # Get the command between the prompt and the cursor. Add
340 # the '(' to the end of the command.
341 self
.ReplaceSelection('')
342 command
= self
.GetTextRange(stoppos
, currpos
) + '('
344 self
.autoCallTipShow(command
)
346 # Allow the normal event handling to take place.
349 def OnKeyDown(self
, event
):
350 """Key down event handler."""
352 key
= event
.KeyCode()
353 # If the auto-complete window is up let it do its thing.
354 if self
.AutoCompActive():
357 # Prevent modification of previously submitted
358 # commands/responses.
359 controlDown
= event
.ControlDown()
360 altDown
= event
.AltDown()
361 shiftDown
= event
.ShiftDown()
362 currpos
= self
.GetCurrentPos()
363 endpos
= self
.GetTextLength()
364 selecting
= self
.GetSelectionStart() != self
.GetSelectionEnd()
365 # Return (Enter) is used to submit a command to the
367 if not controlDown
and key
== wx
.WXK_RETURN
:
368 if self
.CallTipActive():
371 # Ctrl+Return (Cntrl+Enter) is used to insert a line break.
372 elif controlDown
and key
== wx
.WXK_RETURN
:
373 if self
.CallTipActive():
375 if currpos
== endpos
:
378 self
.insertLineBreak()
379 # Let Ctrl-Alt-* get handled normally.
380 elif controlDown
and altDown
:
382 # Clear the current, unexecuted command.
383 elif key
== wx
.WXK_ESCAPE
:
384 if self
.CallTipActive():
388 # Increase font size.
389 elif controlDown
and key
in (ord(']'),):
390 dispatcher
.send(signal
='FontIncrease')
391 # Decrease font size.
392 elif controlDown
and key
in (ord('['),):
393 dispatcher
.send(signal
='FontDecrease')
395 elif controlDown
and key
in (ord('='),):
396 dispatcher
.send(signal
='FontDefault')
397 # Cut to the clipboard.
398 elif (controlDown
and key
in (ord('X'), ord('x'))) \
399 or (shiftDown
and key
== wx
.WXK_DELETE
):
401 # Copy to the clipboard.
402 elif controlDown
and not shiftDown \
403 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
405 # Copy to the clipboard, including prompts.
406 elif controlDown
and shiftDown \
407 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
408 self
.CopyWithPrompts()
409 # Copy to the clipboard, including prefixed prompts.
410 elif altDown
and not controlDown \
411 and key
in (ord('C'), ord('c'), wx
.WXK_INSERT
):
412 self
.CopyWithPromptsPrefixed()
413 # Home needs to be aware of the prompt.
414 elif key
== wx
.WXK_HOME
:
415 home
= self
.promptPosEnd
417 self
.SetCurrentPos(home
)
418 if not selecting
and not shiftDown
:
420 self
.EnsureCaretVisible()
424 # The following handlers modify text, so we need to see if
425 # there is a selection that includes text prior to the prompt.
427 # Don't modify a selection with text prior to the prompt.
428 elif selecting
and key
not in NAVKEYS
and not self
.CanEdit():
430 # Paste from the clipboard.
431 elif (controlDown
and not shiftDown
and key
in (ord('V'), ord('v'))) \
432 or (shiftDown
and not controlDown
and key
== wx
.WXK_INSERT
):
434 # Paste from the clipboard, run commands.
435 elif controlDown
and shiftDown
and key
in (ord('V'), ord('v')):
437 # Replace with the previous command from the history buffer.
438 elif (controlDown
and key
== wx
.WXK_UP
) \
439 or (altDown
and key
in (ord('P'), ord('p'))):
440 self
.OnHistoryReplace(step
=+1)
441 # Replace with the next command from the history buffer.
442 elif (controlDown
and key
== wx
.WXK_DOWN
) \
443 or (altDown
and key
in (ord('N'), ord('n'))):
444 self
.OnHistoryReplace(step
=-1)
445 # Insert the previous command from the history buffer.
446 elif (shiftDown
and key
== wx
.WXK_UP
) and self
.CanEdit():
447 self
.OnHistoryInsert(step
=+1)
448 # Insert the next command from the history buffer.
449 elif (shiftDown
and key
== wx
.WXK_DOWN
) and self
.CanEdit():
450 self
.OnHistoryInsert(step
=-1)
451 # Search up the history for the text in front of the cursor.
452 elif key
== wx
.WXK_F8
:
453 self
.OnHistorySearch()
454 # Don't backspace over the latest non-continuation prompt.
455 elif key
== wx
.WXK_BACK
:
456 if selecting
and self
.CanEdit():
458 elif currpos
> self
.promptPosEnd
:
460 # Only allow these keys after the latest prompt.
461 elif key
in (wx
.WXK_TAB
, wx
.WXK_DELETE
):
464 # Don't toggle between insert mode and overwrite mode.
465 elif key
== wx
.WXK_INSERT
:
467 # Don't allow line deletion.
468 elif controlDown
and key
in (ord('L'), ord('l')):
470 # Don't allow line transposition.
471 elif controlDown
and key
in (ord('T'), ord('t')):
473 # Basic navigation keys should work anywhere.
476 # Protect the readonly portion of the shell.
477 elif not self
.CanEdit():
482 def clearCommand(self
):
483 """Delete the current, unexecuted command."""
484 startpos
= self
.promptPosEnd
485 endpos
= self
.GetTextLength()
486 self
.SetSelection(startpos
, endpos
)
487 self
.ReplaceSelection('')
490 def OnHistoryReplace(self
, step
):
491 """Replace with the previous/next command from the history buffer."""
493 self
.replaceFromHistory(step
)
495 def replaceFromHistory(self
, step
):
496 """Replace selection with command from the history buffer."""
498 self
.ReplaceSelection('')
499 newindex
= self
.historyIndex
+ step
500 if -1 <= newindex
<= len(self
.history
):
501 self
.historyIndex
= newindex
502 if 0 <= newindex
<= len(self
.history
)-1:
503 command
= self
.history
[self
.historyIndex
]
504 command
= command
.replace('\n', os
.linesep
+ ps2
)
505 self
.ReplaceSelection(command
)
507 def OnHistoryInsert(self
, step
):
508 """Insert the previous/next command from the history buffer."""
509 if not self
.CanEdit():
511 startpos
= self
.GetCurrentPos()
512 self
.replaceFromHistory(step
)
513 endpos
= self
.GetCurrentPos()
514 self
.SetSelection(endpos
, startpos
)
516 def OnHistorySearch(self
):
517 """Search up the history buffer for the text in front of the cursor."""
518 if not self
.CanEdit():
520 startpos
= self
.GetCurrentPos()
521 # The text up to the cursor is what we search for.
522 numCharsAfterCursor
= self
.GetTextLength() - startpos
523 searchText
= self
.getCommand(rstrip
=False)
524 if numCharsAfterCursor
> 0:
525 searchText
= searchText
[:-numCharsAfterCursor
]
528 # Search upwards from the current history position and loop
529 # back to the beginning if we don't find anything.
530 if (self
.historyIndex
<= -1) \
531 or (self
.historyIndex
>= len(self
.history
)-2):
532 searchOrder
= range(len(self
.history
))
534 searchOrder
= range(self
.historyIndex
+1, len(self
.history
)) + \
535 range(self
.historyIndex
)
536 for i
in searchOrder
:
537 command
= self
.history
[i
]
538 if command
[:len(searchText
)] == searchText
:
539 # Replace the current selection with the one we found.
540 self
.ReplaceSelection(command
[len(searchText
):])
541 endpos
= self
.GetCurrentPos()
542 self
.SetSelection(endpos
, startpos
)
543 # We've now warped into middle of the history.
544 self
.historyIndex
= i
547 def setStatusText(self
, text
):
548 """Display status information."""
550 # This method will likely be replaced by the enclosing app to
551 # do something more interesting, like write to a status bar.
554 def insertLineBreak(self
):
555 """Insert a new line break."""
557 self
.write(os
.linesep
)
561 def processLine(self
):
562 """Process the line of text at which the user hit Enter."""
564 # The user hit ENTER and we need to decide what to do. They
565 # could be sitting on any line in the shell.
567 thepos
= self
.GetCurrentPos()
568 startpos
= self
.promptPosEnd
569 endpos
= self
.GetTextLength()
571 # If they hit RETURN inside the current command, execute the
574 self
.SetCurrentPos(endpos
)
575 self
.interp
.more
= False
576 command
= self
.GetTextRange(startpos
, endpos
)
577 lines
= command
.split(os
.linesep
+ ps2
)
578 lines
= [line
.rstrip() for line
in lines
]
579 command
= '\n'.join(lines
)
580 if self
.reader
.isreading
:
582 # Match the behavior of the standard Python shell
583 # when the user hits return without entering a
586 self
.reader
.input = command
587 self
.write(os
.linesep
)
590 # Or replace the current command with the other command.
592 # If the line contains a command (even an invalid one).
593 if self
.getCommand(rstrip
=False):
594 command
= self
.getMultilineCommand()
597 # Otherwise, put the cursor back where we started.
599 self
.SetCurrentPos(thepos
)
600 self
.SetAnchor(thepos
)
602 def getMultilineCommand(self
, rstrip
=True):
603 """Extract a multi-line command from the editor.
605 The command may not necessarily be valid Python syntax."""
606 # XXX Need to extract real prompts here. Need to keep track of
607 # the prompt every time a command is issued.
612 # This is a total hack job, but it works.
613 text
= self
.GetCurLine()[0]
614 line
= self
.GetCurrentLine()
615 while text
[:ps2size
] == ps2
and line
> 0:
618 text
= self
.GetCurLine()[0]
619 if text
[:ps1size
] == ps1
:
620 line
= self
.GetCurrentLine()
622 startpos
= self
.GetCurrentPos() + ps1size
625 while self
.GetCurLine()[0][:ps2size
] == ps2
:
628 stoppos
= self
.GetCurrentPos()
629 command
= self
.GetTextRange(startpos
, stoppos
)
630 command
= command
.replace(os
.linesep
+ ps2
, '\n')
631 command
= command
.rstrip()
632 command
= command
.replace('\n', os
.linesep
+ ps2
)
636 command
= command
.rstrip()
639 def getCommand(self
, text
=None, rstrip
=True):
640 """Extract a command from text which may include a shell prompt.
642 The command may not necessarily be valid Python syntax."""
644 text
= self
.GetCurLine()[0]
645 # Strip the prompt off the front leaving just the command.
646 command
= self
.lstripPrompt(text
)
648 command
= '' # Real commands have prompts.
650 command
= command
.rstrip()
653 def lstripPrompt(self
, text
):
654 """Return text without a leading prompt."""
659 # Strip the prompt off the front of text.
660 if text
[:ps1size
] == ps1
:
661 text
= text
[ps1size
:]
662 elif text
[:ps2size
] == ps2
:
663 text
= text
[ps2size
:]
666 def push(self
, command
):
667 """Send command to the interpreter for execution."""
668 self
.write(os
.linesep
)
669 busy
= wx
.BusyCursor()
671 self
.more
= self
.interp
.push(command
)
675 self
.addHistory(command
.rstrip())
678 def addHistory(self
, command
):
679 """Add command to the command history."""
680 # Reset the history position.
681 self
.historyIndex
= -1
682 # Insert this command into the history, unless it's a blank
683 # line or the same as the last command.
685 and (len(self
.history
) == 0 or command
!= self
.history
[0]):
686 self
.history
.insert(0, command
)
688 def write(self
, text
):
689 """Display text in the shell.
691 Replace line endings with OS-specific endings."""
692 text
= self
.fixLineEndings(text
)
694 self
.EnsureCaretVisible()
696 def fixLineEndings(self
, text
):
697 """Return text with line endings replaced by OS-specific endings."""
698 lines
= text
.split('\r\n')
699 for l
in range(len(lines
)):
700 chunks
= lines
[l
].split('\r')
701 for c
in range(len(chunks
)):
702 chunks
[c
] = os
.linesep
.join(chunks
[c
].split('\n'))
703 lines
[l
] = os
.linesep
.join(chunks
)
704 text
= os
.linesep
.join(lines
)
708 """Display proper prompt for the context: ps1, ps2 or ps3.
710 If this is a continuation line, autoindent as necessary."""
711 isreading
= self
.reader
.isreading
714 prompt
= str(sys
.ps3
)
716 prompt
= str(sys
.ps2
)
718 prompt
= str(sys
.ps1
)
719 pos
= self
.GetCurLine()[1]
724 self
.write(os
.linesep
)
726 self
.promptPosStart
= self
.GetCurrentPos()
730 self
.promptPosEnd
= self
.GetCurrentPos()
731 # Keep the undo feature from undoing previous responses.
732 self
.EmptyUndoBuffer()
733 # XXX Add some autoindent magic here if more.
735 self
.write(' '*4) # Temporary hack indentation.
736 self
.EnsureCaretVisible()
737 self
.ScrollToColumn(0)
740 """Replacement for stdin.readline()."""
743 reader
.isreading
= True
746 while not reader
.input:
751 reader
.isreading
= False
752 input = str(input) # In case of Unicode.
756 """Replacement for stdin.readlines()."""
758 while lines
[-1:] != ['\n']:
759 lines
.append(self
.readline())
762 def raw_input(self
, prompt
=''):
763 """Return string based on user input."""
766 return self
.readline()
768 def ask(self
, prompt
='Please enter your response:'):
769 """Get response from the user using a dialog box."""
770 dialog
= wx
.TextEntryDialog(None, prompt
,
771 'Input Dialog (Raw)', '')
773 if dialog
.ShowModal() == wx
.ID_OK
:
774 text
= dialog
.GetValue()
781 """Halt execution pending a response from the user."""
782 self
.ask('Press enter to continue:')
785 """Delete all text from the shell."""
788 def run(self
, command
, prompt
=True, verbose
=True):
789 """Execute command as if it was typed in directly.
790 >>> shell.run('print "this"')
795 # Go to the very bottom of the text.
796 endpos
= self
.GetTextLength()
797 self
.SetCurrentPos(endpos
)
798 command
= command
.rstrip()
799 if prompt
: self
.prompt()
800 if verbose
: self
.write(command
)
803 def runfile(self
, filename
):
804 """Execute all commands in file as if they were typed into the
806 file = open(filename
)
809 for command
in file.readlines():
810 if command
[:6] == 'shell.':
811 # Run shell methods silently.
812 self
.run(command
, prompt
=False, verbose
=False)
814 self
.run(command
, prompt
=False, verbose
=True)
818 def autoCompleteShow(self
, command
):
819 """Display auto-completion popup list."""
820 self
.AutoCompSetAutoHide(self
.autoCompleteAutoHide
)
821 self
.AutoCompSetIgnoreCase(self
.autoCompleteCaseInsensitive
)
822 list = self
.interp
.getAutoCompleteList(command
,
823 includeMagic
=self
.autoCompleteIncludeMagic
,
824 includeSingle
=self
.autoCompleteIncludeSingle
,
825 includeDouble
=self
.autoCompleteIncludeDouble
)
827 options
= ' '.join(list)
829 self
.AutoCompShow(offset
, options
)
831 def autoCallTipShow(self
, command
):
832 """Display argument spec and docstring in a popup window."""
833 if self
.CallTipActive():
835 (name
, argspec
, tip
) = self
.interp
.getCallTip(command
)
837 dispatcher
.send(signal
='Shell.calltip', sender
=self
, calltip
=tip
)
838 if not self
.autoCallTip
:
841 startpos
= self
.GetCurrentPos()
842 self
.write(argspec
+ ')')
843 endpos
= self
.GetCurrentPos()
844 self
.SetSelection(endpos
, startpos
)
846 curpos
= self
.GetCurrentPos()
847 tippos
= curpos
- (len(name
) + 1)
848 fallback
= curpos
- self
.GetColumn(curpos
)
849 # In case there isn't enough room, only go back to the
851 tippos
= max(tippos
, fallback
)
852 self
.CallTipShow(tippos
, tip
)
854 def writeOut(self
, text
):
855 """Replacement for stdout."""
858 def writeErr(self
, text
):
859 """Replacement for stderr."""
862 def redirectStdin(self
, redirect
=True):
863 """If redirect is true then sys.stdin will come from the shell."""
865 sys
.stdin
= self
.reader
867 sys
.stdin
= self
.stdin
869 def redirectStdout(self
, redirect
=True):
870 """If redirect is true then sys.stdout will go to the shell."""
872 sys
.stdout
= PseudoFileOut(self
.writeOut
)
874 sys
.stdout
= self
.stdout
876 def redirectStderr(self
, redirect
=True):
877 """If redirect is true then sys.stderr will go to the shell."""
879 sys
.stderr
= PseudoFileErr(self
.writeErr
)
881 sys
.stderr
= self
.stderr
884 """Return true if text is selected and can be cut."""
885 if self
.GetSelectionStart() != self
.GetSelectionEnd() \
886 and self
.GetSelectionStart() >= self
.promptPosEnd \
887 and self
.GetSelectionEnd() >= self
.promptPosEnd
:
893 """Return true if a paste should succeed."""
894 if self
.CanEdit() and editwindow
.EditWindow
.CanPaste(self
):
900 """Return true if editing should succeed."""
901 if self
.GetSelectionStart() != self
.GetSelectionEnd():
902 if self
.GetSelectionStart() >= self
.promptPosEnd \
903 and self
.GetSelectionEnd() >= self
.promptPosEnd
:
908 return self
.GetCurrentPos() >= self
.promptPosEnd
911 """Remove selection and place it on the clipboard."""
912 if self
.CanCut() and self
.CanCopy():
913 if self
.AutoCompActive():
914 self
.AutoCompCancel()
915 if self
.CallTipActive():
918 self
.ReplaceSelection('')
921 """Copy selection and place it on the clipboard."""
925 command
= self
.GetSelectedText()
926 command
= command
.replace(os
.linesep
+ ps2
, os
.linesep
)
927 command
= command
.replace(os
.linesep
+ ps1
, os
.linesep
)
928 command
= self
.lstripPrompt(text
=command
)
929 data
= wx
.TextDataObject(command
)
932 def CopyWithPrompts(self
):
933 """Copy selection, including prompts, and place it on the clipboard."""
935 command
= self
.GetSelectedText()
936 data
= wx
.TextDataObject(command
)
939 def CopyWithPromptsPrefixed(self
):
940 """Copy selection, including prompts prefixed with four
941 spaces, and place it on the clipboard."""
943 command
= self
.GetSelectedText()
945 command
= spaces
+ command
.replace(os
.linesep
,
947 data
= wx
.TextDataObject(command
)
950 def _clip(self
, data
):
951 if wx
.TheClipboard
.Open():
952 wx
.TheClipboard
.UsePrimarySelection(False)
953 wx
.TheClipboard
.SetData(data
)
954 wx
.TheClipboard
.Flush()
955 wx
.TheClipboard
.Close()
958 """Replace selection with clipboard contents."""
959 if self
.CanPaste() and wx
.TheClipboard
.Open():
961 if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)):
962 data
= wx
.TextDataObject()
963 if wx
.TheClipboard
.GetData(data
):
964 self
.ReplaceSelection('')
965 command
= data
.GetText()
966 command
= command
.rstrip()
967 command
= self
.fixLineEndings(command
)
968 command
= self
.lstripPrompt(text
=command
)
969 command
= command
.replace(os
.linesep
+ ps2
, '\n')
970 command
= command
.replace(os
.linesep
, '\n')
971 command
= command
.replace('\n', os
.linesep
+ ps2
)
973 wx
.TheClipboard
.Close()
975 def PasteAndRun(self
):
976 """Replace selection with clipboard contents, run commands."""
977 if wx
.TheClipboard
.Open():
980 if wx
.TheClipboard
.IsSupported(wx
.DataFormat(wx
.DF_TEXT
)):
981 data
= wx
.TextDataObject()
982 if wx
.TheClipboard
.GetData(data
):
983 endpos
= self
.GetTextLength()
984 self
.SetCurrentPos(endpos
)
985 startpos
= self
.promptPosEnd
986 self
.SetSelection(startpos
, endpos
)
987 self
.ReplaceSelection('')
988 text
= data
.GetText()
990 text
= self
.fixLineEndings(text
)
991 text
= self
.lstripPrompt(text
)
992 text
= text
.replace(os
.linesep
+ ps1
, '\n')
993 text
= text
.replace(os
.linesep
+ ps2
, '\n')
994 text
= text
.replace(os
.linesep
, '\n')
995 lines
= text
.split('\n')
999 if line
.strip() == ps2
.strip():
1000 # If we are pasting from something like a
1001 # web page that drops the trailing space
1002 # from the ps2 prompt of a blank line.
1004 if line
.strip() != '' and line
.lstrip() == line
:
1007 # Add the previous command to the list.
1008 commands
.append(command
)
1009 # Start a new command, which may be multiline.
1012 # Multiline command. Add to the command.
1015 commands
.append(command
)
1016 for command
in commands
:
1017 command
= command
.replace('\n', os
.linesep
+ ps2
)
1020 wx
.TheClipboard
.Close()
1022 def wrap(self
, wrap
=True):
1023 """Sets whether text is word wrapped."""
1025 self
.SetWrapMode(wrap
)
1026 except AttributeError:
1027 return 'Wrapping is not available in this version.'
1029 def zoom(self
, points
=0):
1030 """Set the zoom level.
1032 This number of points is added to the size of all fonts. It
1033 may be positive to magnify or negative to reduce."""
1034 self
.SetZoom(points
)