1
"""Slices 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.
6
Slices is a version of shell modified by David Mashburn."""
8
__author__ = "David N. Mashburn <david.n.mashburn@gmail.com> / "
9
__author__ += "Patrick K. O'Brien <pobrien@orbtech.com>"
10
__cvsid__ = "$Id: sliceshell.py 60100 2009-04-12 02:56:29Z RD $"
11
__revision__ = "$Revision: 60100 $"[11:-2]
21
from buffer import Buffer
27
from pseudo import PseudoFileIn
28
from pseudo import PseudoFileOut
29
from pseudo import PseudoFileErr
30
from version import VERSION
31
from magic import magic
32
from parse import testForContinuations
33
from path import ls,cd,pwd,sx
36
sys.ps3 = '<-- ' # Input prompt.
38
# Force updates from long-running commands after this many seconds
39
PRINT_UPDATE_MAX_TIME=2
41
NAVKEYS = (wx.WXK_HOME, wx.WXK_END, wx.WXK_LEFT, wx.WXK_RIGHT,
42
wx.WXK_UP, wx.WXK_DOWN, wx.WXK_PRIOR, wx.WXK_NEXT)
48
GROUPING_START_FOLDED = 3
52
INPUT_START_FOLDED = 7
56
OUTPUT_START_FOLDED = 11
64
# Could add C integration right into the markers...
65
# Non-editable file marker for auto-loaded files...
66
# Weave VariableInput = 15
68
# C code = 17 (only for use with Pyrex)
69
# Pyrex / Cython code = 18
71
GROUPING_MASK = ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED |
72
1<<GROUPING_MIDDLE | 1<<GROUPING_END )
74
INPUT_MASK = ( 1<<INPUT_START | 1<<INPUT_START_FOLDED |
75
1<<INPUT_MIDDLE | 1<<INPUT_END )
76
OUTPUT_MASK = ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED |
77
1<<OUTPUT_MIDDLE | 1<<OUTPUT_END )
78
IO_MASK = ( INPUT_MASK | OUTPUT_MASK )
80
IO_START_MASK = ( 1<<INPUT_START | 1<<OUTPUT_START )
81
IO_START_FOLDED_MASK = ( 1<<INPUT_START_FOLDED | 1<<OUTPUT_START_FOLDED )
82
IO_ANY_START_MASK = ( 1<<INPUT_START | 1<<OUTPUT_START |
83
1<<INPUT_START_FOLDED | 1<<OUTPUT_START_FOLDED )
84
IO_MIDDLE_MASK = ( 1<<INPUT_MIDDLE | 1<<OUTPUT_MIDDLE )
85
IO_END_MASK = ( 1<<INPUT_END | 1<<OUTPUT_END )
87
usrBinEnvPythonText = '#!/usr/bin/env python\n'
88
pyslicesFormatHeaderText = ['#PySlices Save Format Version 1.1 (PySlices v0.9.7.8 and later)\n',
89
'#PySlices Save Format Version 1.2 (PySlices v0.9.8 and later)\n']
90
groupingStartText = '#PySlices Marker Information -- Begin Grouping Slice\n'
91
inputStartText = '#PySlices Marker Information -- Begin Input Slice\n'
92
outputStartText = '#PySlices Marker Information -- Begin Output Slice\n'
97
------------------------------------------------------------------------
98
PySlices is the newest member of the Py suite!
99
It is a modified version of PyCrust that supports multi-line commands.
101
Input and output are contained in "Slices" shown as markers in the left margin.
102
Input Slices have RED margins (active, editable).
103
Output Slices have BLUE margins (frozen, not editable).
105
Commands in slices can be on more than one line, as with Sage or Mathematica.
106
For example, the command:
110
will all run in sequence, much like a script.
111
Try running the above Input Slice by clicking somewhere in its text and
112
using Ctrl-Return, Shift-Return, or Numpad Enter to execute.
113
Previous commands (Old Slices) can be re-edited and run again in place.
116
* selceted (click on the margin, Shift-click for multiple selection)
117
* folded (click the margin twice)
118
* selected and deleted (hit delete while selected)
120
* merged (Ctrl-M while selecting adjacent, like-colored slices)
122
Try deleting the slice above this one by clicking on the red margin.
124
If you want a more traditional shell feel, try enabling "Shell Mode" in
125
"Options->Settings->Shell Mode" (or try PyCrust).
126
In Shell Mode, two returns in a row executes the command, and
127
Ctrl-Return and Shift-Return always print newlines.
129
Saving and opening "sessions" is now supported! This is a little
130
different to other shells where the history is saved. With PySlices,
131
the whole document is saved in a simple text format!
133
To disable this Tutorial on startup, uncheck it in the menu at:
134
"Options->Startup->Show PySlices tutorial"
136
PySlices may not be the best thing since sliced bread, but
137
I hope it makes using Python a little bit sweeter!
140
class SlicesShellFrame(frame.Frame, frame.ShellFrameMixin):
141
"""Frame containing the sliceshell component."""
143
name = 'SlicesShell Frame'
144
revision = __revision__
146
def __init__(self, parent=None, id=-1, title='PySlicesShell',
147
pos=wx.DefaultPosition, size=wx.DefaultSize,
148
style=wx.DEFAULT_FRAME_STYLE, locals=None,
150
config=None, dataDir=None, filename=None,
152
"""Create SlicesShellFrame instance."""
153
frame.Frame.__init__(self, parent, id, title, pos, size, style,shellName='PySlices')
154
frame.ShellFrameMixin.__init__(self, config, dataDir)
156
if size == wx.DefaultSize:
157
self.SetSize((750, 525))
159
intro = 'PySlices %s - The Flakiest Python Shell... Cut Up!' % VERSION
160
self.SetStatusText(intro.replace('\n', ', '))
161
self.sliceshell = SlicesShell(parent=self, id=-1, introText=intro,
162
locals=locals, InterpClass=InterpClass,
163
startupScript=self.startupScript,
164
execStartupScript=self.execStartupScript,
165
showPySlicesTutorial=self.showPySlicesTutorial,
166
enableShellMode=self.enableShellMode,
167
hideFoldingMargin=self.hideFoldingMargin,
169
self.buffer = self.sliceshell.buffer
171
# Override the shell so that status messages go to the status bar.
172
self.sliceshell.setStatusText = self.SetStatusText
174
self.sliceshell.SetFocus()
177
self.currentDirectory = os.path.expanduser('~')
180
self.bufferOpen(filename)
182
self.Bind(wx.EVT_IDLE, self.OnIdle)
185
def OnClose(self, event):
186
"""Event handler for closing."""
188
# This isn't working the way I want, but I'll leave it for now.
189
#if self.sliceshell.waiting:
190
# if event.CanVeto():
193
# # TODO: Add check for saving
194
# self.SaveSettings()
195
# self.sliceshell.destroy()
198
def OnAbout(self, event):
199
"""Display an About window."""
200
title = 'About PySliceShell'
201
text = 'PySliceShell %s\n\n' % VERSION + \
202
'Yet another Python shell, only flakier.\n\n' + \
203
'Half-baked by Patrick K. O\'Brien,\n' + \
204
'the other half is still in the oven.\n\n' + \
205
'Shell Revision: %s\n' % self.shell.revision + \
206
'Interpreter Revision: %s\n\n' % self.shell.interp.revision + \
207
'Platform: %s\n' % sys.platform + \
208
'Python Version: %s\n' % sys.version.split()[0] + \
209
'wxPython Version: %s\n' % wx.VERSION_STRING + \
210
('\t(%s)\n' % ", ".join(wx.PlatformInfo[1:]))
211
dialog = wx.MessageDialog(self, text, title,
212
wx.OK | wx.ICON_INFORMATION)
217
def OnHelp(self, event):
218
"""Show a help dialog."""
219
frame.ShellFrameMixin.OnHelp(self, event)
222
def LoadSettings(self):
223
if self.config is not None:
224
frame.ShellFrameMixin.LoadSettings(self)
225
frame.Frame.LoadSettings(self, self.config)
226
self.sliceshell.LoadSettings(self.config)
228
def SaveSettings(self, force=False):
229
if self.config is not None:
230
frame.ShellFrameMixin.SaveSettings(self,force)
231
if self.autoSaveSettings or force:
232
frame.Frame.SaveSettings(self, self.config)
233
self.sliceshell.SaveSettings(self.config)
235
def DoSaveSettings(self):
236
if self.config is not None:
237
self.SaveSettings(force=True)
240
def OnEnableShellMode(self,event):
241
"""Change between Slices Mode and Shell Mode"""
242
frame.Frame.OnEnableShellMode(self,event)
243
self.sliceshell.ToggleShellMode(self.enableShellMode)
245
def OnHideFoldingMargin(self,event):
246
"""Change between Slices Mode and Shell Mode"""
247
frame.Frame.OnHideFoldingMargin(self,event)
248
self.sliceshell.ToggleFoldingMargin(self.hideFoldingMargin)
249
# Copied Straight from crustslices.py (update both with any changes...)
250
# Stolen Straight from editor.EditorFrame
251
# Modified a little... :)
254
def OnIdle(self, event):
255
"""Event handler for idle time."""
259
def _updateTitle(self):
260
"""Show current title information."""
261
title = self.GetTitle()
262
if self.bufferHasChanged():
263
if title.startswith('* '):
266
self.SetTitle('* ' + title)
268
if title.startswith('* '):
269
self.SetTitle(title[2:])
272
"""Return True if there is a current buffer."""
278
def bufferClose(self):
280
if self.buffer.hasChanged():
281
cancel = self.bufferSuggestSave()
286
self.sliceshell.destroy()
292
def bufferCreate(self, filename=None):
293
"""Create new buffer."""
296
self.panel = panel = wx.Panel(parent=self, id=-1)
297
panel.Bind (wx.EVT_ERASE_BACKGROUND, lambda x: x)
298
editor = Editor(parent=panel)
299
panel.editor = editor
300
sizer = wx.BoxSizer(wx.VERTICAL)
301
sizer.Add(editor.window, 1, wx.EXPAND)
302
panel.SetSizer(sizer)
303
panel.SetAutoLayout(True)
305
buffer.addEditor(editor)
306
buffer.open(filename)
307
self.setEditor(editor)
308
self.editor.setFocus()
312
def bufferDestroy(self):
313
"""Destroy the current buffer."""
319
def bufferHasChanged(self):
320
"""Return True if buffer has changed since last save."""
322
return self.buffer.hasChanged()
327
"""Create new buffer."""
328
cancel = self.bufferSuggestSave()
333
self.SetTitle( 'PySlices')
334
self.sliceshell.NeedsCheckForSave=False
335
self.sliceshell.SetSavePoint()
336
self.buffer.doc = document.Document()
337
self.buffer.name = 'This shell'
338
self.buffer.modulename = self.buffer.doc.filebase
342
def bufferOpen(self,file=None):
343
"""Open file in buffer."""
344
if self.bufferHasChanged():
345
cancel = self.bufferSuggestSave()
350
file=wx.FileSelector('Open a PySlices File',
351
wildcard='*.pyslices',
352
default_path=self.currentDirectory)
353
if file!=None and file!=u'':
355
self.sliceshell.LoadPySlicesFile(fid)
357
self.currentDirectory = os.path.split(file)[0]
358
self.SetTitle( os.path.split(file)[1] + ' - PySlices')
359
self.sliceshell.NeedsCheckForSave=False
360
self.sliceshell.SetSavePoint()
361
self.buffer.doc = document.Document(file)
362
self.buffer.name = self.buffer.doc.filename
363
self.buffer.modulename = self.buffer.doc.filebase
364
self.sliceshell.ScrollToLine(0)
367
## def bufferPrint(self):
368
## """Print buffer."""
371
## def bufferRevert(self):
372
## """Revert buffer to version of file on disk."""
375
# was self.buffer.save(self): # """Save buffer."""
376
def simpleSave(self,confirmed=False):
377
filepath = self.buffer.doc.filepath
378
self.buffer.confirmed = confirmed
380
return # XXX Get filename
381
if not os.path.exists(filepath):
382
self.buffer.confirmed = True
383
if not self.buffer.confirmed:
384
self.buffer.confirmed = self.buffer.overwriteConfirm(filepath)
385
if self.buffer.confirmed:
387
fid = open(filepath, 'wb')
388
self.sliceshell.SavePySlicesFile(fid)
392
self.sliceshell.SetSavePoint()
393
self.SetTitle( os.path.split(filepath)[1] + ' - PySlices')
394
self.sliceshell.NeedsCheckForSave=False
396
def bufferSave(self):
397
"""Save buffer to its file."""
398
if self.buffer.doc.filepath:
400
self.simpleSave(confirmed=True)
403
cancel = self.bufferSaveAs()
406
def bufferSaveAs(self):
407
"""Save buffer to a new filename."""
408
if self.bufferHasChanged() and self.buffer.doc.filepath:
409
cancel = self.bufferSuggestSave()
413
if self.buffer and self.buffer.doc.filedir:
414
filedir = self.buffer.doc.filedir
415
result = editor.saveSingle(title='Save PySlices File',directory=filedir,
416
wildcard='PySlices Files (*.pyslices)|*.pyslices')
417
if result.path not in ['',None]:
418
if result.path[-9:]!=".pyslices":
419
result.path+=".pyslices"
421
self.buffer.doc = document.Document(result.path)
422
self.buffer.name = self.buffer.doc.filename
423
self.buffer.modulename = self.buffer.doc.filebase
424
self.simpleSave(confirmed=True) # allow overwrite
430
def bufferSaveACopy(self):
431
"""Save buffer to a new filename."""
433
if self.buffer and self.buffer.doc.filedir:
434
filedir = self.buffer.doc.filedir
435
result = editor.saveSingle(title='Save a Copy of PySlices File',directory=filedir,
436
wildcard='PySlices Files (*.pyslices)|*.pyslices')
438
if result.path not in ['',None]:
439
if result.path[-9:]!=".pyslices":
440
result.path+=".pyslices"
442
# if not os.path.exists(result.path):
443
try: # Allow overwrite...
444
fid = open(result.path, 'wb')
445
self.sliceshell.SavePySlicesFile(fid)
455
def bufferSuggestSave(self):
456
"""Suggest saving changes. Return True if user selected Cancel."""
457
result = editor.messageDialog(parent=None,
458
message='%s has changed.\n'
459
'Would you like to save it first'
460
'?' % self.buffer.name,
461
title='Save current file?',
462
style=wx.YES_NO | wx.CANCEL | wx.NO_DEFAULT |
463
wx.CENTRE | wx.ICON_QUESTION )
465
cancel = self.bufferSave()
467
cancel = result.text == 'Cancel'
470
def updateNamespace(self):
471
"""Update the buffer namespace for autocompletion and calltips."""
472
if self.buffer.updateNamespace():
473
self.SetStatusText('Namespace updated')
475
self.SetStatusText('Error executing, unable to update namespace')
479
# TODO : Update the help text
482
Home Go to the beginning of the line.
483
End Go to the end of the line.
484
Shift+Home Select to the beginning of the line.
485
Shift+End Select to the end of the line.
486
Ctrl-Home Jump to the beginning of the slice;
487
If already there, jump to beginning of previous slice
488
Ctrl-End Jump to the end of the slice;
489
If already there, jump to end of next slice
490
Ctrl-PageUp Jump to the beginning of the shell
491
Ctrl-PageDown Jump to the end of the shell
492
Ctrl+C Copy selected text, removing prompts.
493
Ctrl+Shift+C Copy selected text, retaining prompts.
494
Alt+C Copy to the clipboard, including prefixed prompts.
495
Ctrl+X Cut selected text.
496
Ctrl+V Paste from clipboard.
497
Ctrl+Shift+V Paste and run multiple commands from clipboard.
498
Ctrl+Up Arrow Retrieve Previous History item.
499
Alt+P Retrieve Previous History item.
500
Ctrl+Down Arrow Retrieve Next History item.
501
Alt+N Retrieve Next History item.
502
Shift+Up Arrow Insert Previous History item.
503
Shift+Down Arrow Insert Next History item.
504
F8 Command-completion of History item.
505
(Type a few characters of a previous command and press F8.)
506
Ctrl+] Increase font size.
507
Ctrl+[ Decrease font size.
508
Ctrl+= Default font size.
510
Ctrl-Space Show Auto Completion.
511
Ctrl-Shift-Space Show Call Tip.
512
Ctrl-Shift-H Complete Text from History.
516
F12 on/off "free-edit" mode
517
For testing only -- This does not preserve markers!
520
Return Insert new line
521
Enter (Numpad) Run command in slice
526
Return or Enter Insert a new line
529
2 Returns in a row Run command in slice
532
class SlicesShellFacade:
533
"""Simplified interface to all shell-related functionality.
535
This is a semi-transparent facade, in that all attributes of other
536
are accessible, even though only some are visible to the user."""
538
name = 'SlicesShell Interface'
539
revision = __revision__
541
def __init__(self, other):
542
"""Create a SlicesShellFacade instance."""
545
d['helpText'] = HELP_TEXT
546
d['this'] = other.this
549
"""Display some useful information about how to use the slices shell."""
550
self.write(self.helpText,type='Output')
552
def __getattr__(self, name):
553
if hasattr(self.other, name):
554
return getattr(self.other, name)
556
raise AttributeError, name
558
def __setattr__(self, name, value):
559
if self.__dict__.has_key(name):
560
self.__dict__[name] = value
561
elif hasattr(self.other, name):
562
setattr(self.other, name, value)
564
raise AttributeError, name
566
def _getAttributeNames(self):
567
"""Return list of magic attributes to extend introspection."""
573
'autoCompleteAutoHide',
574
'autoCompleteCaseInsensitive',
575
'autoCompleteIncludeDouble',
576
'autoCompleteIncludeMagic',
577
'autoCompleteIncludeSingle',
597
Py Slices Shell Revision: %s
598
Py Interpreter Revision: %s
601
wxPython PlatformInfo: %s
604
class SlicesShell(editwindow.EditWindow):
605
"""Notebook Shell based on StyledTextCtrl."""
608
revision = __revision__
610
def __init__(self, parent, id=-1, pos=wx.DefaultPosition,
611
size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
612
introText='', locals=None, InterpClass=None,
613
startupScript=None, execStartupScript=True,
614
showPySlicesTutorial=True,enableShellMode=False,
615
hideFoldingMargin=False, *args, **kwds):
616
"""Create Shell instance."""
617
editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
621
locals = __main__.__dict__
623
# Grab these so they can be restored by self.redirect* methods.
624
self.stdin = sys.stdin
625
self.stdout = sys.stdout
626
self.stderr = sys.stderr
628
# Import a default interpreter class if one isn't provided.
629
if InterpClass == None:
630
from interpreter import Interpreter
632
Interpreter = InterpClass
634
# Create a replacement for stdin.
635
self.reader = PseudoFileIn(self.readline, self.readlines)
636
self.reader.input = ''
637
self.reader.isreading = False
639
# Set up the interpreter.
640
self.interp = Interpreter(locals=locals,
641
rawin=self.raw_input,
643
stdout=PseudoFileOut(self.writeOut),
644
stderr=PseudoFileErr(self.writeErr),
648
self.buffer = Buffer()
649
self.id = self.GetId()
650
self.buffer.addEditor(self)
651
self.buffer.name='This shell'
652
self.NeedsCheckForSave=False
654
# Find out for which keycodes the interpreter will autocomplete.
655
self.autoCompleteKeys = self.interp.getAutoCompleteKeys()
657
# Keep track of the last non-continuation prompt positions.
658
# Removed all references to these... solved a lot of odd bugs...
659
# self.promptPosStart = 0
660
# self.promptPosEnd = 0
662
# Keep track of multi-line commands.
665
# Use Margins to track input / output / slice number
668
# For use with forced updates during long-running scripts
672
# margin 1 is already defined for the line numbers
673
# may eventually change it back to 0 like it ought to be...
674
self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
675
self.SetMarginType(3, stc.STC_MARGIN_SYMBOL)
676
self.SetMarginType(4, stc.STC_MARGIN_SYMBOL)
677
self.SetMarginWidth(2, 22)
678
self.SetMarginWidth(3, 22)
679
self.SetMarginWidth(4, 12)
680
self.SetMarginSensitive(2,True)
681
self.SetMarginSensitive(3,True)
682
self.SetMarginSensitive(4,True)
683
self.SetProperty("fold", "1")
684
# tabs are bad, use spaces
685
self.SetProperty("tab.timmy.whinge.level", "4")
689
self.SetMarginMask(2, GROUPING_MASK | 1<<GROUPING_SELECTING )
690
# Display Markers -24...
691
self.SetMarginMask(3, IO_MASK | 1<<IO_SELECTING | 1<<READLINE_BG | 1<<INPUT_READLINE )
692
self.SetMarginMask(4, stc.STC_MASK_FOLDERS)
693
# Set the mask for the line markers, too...
694
self.SetMarginMask(1, 0)
696
if hideFoldingMargin:
697
self.SetMarginWidth(4, 0)
698
self.hideFoldingMargin=hideFoldingMargin
701
grouping_color="black"
705
self.MarkerDefine(GROUPING_SELECTING, stc.STC_MARK_FULLRECT,
706
sel_color, sel_color)
707
self.MarkerDefine(IO_SELECTING, stc.STC_MARK_FULLRECT,
708
sel_color, sel_color)
710
self.MarkerDefine(GROUPING_START, stc.STC_MARK_BOXMINUS,
711
"white", grouping_color)
712
self.MarkerDefine(GROUPING_START_FOLDED, stc.STC_MARK_BOXPLUS,
713
"white", grouping_color)
714
self.MarkerDefine(GROUPING_MIDDLE, stc.STC_MARK_VLINE,
715
"white", grouping_color)
716
self.MarkerDefine(GROUPING_END, stc.STC_MARK_LCORNER,
717
"white", grouping_color)
719
self.MarkerDefine(READLINE_BG, stc.STC_MARK_FULLRECT,
720
wx.Colour(191,191,191), wx.Colour(191,191,191))
721
self.MarkerDefine(INPUT_READLINE, stc.STC_MARK_CHARACTER+ord('<'),
722
input_color, wx.Colour(191,191,191))
725
self.mode='ShellMode'
727
self.mode='SlicesMode'
729
self.execOnNextReturn=False
730
if self.mode=='SlicesMode':
731
self.MarkerDefine(INPUT_START, stc.STC_MARK_BOXMINUS,
732
"white", input_color)
733
self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS,
734
"white", input_color)
735
self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_VLINE,
736
"white", input_color)
737
self.MarkerDefine(INPUT_END, stc.STC_MARK_LCORNER,
738
"white", input_color)
739
elif self.mode=='ShellMode':
740
self.MarkerDefine(INPUT_START, stc.STC_MARK_ARROWS,
741
input_color, "white")
742
self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS,
743
"white", input_color)
744
self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_DOTDOTDOT,
745
input_color, "white")
746
self.MarkerDefine(INPUT_END, stc.STC_MARK_DOTDOTDOT,
747
input_color, "white")
749
self.MarkerDefine(OUTPUT_START, stc.STC_MARK_BOXMINUS,
750
"white", output_color)
751
self.MarkerDefine(OUTPUT_START_FOLDED, stc.STC_MARK_BOXPLUS,
752
"white", output_color)
753
self.MarkerDefine(OUTPUT_MIDDLE, stc.STC_MARK_VLINE,
754
"white", output_color)
755
self.MarkerDefine(OUTPUT_END, stc.STC_MARK_LCORNER,
756
"white", output_color)
758
self.MarkerDefine(OUTPUT_BG, stc.STC_MARK_BACKGROUND,
759
"white", wx.Colour(242,242,255))
761
# Markers for folding margin...
762
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS,
764
self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS,
766
self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE,
768
self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER,
770
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_BOXPLUSCONNECTED,
772
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_BOXMINUSCONNECTED,
774
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_TCORNER,
777
# Create the command history. Commands are added into the
778
# front of the list (ie. at index 0) as they are entered.
779
# self.historyIndex is the current position in the history; it
780
# gets incremented as you retrieve the previous command,
781
# decremented as you retrieve the next, and reset when you hit
782
# Enter. self.historyIndex == -1 means you're on the current
783
# command, not in the history.
785
self.historyIndex = -1
787
#DNM -- disable these markers...
788
#seb add mode for "free edit"
790
#self.MarkerDefine(0,stc.STC_MARK_ROUNDRECT) # marker for hidden
793
# Assign handlers for keyboard events.
794
self.Bind(wx.EVT_CHAR, self.OnChar)
795
self.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
797
self.Bind(wx.stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
798
# TODO : Add a general functions to handle mouse clicks in the
799
# TODO: STC window whose sole purpose is to make it so
800
# TODO: that margin selection becomes unselected...
802
# Assign handler for the context menu
803
self.Bind(wx.EVT_CONTEXT_MENU, self.OnContextMenu)
804
self.Bind(wx.EVT_UPDATE_UI, self.OnUpdateUI)
806
# Assign handlers for edit events
807
self.Bind(wx.EVT_MENU, lambda evt: self.Cut(), id=wx.ID_CUT)
808
self.Bind(wx.EVT_MENU, lambda evt: self.Copy(), id=wx.ID_COPY)
809
self.Bind(wx.EVT_MENU, lambda evt: self.CopyWithPrompts(), id=frame.ID_COPY_PLUS)
810
self.Bind(wx.EVT_MENU, lambda evt: self.Paste(), id=wx.ID_PASTE)
811
self.Bind(wx.EVT_MENU, lambda evt: self.PasteAndRun(), id=frame.ID_PASTE_PLUS)
812
self.Bind(wx.EVT_MENU, lambda evt: self.SelectAll(), id=wx.ID_SELECTALL)
813
self.Bind(wx.EVT_MENU, lambda evt: self.Clear(), id=wx.ID_CLEAR)
814
self.Bind(wx.EVT_MENU, lambda evt: self.Undo(), id=wx.ID_UNDO)
815
self.Bind(wx.EVT_MENU, lambda evt: self.Redo(), id=wx.ID_REDO)
817
# Assign handler for idle time.
819
self.Bind(wx.EVT_IDLE, self.OnIdle)
821
# Display the introductory banner information.
822
self.showIntro(introText)
824
outStart,outEnd,inStart,inMiddle,inEnd = [[],[],[],[],[]]
826
# Make "executed startup script move to the top..."
827
if showPySlicesTutorial:
828
self.write(tutorialText,'Output')
831
outStart=[tutStart,testStart+3]
832
outEnd=[tutStart-1,testStart-1]
834
inMiddle=[testStart+1]
837
# Assign some pseudo keywords to the interpreter's namespace.
838
self.setBuiltinKeywords()
840
# Add 'shell' to the interpreter's local namespace.
843
# Do this last so the user has complete control over their
844
# environment. They can override anything they want.
845
if execStartupScript:
846
if startupScript is None:
847
startupScript = os.environ.get('PYTHONSTARTUP')
848
self.execStartupScript(startupScript)
853
outEnd+=[self.GetLineCount()-2]
854
inStart+=[self.GetLineCount()-1]
855
# Set all the line markers to the proper initial states...
856
for i in range(self.GetLineCount()):
857
self.clearGroupingMarkers(i)
858
self.clearIOMarkers(i)
860
self.MarkerAdd(i,GROUPING_START)
861
self.MarkerAdd(i,OUTPUT_START)
862
# Background color is confusing for tutorial... skip it!
863
#self.MarkerAdd(i,OUTPUT_BG)
865
self.MarkerAdd(i,GROUPING_END)
866
self.MarkerAdd(i,OUTPUT_END)
867
#self.MarkerAdd(i,OUTPUT_BG)
869
self.MarkerAdd(i,GROUPING_START)
870
self.MarkerAdd(i,INPUT_START)
872
self.MarkerAdd(i,GROUPING_MIDDLE)
873
self.MarkerAdd(i,INPUT_MIDDLE)
875
self.MarkerAdd(i,GROUPING_END)
876
self.MarkerAdd(i,INPUT_END)
878
self.MarkerAdd(i,GROUPING_MIDDLE)
879
self.MarkerAdd(i,OUTPUT_MIDDLE)
880
#self.MarkerAdd(i,OUTPUT_BG)
882
self.SliceSelection=False
883
self.runningSlice=None
885
## NOTE: See note at bottom of this file...
886
## #seb: File drag and drop
887
## self.SetDropTarget( FileDropTarget(self) )
890
# Everywhere "ADD UNDO" appears, there is new code to handle markers
891
self.EmptyUndoBuffer()
893
wx.CallAfter(self.ScrollToLine, 0)
895
def ToggleShellMode(self,enableShellMode=None):
896
if enableShellMode==None:
897
if self.mode=='ShellMode': self.mode='SlicesMode'
898
elif self.mode=='SlicesMode': self.mode='ShellMode'
899
elif enableShellMode:
900
self.mode='ShellMode'
902
self.mode='SlicesMode'
905
if self.mode=='SlicesMode':
906
self.MarkerDefine(INPUT_START, stc.STC_MARK_BOXMINUS,
907
"white", input_color)
908
self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS,
909
"white", input_color)
910
self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_VLINE,
911
"white", input_color)
912
self.MarkerDefine(INPUT_END, stc.STC_MARK_LCORNER,
913
"white", input_color)
914
elif self.mode=='ShellMode':
915
self.MarkerDefine(INPUT_START, stc.STC_MARK_ARROWS,
916
input_color, "white")
917
self.MarkerDefine(INPUT_START_FOLDED, stc.STC_MARK_BOXPLUS,
918
"white", input_color)
919
self.MarkerDefine(INPUT_MIDDLE, stc.STC_MARK_DOTDOTDOT,
920
input_color, "white")
921
self.MarkerDefine(INPUT_END, stc.STC_MARK_DOTDOTDOT,
922
input_color, "white")
924
def ToggleFoldingMargin(self,hideFoldingMargin=None):
925
if hideFoldingMargin==None:
926
self.hideFoldingMargin = not self.hideFoldingMargin
928
self.hideFoldingMargin = hideFoldingMargin
930
if self.hideFoldingMargin:
931
self.SetMarginWidth(4, 0)
933
self.SetMarginWidth(4, 12)
935
def clearHistory(self):
937
self.historyIndex = -1
938
dispatcher.send(signal="SlicesShell.clearHistory")
945
"""Set focus to the slices shell."""
948
def OnIdle(self, event):
949
"""Free the CPU to do other things."""
954
def showIntro(self, text=''):
955
"""Display introductory text in the slices shell."""
957
self.write(text,type='Output')
959
if self.interp.introText:
960
if text and not text.endswith(os.linesep):
961
self.write(os.linesep,type='Output')
962
self.write(self.interp.introText,type='Output')
963
except AttributeError:
966
def setBuiltinKeywords(self):
967
"""Create pseudo keywords as part of builtins.
969
This sets "close", "exit" and "quit" to a helpful string.
972
__builtin__.close = __builtin__.exit = __builtin__.quit = \
973
'Click on the close button to leave the application.'
976
__builtin__.pwd = pwd
981
"""Quit the application."""
982
# XXX Good enough for now but later we want to send a close event.
983
# In the close event handler we can make sure they want to
984
# quit. Other applications, like PythonCard, may choose to
985
# hide rather than quit so we should just post the event and
986
# let the surrounding app decide what it wants to do.
987
self.write('Click on the close button to leave the application.',
991
def setLocalShell(self):
992
"""Add 'slicesshell' to locals as reference to ShellFacade instance."""
993
self.interp.locals['slicesshell'] = SlicesShellFacade(other=self)
996
def execStartupScript(self, startupScript):
997
"""Execute the user's PYTHONSTARTUP script if they have one."""
998
if startupScript and os.path.isfile(startupScript):
999
text = 'Startup script executed: ' + startupScript
1000
self.push('print %r; execfile(%r)' % (text, startupScript))
1001
self.interp.startupScript = startupScript
1007
"""Display information about Py."""
1008
text = DISPLAY_TEXT % \
1009
(__author__, VERSION, self.revision, self.interp.revision,
1010
sys.version.split()[0], wx.VERSION_STRING, str(wx.PlatformInfo),
1012
self.write(text.strip(),type='Output')
1014
def BreakTextIntoCommands(self,text):
1015
"""Turn a text block into multiple multi-line commands."""
1017
#text = text.lstrip() # This should not be done!
1018
text = self.fixLineEndings(text)
1019
text = self.lstripPrompt(text)
1020
text = text.replace(os.linesep, '\n')
1021
lines = text.split('\n')
1023
continuations = testForContinuations(text)
1025
if len(continuations)==2: # Error case...
1026
return None,continuations[1]
1027
elif len(continuations)==4:
1028
stringContinuationList,indentationBlockList, \
1029
lineContinuationList,parentheticalContinuationList = continuations
1033
for j,line in enumerate(lines):
1034
lstrip = line.lstrip()
1036
# Get the first alnum word:
1040
first_word.append(i)
1043
first_word = ''.join(first_word)
1045
# Continue the command if it is blank, has indentation,
1046
# starts with else, elif,except, or finally
1047
# or previous line had a line continuation \
1053
stringCont = stringContinuationList[j-1]
1054
lineCont = lineContinuationList[j-1]
1056
if line.strip() == '' or lstrip != line or \
1057
first_word in ['else','elif','except','finally'] or \
1058
stringCont or lineCont:
1059
# Multiline command. Add to the command.
1065
# Add the previous command to the list.
1066
commands.append(command)
1067
# Start a new command, which may be multiline.
1070
commands.append(command)
1074
def MarkerSet(self,line,markerBitsSet):
1075
"""MarkerSet is the Set command for MarkerGet"""
1076
markerBits=self.MarkerGet(line)
1079
for i in range(numMarkers):
1080
if (markerBitsSet & (1<<i)) and not (markerBits & (1<<i)):
1081
self.MarkerAdd(line,i)
1082
elif not (markerBitsSet & (1<<i)) and (markerBits & (1<<i)):
1083
self.MarkerDelete(line,i)
1084
def GetGroupingSlice(self,line_num=None):
1085
"""Get the start/stop lines for the slice based on any line in the slice"""
1087
line_num=self.GetCurrentLine()
1089
num_lines=self.GetLineCount()
1091
for i in range(line_num,-1,-1):
1092
if self.MarkerGet(i) & (1<<GROUPING_START | 1<<GROUPING_START_FOLDED):
1098
for i in range(line_num,num_lines):
1099
if self.MarkerGet(i) & 1<<GROUPING_END:
1101
elif (i>line_num) and ( self.MarkerGet(i)
1102
& (1<<GROUPING_START | 1<<GROUPING_START_FOLDED) ):
1104
break # the solo case...
1105
stop_line=i+addition
1107
return start_line,stop_line
1109
def GetIOSlice(self,line_num=None):
1110
"""Get the start/stop lines for the slice based on any line in the slice"""
1112
line_num=self.GetCurrentLine()
1114
num_lines=self.GetLineCount()
1116
for i in range(line_num,-1,-1):
1117
if self.MarkerGet(i) & IO_ANY_START_MASK:
1123
for i in range(line_num,num_lines):
1124
if self.MarkerGet(i) & IO_END_MASK:
1126
elif (i>line_num) and (self.MarkerGet(i) & IO_ANY_START_MASK):
1128
break # the solo case...
1129
stop_line=i+addition
1131
return start_line,stop_line
1133
def FoldGroupingSlice(self,line_num=None):
1135
line_num=self.GetCurrentLine()
1137
start,end=self.GetGroupingSlice(line_num)
1138
self.HideLines(start+1,end)
1139
marker=self.MarkerGet(start)
1140
self.clearGroupingMarkers(start)
1141
self.MarkerAdd(start,GROUPING_START_FOLDED)
1142
self.clearIOMarkers(start)
1143
if marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ):
1144
self.MarkerAdd(start,INPUT_START_FOLDED)
1145
elif marker & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ):
1146
self.MarkerAdd(start,OUTPUT_START_FOLDED)
1147
self.MarkerAdd(start,OUTPUT_BG)
1149
pass #print 'Bad Markers!!!'
1150
def FoldIOSlice(self,line_num=None):
1152
line_num=self.GetCurrentLine()
1154
start,end=self.GetIOSlice(line_num)
1155
self.HideLines(start+1,end)
1156
marker=self.MarkerGet(start)
1157
if (self.MarkerGet(start) & \
1158
(1<<GROUPING_START | 1<<GROUPING_START_FOLDED )) and \
1159
(self.MarkerGet(end) & 1<<GROUPING_END):
1160
self.clearGroupingMarkers(start)
1161
self.MarkerAdd(start,GROUPING_START_FOLDED)
1162
self.clearIOMarkers(start)
1163
if marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ):
1164
self.MarkerAdd(start,INPUT_START_FOLDED)
1165
elif marker & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ):
1166
self.MarkerAdd(start,OUTPUT_START_FOLDED)
1167
self.MarkerAdd(start,OUTPUT_BG)
1169
pass #print 'Bad Markers!!!'
1170
def UnFoldGroupingSlice(self,line_num=None):
1172
line_num=self.GetCurrentLine()
1174
start,end=self.GetGroupingSlice(line_num)
1175
self.ShowLines(start+1,end)
1176
self.clearGroupingMarkers(start)
1177
self.MarkerAdd(start,GROUPING_START)
1178
for i in range(start,end):
1179
marker=self.MarkerGet(i)
1180
if marker & (1<<INPUT_START | 1<<INPUT_START_FOLDED):
1181
self.clearIOMarkers(i)
1182
self.MarkerAdd(i,INPUT_START)
1183
elif marker & (1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED):
1184
self.clearIOMarkers(i)
1185
self.MarkerAdd(i,OUTPUT_START)
1186
self.MarkerAdd(i,OUTPUT_BG)
1188
def UnFoldIOSlice(self,line_num=None):
1190
line_num=self.GetCurrentLine()
1192
start,end=self.GetIOSlice(line_num)
1193
self.ShowLines(start+1,end)
1194
marker=self.MarkerGet(start)
1195
if (self.MarkerGet(start) & \
1196
(1<<GROUPING_START | 1<<GROUPING_START_FOLDED )) and \
1197
(self.MarkerGet(end) & 1<<GROUPING_END):
1198
self.clearGroupingMarkers(start)
1199
self.MarkerAdd(start,GROUPING_START)
1200
self.clearIOMarkers(start)
1201
if marker & 1<<INPUT_START_FOLDED:
1202
self.MarkerAdd(start,INPUT_START)
1203
elif marker & 1<<OUTPUT_START_FOLDED:
1204
self.MarkerAdd(start,OUTPUT_START)
1205
self.MarkerAdd(start,OUTPUT_BG)
1207
def DeleteOutputSlicesAfter(self,line_num=None):
1208
"""Delete all outputs after an input"""
1210
line_num=self.GetCurrentLine()
1212
num_lines=self.GetLineCount()
1214
if self.MarkerGet(line_num) & OUTPUT_MASK:
1215
#print 'You can only run "DeleteOutputSlicesAfter" from an Input slice!'
1218
startIn,endIn=self.GetIOSlice(line_num)
1219
startGrouping,endGrouping=self.GetGroupingSlice(line_num)
1221
if endIn<endGrouping:
1222
self.SetSelection(self.PositionFromLine(endIn+1),
1223
self.PositionFromLine(endGrouping+1))
1224
self.ReplaceSelection('',sliceDeletion=True)
1226
new_pos=self.GetLineEndPosition(line_num)
1227
self.SetCurrentPos(new_pos)
1228
self.SetSelection(new_pos,new_pos)
1230
def SplitSlice(self,line_num=None):
1232
line_num=self.GetCurrentLine()
1234
start_num,end_num=self.GetIOSlice(line_num)
1236
if self.MarkerGet(line_num) & INPUT_MASK:
1241
elif self.MarkerGet(line_num) & OUTPUT_MASK:
1247
if start_num==end_num:
1248
return # Can't split one line!
1249
elif start_num==line_num:
1250
self.clearIOMarkers(line_num+1)
1251
self.MarkerAdd(line_num+1,start)
1252
if type=='Output': self.MarkerAdd(line_num+1,OUTPUT_BG)
1254
self.clearGroupingMarkers(line_num+1)
1255
self.MarkerAdd(line_num+1,GROUPING_START)
1257
self.clearIOMarkers(line_num)
1258
self.MarkerAdd(line_num,start)
1259
if type=='Output': self.MarkerAdd(line_num,OUTPUT_BG)
1261
self.clearGroupingMarkers(line_num)
1262
self.MarkerAdd(line_num,GROUPING_START)
1263
if line_num-1>start_num:
1264
self.clearIOMarkers(line_num-1)
1265
self.MarkerAdd(line_num-1,end)
1266
if type=='Output': self.MarkerAdd(line_num-1,OUTPUT_BG)
1268
self.clearGroupingMarkers(line_num-1)
1269
self.MarkerAdd(line_num-1,GROUPING_END)
1271
def BackspaceWMarkers(self,force=False):
1272
# Warning: This is not good at checking for bad markers!
1273
c_before=self.GetCharAt(self.GetCurrentPos() - 1)
1274
c_after=self.GetCharAt(self.GetCurrentPos())
1277
# Disallow deleting the first line or it will destroy the markers...
1279
elif c_before in (ord('\n'),ord('\r')):
1280
line_num=self.GetCurrentLine()
1282
marker=self.MarkerGet(line_num)
1283
marker_before=self.MarkerGet(line_num-1)
1284
marker_after=self.MarkerGet(line_num+1)
1285
if marker_before & ( 1<<GROUPING_END ) :
1286
return False # Disallow deleting lines between slices...
1287
elif marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
1288
return False # Disallow deleting lines between slices...
1290
if marker_before & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
1291
self.clearGroupingMarkers(line_num)
1292
elif marker & ( 1<<GROUPING_END ) :
1293
self.clearGroupingMarkers(line_num-1)
1295
if (marker_before & 1<<INPUT_END) and force:
1296
# Special case for use in processLine
1297
self.clearIOMarkers(line_num)
1298
elif marker_before & (1<<INPUT_END | 1<<OUTPUT_END):
1299
return False # Disallow deleting lines between slices...
1300
elif marker & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ) :
1301
return False # Disallow deleting lines between slices...
1303
if marker_before & (1<<INPUT_START | 1<<INPUT_START_FOLDED |
1304
1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED):
1305
self.clearIOMarkers(line_num)
1306
elif marker & ( 1<<INPUT_END | 1<<OUTPUT_END ) :
1307
self.clearIOMarkers(line_num-1)
1309
return True # If everything went well, return True and do the delete...
1311
def ForwardDeleteWMarkers(self):
1312
c_before=self.GetCharAt(self.GetCurrentPos() - 1)
1313
c_after=self.GetCharAt(self.GetCurrentPos())
1315
# Disallow deleting the first line or it will destroy the markers...
1317
elif c_after in (ord('\n'),ord('\r')):
1318
line_num=self.GetCurrentLine()
1320
marker=self.MarkerGet(line_num)
1321
marker_before=self.MarkerGet(line_num-1)
1322
marker_after=self.MarkerGet(line_num+1)
1323
if marker & ( 1<<GROUPING_END ) :
1324
return False # Disallow deleting lines between slices...
1325
elif marker_after & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
1326
return False # Disallow deleting lines between slices...
1328
if marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
1329
self.clearGroupingMarkers(line_num+1)
1330
elif marker_after & ( 1<<GROUPING_END ) :
1331
self.clearGroupingMarkers(line_num)
1333
if marker & ( 1<<INPUT_END | 1<<OUTPUT_END ) :
1334
return False # Disallow deleting lines between slices...
1335
elif marker_after & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ) :
1336
return False # Disallow deleting lines between slices...
1338
if marker & (1<<INPUT_START | 1<<INPUT_START_FOLDED |
1339
1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED) :
1340
self.clearIOMarkers(line_num+1)
1341
elif marker_after & ( 1<<INPUT_END | 1<<OUTPUT_END ) :
1342
self.clearIOMarkers(line_num)
1346
def GetIOSelection(self):
1349
end=self.GetLineCount()-1
1351
for i in range(self.GetLineCount()):
1352
if self.MarkerGet(i) & 1<<IO_SELECTING:
1355
if self.MarkerGet(i) & INPUT_MASK:
1357
elif self.MarkerGet(i) & OUTPUT_MASK:
1360
if self.MarkerGet(i) & INPUT_MASK:
1364
elif self.MarkerGet(i) & OUTPUT_MASK:
1374
#print 'No Selection!!'
1375
self.SliceSelection=False
1379
def MergeAdjacentSlices(self):
1380
"""This function merges all adjacent selected slices.\n""" + \
1381
"""Right now, only IO Merging is allowed."""
1384
end=self.GetLineCount()-1
1386
for i in range(self.GetLineCount()):
1387
if self.MarkerGet(i) & 1<<IO_SELECTING:
1390
if self.MarkerGet(i) & INPUT_MASK:
1392
elif self.MarkerGet(i) & OUTPUT_MASK:
1395
if self.MarkerGet(i) & INPUT_MASK:
1400
self.clearIOMarkers(i)
1401
self.clearGroupingMarkers(i)
1402
self.MarkerAdd(i,INPUT_MIDDLE)
1403
self.MarkerAdd(i,GROUPING_MIDDLE)
1404
elif self.MarkerGet(i) & OUTPUT_MASK:
1409
self.clearIOMarkers(i)
1410
self.clearGroupingMarkers(i)
1411
self.MarkerAdd(i,OUTPUT_MIDDLE)
1412
self.MarkerAdd(i,OUTPUT_BG)
1413
self.MarkerAdd(i,GROUPING_MIDDLE)
1419
if started and end!=start:
1420
self.clearIOMarkers(end)
1421
self.clearGroupingMarkers(end)
1423
self.MarkerAdd(end,INPUT_END)
1424
if end+1<self.GetLineCount():
1425
if self.MarkerGet(end+1) & OUTPUT_MASK:
1426
self.MarkerAdd(end,GROUPING_MIDDLE)
1428
self.MarkerAdd(end,GROUPING_END)
1430
self.MarkerAdd(end,GROUPING_END)
1432
if self.MarkerGet(start) & 1<<GROUPING_END:
1433
self.clearGroupingMarkers(start)
1434
self.MarkerAdd(start,GROUPING_MIDDLE)
1435
self.MarkerAdd(end,OUTPUT_END)
1436
self.MarkerAdd(end,OUTPUT_BG)
1437
self.MarkerAdd(end,GROUPING_END)
1440
def SliceSelectionDelete(self):
1441
"""Deletion of any selected and possibly discontinuous slices."""
1442
if not self.SliceSelection:
1445
# collect the line numbers to be deleted...
1448
for i in range(self.GetLineCount()):
1449
if self.MarkerGet(i) & (1<<GROUPING_SELECTING | 1<<IO_SELECTING):
1454
selectedSlices.append([start,end])
1457
selectedSlices.append([start,end])
1459
# Unselect everything
1460
self.MarginUnselectAll()
1461
self.SliceSelection=False
1463
# Going in reverse, delete the selections, fixing the markers as we go...
1464
for i in range(len(selectedSlices)-1,-1,-1):
1465
self.SetSelection(self.PositionFromLine(selectedSlices[i][0]),
1466
self.GetLineEndPosition(selectedSlices[i][1])+1)
1468
markerNext = self.MarkerGet(selectedSlices[i][1]+1)
1470
self.ReplaceSelection('',sliceDeletion=True)
1472
cur_line=self.GetCurrentLine()
1474
# If we've made a mess of the grouping markers, clean it up...
1475
if ((self.MarkerGet(cur_line-1) & 1<<GROUPING_END) and
1476
(self.MarkerGet(cur_line) & ( 1<<GROUPING_MIDDLE | 1<<GROUPING_END ) )):
1477
self.clearGroupingMarkers(cur_line)
1478
self.MarkerAdd(cur_line,GROUPING_START)
1479
elif (( self.MarkerGet(cur_line-1) & 1<<GROUPING_MIDDLE ) and
1480
( self.MarkerGet(cur_line) &
1481
( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) )):
1482
self.clearGroupingMarkers(cur_line-1)
1483
self.MarkerAdd(cur_line-1,GROUPING_END)
1485
if markerNext & 1<<OUTPUT_START:
1486
self.clearIOMarkers(cur_line)
1487
self.MarkerAdd(cur_line,OUTPUT_START)
1488
self.MarkerAdd(cur_line,OUTPUT_BG)
1489
elif markerNext & 1<<OUTPUT_START_FOLDED:
1490
self.clearIOMarkers(cur_line)
1491
self.MarkerAdd(cur_line,OUTPUT_START_FOLDED)
1492
self.MarkerAdd(cur_line,OUTPUT_BG)
1496
def OnChar(self, event):
1497
"""Keypress event handler.
1499
Only receives an event if OnKeyDown calls event.Skip() for the
1500
corresponding event."""
1506
# Prevent modification of output slices
1507
if not self.CanEdit():
1509
key = event.GetKeyCode()
1510
currpos = self.GetCurrentPos()
1511
stoppos = self.PositionFromLine(self.GetCurrentLine())
1513
# Return (Enter) needs to be ignored in this handler.
1514
if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
1516
elif key in self.autoCompleteKeys:
1517
# Usually the dot (period) key activates auto completion.
1518
# Get the command between the prompt and the cursor. Add
1519
# the autocomplete character to the end of the command.
1520
if self.AutoCompActive():
1521
self.AutoCompCancel()
1522
command = self.GetTextRange(stoppos, currpos) + chr(key)
1524
# write with undo wrapper...
1525
cpos=self.GetCurrentPos()
1528
self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s),
1529
forceNewAction=False)
1530
self.write(s,type='Input')
1531
self.UpdateUndoHistoryAfter()
1533
if self.autoComplete:
1534
self.autoCompleteShow(command)
1535
elif key == ord('('):
1536
# The left paren activates a call tip and cancels an
1537
# active auto completion.
1538
if self.AutoCompActive():
1539
self.AutoCompCancel()
1540
# Get the command between the prompt and the cursor. Add
1541
# the '(' to the end of the command.
1542
self.ReplaceSelection('')
1543
command = self.GetTextRange(stoppos, currpos) + '('
1545
# write with undo wrapper...
1546
cpos=self.GetCurrentPos()
1548
self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s),
1549
forceNewAction=True)
1550
self.undoHistory[self.undoIndex]['allowsAppend']=True
1551
self.write(s,type='Input')
1552
self.UpdateUndoHistoryAfter()
1554
self.autoCallTipShow(command,
1555
self.GetCurrentPos() == self.GetTextLength())
1557
# Allow the normal event handling to take place.
1559
cpos=self.GetCurrentPos()
1562
self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s))
1564
self.UpdateUndoHistoryAfter()
1568
def AutoCompActiveCallback(self):
1569
numChars=self.GetTextLength()-self.TotalLengthForAutoCompActiveCallback
1572
del(self.undoHistory[-1])
1576
cpos=uH[uI]['posStart']
1577
s=''.join([chr(self.GetCharAt(cpos+i)) for i in range(numChars)])
1578
s.replace(os.linesep,'\n')
1579
self.undoHistory[self.undoIndex]['charList'] = s
1580
self.undoHistory[self.undoIndex]['posEnd'] = cpos + numChars
1581
self.undoHistory[self.undoIndex]['numLines'] = len(s.split('\n'))
1582
self.UpdateUndoHistoryAfter()
1584
def OnKeyDown(self, event):
1585
"""Key down event handler."""
1587
key = event.GetKeyCode()
1588
# If the auto-complete window is up let it do its thing.
1589
if self.AutoCompActive():
1590
if key in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]:
1591
cpos=self.GetCurrentPos()
1592
self.UpdateUndoHistoryBefore('insert','dummy',cpos,cpos+5,
1593
forceNewAction=True)
1594
self.undoHistory[self.undoIndex]['allowsAppend'] = True
1595
self.TotalLengthForAutoCompActiveCallback=self.GetTextLength()
1597
wx.CallAfter(self.AutoCompActiveCallback)
1598
if key in [wx.WXK_DELETE,wx.WXK_BACK]:
1599
self.AutoCompCancel()
1605
# Prevent modification of output slices
1606
controlDown = event.ControlDown()
1607
altDown = event.AltDown()
1608
shiftDown = event.ShiftDown()
1609
currpos = self.GetCurrentPos()
1610
endpos = self.GetTextLength()
1611
selecting = self.GetSelectionStart() != self.GetSelectionEnd()
1613
if key == wx.WXK_F12: #seb
1615
# self.promptPosStart not used anyway - or ?
1616
## # We don't need to do this any more!
1617
## self.promptPosEnd = self.PositionFromLine(self.GetLineCount()-1 ) +
1618
## len(str(sys.ps1))
1619
## self.GotoLine(self.GetLineCount())
1620
## self.GotoPos(self.promptPosEnd)
1621
## self.prompt() #make sure we have a prompt
1622
self.SetCaretForeground("black")
1623
self.SetCaretWidth(1) #default
1624
self.SetCaretPeriod(500) #default
1626
self.SetCaretForeground("red")
1627
self.SetCaretWidth(4)
1628
self.SetCaretPeriod(0) #steady
1630
self.noteMode = not self.noteMode
1637
doSubmitCommand=False
1639
# Return is used to insert a line break.
1640
# In Shell Mode, hit Return or Enter twice to submit a command
1641
if ((not controlDown and not shiftDown and not altDown) and
1642
key in [wx.WXK_RETURN,]):
1643
if self.mode=='SlicesMode':
1645
elif self.mode=='ShellMode':
1646
startLine,endLine = self.GetIOSlice()
1647
startpos = self.PositionFromLine(startLine)
1648
endpos = self.GetLineEndPosition(endLine)
1649
command = self.GetTextRange(startpos, endpos)
1650
strCont,indentBlock,lineCont,parenCont = testForContinuations(command,ignoreErrors=True)
1652
lastLine = command.split('\n')[-1]
1653
if lastLine.lstrip()=='': # all whitespace...
1655
elif lastLine[0]==' ':
1660
if strCont[-1] or indentBlock[-1] or lineCont[-1] or \
1664
new_pos=self.GetLineEndPosition(endLine)
1665
self.SetCurrentPos(new_pos)
1666
self.SetSelection(new_pos,new_pos)
1668
elif self.GetCurrentLine()!=endLine:
1669
new_pos=self.GetLineEndPosition(endLine)
1670
self.SetCurrentPos(new_pos)
1671
self.SetSelection(new_pos,new_pos)
1674
doSubmitCommand=True
1675
# Enter (Shift/Ctrl + Enter/Return) submits a command to the interpreter.
1676
# In Shell Mode, hit Return or Enter twice to submit a command
1677
elif ( key in [wx.WXK_NUMPAD_ENTER,] or
1678
( (shiftDown or controlDown) and key in [wx.WXK_RETURN,
1679
wx.WXK_NUMPAD_ENTER] ) ):
1680
if self.mode=='SlicesMode':
1681
doSubmitCommand=True
1682
elif self.mode=='ShellMode':
1685
#Only relevant in ShellMode...
1689
elif doLineBreak or doSubmitCommand:
1690
if self.CallTipActive():
1691
self.CallTipCancel()
1692
elif self.SliceSelection:
1693
for i in range(self.GetLineCount()):
1694
if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
1695
self.DoMarginClick(i, 2, shiftDown, controlDown)
1697
elif self.MarkerGet(i) & 1<<IO_SELECTING:
1698
self.DoMarginClick(i, 3, shiftDown, controlDown)
1701
self.insertLineBreak()
1702
#Only relevant in ShellMode...
1703
elif doSubmitCommand:
1704
self.DeleteOutputSlicesAfter()
1707
# Let Ctrl-Alt-* get handled normally.
1708
elif controlDown and altDown:
1711
# Clear the current, unexecuted command.
1712
elif key == wx.WXK_ESCAPE:
1713
if self.CallTipActive():
1715
# Clear the current command
1716
elif key == wx.WXK_BACK and controlDown and shiftDown:
1719
# Increase font size.
1720
elif controlDown and key in (ord(']'), wx.WXK_NUMPAD_ADD):
1721
dispatcher.send(signal='FontIncrease')
1723
# Decrease font size.
1724
elif controlDown and key in (ord('['), wx.WXK_NUMPAD_SUBTRACT):
1725
dispatcher.send(signal='FontDecrease')
1727
# Default font size.
1728
elif controlDown and key in (ord('='), wx.WXK_NUMPAD_DIVIDE):
1729
dispatcher.send(signal='FontDefault')
1731
# Cut to the clipboard.
1732
elif (controlDown and key in (ord('X'), ord('x'))) \
1733
or (shiftDown and key == wx.WXK_DELETE):
1736
# Copy to the clipboard.
1737
elif controlDown and not shiftDown \
1738
and key in (ord('C'), ord('c'), wx.WXK_INSERT):
1741
# Copy to the clipboard, including prompts.
1742
elif controlDown and shiftDown \
1743
and key in (ord('C'), ord('c'), wx.WXK_INSERT):
1744
self.CopyWithPrompts()
1746
# Copy to the clipboard, including prefixed prompts.
1747
elif altDown and not controlDown \
1748
and key in (ord('C'), ord('c'), wx.WXK_INSERT):
1749
self.CopyWithPromptsPrefixed()
1751
# Home needs to be aware of the prompt.
1752
elif controlDown and key == wx.WXK_HOME:
1753
# Go to the beginning of the IO Slice
1754
curLine = self.GetCurrentLine()
1755
IOstart = self.GetIOSlice(curLine)[0]
1756
home = self.PositionFromLine(IOstart)
1757
if currpos == home and \
1759
home = self.PositionFromLine(self.GetIOSlice(curLine-1)[0])
1760
self.SetCurrentPos(home)
1761
if not selecting and not shiftDown:
1762
self.SetAnchor(home)
1763
self.EnsureCaretVisible()
1765
elif controlDown and key == wx.WXK_END:
1766
curLine = self.GetCurrentLine()
1767
IOend = self.GetIOSlice(curLine)[1]
1768
end = self.GetLineEndPosition(IOend)
1769
if currpos == end and \
1770
IOend < self.GetLineCount()-1:
1771
end = self.GetLineEndPosition(self.GetIOSlice(curLine+1)[1])
1772
self.SetCurrentPos(end)
1773
if not selecting and not shiftDown:
1775
self.EnsureCaretVisible()
1777
elif controlDown and key == wx.WXK_PAGEUP:
1780
self.SetCurrentPos(pos)
1781
if not selecting and not shiftDown:
1783
self.EnsureCaretVisible()
1785
elif controlDown and key == wx.WXK_PAGEDOWN:
1786
pos = self.GetLineEndPosition(self.GetLineCount()-1)
1788
self.SetCurrentPos(pos)
1789
if not selecting and not shiftDown:
1791
self.EnsureCaretVisible()
1793
elif selecting and key not in NAVKEYS and not self.CanEdit():
1796
# Paste from the clipboard.
1797
elif (controlDown and not shiftDown and key in (ord('V'), ord('v'))) \
1798
or (shiftDown and not controlDown and key == wx.WXK_INSERT):
1801
# Paste from the clipboard, run commands.
1802
elif controlDown and shiftDown and \
1803
key in (ord('V'), ord('v')) and self.CanEdit():
1806
# Replace with the previous command from the history buffer.
1807
elif (controlDown and not shiftDown and key == wx.WXK_UP) \
1808
or (altDown and key in (ord('P'), ord('p'))) and self.CanEdit():
1809
self.OnHistoryReplace(step=+1)
1811
# Replace with the next command from the history buffer.
1812
elif (controlDown and not shiftDown and key == wx.WXK_DOWN) \
1813
or (altDown and key in (ord('N'), ord('n'))) and self.CanEdit():
1814
self.OnHistoryReplace(step=-1)
1816
# Insert the previous command from the history buffer.
1817
elif (controlDown and shiftDown and key == wx.WXK_UP) and \
1819
self.OnHistoryInsert(step=+1)
1821
# Insert the next command from the history buffer.
1822
elif (controlDown and shiftDown and key == wx.WXK_DOWN) and \
1824
self.OnHistoryInsert(step=-1)
1826
# Ctrl-Space shows Auto Completion
1827
# Ctrl-Shift-Space shows CallTips
1828
elif controlDown and key == wx.WXK_SPACE:
1829
self.OnCallTipAutoCompleteManually(shiftDown)
1831
# Ctrl+Shift+H is used to complete Text (from already typed words)
1832
elif controlDown and shiftDown and key in [ord('H')]:
1833
self.OnShowCompHistory()
1835
# Search up the history for the text in front of the cursor.
1836
elif key == wx.WXK_F8:
1837
self.OnHistorySearch()
1839
# Don't backspace over the latest non-continuation prompt.
1840
elif key == wx.WXK_BACK:
1841
if self.SliceSelection:
1842
self.SliceSelectionDelete()
1843
wx.CallAfter(self.RestoreFirstMarker)
1844
elif selecting and self.CanEdit():
1845
self.ReplaceSelection('')
1847
elif self.CanEdit():
1849
cur_line=self.GetCurrentLine()
1850
if not cur_line==0 and \
1851
self.GetCurrentPos()==self.PositionFromLine(cur_line):
1852
if self.MarkerGet(cur_line-1) & OUTPUT_MASK:
1856
cpos=self.GetCurrentPos()
1857
s=chr(self.GetCharAt(cpos-1))
1858
self.UpdateUndoHistoryBefore('delete',s,cpos-1,cpos)
1859
if self.BackspaceWMarkers():
1862
wx.CallAfter(self.RestoreFirstMarker)
1864
elif key == wx.WXK_DELETE:
1865
if self.SliceSelection:
1866
self.SliceSelectionDelete()
1867
wx.CallAfter(self.RestoreFirstMarker)
1868
elif selecting and self.CanEdit():
1869
self.ReplaceSelection('')
1871
elif self.CanEdit():
1873
cur_line=self.GetCurrentLine()
1874
if not cur_line==self.GetLineCount()-1 and \
1875
self.GetCurrentPos()==self.GetLineEndPosition(cur_line):
1876
if self.MarkerGet(cur_line+1) & OUTPUT_MASK:
1880
cpos=self.GetCurrentPos()
1881
s=chr(self.GetCharAt(cpos))
1882
self.UpdateUndoHistoryBefore('delete',s,cpos,cpos+1)
1883
if self.ForwardDeleteWMarkers():
1886
wx.CallAfter(self.RestoreFirstMarker)
1888
# Only allow these keys after the latest prompt.
1889
elif key == wx.WXK_TAB and self.CanEdit():
1890
# use the same mechanism as with autocmplete...
1891
cpos=self.GetCurrentPos()
1892
self.UpdateUndoHistoryBefore('insert','dummy',cpos,cpos+5,
1893
forceNewAction=True)
1894
self.undoHistory[self.undoIndex]['allowsAppend'] = True
1895
self.TotalLengthForAutoCompActiveCallback=self.GetTextLength()
1897
wx.CallAfter(self.AutoCompActiveCallback)
1899
# Don't toggle between insert mode and overwrite mode.
1900
elif key == wx.WXK_INSERT:
1903
# Don't allow line deletion.
1904
#elif controlDown and key in (ord('L'), ord('l')):
1905
# TODO : Allow line deletion eventually ??
1909
# Don't allow line transposition.
1910
# Toggle Shell Mode / Slices Mode
1911
elif controlDown and key in (ord('T'), ord('t')):
1912
self.ToggleShellMode()
1914
#Open and Save now work when using CrustSlicesFrames
1915
elif controlDown and key in (ord('L'), ord('l')):
1917
file=wx.FileSelector("Load File As New Slice")
1920
self.LoadPyFileAsSlice(fid)
1923
elif controlDown and key in (ord('D'), ord('d')):
1924
#Disallow line duplication in favor of divide slices
1925
if self.MarkerGet(self.GetCurrentLine()) & INPUT_MASK:
1927
cpos=self.GetCurrentPos()
1928
start,end = map(self.PositionFromLine,
1929
self.GetGroupingSlice(self.LineFromPosition(cpos)))
1930
self.UpdateUndoHistoryBefore('marker','',start,end,
1931
forceNewAction=True)
1933
# Turn off selecting
1934
self.SetSelection(cpos,cpos)
1935
self.ReplaceSelection('')
1936
self.UpdateUndoHistoryAfter()
1938
elif controlDown and key in (ord('M'), ord('m')):
1940
if self.SliceSelection:
1941
cpos=self.GetCurrentPos()
1942
ioSel=self.GetIOSelection()
1943
if self.SliceSelection:
1944
start,end = map(self.PositionFromLine,ioSel)
1945
self.UpdateUndoHistoryBefore('marker','',start,end,
1946
forceNewAction=True)
1947
self.MergeAdjacentSlices()
1948
# Turn off selecting
1949
self.SetSelection(cpos,cpos)
1950
self.ReplaceSelection('')
1951
self.UpdateUndoHistoryAfter()
1954
# Change arrow keys to allow margin behaviors...
1955
elif self.SliceSelection and \
1956
key in [wx.WXK_UP,wx.WXK_DOWN,wx.WXK_RIGHT,wx.WXK_LEFT]:
1957
# TODO : This is useful, but not optimal!
1959
for i in range(self.GetLineCount()):
1960
if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
1962
self.DoMarginClick(i-1, 2, shiftDown, controlDown)
1964
elif self.MarkerGet(i) & 1<<IO_SELECTING:
1966
self.DoMarginClick(i-1, 3, shiftDown, controlDown)
1968
elif key==wx.WXK_DOWN:
1969
for i in range(self.GetLineCount()-1,-1,-1):
1970
if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
1971
if i<self.GetLineCount()-1: #Grouping
1972
self.DoMarginClick(i+1, 2, shiftDown, controlDown)
1974
elif self.MarkerGet(i) & 1<<IO_SELECTING:
1975
if i<self.GetLineCount()-1: #IO
1976
self.DoMarginClick(i+1, 3, shiftDown, controlDown)
1978
elif key==wx.WXK_RIGHT:
1979
for i in range(self.GetLineCount()):
1980
if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
1981
self.DoMarginClick(i, 3, shiftDown, controlDown)
1983
elif self.MarkerGet(i) & 1<<IO_SELECTING:
1984
self.MarginUnselectAll()
1985
# Go to the beginning of the IO Slice
1986
self.SetCurrentPos(self.PositionFromLine(i))
1987
if not selecting and not shiftDown:
1988
self.SetAnchor(self.PositionFromLine(i))
1989
self.EnsureCaretVisible()
1991
elif key==wx.WXK_LEFT:
1992
for i in range(self.GetLineCount()):
1993
if self.MarkerGet(i) & 1<<GROUPING_SELECTING:
1995
elif self.MarkerGet(i) & 1<<IO_SELECTING:
1996
self.DoMarginClick(i, 2, shiftDown, controlDown)
1998
# Basic navigation keys should work anywhere.
1999
elif key in NAVKEYS:
2001
# Protect the readonly portion of the slices shell.
2002
elif not self.CanEdit():
2005
# Check to see if we're selecting
2006
if self.GetSelectionEnd()>self.GetSelectionStart():
2007
# Check to see if a normal input took place
2008
if not controlDown and not altDown and key<256:
2009
self.ReplaceSelection('') # This seems to work...
2012
if self.SliceSelection:
2013
if key not in [wx.WXK_UP,wx.WXK_DOWN,wx.WXK_RIGHT,wx.WXK_LEFT,
2014
wx.WXK_ALT,wx.WXK_COMMAND,wx.WXK_CONTROL,wx.WXK_SHIFT]:
2015
self.MarginUnselectAll()
2018
def MarginSelectAll(self):
2019
num_lines=self.GetLineCount()
2020
for i in range(num_lines):
2021
self.MarkerAdd(i,GROUPING_SELECTING)
2022
self.MarkerDelete(i,IO_SELECTING)
2024
def MarginUnselectAll(self):
2025
num_lines=self.GetLineCount()
2026
for i in range(num_lines):
2027
self.MarkerDelete(i,IO_SELECTING)
2028
self.MarkerDelete(i,GROUPING_SELECTING)
2029
self.SliceSelection=False
2031
def DoMarginClick(self, lineClicked, margin, shiftDown, controlDown):
2032
num_lines=self.GetLineCount()
2035
pass # these events are not sent right now...
2037
self.SliceSelection=True
2038
start,end=self.GetGroupingSlice(lineClicked)
2039
startPos=self.PositionFromLine(start)
2040
self.SetCurrentPos(startPos)
2041
self.SetSelection(startPos,startPos)
2042
start_marker=self.MarkerGet(start)
2043
if self.MarkerGet(lineClicked) & 1<<GROUPING_SELECTING:
2044
toggle=self.MarkerDelete
2046
if start_marker & 1<<GROUPING_START:
2047
self.FoldGroupingSlice(lineClicked)
2048
elif start_marker & 1<<GROUPING_START_FOLDED:
2049
self.UnFoldGroupingSlice(lineClicked)
2051
toggle=self.MarkerAdd
2054
self.MarginUnselectAll()
2056
for i in range(start,end+1):
2057
toggle(i,GROUPING_SELECTING)
2059
self.SliceSelection=True
2060
start,end=self.GetIOSlice(lineClicked)
2061
startPos=self.PositionFromLine(start)
2062
self.SetCurrentPos(startPos)
2063
self.SetSelection(startPos,startPos)
2064
start_marker=self.MarkerGet(start)
2065
if self.MarkerGet(lineClicked) & 1<<IO_SELECTING:
2066
toggle=self.MarkerDelete
2068
if start_marker & IO_START_MASK:
2069
self.FoldIOSlice(lineClicked)
2070
elif start_marker & IO_START_FOLDED_MASK:
2071
self.UnFoldIOSlice(lineClicked)
2073
toggle=self.MarkerAdd
2076
self.MarginUnselectAll()
2078
for i in range(start,end+1):
2079
toggle(i,IO_SELECTING)
2085
if 1:#self.MarkerGet(lineClicked) & ( 1<<7 | 1<<8 ):
2087
self.SetFoldExpanded(lineClicked, True)
2088
self.Expand(lineClicked, True, True, 1)
2090
if self.GetFoldExpanded(lineClicked):
2091
self.SetFoldExpanded(lineClicked, False)
2092
self.Expand(lineClicked, False, True, 0)
2094
self.SetFoldExpanded(lineClicked, True)
2095
self.Expand(lineClicked, True, True, 100)
2097
self.ToggleFold(lineClicked)
2099
self.MarginUnselectAll()
2101
if toggle==self.MarkerDelete and not shiftDown:
2102
self.SliceSelection=False
2104
self.SliceSelection=True
2106
def OnMarginClick(self, evt):
2108
# fold and unfold as neededNAVKEYS
2109
lineClicked = self.LineFromPosition(evt.GetPosition())
2110
self.DoMarginClick(lineClicked,evt.GetMargin(),evt.GetShift(),evt.GetControl())
2113
def OnShowCompHistory(self):
2114
"""Show possible autocompletion Words from already typed words."""
2117
his = self.history[:]
2119
#put together in one string
2120
joined = " ".join (his)
2123
#sort out only "good" words
2124
newlist = re.split("[ \.\[\]=}(\)\,0-9\"]", joined)
2126
#length > 1 (mix out "trash")
2132
#unique (no duplicate words
2133
#oneliner from german python forum => unique list
2134
unlist = [thlist[i] for i in xrange(len(thlist)) if thlist[i] not in thlist[:i]]
2137
unlist.sort(lambda a, b: cmp(a.lower(), b.lower()))
2139
#this is more convenient, isn't it?
2140
self.AutoCompSetIgnoreCase(True)
2142
#join again together in a string
2143
stringlist = " ".join(unlist)
2145
#pos von 0 noch ausrechnen
2147
#how big is the offset?
2148
cpos = self.GetCurrentPos() - 1
2149
while chr (self.GetCharAt (cpos)).isalnum():
2152
#the most important part
2153
self.AutoCompShow(self.GetCurrentPos() - cpos -1, stringlist)
2155
def ReplaceSelection(self,text,sliceDeletion=False,*args,**kwds):
2156
startIO,endIO=self.GetIOSlice()
2157
startGrouping,endGrouping=self.GetGroupingSlice()
2158
startSel = self.LineFromPosition(self.GetSelectionStart())
2159
endSel = self.LineFromPosition(self.GetSelectionEnd())
2162
cpos=self.GetSelectionStart()
2163
s=self.GetSelectedText()
2165
self.UpdateUndoHistoryBefore('delete',s,cpos,cpos+len(s),
2166
forceNewAction=True)
2167
editwindow.EditWindow.ReplaceSelection(self,'',*args,**kwds)
2168
if s!='' and not sliceDeletion:
2169
self.UpdateUndoHistoryAfter()
2171
if endSel-startSel>0 and not sliceDeletion:
2172
if endSel==endIO and startIO!=self.GetCurrentLine():
2173
self.clearIOMarkers()
2174
self.MarkerAdd(self.GetCurrentLine(),INPUT_END)
2176
if endSel==endGrouping and startGrouping!=self.GetCurrentLine():
2177
self.clearGroupingMarkers()
2178
self.MarkerAdd(self.GetCurrentLine(),GROUPING_END)
2180
cpos=self.GetSelectionStart()
2183
self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s),
2184
forceNewAction=True)
2186
self.UpdateUndoHistoryAfter()
2188
self.ensureSingleGroupingMarker()
2189
self.ensureSingleIOMarker()
2192
def clearCommand(self):
2193
"""Delete the current, unexecuted command."""
2194
if not self.CanEdit():
2196
start,end=self.GetIOSlice()
2197
startpos = self.PositionFromLine(start)
2198
endpos = self.GetLineEndPosition(end)
2199
self.SetSelection(startpos, endpos)
2200
self.ReplaceSelection('')
2203
def OnHistoryReplace(self, step):
2204
"""Replace with the previous/next command from the history buffer."""
2205
if not self.CanEdit():
2208
self.replaceFromHistory(step)
2210
def replaceFromHistory(self, step):
2211
"""Replace selection with command from the history buffer."""
2212
if not self.CanEdit():
2214
self.ReplaceSelection('')
2215
newindex = self.historyIndex + step
2216
if -1 <= newindex <= len(self.history):
2217
self.historyIndex = newindex
2218
if 0 <= newindex <= len(self.history)-1:
2219
command = self.history[self.historyIndex]
2220
command = command.replace('\n', os.linesep)# + ps2)
2221
self.ReplaceSelection(command)
2223
def OnHistoryInsert(self, step):
2224
"""Insert the previous/next command from the history buffer."""
2225
if not self.CanEdit():
2227
startpos = self.GetCurrentPos()
2228
self.replaceFromHistory(step)
2229
endpos = self.GetCurrentPos()
2230
self.SetSelection(endpos, startpos)
2233
def OnHistorySearch(self):
2234
"""Search up the history buffer for the text in front of the cursor."""
2235
if not self.CanEdit():
2237
startpos = self.GetCurrentPos()
2238
# The text up to the cursor is what we search for.
2239
numCharsAfterCursor = self.GetTextLength() - startpos
2240
searchText = self.getCommand(rstrip=False)
2241
#print 'history search', startpos,numCharsAfterCursor,searchText
2242
if numCharsAfterCursor > 0:
2243
searchText = searchText[:-numCharsAfterCursor]
2246
# Search upwards from the current history position and loop
2247
# back to the beginning if we don't find anything.
2248
if (self.historyIndex <= -1) \
2249
or (self.historyIndex >= len(self.history)-2):
2250
searchOrder = range(len(self.history))
2252
searchOrder = range(self.historyIndex+1, len(self.history)) + \
2253
range(self.historyIndex)
2254
for i in searchOrder:
2255
command = self.history[i]
2256
if command[:len(searchText)] == searchText:
2257
# Replace the current selection with the one we found.
2258
self.ReplaceSelection(command[len(searchText):])
2259
endpos = self.GetCurrentPos()
2260
self.SetSelection(endpos, startpos)
2261
# We've now warped into middle of the history.
2262
self.historyIndex = i
2265
def setStatusText(self, text):
2266
"""Display status information."""
2268
# This method will likely be replaced by the enclosing app to
2269
# do something more interesting, like write to a status bar.
2272
def insertLineBreak(self):
2273
"""Insert a new line break."""
2274
if not self.CanEdit():
2276
elif self.reader.isreading:
2281
# write with undo wrapper...
2282
cpos=self.GetCurrentPos()
2284
self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+1)
2285
self.write(s,type='Input')
2286
self.UpdateUndoHistoryAfter()
2291
def processLine(self):
2292
"""Process the line of text at which the user hit Enter or Shift+RETURN."""
2293
# The user hit ENTER (Shift+RETURN) (Shift+ENTER) and we need to
2294
# decide what to do. They could be sitting on any line in the slices shell.
2295
thepos = self.GetCurrentPos()
2296
cur_line = self.GetCurrentLine()
2297
marker=self.MarkerGet(cur_line)
2298
if marker & INPUT_MASK:
2300
elif marker & OUTPUT_MASK:
2303
pass #print 'BLANK LINE!!'
2305
startline,endline=self.GetIOSlice(cur_line)
2310
startpos=self.PositionFromLine(startline)
2312
endpos=self.GetLineEndPosition(endline)
2314
# If they hit ENTER inside the current command, execute the command.
2316
self.SetCurrentPos(endpos)
2317
self.interp.more = False
2318
command = self.GetTextRange(startpos, endpos)
2319
lines = command.split(os.linesep)
2320
lines = [line.rstrip() for line in lines]
2321
command = '\n'.join(lines)
2322
if self.reader.isreading:
2324
# Match the behavior of the standard Python shell
2325
# when the user hits return without entering a value.
2327
self.reader.input = command
2328
self.write(os.linesep,'Input')
2329
self.MarkerSet(self.GetCurrentLine(),READLINE_BG)
2330
self.MarkerSet(self.GetCurrentLine(),INPUT_READLINE)
2332
self.runningSlice = (startline,endline)
2333
self.push(command,useMultiCommand=True)
2334
#print 'command: ',command
2335
wx.FutureCall(1, self.EnsureCaretVisible)
2336
self.runningSlice=None
2338
skip=self.BackspaceWMarkers(force=True)
2342
if self.GetCurrentLine()==self.GetLineCount()-1:
2343
self.write(os.linesep,type='Input')
2344
cpos=self.GetCurrentLine()
2345
if self.MarkerGet(cpos-1) & OUTPUT_MASK:
2346
self.MarkerAdd(cpos-1,OUTPUT_BG)
2349
cur_line=self.GetCurrentLine()
2350
new_pos=self.GetLineEndPosition(cur_line+1)
2351
self.SetSelection(new_pos,new_pos)
2352
self.SetCurrentPos(new_pos)
2354
self.EmptyUndoBuffer()
2355
self.NeedsCheckForSave=True
2356
if self.hasSyntaxError:
2357
pos=self.GetLineEndPosition(self.syntaxErrorRealLine)
2358
self.SetCurrentPos(pos)
2359
self.SetSelection(pos,pos)
2362
def getMultilineCommand(self, rstrip=True):
2363
"""Extract a multi-line command from the editor.
2365
The command may not necessarily be valid Python syntax."""
2367
# XXX Need to extract real prompts here. Need to keep track of
2368
# the prompt every time a command is issued.
2369
text = self.GetCurLine()[0]
2370
line = self.GetCurrentLine()
2371
# Add Marker testing here...
2372
while text == '' and line > 0: # Need to add markers handling...
2375
text = self.GetCurLine()[0]
2377
line = self.GetCurrentLine()
2379
startpos = self.GetCurrentPos()
2382
while self.GetCurLine()[0]=='':
2385
stoppos = self.GetCurrentPos()
2386
command = self.GetTextRange(startpos, stoppos)
2387
command = command.replace(os.linesep, '\n')
2388
command = command.rstrip()
2389
command = command.replace('\n', os.linesep)
2393
command = command.rstrip()
2396
def getCommand(self, text=None, rstrip=True):
2397
"""Extract a command from text which may include a shell prompt.
2399
The command may not necessarily be valid Python syntax."""
2401
text = self.GetCurLine()[0]
2402
# Strip the prompt off the front leaving just the command.
2403
command = self.lstripPrompt(text)
2404
# Change this -- Nothing has prompts!
2405
#if command == text:
2406
# command = '' # Real commands have prompts.
2408
command = command.rstrip()
2411
def lstripPrompt(self, text):
2412
"""Return text without a leading prompt."""
2417
# Strip the prompt off the front of text.
2418
if text[:ps1size] == ps1:
2419
text = text[ps1size:]
2420
elif text[:ps2size] == ps2:
2421
text = text[ps2size:]
2424
def push(self, command, silent = False,useMultiCommand=False):
2425
"""Send command to the interpreter for execution."""
2427
self.write(os.linesep,type='Output')
2428
# TODO : What other magic might we insert here?
2429
# TODO : Is there a good reason not to include magic?
2431
command=magic(command)
2433
# Allows multi-component commands...
2434
self.hasSyntaxError=False
2436
result = self.BreakTextIntoCommands(command)
2437
if result[0] == None:
2439
self.hasSyntaxError=True
2440
syntaxErrorLine=result[1]+1
2441
self.syntaxErrorRealLine = self.GetCurrentLine()+result[1]-len(command.split('\n'))
2447
busy = wx.BusyCursor()
2449
self.lastUpdate=None
2452
if self.hasSyntaxError:
2453
lineno=syntaxErrorLine
2454
offset=0 # not sure how to easily recover this information...
2455
self.write(' File "<input>", line '+str(lineno)+'\n '+i.split('\n')[lineno-1]+'\n'+' '*offset+' ^\nSyntaxError: invalid syntax\n','Error')
2457
self.more = self.interp.push(i+'\n')
2458
# (the \n stops many things from bouncing at the interpreter)
2459
# I could do the following, but I don't really like it!
2460
#if useMultiCommand:
2462
self.lastUpdate=None
2465
self.MarkerAdd(self.GetIOSlice()[0],OUTPUT_BG)
2467
self.waiting = False
2469
if not self.more: # could loop-add to history, too, but I don't like it!
2470
self.addHistory(command.rstrip())
2475
def addHistory(self, command):
2476
"""Add command to the command history."""
2477
# Reset the history position.
2478
self.historyIndex = -1
2479
# Insert this command into the history, unless it's a blank
2480
# line or the same as the last command.
2481
if command!='' and ( len(self.history)==0 or command!=self.history[0] ):
2482
self.history.insert(0, command)
2483
dispatcher.send(signal="SlicesShell.addHistory", command=command)
2485
def clearGroupingMarkers(self,line_num=None):
2487
line_num=self.GetCurrentLine()
2488
self.MarkerDelete(line_num,GROUPING_START)
2489
self.MarkerDelete(line_num,GROUPING_START_FOLDED)
2490
self.MarkerDelete(line_num,GROUPING_MIDDLE)
2491
self.MarkerDelete(line_num,GROUPING_END)
2492
def clearIOMarkers(self,line_num=None):
2494
line_num=self.GetCurrentLine()
2495
self.MarkerDelete(line_num,INPUT_START)
2496
self.MarkerDelete(line_num,INPUT_START_FOLDED)
2497
self.MarkerDelete(line_num,INPUT_MIDDLE)
2498
self.MarkerDelete(line_num,INPUT_END)
2499
self.MarkerDelete(line_num,OUTPUT_START)
2500
self.MarkerDelete(line_num,OUTPUT_START_FOLDED)
2501
self.MarkerDelete(line_num,OUTPUT_MIDDLE)
2502
self.MarkerDelete(line_num,OUTPUT_END)
2503
self.MarkerDelete(line_num,OUTPUT_BG)
2504
self.MarkerDelete(line_num,READLINE_BG)
2505
self.MarkerDelete(line_num,INPUT_READLINE)
2506
def ensureSingleGroupingMarker(self,line_num=None):
2508
line_num=self.GetCurrentLine()
2509
marker=self.MarkerGet(line_num)
2510
if marker & 1<<GROUPING_START:
2511
self.MarkerDelete(line_num,GROUPING_START_FOLDED)
2512
self.MarkerDelete(line_num,GROUPING_MIDDLE)
2513
self.MarkerDelete(line_num,GROUPING_END)
2514
elif marker & 1<<GROUPING_START_FOLDED:
2515
self.MarkerDelete(line_num,GROUPING_MIDDLE)
2516
self.MarkerDelete(line_num,GROUPING_END)
2517
elif marker & 1<<GROUPING_MIDDLE:
2518
self.MarkerDelete(line_num,GROUPING_END)
2519
elif marker & 1<<GROUPING_END:
2522
#print 'ERROR! NO GROUPING MARKERS!'
2523
return 1 # Blank marker
2527
def ensureSingleIOMarker(self,line_num=None):
2529
line_num=self.GetCurrentLine()
2530
marker=self.MarkerGet(line_num)
2531
if marker & INPUT_MASK:
2532
self.MarkerDelete(line_num,OUTPUT_START)
2533
self.MarkerDelete(line_num,OUTPUT_START_FOLDED)
2534
self.MarkerDelete(line_num,OUTPUT_MIDDLE)
2535
self.MarkerDelete(line_num,OUTPUT_END)
2536
self.MarkerDelete(line_num,OUTPUT_BG)
2537
[start,start_folded] = [INPUT_START,INPUT_START_FOLDED]
2538
[middle,end] = [INPUT_MIDDLE,INPUT_END]
2539
elif marker & OUTPUT_MASK:
2540
self.MarkerDelete(line_num,INPUT_START)
2541
self.MarkerDelete(line_num,INPUT_START_FOLDED)
2542
self.MarkerDelete(line_num,INPUT_MIDDLE)
2543
self.MarkerDelete(line_num,INPUT_END)
2544
[start,start_folded] = [OUTPUT_START,OUTPUT_START_FOLDED]
2545
[middle,end] = [OUTPUT_MIDDLE,OUTPUT_END]
2547
#print 'ERROR! NO IO MARKERS!'
2548
return 1 # Blank marker
2550
if marker & 1<<start:
2551
self.MarkerDelete(line_num,start_folded)
2552
self.MarkerDelete(line_num,middle)
2553
self.MarkerDelete(line_num,end)
2554
elif marker & 1<<start_folded:
2555
self.MarkerDelete(line_num,middle)
2556
self.MarkerDelete(line_num,end)
2557
elif marker & 1<<middle:
2558
self.MarkerDelete(line_num,end)
2559
elif marker & 1<<end:
2564
def RestoreFirstMarker(self):
2565
first_marker=self.MarkerGet(0)
2566
self.clearGroupingMarkers(0)
2567
self.clearIOMarkers(0)
2569
if first_marker & 1<<GROUPING_START :
2570
self.MarkerAdd(0,GROUPING_START)
2571
elif first_marker & 1<<GROUPING_START_FOLDED :
2572
self.MarkerAdd(0,GROUPING_START_FOLDED)
2574
self.MarkerAdd(0,GROUPING_START)
2576
if first_marker & 1<<INPUT_START :
2577
self.MarkerAdd(0,INPUT_START)
2578
elif first_marker & 1<<INPUT_START_FOLDED :
2579
self.MarkerAdd(0,INPUT_START_FOLDED)
2580
elif first_marker & 1<<OUTPUT_START :
2581
self.MarkerAdd(0,OUTPUT_START)
2582
#self.MarkerAdd(0,OUTPUT_BG) # More harm than good??
2583
elif first_marker & 1<<OUTPUT_START_FOLDED :
2584
self.MarkerAdd(0,OUTPUT_START_FOLDED)
2585
#self.MarkerAdd(0,OUTPUT_BG) # More harm than good??
2587
self.MarkerAdd(0,INPUT_START)
2589
if self.doHistUpdate:
2590
self.UpdateUndoHistoryAfter()
2592
def IsAllowedPair(self,m1,m2):
2593
"""This testing function ensures that two adjacent markers are valid"""
2594
i_s = 1<<INPUT_START | 1<<INPUT_START_FOLDED
2595
o_s = 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED
2596
g_s = 1<<GROUPING_START | 1<<GROUPING_START_FOLDED
2597
i_m,o_m,g_m = 1<<INPUT_MIDDLE, 1<<OUTPUT_MIDDLE, 1<<GROUPING_MIDDLE
2598
i_e,o_e,g_e = 1<<INPUT_END, 1<<OUTPUT_END, 1<<GROUPING_END
2600
if (m1 & i_s) and (m1 & g_s): #1
2601
if (m2 & i_s) and (m2 & g_s): return True #1
2602
elif (m2 & i_m) and (m2 & g_m): return True #2
2603
elif (m2 & i_e) and (m2 & g_m): return True #3
2604
elif (m2 & i_e) and (m2 & g_e): return True #4
2605
elif (m2 & o_s) and (m2 & g_s): return False #5
2606
elif (m2 & o_s) and (m2 & g_m): return True #6
2607
elif (m2 & o_s) and (m2 & g_e): return True #7
2608
elif (m2 & o_m) and (m2 & g_m): return False #8
2609
elif (m2 & o_e) and (m2 & g_e): return False #9
2611
elif (m1 & i_m) and (m1 & g_m): #2
2612
if (m2 & i_m) and (m2 & g_m): return True #2
2613
elif (m2 & i_e) and (m2 & g_m): return True #3
2614
elif (m2 & i_e) and (m2 & g_e): return True #4
2616
elif (m1 & i_e) and (m1 & g_m): #3
2617
if (m2 & o_s) and (m2 & g_m): return True #6
2618
elif (m2 & o_s) and (m2 & g_e): return True #7
2620
elif (m1 & i_e) and (m1 & g_e): #4
2621
if (m2 & i_s) and (m2 & g_s): return True #1
2622
elif (m2 & o_s) and (m2 & g_s): return True #5
2624
elif (m1 & o_s) and (m1 & g_s): #5
2625
if (m2 & i_s) and (m2 & g_s): return True #1
2626
elif (m2 & i_m) and (m2 & g_m): return False #2
2627
elif (m2 & i_e) and (m2 & g_m): return False #3
2628
elif (m2 & i_e) and (m2 & g_e): return False #4
2629
elif (m2 & o_s) and (m2 & g_s): return True #5
2630
elif (m2 & o_s) and (m2 & g_m): return False #6
2631
elif (m2 & o_s) and (m2 & g_e): return False #7
2632
elif (m2 & o_m) and (m2 & g_m): return True #8
2633
elif (m2 & o_e) and (m2 & g_e): return True #9
2635
elif (m1 & o_s) and (m1 & g_m): #6
2636
if (m2 & o_m) and (m2 & g_m): return True #8
2637
elif (m2 & o_e) and (m2 & g_e): return True #9
2639
elif (m1 & o_s) and (m1 & g_e): #7
2640
if (m2 & i_s) and (m2 & g_s): return True #1
2641
elif (m2 & o_s) and (m2 & g_s): return True #5
2643
elif (m1 & o_m) and (m1 & g_m): #8
2644
if (m2 & o_m) and (m2 & g_m): return True #8
2645
elif (m2 & o_e) and (m2 & g_e): return True #9
2647
elif (m1 & o_e) and (m1 & g_e): #9
2648
if (m2 & i_s) and (m2 & g_s): return True #1
2649
elif (m2 & o_s) and (m2 & g_s): return True #5
2655
def CleanAllMarkers(self):
2656
self.RestoreFirstMarker()
2657
first_marker=self.MarkerGet(0)
2658
last_line_num=self.GetLineCount()-1
2660
for i in range(1,last_line_num):
2661
self.ensureSingleGroupingMarker(i)
2662
self.ensureSingleIOMarker(i)
2664
previous_marker=self.MarkerGet(i-1)
2665
marker=self.MarkerGet(i)
2667
if not self.IsAllowedPair(previous_marker,marker):
2671
def write(self, text,type='Input',silent=False):
2672
"""Display text in the slices shell.
2674
Replace line endings with OS-specific endings."""
2675
text = self.fixLineEndings(text)
2676
split=text.split(os.linesep)
2679
# This part handles all the marker stuff that accompanies
2680
# adding or removing new lines of text...
2681
# Get the total number of lines in the Document == last line number
2682
last_line_num=self.GetLineCount()-1
2683
# Get the line number we ended on in the write
2684
end_line_num=self.GetCurrentLine()
2685
# Get the number of returns we are using == number of lines we pasted -1
2686
num_new_lines=text.count(os.linesep)
2687
# So if num_new_lines==0, start_line_num and end_line_num are the same
2688
start_line_num=end_line_num-num_new_lines+1
2690
# This is a little unnecessary because there will always
2691
# be a line before if we just inserted a newline!
2692
if start_line_num == 0:
2693
previous_line_num=None
2695
previous_line_num=start_line_num-1
2697
#However, this is very important...
2698
if end_line_num == last_line_num:
2701
next_line_num=end_line_num+1
2705
start_folded = INPUT_START_FOLDED
2706
middle = INPUT_MIDDLE
2708
# preparation for more io types...
2709
opposite_start_mask = 1<<OUTPUT_START
2710
opposite_start_folded_mask = 1<<OUTPUT_START_FOLDED
2711
opposite_middle_mask = 1<<OUTPUT_MIDDLE # To test for bad writes...
2712
opposite_end_mask = 1<<OUTPUT_END # To test for bad writes...
2713
elif type in ['Output','Error']:
2714
#self.MarkerAdd(start_line_num,GROUPING_START_FOLDED)
2716
start_folded=OUTPUT_START_FOLDED
2717
middle=OUTPUT_MIDDLE
2719
# preparation for more io types...
2720
opposite_start_mask = 1<<INPUT_START
2721
opposite_start_folded_mask = 1<<INPUT_START_FOLDED
2722
opposite_middle_mask = 1<<INPUT_MIDDLE # To test for bad writes...
2723
opposite_end_mask = 1<<INPUT_END # To test for bad writes...
2725
if num_new_lines>0: #Do nothing if typing within a line...
2726
# Update the Grouping Markers
2727
# For the previous line and the start_line
2728
# Test to make sure we can write ... but not here ...
2729
# test this before we call write or before we add text...
2730
# So we assume it already obeys the rules
2735
if previous_line_num==None:
2736
# This is an impossible case, here just for completeness...
2737
self.clearGroupingMarkers(start_line_num)
2738
self.MarkerAdd(start_line_num,GROUPING_START)
2740
self.clearIOMarkers(start_line_num)
2741
self.MarkerAdd(start_line_num,start)
2742
if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG)
2744
previous_marker=self.MarkerGet(previous_line_num)
2745
if previous_marker & opposite_middle_mask:
2748
if next_line_num==None:
2749
self.MarkerAdd(end_line_num,GROUPING_END)
2750
self.MarkerAdd(end_line_num,end)
2751
if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
2753
# May be overwritten below if start_line_num==end_line_num...
2755
next_marker=self.MarkerGet(next_line_num)
2757
if next_marker & ( opposite_middle_mask | opposite_end_mask ):
2761
# ensure previous_line only has one marker & turn end into middle
2762
if previous_line_num!=None:
2763
# Adjust previous line appropriately, ensure only one marker
2764
# Only print errors if we are on input!
2766
blank=blank or self.ensureSingleGroupingMarker(previous_line_num)
2767
blank=blank or self.ensureSingleIOMarker(previous_line_num)
2770
#if type=='Input' and not silent: print 'BLANK LINE!' # BAD CASE
2773
if previous_marker & 1<<GROUPING_END :
2774
# Make GROUPING slice continue unless we hit
2775
# an output end and are starting a new input...
2776
if (previous_marker & OUTPUT_MASK) and type=='Input':
2779
self.MarkerDelete(previous_line_num,GROUPING_END)
2780
# ONLY CHANGING CASE
2781
self.MarkerAdd(previous_line_num,GROUPING_MIDDLE)
2783
if previous_marker & 1<<end :
2784
self.MarkerDelete(previous_line_num,end)
2785
self.MarkerAdd(previous_line_num,middle) # ONLY CHANGING CASE
2786
if type in ['Output','Error']: self.MarkerAdd(previous_line_num,OUTPUT_BG)
2787
elif previous_marker & opposite_middle_mask :
2789
if type=='Input' and not silent:
2790
#print 'Should have been a bad marker!'
2793
# We can only add input to an input slice
2794
# And can only add output to an output slice
2796
if previous_marker & ( opposite_start_mask |
2797
opposite_start_folded_mask |
2798
opposite_end_mask ):
2800
self.clearGroupingMarkers(start_line_num)
2801
self.MarkerAdd(start_line_num,GROUPING_START)
2802
if start_line_num==end_line_num:
2805
if start_line_num==end_line_num:
2807
self.clearIOMarkers(start_line_num)
2808
self.MarkerAdd(start_line_num,start)
2809
if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG)
2811
if next_line_num!=None:
2812
self.clearGroupingMarkers(start_line_num)
2813
self.clearIOMarkers(start_line_num)
2814
self.MarkerAdd(start_line_num,GROUPING_MIDDLE)
2815
self.MarkerAdd(start_line_num,middle)
2816
if type in ['Output','Error']: self.MarkerAdd(start_line_num,OUTPUT_BG)
2817
# This may be overwritten if start_line_num==end_line_num
2819
# Take care of all the middle lines...
2820
# Does nothing for only one line...
2821
for i in range(start_line_num,end_line_num):
2822
self.clearGroupingMarkers(i)
2823
self.MarkerAdd(i,GROUPING_MIDDLE)
2825
self.clearIOMarkers(i)
2826
self.MarkerAdd(i,middle)
2827
if type in ['Output','Error']: self.MarkerAdd(i,OUTPUT_BG)
2830
# Take care of the end_line if we haven't already done so...
2832
blank=blank or self.ensureSingleGroupingMarker(next_line_num)
2833
blank=blank or self.ensureSingleIOMarker(next_line_num)
2836
if type=='Input' and not silent:
2837
#print 'BLANK LINE!' # BAD CASE
2840
self.clearGroupingMarkers(end_line_num)
2842
self.clearIOMarkers(end_line_num)
2844
if next_marker & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ) :
2845
self.MarkerAdd(end_line_num,GROUPING_END)
2846
elif next_marker & ( 1<<GROUPING_MIDDLE | 1<<GROUPING_END ) :
2847
self.MarkerAdd(end_line_num,GROUPING_MIDDLE)
2850
if next_marker & ( 1<<start | 1<<start_folded ) :
2851
self.MarkerAdd(end_line_num,end)
2852
if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
2853
elif next_marker & ( 1<<middle | 1<<end ) :
2854
self.MarkerAdd(end_line_num,middle)
2855
if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
2856
elif next_marker & ( opposite_start_mask |
2857
opposite_start_folded_mask ):
2858
self.MarkerAdd(end_line_num,end)
2859
if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
2861
self.MarkerAdd(end_line_num,start_folded)
2862
if type in ['Output','Error']: self.MarkerAdd(end_line_num,OUTPUT_BG)
2863
if type=='Input' and not silent:
2864
#print 'BAD MARKERS!'
2867
if type=='Input' and not silent:
2868
#print 'BAD MARKERS!!!'
2871
self.EnsureCaretVisible()
2874
if self.lastUpdate==None:
2875
self.lastUpdate=time.time()
2876
if time.time()-self.lastUpdate > PRINT_UPDATE_MAX_TIME:
2878
self.lastUpdate=time.time()
2880
def fixLineEndings(self, text):
2881
"""Return text with line endings replaced by OS-specific endings."""
2882
lines = text.split('\r\n')
2883
for l in range(len(lines)):
2884
chunks = lines[l].split('\r')
2885
for c in range(len(chunks)):
2886
chunks[c] = os.linesep.join(chunks[c].split('\n'))
2887
lines[l] = os.linesep.join(chunks)
2888
text = os.linesep.join(lines)
2891
def prompt(self): # Autoindent added!!!
2892
"""Display proper prompt for the context: ps1, ps2 or ps3.
2894
If this is a continuation line, autoindent as necessary."""
2895
# TODO : How much of this can I do away with now without prompts??
2897
isreading = self.reader.isreading
2901
prompt = str(sys.ps3)
2903
prompt = str(sys.ps2)
2905
prompt = str(sys.ps1)
2906
pos = self.GetCurLine()[1]
2911
self.write(os.linesep,type='Input')
2913
# Not needed anymore! # self.promptPosStart = self.GetCurrentPos()
2916
self.write(prompt,type='Input')
2918
# Not needed anymore! # self.promptPosEnd = self.GetCurrentPos()
2919
# Clear the undo history after running a command.
2920
self.EmptyUndoBuffer()
2924
# Match the indent of the line above
2925
# UNLESS the line above ends in a colon...then add four spaces
2926
# (after valid keywords (if, else, etc...) only)
2928
line_num=self.GetCurrentLine()
2929
currentLine=self.GetLine(line_num)
2930
previousLine=self.GetLine(line_num-1)
2931
pstrip=previousLine.strip()
2932
lstrip=previousLine.lstrip()
2935
# because it is all whitespace!
2936
indent=previousLine.strip('\n').strip('\r')
2938
indent=previousLine[:(len(previousLine)-len(lstrip))]
2939
if testForContinuations(previousLine,ignoreErrors=True)[1][0]:
2943
cpos=self.GetCurrentPos()
2945
self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s))
2946
self.write(s,type='Input')
2947
self.UpdateUndoHistoryAfter()
2950
self.EnsureCaretVisible()
2951
self.ScrollToColumn(0)
2954
"""Replacement for stdin.readline()."""
2956
reader = self.reader
2957
reader.isreading = True
2960
# Ensure that we get a new line and that it's got an input marker...
2961
# Also need to temporarily block any other action...
2962
cLine = self.GetCurrentLine()
2963
self.clearIOMarkers(cLine)
2964
self.MarkerAdd(cLine,INPUT_START)
2965
self.MarkerAdd(cLine,READLINE_BG)
2966
self.MarkerAdd(cLine,INPUT_READLINE)
2969
while not reader.input:
2971
input = reader.input
2973
start,end = self.GetIOSlice()
2974
start = self.runningSlice[1] + 1
2975
for i in range(start,end+1):
2976
self.clearIOMarkers(i)
2977
self.clearGroupingMarkers(i)
2978
self.MarkerAdd(i,OUTPUT_BG)
2979
if i == start: self.MarkerAdd(i,OUTPUT_START)
2980
elif i==end: self.MarkerAdd(i,OUTPUT_END)
2981
else: self.MarkerAdd(i,OUTPUT_MIDDLE)
2983
if i==end: self.MarkerAdd(i,GROUPING_END)
2984
else: self.MarkerAdd(i,GROUPING_MIDDLE)
2986
reader.isreading = False
2987
input = str(input) # In case of Unicode.
2990
def readlines(self):
2991
"""Replacement for stdin.readlines()."""
2993
while lines[-1:] != ['\n']:
2994
lines.append(self.readline())
2997
def raw_input(self, prompt=''):
2998
"""Return string based on user input."""
3000
self.write(prompt,type='Output')
3001
return self.readline()
3003
def ask(self, prompt='Please enter your response:'):
3004
"""Get response from the user using a dialog box."""
3005
dialog = wx.TextEntryDialog(None, prompt,
3006
'Input Dialog (Raw)', '')
3008
if dialog.ShowModal() == wx.ID_OK:
3009
text = dialog.GetValue()
3016
"""Halt execution pending a response from the user."""
3017
self.ask('Press enter to continue:')
3020
"""Delete all text from the slices shell."""
3022
self.MarkerAdd(0,GROUPING_START)
3023
self.MarkerAdd(0,INPUT_START)
3025
def run(self, command, prompt=True, verbose=True):
3026
"""Execute command as if it was typed in directly.
3027
>>> shell.run('print "this"')
3032
# Go to the very bottom of the text.
3033
endpos = self.GetTextLength()
3034
self.SetCurrentPos(endpos)
3035
command = command.rstrip()
3036
if prompt: self.prompt()
3037
if verbose: self.write(command,type='Input')
3040
# TODO : Will have to fix this to handle other kinds of errors mentioned before...
3041
def runfile(self, filename):
3042
"""Execute all commands in file as if they were typed into the shell."""
3043
file = open(filename)
3046
for command in file.readlines():
3047
if command[:6] == 'shell.':
3048
# Run shell methods silently.
3049
self.run(command, prompt=False, verbose=False)
3051
self.run(command, prompt=False, verbose=True)
3055
def autoCompleteShow(self, command, offset = 0):
3056
"""Display auto-completion popup list."""
3057
self.AutoCompSetAutoHide(self.autoCompleteAutoHide)
3058
self.AutoCompSetIgnoreCase(self.autoCompleteCaseInsensitive)
3059
list = self.interp.getAutoCompleteList(command,
3060
includeMagic=self.autoCompleteIncludeMagic,
3061
includeSingle=self.autoCompleteIncludeSingle,
3062
includeDouble=self.autoCompleteIncludeDouble)
3064
options = ' '.join(list)
3066
self.AutoCompShow(offset, options)
3068
def autoCallTipShow(self, command, insertcalltip = True, forceCallTip = False):
3069
"""Display argument spec and docstring in a popup window."""
3070
if self.CallTipActive():
3071
self.CallTipCancel()
3072
(name, argspec, tip) = self.interp.getCallTip(command)
3074
dispatcher.send(signal='SlicesShell.calltip', sender=self, calltip=tip)
3075
if not self.autoCallTip and not forceCallTip:
3077
startpos = self.GetCurrentPos()
3078
if argspec and insertcalltip and self.callTipInsert:
3079
# write with undo history...
3080
cpos=self.GetCurrentPos()
3082
self.UpdateUndoHistoryBefore('insert',s,cpos,cpos+len(s))
3083
self.write(s,type='Input')
3084
self.UpdateUndoHistoryAfter()
3086
endpos = self.GetCurrentPos()
3087
self.SetSelection(startpos, endpos)
3089
tippos = startpos - (len(name) + 1)
3090
fallback = startpos - self.GetColumn(startpos)
3091
# In case there isn't enough room, only go back to the fallback.
3092
tippos = max(tippos, fallback)
3093
self.CallTipShow(tippos, tip)
3095
def OnCallTipAutoCompleteManually (self, shiftDown):
3096
"""AutoComplete and Calltips manually."""
3097
if self.AutoCompActive():
3098
self.AutoCompCancel()
3099
currpos = self.GetCurrentPos()
3100
stoppos = self.PositionFromLine(self.GetIOSlice()[0])
3103
#go back until '.' is found
3105
while cpos >= stoppos:
3106
if self.GetCharAt(cpos) == ord ('.'):
3107
pointavailpos = cpos
3111
#word from non whitespace until '.'
3112
if pointavailpos != -1:
3113
#look backward for first whitespace char
3114
textbehind = self.GetTextRange (pointavailpos + 1, currpos)
3119
stoppos = self.PositionFromLine(self.GetIOSlice()[0])
3120
textbefore = self.GetTextRange(stoppos, pointavailpos)
3121
self.autoCompleteShow(textbefore, len (textbehind))
3124
cpos = pointavailpos
3126
while cpos > stoppos:
3127
if chr(self.GetCharAt(cpos)).isspace():
3133
ctips = self.GetTextRange (begpos, currpos)
3134
ctindex = ctips.find ('(')
3135
if ctindex != -1 and not self.CallTipActive():
3136
#insert calltip, if current pos is '(', otherwise show it only
3137
self.autoCallTipShow( ctips[:ctindex + 1],
3138
self.GetCharAt(currpos - 1) == ord('(') and
3139
self.GetCurrentPos() == self.GetTextLength(),
3143
def writeOut(self, text):
3144
"""Replacement for stdout."""
3145
self.write(text,type='Output')
3146
# TODO : FLUSH?? How to make this update real-time...
3148
def writeErr(self, text):
3149
"""Replacement for stderr."""
3150
self.write(text,type='Error')
3152
def redirectStdin(self, redirect=True):
3153
"""If redirect is true then sys.stdin will come from the shell."""
3155
sys.stdin = self.reader
3157
sys.stdin = self.stdin
3159
def redirectStdout(self, redirect=True):
3160
"""If redirect is true then sys.stdout will go to the shell."""
3162
sys.stdout = PseudoFileOut(self.writeOut)
3164
sys.stdout = self.stdout
3166
def redirectStderr(self, redirect=True):
3167
"""If redirect is true then sys.stderr will go to the shell."""
3169
sys.stderr = PseudoFileErr(self.writeErr)
3171
sys.stderr = self.stderr
3173
# Take a spashot of the WHOLE grouping slice (or slices)
3174
# The argument s is either what got added or deleted
3175
def UpdateUndoHistoryBefore(self,actionType,s,posStart,posEnd,
3176
forceNewAction=False):
3180
s=s.replace(os.linesep,'\n')
3181
startLine=self.LineFromPosition(posStart)
3183
if actionType=='marker':
3184
numLines = self.LineFromPosition(posEnd) - startLine
3186
numLines=s.count('\n')
3188
makeNewAction=forceNewAction
3192
elif self.undoIndex==-1:
3194
elif not uH[uI]['allowsAppend']:
3196
elif actionType!=uH[uI]['actionType']:
3198
elif actionType=='insert':
3199
if posStart!=uH[uI]['posEnd']:
3201
else: # This is a continuation of the previous insert
3202
uH[uI]['charList'] = uH[uI]['charList']+s
3203
uH[uI]['posEnd'] = posEnd # posStart cannot move
3204
uH[uI]['numLines'] = uH[uI]['numLines']+numLines
3205
elif actionType=='delete':
3206
# This is a forward continuation of the previous delete
3207
if posStart==uH[uI]['posStart']:
3208
uH[uI]['charList'] = uH[uI]['charList']+s
3209
uH[uI]['posEnd'] = posEnd
3210
uH[uI]['numLines'] = uH[uI]['numLines']+numLines
3211
# This is a backward continuation of the previous delete
3212
elif posEnd==uH[uI]['posStart']:
3213
uH[uI]['charList'] = s+uH[uI]['charList']
3214
uH[uI]['posStart'] = posStart
3215
uH[uI]['startLine'] = startLine
3216
uH[uI]['numLines'] = uH[uI]['numLines']+numLines
3220
elif actionType=='marker':
3223
pass #print 'Unsupported Action Type!!'
3226
del(self.undoHistory[uI+1:]) # remove actions after undoIndex
3229
'actionType' : actionType, # Action type ('insert','delete','marker')
3230
'allowsAppend': not forceNewAction, # Can action be joined with others?
3231
'charList' : s, # Character list
3232
'posStart' : posStart, # Cursor poition at the start of the action
3233
'posEnd' : posEnd, # Cursor position at the end of the action
3234
'startLine' : startLine, # Start line number,
3235
'numLines' : numLines, # Number of newlines involved
3236
'mBStart' : None, # Starting line for markers BEFORE action
3237
'mAStart' : None, # Starting line for markers AFTER action
3238
'markersBefore' : None, # [markers BEFORE action]
3239
'markersAfter' : None # [markers AFTER action]
3244
# Only update the before when starting a new action
3246
if actionType=='insert':
3249
end = start + numLines
3251
# Update Marker Info
3252
newStart=self.GetGroupingSlice(start)[0]
3253
newEnd=self.GetGroupingSlice(end)[1]
3254
self.undoHistory[self.undoIndex]['markersBefore'] = \
3255
[self.MarkerGet(i) for i in range(newStart,newEnd+1)]
3256
self.undoHistory[self.undoIndex]['mBStart']=newStart
3258
self.doHistUpdate=True
3260
def UpdateUndoHistoryAfter(self): # s is either what got added or deleted
3261
start = self.undoHistory[self.undoIndex]['startLine']
3262
if self.undoHistory[self.undoIndex]['actionType']=='delete':
3265
end = start + self.undoHistory[self.undoIndex]['numLines']
3267
newStart=min(self.GetGroupingSlice(start)[0]-1, 0)
3268
newEnd=max(self.GetGroupingSlice(end)[1]+1, self.GetLineCount()-1)
3269
self.undoHistory[self.undoIndex]['markersAfter'] = \
3270
[self.MarkerGet(i) for i in range(newStart,newEnd+1)]
3271
self.undoHistory[self.undoIndex]['mAStart']=newStart
3273
self.doHistUpdate=False
3277
#Skip undo if there are no actions...
3278
if self.undoIndex==-1:
3281
uHI=self.undoHistory[self.undoIndex]
3283
if uHI['actionType'] in ['insert','delete']:
3284
# This will perform the opposite of the action given
3285
editwindow.EditWindow.Undo(self)
3286
elif uHI['actionType']=='marker': # No text changed, don't pass to STC
3289
#print 'Unsupported actionType in undoHistory!!'
3292
numLines=len(uHI['markersBefore'])
3293
for i in range(numLines):
3294
self.MarkerSet( uHI['mBStart']+i , uHI['markersBefore'][i] )
3300
# First check to see if there are any redo operations available
3301
# Note that for redo, undoIndex=-1 is a valid input
3302
if self.undoIndex >= len(self.undoHistory)-1:
3305
uHI=self.undoHistory[self.undoIndex]
3307
if uHI['actionType'] in ['insert','delete']:
3308
# This will re-perform the given action
3309
editwindow.EditWindow.Redo(self)
3310
elif uHI['actionType']=='marker': # No text changed, don't pass to STC
3313
#print 'Unsupported actionType in undoHistory!!'
3316
numLines=len(uHI['markersAfter'])
3317
for i in range(numLines):
3318
self.MarkerSet( uHI['mAStart']+i , uHI['markersAfter'][i] )
3320
def EmptyUndoBuffer(self):
3321
editwindow.EditWindow.EmptyUndoBuffer(self)
3324
self.doHistUpdate=False
3327
return self.CanEdit() and \
3328
(self.GetSelectionStart() != self.GetSelectionEnd())
3331
"""Return true if a paste should succeed."""
3332
if self.CanEdit() and editwindow.EditWindow.CanPaste(self):
3338
"""Return true if editing should succeed."""
3339
marker=self.MarkerGet(self.GetCurrentLine())
3341
if marker & OUTPUT_MASK:
3343
elif marker & INPUT_MASK:
3344
if self.reader.isreading and not \
3345
(self.MarkerGet(self.GetCurrentLine()) & 1<<INPUT_READLINE ):
3347
start,end=self.GetIOSlice()
3348
sliceStartPos=self.PositionFromLine(start)
3349
sliceEndPos=self.GetLineEndPosition(end)
3350
"""Return true if text is selected and can be cut."""
3351
if self.GetSelectionStart() == self.GetSelectionEnd():
3353
elif self.GetSelectionStart() != self.GetSelectionEnd() \
3354
and self.GetSelectionStart() >= sliceStartPos \
3355
and self.GetSelectionEnd() >= sliceStartPos \
3356
and self.GetSelectionStart() <= sliceEndPos \
3357
and self.GetSelectionEnd() <= sliceEndPos:
3363
"""Remove selection and place it on the clipboard."""
3364
if self.CanCut() and self.CanCopy():
3365
if self.AutoCompActive():
3366
self.AutoCompCancel()
3367
if self.CallTipActive():
3368
self.CallTipCancel()
3370
self.ReplaceSelection('')
3373
"""Copy selection and place it on the clipboard."""
3377
command = self.GetSelectedText()
3378
command = command.replace(os.linesep + ps2, os.linesep)
3379
command = command.replace(os.linesep + ps1, os.linesep)
3380
command = self.lstripPrompt(text=command)
3381
data = wx.TextDataObject(command)
3384
def CopyWithPrompts(self):
3385
"""Copy selection, including prompts, and place it on the clipboard."""
3387
command = self.GetSelectedText()
3388
data = wx.TextDataObject(command)
3391
def CopyWithPromptsPrefixed(self):
3392
"""Copy selection, including prompts prefixed with four
3393
spaces, and place it on the clipboard."""
3395
command = self.GetSelectedText()
3397
command = spaces + command.replace(os.linesep,
3398
os.linesep + spaces)
3399
data = wx.TextDataObject(command)
3402
def _clip(self, data):
3403
if wx.TheClipboard.Open():
3404
wx.TheClipboard.UsePrimarySelection(False)
3405
wx.TheClipboard.SetData(data)
3406
wx.TheClipboard.Flush()
3407
wx.TheClipboard.Close()
3410
"""Replace selection with clipboard contents."""
3413
if self.CanPaste() and wx.TheClipboard.Open():
3415
if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
3416
data = wx.TextDataObject()
3417
if wx.TheClipboard.GetData(data):
3418
self.ReplaceSelection('')
3419
command = data.GetText()
3420
command = command.rstrip()
3421
command = self.fixLineEndings(command)
3422
command = self.lstripPrompt(text=command)
3423
# TODO : This is still useful... Add it back other places?
3424
command = command.replace(os.linesep + ps2, '\n')
3425
command = command.replace(os.linesep, '\n')
3426
#DNM--Don't use '... '
3427
command = command.replace('\n', os.linesep)# + ps2)
3429
cpos=self.GetCurrentPos()
3431
self.UpdateUndoHistoryBefore('insert', s, cpos,
3432
cpos+len(s), forceNewAction=True)
3433
self.write(s,type='Input')
3434
self.UpdateUndoHistoryAfter()
3436
# Makes paste -> type -> undo consistent with other STC apps
3437
self.ReplaceSelection('')
3438
wx.TheClipboard.Close()
3441
def PasteAndRun(self):
3442
"""Replace selection with clipboard contents, run commands."""
3444
if wx.TheClipboard.Open():
3445
if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_TEXT)):
3446
data = wx.TextDataObject()
3447
if wx.TheClipboard.GetData(data):
3448
text = data.GetText()
3449
wx.TheClipboard.Close()
3454
def Execute(self, text):
3455
"""Replace selection with text and run commands."""
3456
start,end=self.GetIOSlice()
3457
startpos=self.PositionFromLine(start)
3458
endpos=self.GetLineEndPosition(end)
3460
self.SetCurrentPos(endpos)
3461
self.SetSelection(startpos, endpos)
3462
self.ReplaceSelection('')
3464
hasSyntaxError=False
3465
result = self.BreakTextIntoCommands(command)
3466
if result[0] == None:
3472
for command in commands:
3473
command = command.replace('\n', os.linesep)
3477
def wrap(self, wrap=True):
3478
"""Sets whether text is word wrapped."""
3480
self.SetWrapMode(wrap)
3481
except AttributeError:
3482
return 'Wrapping is not available in this version.'
3484
def zoom(self, points=0):
3485
"""Set the zoom level.
3487
This number of points is added to the size of all fonts. It
3488
may be positive to magnify or negative to reduce."""
3489
self.SetZoom(points)
3491
def LoadSettings(self, config):
3492
self.autoComplete = \
3493
config.ReadBool('Options/AutoComplete', True)
3494
self.autoCompleteIncludeMagic = \
3495
config.ReadBool('Options/AutoCompleteIncludeMagic', True)
3496
self.autoCompleteIncludeSingle = \
3497
config.ReadBool('Options/AutoCompleteIncludeSingle', True)
3498
self.autoCompleteIncludeDouble = \
3499
config.ReadBool('Options/AutoCompleteIncludeDouble', True)
3500
self.autoCallTip = \
3501
config.ReadBool('Options/AutoCallTip', True)
3502
self.callTipInsert = \
3503
config.ReadBool('Options/CallTipInsert', True)
3505
self.SetWrapMode(config.ReadBool('View/WrapMode', True))
3507
self.lineNumbers = \
3508
config.ReadBool('View/ShowLineNumbers', True)
3509
self.setDisplayLineNumbers (self.lineNumbers)
3510
zoom = config.ReadInt('View/Zoom/Shell', -99)
3516
def SaveSettings(self, config):
3517
config.WriteBool('Options/AutoComplete', self.autoComplete)
3518
config.WriteBool('Options/AutoCompleteIncludeMagic',
3519
self.autoCompleteIncludeMagic)
3520
config.WriteBool('Options/AutoCompleteIncludeSingle',
3521
self.autoCompleteIncludeSingle)
3522
config.WriteBool('Options/AutoCompleteIncludeDouble',
3523
self.autoCompleteIncludeDouble)
3524
config.WriteBool('Options/AutoCallTip', self.autoCallTip)
3525
config.WriteBool('Options/CallTipInsert', self.callTipInsert)
3526
config.WriteBool('View/WrapMode', self.GetWrapMode())
3527
config.WriteBool('View/ShowLineNumbers', self.lineNumbers)
3528
config.WriteInt('View/Zoom/Shell', self.GetZoom())
3530
def GetContextMenu(self):
3532
Create and return a context menu for the slices shell.
3533
This is used instead of the scintilla default menu
3534
in order to correctly respect our immutable buffer.
3537
menu.Append(wx.ID_UNDO, "Undo")
3538
menu.Append(wx.ID_REDO, "Redo")
3540
menu.AppendSeparator()
3542
menu.Append(wx.ID_CUT, "Cut")
3543
menu.Append(wx.ID_COPY, "Copy")
3544
menu.Append(frame.ID_COPY_PLUS, "Copy Plus")
3545
menu.Append(wx.ID_PASTE, "Paste")
3546
menu.Append(frame.ID_PASTE_PLUS, "Paste Plus")
3547
menu.Append(wx.ID_CLEAR, "Clear")
3549
menu.AppendSeparator()
3551
menu.Append(wx.ID_SELECTALL, "Select All")
3554
def OnContextMenu(self, evt):
3555
menu = self.GetContextMenu()
3556
self.PopupMenu(menu)
3558
def OnUpdateUI(self, evt):
3560
if id in (wx.ID_CUT, wx.ID_CLEAR):
3561
evt.Enable(self.CanCut())
3562
elif id in (wx.ID_COPY, frame.ID_COPY_PLUS):
3563
evt.Enable(self.CanCopy())
3564
elif id in (wx.ID_PASTE, frame.ID_PASTE_PLUS):
3565
evt.Enable(self.CanPaste())
3566
elif id == wx.ID_UNDO:
3567
evt.Enable(self.CanUndo())
3568
elif id == wx.ID_REDO:
3569
evt.Enable(self.CanRedo())
3571
def LoadPySlicesFile(self,fid):
3572
invalidFileString = 'Not a valid input format'
3574
groupingStartLines=[0]
3579
# Read the initial three (or four) lines that have version and marker information
3581
if line == usrBinEnvPythonText:
3582
line=fid.readline() # Add the option to place #!/usr/bin/env python at the top
3583
if line not in pyslicesFormatHeaderText: print invalidFileString ; return
3585
if line != groupingStartText: print invalidFileString ; return
3587
if line == inputStartText: ioStartTypes.append('input');removeComment=False
3588
elif line == outputStartText: ioStartTypes.append('output');removeComment=True
3589
else: print invalidFileString ; return
3593
# Write the file's text to the text area
3594
# Capture Marker information to
3596
if i==groupingStartText:
3597
groupingStartLines.append(lineCount)
3598
elif i==inputStartText:
3599
ioStartLines.append(lineCount)
3600
ioStartTypes.append('input')
3602
elif i==outputStartText:
3603
ioStartLines.append(lineCount)
3604
ioStartTypes.append('output')
3607
if removeComment: w=i[1:].replace(os.linesep,'\n')
3608
else: w=i.replace(os.linesep,'\n')
3609
self.write(w,'Input',silent=True)
3615
for i in range(lineCount+1):
3616
self.clearGroupingMarkers(i)
3617
self.clearIOMarkers(i)
3621
if groupingStartLines!=[]:
3622
if i == groupingStartLines[0]:
3623
self.MarkerAdd(i,GROUPING_START)
3624
del groupingStartLines[0]
3625
elif i+1 == groupingStartLines[0]:
3629
elif i==lineCount-1:
3635
self.MarkerAdd(i,GROUPING_MIDDLE)
3637
self.MarkerAdd(i,GROUPING_END)
3641
if ioStartLines!=[]:
3642
if i == ioStartLines[0]:
3643
# Delete the old ioStartTypes (keep the current copy for later use)
3644
if i>0: del ioStartTypes[0]
3646
if ioStartTypes[0]=='input':
3647
self.MarkerAdd(i,INPUT_START)
3648
elif ioStartTypes[0]=='output':
3649
self.MarkerAdd(i,OUTPUT_START)
3650
self.MarkerAdd(i,OUTPUT_BG)
3652
#print 'Invalid Type!';
3655
# Only delete markers we are totally finished with...
3656
# Keep one more "StartTypes" than "StartLines"
3658
elif i+1 == ioStartLines[0]:
3662
elif i==lineCount-1:
3668
if ioStartTypes[0]=='input':
3669
self.MarkerAdd(i,INPUT_MIDDLE)
3670
elif ioStartTypes[0]=='output':
3671
self.MarkerAdd(i,OUTPUT_MIDDLE)
3672
self.MarkerAdd(i,OUTPUT_BG)
3674
#print 'Invalid Type!';
3677
if ioStartTypes[0]=='input':
3678
self.MarkerAdd(i,INPUT_END)
3679
elif ioStartTypes[0]=='output':
3680
self.MarkerAdd(i,OUTPUT_END)
3681
self.MarkerAdd(i,OUTPUT_BG)
3683
#print 'Invalid Type!';
3686
self.EmptyUndoBuffer() # maybe not?
3689
def SavePySlicesFile(self,fid):
3691
fid.write(usrBinEnvPythonText.replace('\n',os.linesep))
3692
fid.write(pyslicesFormatHeaderText[-1].replace('\n',os.linesep))
3693
for i in range(self.GetLineCount()):
3694
markers=self.MarkerGet(i)
3695
if markers & ( 1<<GROUPING_START | 1<<GROUPING_START_FOLDED ):
3696
fid.write(groupingStartText.replace('\n',os.linesep))
3697
if markers & ( 1<<INPUT_START | 1<<INPUT_START_FOLDED ):
3698
fid.write(inputStartText.replace('\n',os.linesep))
3700
if markers & ( 1<<OUTPUT_START | 1<<OUTPUT_START_FOLDED ):
3701
fid.write(outputStartText.replace('\n',os.linesep))
3703
if addComment: fid.write('#')
3704
fid.write(self.GetLine(i).replace('\n',os.linesep))
3707
def LoadPyFileAsSlice(self,fid):
3708
curpos=self.GetCurrentPos()
3709
start,end = self.GetGroupingSlice()
3711
endpos=self.GetLineEndPosition(end)
3712
self.SetCurrentPos(endpos)
3713
self.SetSelection(endpos, endpos)
3715
text='\n'+fid.read()
3716
self.write(text,'Input')
3717
newpos=self.GetCurrentPos()
3719
self.SetCurrentPos(curpos)
3720
self.SetSelection(curpos,curpos)
3722
#self.SetCurrentPos(newpos)
3723
#self.SetSelection(newpos,newpos)
3725
def hasChanged(self):
3726
"""Return True if contents have changed."""
3727
return self.GetModify() or self.NeedsCheckForSave
3731
## NOTE: The DnD of file names is disabled until we can figure out how
3732
## best to still allow DnD of text.
3734
## #seb : File drag and drop
3735
## class FileDropTarget(wx.FileDropTarget):
3736
## def __init__(self, obj):
3737
## wx.FileDropTarget.__init__(self)
3739
## def OnDropFiles(self, x, y, filenames):
3740
## if len(filenames) == 1:
3741
## txt = 'r\"%s\"' % filenames[0]
3744
## for f in filenames:
3745
## txt += 'r\"%s\" , ' % f
3747
## self.obj.AppendText(txt)
3748
## pos = self.obj.GetCurrentPos()
3749
## self.obj.SetCurrentPos( pos )
3750
## self.obj.SetSelection( pos, pos )
3754
## class TextAndFileDropTarget(wx.DropTarget):
3755
## def __init__(self, sliceshell):
3756
## wx.DropTarget.__init__(self)
3757
## self.sliceshell = sliceshell
3758
## self.compdo = wx.DataObjectComposite()
3759
## self.textdo = wx.TextDataObject()
3760
## self.filedo = wx.FileDataObject()
3761
## self.compdo.Add(self.textdo)
3762
## self.compdo.Add(self.filedo, True)
3764
## self.SetDataObject(self.compdo)
3766
## def OnDrop(self, x, y):
3769
## def OnData(self, x, y, result):
3771
## if self.textdo.GetTextLength() > 1:
3772
## text = self.textdo.GetText()
3773
## # *** Do somethign with the dragged text here...
3774
## self.textdo.SetText('')
3776
## filenames = str(self.filename.GetFilenames())
3777
## if len(filenames) == 1:
3778
## txt = 'r\"%s\"' % filenames[0]
3781
## for f in filenames:
3782
## txt += 'r\"%s\" , ' % f
3784
## self.sliceshell.AppendText(txt)
3785
## pos = self.sliceshell.GetCurrentPos()
3786
## self.sliceshell.SetCurrentPos( pos )
3787
## self.sliceshell.SetSelection( pos, pos )