mwxlib 0.99.0__py3-none-any.whl → 1.7.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mwx/__init__.py +7 -5
- mwx/bookshelf.py +133 -57
- mwx/controls.py +555 -518
- mwx/framework.py +569 -544
- mwx/graphman.py +708 -702
- mwx/images.py +29 -0
- mwx/matplot2.py +226 -217
- mwx/matplot2g.py +733 -657
- mwx/matplot2lg.py +192 -183
- mwx/mgplt.py +21 -23
- mwx/nutshell.py +1162 -1019
- mwx/plugins/ffmpeg_view.py +74 -76
- mwx/plugins/fft_view.py +14 -16
- mwx/plugins/frame_listview.py +56 -53
- mwx/plugins/line_profile.py +2 -2
- mwx/py/filling.py +9 -8
- mwx/testsuite.py +38 -0
- mwx/utilus.py +273 -208
- mwx/wxmon.py +45 -40
- mwx/wxpdb.py +81 -92
- mwx/wxwil.py +18 -18
- mwx/wxwit.py +49 -51
- {mwxlib-0.99.0.dist-info → mwxlib-1.7.13.dist-info}/METADATA +19 -17
- mwxlib-1.7.13.dist-info/RECORD +28 -0
- {mwxlib-0.99.0.dist-info → mwxlib-1.7.13.dist-info}/WHEEL +1 -1
- mwxlib-0.99.0.dist-info/LICENSE +0 -21
- mwxlib-0.99.0.dist-info/RECORD +0 -28
- {mwxlib-0.99.0.dist-info → mwxlib-1.7.13.dist-info}/top_level.txt +0 -0
mwx/nutshell.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#! python3
|
|
2
2
|
"""mwxlib Nautilus in the shell.
|
|
3
3
|
"""
|
|
4
|
+
from contextlib import contextmanager
|
|
4
5
|
from functools import wraps
|
|
5
6
|
from importlib import import_module
|
|
6
|
-
from
|
|
7
|
+
from pathlib import Path
|
|
7
8
|
from pprint import pformat
|
|
8
9
|
from bdb import BdbQuit
|
|
9
10
|
import traceback
|
|
@@ -17,7 +18,8 @@ import sys
|
|
|
17
18
|
import os
|
|
18
19
|
import re
|
|
19
20
|
import wx
|
|
20
|
-
from wx import
|
|
21
|
+
from wx import aui
|
|
22
|
+
from wx import stc
|
|
21
23
|
from wx.py import dispatcher
|
|
22
24
|
from wx.py import introspect
|
|
23
25
|
from wx.py import interpreter
|
|
@@ -25,34 +27,116 @@ from wx.py.shell import Shell
|
|
|
25
27
|
from wx.py.editwindow import EditWindow
|
|
26
28
|
|
|
27
29
|
from .utilus import funcall as _F
|
|
28
|
-
from .utilus import
|
|
29
|
-
from .utilus import split_words,
|
|
30
|
+
from .utilus import typename, fix_fnchars
|
|
31
|
+
from .utilus import split_words, split_parts, split_tokens, find_modules
|
|
30
32
|
from .framework import CtrlInterface, AuiNotebook, Menu
|
|
31
33
|
|
|
32
34
|
|
|
33
|
-
## URL pattern (flag = re.M | re.A)
|
|
34
|
-
url_re = r"https?://[\w/:%#$&?()~.=+-]+"
|
|
35
|
+
## URL pattern (flag = re.M | re.A).
|
|
36
|
+
# url_re = r"https?://[\w/:%#$&?()~.=+-]+"
|
|
37
|
+
url_re = r"https?://[\w/:%#$&?!@~.,;=+-]+" # excluding ()
|
|
35
38
|
|
|
36
|
-
##
|
|
37
|
-
nofile_re = r'[\/:*?"<>|]'
|
|
38
|
-
|
|
39
|
-
## Python syntax pattern
|
|
39
|
+
## Python syntax patterns.
|
|
40
40
|
py_indent_re = r"if|else|elif|for|while|with|def|class|try|except|finally"
|
|
41
41
|
py_outdent_re = r"else:|elif\s+.*:|except(\s+.*)?:|finally:"
|
|
42
42
|
py_closing_re = r"break|pass|return|raise|continue"
|
|
43
43
|
|
|
44
|
-
## Python
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
## Python traceback pattern.
|
|
45
|
+
py_trace_re = r' +File "(.*?)", line (\d+)'
|
|
46
|
+
|
|
47
|
+
## Custom constants in wx.stc.
|
|
48
|
+
stc.STC_STYLE_CARETLINE = 40
|
|
49
|
+
stc.STC_STYLE_ANNOTATION = 41
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class Stylus:
|
|
53
|
+
py_log_mode = {
|
|
54
|
+
stc.STC_STYLE_DEFAULT : "fore:#7f7f7f,back:#ffffb8,size:9,face:MS Gothic",
|
|
55
|
+
stc.STC_STYLE_LINENUMBER : "fore:#000000,back:#ffffb8,size:9",
|
|
56
|
+
stc.STC_STYLE_BRACELIGHT : "fore:#000000,back:#ffffb8,bold",
|
|
57
|
+
stc.STC_STYLE_BRACEBAD : "fore:#000000,back:#ff0000,bold",
|
|
58
|
+
stc.STC_STYLE_CONTROLCHAR : "size:6",
|
|
59
|
+
stc.STC_STYLE_INDENTGUIDE : "",
|
|
60
|
+
stc.STC_STYLE_CARETLINE : "fore:#000000,back:#ffff7f,size:2", # optional
|
|
61
|
+
stc.STC_STYLE_ANNOTATION : "fore:#7f0000,back:#ff7f7f", # optional
|
|
62
|
+
stc.STC_P_DEFAULT : "fore:#000000",
|
|
63
|
+
stc.STC_P_OPERATOR : "fore:#000000",
|
|
64
|
+
stc.STC_P_IDENTIFIER : "fore:#000000",
|
|
65
|
+
stc.STC_P_COMMENTLINE : "fore:#007f7f,back:#ffcfcf",
|
|
66
|
+
stc.STC_P_COMMENTBLOCK : "fore:#007f7f,back:#ffcfcf,eol",
|
|
67
|
+
stc.STC_P_NUMBER : "fore:#7f0000",
|
|
68
|
+
stc.STC_P_CHARACTER : "fore:#7f7f7f",
|
|
69
|
+
stc.STC_P_STRING : "fore:#7f7f7f",
|
|
70
|
+
stc.STC_P_TRIPLE : "fore:#7f7f7f",
|
|
71
|
+
stc.STC_P_TRIPLEDOUBLE : "fore:#7f7f7f",
|
|
72
|
+
stc.STC_P_STRINGEOL : "fore:#000000,back:#ffcfcf,eol",
|
|
73
|
+
stc.STC_P_CLASSNAME : "fore:#7f00ff,bold",
|
|
74
|
+
stc.STC_P_DEFNAME : "fore:#0000ff,bold",
|
|
75
|
+
stc.STC_P_WORD : "fore:#0000ff",
|
|
76
|
+
stc.STC_P_WORD2 : "fore:#b8007f",
|
|
77
|
+
stc.STC_P_DECORATOR : "fore:#e08040",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
py_text_mode = {
|
|
81
|
+
stc.STC_STYLE_DEFAULT : "fore:#7f7f7f,back:#fffff8,size:9,face:MS Gothic",
|
|
82
|
+
stc.STC_STYLE_LINENUMBER : "fore:#000000,back:#fffff8,size:9",
|
|
83
|
+
stc.STC_STYLE_BRACELIGHT : "fore:#000000,back:#cccccc,bold",
|
|
84
|
+
stc.STC_STYLE_BRACEBAD : "fore:#000000,back:#ff0000,bold",
|
|
85
|
+
stc.STC_STYLE_CONTROLCHAR : "size:6",
|
|
86
|
+
stc.STC_STYLE_INDENTGUIDE : "",
|
|
87
|
+
stc.STC_STYLE_CARETLINE : "fore:#000000,back:#f0f0ff,size:2", # optional
|
|
88
|
+
stc.STC_STYLE_ANNOTATION : "fore:#7f0000,back:#ff7f7f", # optional
|
|
89
|
+
stc.STC_P_DEFAULT : "fore:#000000",
|
|
90
|
+
stc.STC_P_OPERATOR : "fore:#000000",
|
|
91
|
+
stc.STC_P_IDENTIFIER : "fore:#000000",
|
|
92
|
+
stc.STC_P_COMMENTLINE : "fore:#007f00,back:#f0fff0",
|
|
93
|
+
stc.STC_P_COMMENTBLOCK : "fore:#007f00,back:#f0fff0,eol",
|
|
94
|
+
stc.STC_P_NUMBER : "fore:#e02000",
|
|
95
|
+
stc.STC_P_CHARACTER : "fore:#7f7f7f",
|
|
96
|
+
stc.STC_P_STRING : "fore:#7f7f7f",
|
|
97
|
+
stc.STC_P_TRIPLE : "fore:#7f7f7f",
|
|
98
|
+
stc.STC_P_TRIPLEDOUBLE : "fore:#7f7f7f",
|
|
99
|
+
stc.STC_P_STRINGEOL : "fore:#7f7f7f,back:#ffc0c0,eol",
|
|
100
|
+
stc.STC_P_CLASSNAME : "fore:#7f00ff,bold",
|
|
101
|
+
stc.STC_P_DEFNAME : "fore:#0000ff,bold",
|
|
102
|
+
stc.STC_P_WORD : "fore:#0000ff",
|
|
103
|
+
stc.STC_P_WORD2 : "fore:#7f007f",
|
|
104
|
+
stc.STC_P_DECORATOR : "fore:#c04040,bold",
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
py_shell_mode = {
|
|
108
|
+
stc.STC_STYLE_DEFAULT : "fore:#7f7f7f,back:#202020,size:9,face:MS Gothic",
|
|
109
|
+
stc.STC_STYLE_LINENUMBER : "fore:#000000,back:#f0f0f0,size:9",
|
|
110
|
+
stc.STC_STYLE_BRACELIGHT : "fore:#ffffff,back:#202020,bold",
|
|
111
|
+
stc.STC_STYLE_BRACEBAD : "fore:#ffffff,back:#ff0000,bold",
|
|
112
|
+
stc.STC_STYLE_CONTROLCHAR : "size:6",
|
|
113
|
+
stc.STC_STYLE_INDENTGUIDE : "",
|
|
114
|
+
stc.STC_STYLE_CARETLINE : "fore:#ffffff,back:#123460,size:2", # optional
|
|
115
|
+
stc.STC_STYLE_ANNOTATION : "fore:#7f0000,back:#ff7f7f", # optional
|
|
116
|
+
stc.STC_P_DEFAULT : "fore:#cccccc",
|
|
117
|
+
stc.STC_P_OPERATOR : "fore:#cccccc",
|
|
118
|
+
stc.STC_P_IDENTIFIER : "fore:#cccccc",
|
|
119
|
+
stc.STC_P_COMMENTLINE : "fore:#42c18c,back:#004040",
|
|
120
|
+
stc.STC_P_COMMENTBLOCK : "fore:#42c18c,back:#004040,eol",
|
|
121
|
+
stc.STC_P_NUMBER : "fore:#ffc080",
|
|
122
|
+
stc.STC_P_CHARACTER : "fore:#a0a0a0",
|
|
123
|
+
stc.STC_P_STRING : "fore:#a0c0ff",
|
|
124
|
+
stc.STC_P_TRIPLE : "fore:#a0a0a0",
|
|
125
|
+
stc.STC_P_TRIPLEDOUBLE : "fore:#a0c0ff",
|
|
126
|
+
stc.STC_P_STRINGEOL : "fore:#cccccc,back:#400000,eol",
|
|
127
|
+
stc.STC_P_CLASSNAME : "fore:#61d6d6,bold",
|
|
128
|
+
stc.STC_P_DEFNAME : "fore:#3a96ff,bold",
|
|
129
|
+
stc.STC_P_WORD : "fore:#80c0ff",
|
|
130
|
+
stc.STC_P_WORD2 : "fore:#ff80ff",
|
|
131
|
+
stc.STC_P_DECORATOR : "fore:#ff8040",
|
|
132
|
+
}
|
|
49
133
|
|
|
50
134
|
|
|
51
135
|
def skip(evt):
|
|
52
136
|
evt.Skip()
|
|
53
137
|
|
|
54
138
|
|
|
55
|
-
def
|
|
139
|
+
def editable(f):
|
|
56
140
|
@wraps(f)
|
|
57
141
|
def _f(self, *v, **kw):
|
|
58
142
|
if self.CanEdit():
|
|
@@ -63,10 +147,14 @@ def can_edit(f):
|
|
|
63
147
|
def ask(f, prompt="Enter value", type=str):
|
|
64
148
|
"""Get response from the user using a dialog box."""
|
|
65
149
|
@wraps(f)
|
|
66
|
-
def _f(
|
|
67
|
-
with wx.TextEntryDialog(None, prompt, f.__name__) as dlg:
|
|
150
|
+
def _f(evt, value=''):
|
|
151
|
+
with wx.TextEntryDialog(None, prompt, f.__name__, value) as dlg:
|
|
68
152
|
if dlg.ShowModal() == wx.ID_OK:
|
|
69
|
-
|
|
153
|
+
value = dlg.Value
|
|
154
|
+
try:
|
|
155
|
+
return f(type(value))
|
|
156
|
+
except ValueError:
|
|
157
|
+
return _f(evt, value) # show the prompt again
|
|
70
158
|
return _f
|
|
71
159
|
|
|
72
160
|
|
|
@@ -86,15 +174,15 @@ class AutoCompInterfaceMixin:
|
|
|
86
174
|
"""
|
|
87
175
|
history = [] # used in history-comp mode
|
|
88
176
|
modules = set() # used in module-comp mode
|
|
89
|
-
fragmwords = set(keyword.kwlist + dir(builtins))
|
|
90
|
-
|
|
177
|
+
fragmwords = set(keyword.kwlist + dir(builtins)) # used in text-comp mode
|
|
178
|
+
|
|
91
179
|
def __init__(self):
|
|
92
180
|
## cf. sys.modules
|
|
93
181
|
if not self.modules:
|
|
94
182
|
force = wx.GetKeyState(wx.WXK_CONTROL)\
|
|
95
183
|
& wx.GetKeyState(wx.WXK_SHIFT)
|
|
96
184
|
AutoCompInterfaceMixin.modules = set(find_modules(force))
|
|
97
|
-
|
|
185
|
+
|
|
98
186
|
def CallTipShow(self, pos, tip, N=11):
|
|
99
187
|
"""Show a call tip containing a definition near position pos.
|
|
100
188
|
|
|
@@ -107,11 +195,14 @@ class AutoCompInterfaceMixin:
|
|
|
107
195
|
lines[N+1:] = ["\n...(snip) This tips are too long... "
|
|
108
196
|
"Click to show more details."]
|
|
109
197
|
tip = '\n'.join(lines)
|
|
110
|
-
self._calltips[-1] = True
|
|
198
|
+
self._calltips[-1] = True # snipped (needs to be shown)
|
|
111
199
|
super().CallTipShow(pos, tip)
|
|
112
|
-
|
|
200
|
+
|
|
113
201
|
def autoCallTipShow(self, command, insertcalltip=True):
|
|
114
|
-
"""Display argument spec and docstring in a popup window.
|
|
202
|
+
"""Display argument spec and docstring in a popup window.
|
|
203
|
+
|
|
204
|
+
(override) Fix cursor position on calltip insertion.
|
|
205
|
+
"""
|
|
115
206
|
if self.CallTipActive():
|
|
116
207
|
self.CallTipCancel()
|
|
117
208
|
|
|
@@ -120,50 +211,50 @@ class AutoCompInterfaceMixin:
|
|
|
120
211
|
dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip)
|
|
121
212
|
p = self.cpos
|
|
122
213
|
if argspec and insertcalltip:
|
|
123
|
-
self.AddText(argspec + ')')
|
|
124
|
-
self.
|
|
214
|
+
self.AddText(argspec + ')')
|
|
215
|
+
self.cpos = p # selection backward to the point
|
|
125
216
|
if tip:
|
|
126
217
|
## In case there isn't enough room, only go back to bol fallback.
|
|
127
218
|
tippos = max(self.bol, p - len(name) - 1)
|
|
128
219
|
self.CallTipShow(tippos, tip)
|
|
129
|
-
|
|
220
|
+
|
|
130
221
|
def call_helpDoc(self, evt):
|
|
131
222
|
"""Show help:str for the selected topic."""
|
|
132
223
|
if self.CallTipActive():
|
|
133
224
|
self.CallTipCancel()
|
|
134
225
|
|
|
135
|
-
text =
|
|
226
|
+
text = self.SelectedText or self.expr_at_caret or self.line_at_caret
|
|
136
227
|
if text:
|
|
137
228
|
text = introspect.getRoot(text, terminator='(')
|
|
138
229
|
try:
|
|
139
230
|
obj = self.eval(text)
|
|
140
231
|
self.help(obj)
|
|
141
232
|
except Exception as e:
|
|
142
|
-
self.message(
|
|
143
|
-
|
|
233
|
+
self.message(e)
|
|
234
|
+
|
|
144
235
|
def call_helpTip(self, evt):
|
|
145
236
|
"""Show a calltip for the selected function."""
|
|
146
237
|
if self.CallTipActive():
|
|
147
238
|
self.CallTipCancel()
|
|
148
239
|
|
|
149
|
-
text =
|
|
240
|
+
text = self.SelectedText or self.expr_at_caret or self.line_at_caret
|
|
150
241
|
if text:
|
|
151
242
|
p = self.cpos
|
|
152
243
|
self.autoCallTipShow(text,
|
|
153
|
-
p == self.eol and self.get_char(p-1) == '(')
|
|
154
|
-
|
|
244
|
+
p == self.eol and self.get_char(p-1) == '(') # => CallTipShow
|
|
245
|
+
|
|
155
246
|
def on_completion_forward(self, evt):
|
|
156
247
|
if not self.AutoCompActive():
|
|
157
248
|
self.handler('quit', evt)
|
|
158
249
|
return
|
|
159
250
|
self._on_completion(1)
|
|
160
|
-
|
|
251
|
+
|
|
161
252
|
def on_completion_backward(self, evt):
|
|
162
253
|
if not self.AutoCompActive():
|
|
163
254
|
self.handler('quit', evt)
|
|
164
255
|
return
|
|
165
256
|
self._on_completion(-1)
|
|
166
|
-
|
|
257
|
+
|
|
167
258
|
def _on_completion(self, step=0):
|
|
168
259
|
"""Show completion with selection."""
|
|
169
260
|
try:
|
|
@@ -174,18 +265,18 @@ class AutoCompInterfaceMixin:
|
|
|
174
265
|
n = len(self.__comp_hint)
|
|
175
266
|
p = self.cpos
|
|
176
267
|
if not self.SelectedText:
|
|
177
|
-
p, q, sty = self.get_following_atom(p)
|
|
268
|
+
p, q, sty = self.get_following_atom(p) # word-right-selection
|
|
178
269
|
if sty == 'word':
|
|
179
270
|
self.anchor = q
|
|
180
271
|
with self.off_undocollection():
|
|
181
272
|
self.ReplaceSelection(word[n:])
|
|
182
|
-
self.cpos = p
|
|
273
|
+
self.cpos = p # selection backward to the point
|
|
183
274
|
self.__comp_ind = j
|
|
184
275
|
except IndexError:
|
|
185
276
|
self.message("No completion words")
|
|
186
|
-
|
|
277
|
+
|
|
187
278
|
def _gen_autocomp(self, j, hint, words, sep=' ', mode=True):
|
|
188
|
-
## Prepare on_completion_forward/backward
|
|
279
|
+
## Prepare on_completion_forward/backward.
|
|
189
280
|
self.__comp_ind = j
|
|
190
281
|
self.__comp_hint = hint
|
|
191
282
|
self.__comp_words = words
|
|
@@ -195,12 +286,20 @@ class AutoCompInterfaceMixin:
|
|
|
195
286
|
elif words:
|
|
196
287
|
self.AutoCompSetSeparator(ord(sep))
|
|
197
288
|
self.AutoCompShow(len(hint), sep.join(words))
|
|
198
|
-
|
|
289
|
+
|
|
199
290
|
def _get_words_hint(self):
|
|
200
291
|
cmdl = self.GetTextRange(self.bol, self.cpos)
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
292
|
+
if cmdl.endswith(' '): # 前の文字が空白の場合はスキップする
|
|
293
|
+
text = ''
|
|
294
|
+
else:
|
|
295
|
+
text = next(split_words(cmdl, reverse=1), '')
|
|
296
|
+
return text.rpartition('.') # -> text, sep, hint
|
|
297
|
+
|
|
298
|
+
def clear_autocomp(self, evt):
|
|
299
|
+
if self.AutoCompActive():
|
|
300
|
+
self.AutoCompCancel()
|
|
301
|
+
self.message("")
|
|
302
|
+
|
|
204
303
|
def call_history_comp(self, evt):
|
|
205
304
|
"""Called when history-comp mode."""
|
|
206
305
|
if not self.CanEdit():
|
|
@@ -209,19 +308,19 @@ class AutoCompInterfaceMixin:
|
|
|
209
308
|
|
|
210
309
|
cmdl = self.GetTextRange(self.bol, self.cpos)
|
|
211
310
|
if cmdl.isspace() or self.bol != self.bolc:
|
|
212
|
-
self.handler('skip', evt)
|
|
311
|
+
self.handler('skip', evt) # [tab pressed] => on_indent_line
|
|
213
312
|
return
|
|
214
313
|
|
|
215
314
|
hint = cmdl.strip()
|
|
216
315
|
ls = [x.replace('\n', os.linesep + sys.ps2)
|
|
217
|
-
for x in self.history if x.startswith(hint)]
|
|
218
|
-
words = sorted(set(ls), key=ls.index, reverse=0)
|
|
316
|
+
for x in self.history if x.startswith(hint)] # case-sensitive match
|
|
317
|
+
words = sorted(set(ls), key=ls.index, reverse=0) # keep order, no duplication
|
|
219
318
|
|
|
220
|
-
##
|
|
319
|
+
## The latest history stacks in the head of the list (time-descending).
|
|
221
320
|
self._gen_autocomp(0, hint, words, mode=False)
|
|
222
321
|
self.message("[history] {} candidates matched"
|
|
223
322
|
" with {!r}".format(len(words), hint))
|
|
224
|
-
|
|
323
|
+
|
|
225
324
|
def call_text_autocomp(self, evt):
|
|
226
325
|
"""Called when text-comp mode."""
|
|
227
326
|
if not self.CanEdit():
|
|
@@ -229,15 +328,17 @@ class AutoCompInterfaceMixin:
|
|
|
229
328
|
return
|
|
230
329
|
|
|
231
330
|
cmdl = self.GetTextRange(self.bol, self.cpos)
|
|
232
|
-
hint = re.search(r"[\w.]*$", cmdl).group(0)
|
|
331
|
+
hint = re.search(r"[\w.]*$", cmdl).group(0) # extract the last word
|
|
233
332
|
|
|
234
|
-
ls = [x for x in self.fragmwords if x.startswith(hint)]
|
|
235
|
-
|
|
333
|
+
## ls = [x for x in self.fragmwords if x.startswith(hint)] # case-sensitive match
|
|
334
|
+
q = hint.lower()
|
|
335
|
+
ls = [x for x in self.fragmwords if x.lower().startswith(q)] # case-insensitive match
|
|
336
|
+
words = sorted(ls, key=lambda s: s.upper())
|
|
236
337
|
|
|
237
338
|
self._gen_autocomp(0, hint, words)
|
|
238
339
|
self.message("[text] {} candidates matched"
|
|
239
340
|
" with {!r}".format(len(words), hint))
|
|
240
|
-
|
|
341
|
+
|
|
241
342
|
def call_module_autocomp(self, evt, force=False):
|
|
242
343
|
"""Called when module-comp mode."""
|
|
243
344
|
if not self.CanEdit():
|
|
@@ -248,13 +349,14 @@ class AutoCompInterfaceMixin:
|
|
|
248
349
|
if not hints.endswith(' '):
|
|
249
350
|
h = hints.strip()
|
|
250
351
|
if not h.endswith(','):
|
|
251
|
-
lh = h.split(',')[-1].strip()
|
|
252
|
-
if ' ' not in lh:
|
|
352
|
+
lh = h.split(',')[-1].strip() # 'x, y, z|' last hint after ','
|
|
353
|
+
if ' ' not in lh: # 'x, y as|' contains no spaces.
|
|
253
354
|
return lh
|
|
254
355
|
|
|
255
356
|
cmdl = self.GetTextRange(self.bol, self.cpos)
|
|
256
|
-
hint = re.search(r"[\w.]*$", cmdl).group(0)
|
|
357
|
+
hint = re.search(r"[\w.]*$", cmdl).group(0) # extract the last word including dots
|
|
257
358
|
try:
|
|
359
|
+
## from * import ...
|
|
258
360
|
if (m := re.match(r"from\s+([\w.]+)\s+import\s+(.*)", cmdl)):
|
|
259
361
|
text, hints = m.groups()
|
|
260
362
|
if not _continue(hints) and not force:
|
|
@@ -270,17 +372,18 @@ class AutoCompInterfaceMixin:
|
|
|
270
372
|
self.message("\b failed.", e)
|
|
271
373
|
return
|
|
272
374
|
else:
|
|
273
|
-
## Add unimported module names.
|
|
274
|
-
|
|
275
|
-
keys = [x[len(text)+1:] for x in self.modules if x.startswith(
|
|
375
|
+
## Add unimported module names (case-insensitive match).
|
|
376
|
+
q = f"{text}.{hint}".lower()
|
|
377
|
+
keys = [x[len(text)+1:] for x in self.modules if x.lower().startswith(q)]
|
|
276
378
|
modules.update(k for k in keys if '.' not in k)
|
|
277
|
-
|
|
379
|
+
## import ...
|
|
278
380
|
elif (m := re.match(r"(import|from)\s+(.*)", cmdl)):
|
|
279
381
|
text, hints = m.groups()
|
|
280
382
|
if not _continue(hints) and not force:
|
|
281
383
|
self.message("[module]>>> waiting for key input...")
|
|
282
384
|
return
|
|
283
385
|
modules = self.modules
|
|
386
|
+
## Module X.Y.Z
|
|
284
387
|
else:
|
|
285
388
|
text, sep, hint = self._get_words_hint()
|
|
286
389
|
obj = self.eval(text)
|
|
@@ -288,7 +391,7 @@ class AutoCompInterfaceMixin:
|
|
|
288
391
|
|
|
289
392
|
P = re.compile(hint)
|
|
290
393
|
p = re.compile(hint, re.I)
|
|
291
|
-
words = sorted([x for x in modules if p.match(x)], key=lambda s:s.upper())
|
|
394
|
+
words = sorted([x for x in modules if p.match(x)], key=lambda s: s.upper())
|
|
292
395
|
|
|
293
396
|
j = next((k for k, w in enumerate(words) if P.match(w)),
|
|
294
397
|
next((k for k, w in enumerate(words) if p.match(w)), -1))
|
|
@@ -303,19 +406,24 @@ class AutoCompInterfaceMixin:
|
|
|
303
406
|
self.message("- {} : {!r}".format(e, text))
|
|
304
407
|
except Exception as e:
|
|
305
408
|
self.message("- {} : {!r}".format(e, text))
|
|
306
|
-
|
|
409
|
+
|
|
307
410
|
def call_word_autocomp(self, evt):
|
|
308
411
|
"""Called when word-comp mode."""
|
|
309
412
|
if not self.CanEdit():
|
|
310
413
|
self.handler('quit', evt)
|
|
311
414
|
return
|
|
415
|
+
|
|
416
|
+
text, sep, hint = self._get_words_hint()
|
|
417
|
+
if not text:
|
|
418
|
+
self.handler('quit', evt)
|
|
419
|
+
self.message("- No autocompletion candidates or hints found.")
|
|
420
|
+
return
|
|
312
421
|
try:
|
|
313
|
-
|
|
422
|
+
## dir = introspect.getAttributeNames # @TODO in wx ver 4.2.3
|
|
314
423
|
obj = self.eval(text)
|
|
315
|
-
|
|
316
424
|
P = re.compile(hint)
|
|
317
425
|
p = re.compile(hint, re.I)
|
|
318
|
-
words = sorted([x for x in dir(obj) if p.match(x)], key=lambda s:s.upper())
|
|
426
|
+
words = sorted([x for x in dir(obj) if p.match(x)], key=lambda s: s.upper())
|
|
319
427
|
|
|
320
428
|
j = next((k for k, w in enumerate(words) if P.match(w)),
|
|
321
429
|
next((k for k, w in enumerate(words) if p.match(w)), -1))
|
|
@@ -330,19 +438,24 @@ class AutoCompInterfaceMixin:
|
|
|
330
438
|
self.message("- {} : {!r}".format(e, text))
|
|
331
439
|
except Exception as e:
|
|
332
440
|
self.message("- {} : {!r}".format(e, text))
|
|
333
|
-
|
|
441
|
+
|
|
334
442
|
def call_apropos_autocomp(self, evt):
|
|
335
443
|
"""Called when apropos mode."""
|
|
336
444
|
if not self.CanEdit():
|
|
337
445
|
self.handler('quit', evt)
|
|
338
446
|
return
|
|
447
|
+
|
|
448
|
+
text, sep, hint = self._get_words_hint()
|
|
449
|
+
if not text:
|
|
450
|
+
self.handler('quit', evt)
|
|
451
|
+
self.message("- No autocompletion candidates or hints found.")
|
|
452
|
+
return
|
|
339
453
|
try:
|
|
340
|
-
|
|
454
|
+
## dir = introspect.getAttributeNames # @TODO in wx ver 4.2.3.
|
|
341
455
|
obj = self.eval(text)
|
|
342
|
-
|
|
343
456
|
P = re.compile(hint)
|
|
344
457
|
p = re.compile(hint, re.I)
|
|
345
|
-
words = sorted([x for x in dir(obj) if p.search(x)], key=lambda s:s.upper())
|
|
458
|
+
words = sorted([x for x in dir(obj) if p.search(x)], key=lambda s: s.upper())
|
|
346
459
|
|
|
347
460
|
j = next((k for k, w in enumerate(words) if P.match(w)),
|
|
348
461
|
next((k for k, w in enumerate(words) if p.match(w)), -1))
|
|
@@ -366,8 +479,8 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
366
479
|
This class is mixed-in ``wx.stc.StyledTextCtrl``.
|
|
367
480
|
"""
|
|
368
481
|
def __init__(self):
|
|
369
|
-
CtrlInterface.__init__(self)
|
|
370
482
|
AutoCompInterfaceMixin.__init__(self)
|
|
483
|
+
CtrlInterface.__init__(self)
|
|
371
484
|
|
|
372
485
|
def dispatch(evt):
|
|
373
486
|
"""Fork events to the parent."""
|
|
@@ -384,7 +497,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
384
497
|
'pointer_unset' : [ None, dispatch ],
|
|
385
498
|
},
|
|
386
499
|
0 : {
|
|
387
|
-
'insert pressed' : (0, _F(self.over, None, doc="toggle-over")),
|
|
500
|
+
'insert pressed' : (0, _F(self.over, mode=None, doc="toggle-over")),
|
|
388
501
|
'C-left pressed' : (0, _F(self.WordLeft)),
|
|
389
502
|
'C-right pressed' : (0, _F(self.WordRightEnd)),
|
|
390
503
|
'C-S-left pressed' : (0, _F(self.selection_backward_word_or_paren)),
|
|
@@ -395,29 +508,29 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
395
508
|
'C-e pressed' : (0, _F(self.end_of_line)),
|
|
396
509
|
'M-a pressed' : (0, _F(self.back_to_indentation)),
|
|
397
510
|
'M-e pressed' : (0, _F(self.end_of_line)),
|
|
398
|
-
'M-g pressed' : (0, ask(self.goto_line, "Line to goto:", lambda x:int(x)-1),
|
|
511
|
+
'M-g pressed' : (0, ask(self.goto_line, "Line to goto:", lambda x: int(x)-1),
|
|
399
512
|
_F(self.recenter)),
|
|
400
513
|
'M-f pressed' : (10, _F(self.filter_text), self.on_itext_enter),
|
|
401
514
|
'C-k pressed' : (0, _F(self.kill_line)),
|
|
402
515
|
'C-S-c pressed' : (0, _F(self.Copy)),
|
|
403
516
|
'C-S-v pressed' : (0, _F(self.Paste)),
|
|
404
517
|
'C-l pressed' : (0, _F(self.recenter)),
|
|
405
|
-
'C-S-l pressed' : (0, _F(self.recenter)),
|
|
406
|
-
'C-S-f pressed' : (0, _F(self.set_mark)),
|
|
518
|
+
# 'C-S-l pressed' : (0, _F(self.recenter)), # overrides delete-line
|
|
519
|
+
# 'C-S-f pressed' : (0, _F(self.set_mark)), # overrides mark
|
|
407
520
|
'C-space pressed' : (0, _F(self.set_mark)),
|
|
408
|
-
'C-S-space pressed' : (0, _F(self.
|
|
409
|
-
'C-backspace pressed' : (0,
|
|
521
|
+
'C-S-space pressed' : (0, _F(self.toggle_pointer)),
|
|
522
|
+
'C-backspace pressed' : (0, _F(self.backward_kill_word)),
|
|
410
523
|
'S-backspace pressed' : (0, _F(self.backward_kill_line)),
|
|
524
|
+
'C-delete pressed' : (0, _F(self.kill_word)),
|
|
525
|
+
'S-delete pressed' : (0, _F(self.kill_line)),
|
|
411
526
|
'C-tab pressed' : (0, _F(self.insert_space_like_tab)),
|
|
412
527
|
'C-S-tab pressed' : (0, _F(self.delete_backward_space_like_tab)),
|
|
413
528
|
'tab pressed' : (0, self.on_indent_line),
|
|
414
529
|
'S-tab pressed' : (0, self.on_outdent_line),
|
|
415
|
-
|
|
416
|
-
|
|
530
|
+
# 'C-/ pressed' : (0, ), # cf. C-a home
|
|
531
|
+
# 'C-\ pressed' : (0, ), # cf. C-e end
|
|
417
532
|
'C-; pressed' : (0, _F(self.comment_out_line)),
|
|
418
|
-
'C-S-; pressed' : (0, _F(self.comment_out_line)),
|
|
419
533
|
'C-: pressed' : (0, _F(self.uncomment_line)),
|
|
420
|
-
'C-S-: pressed' : (0, _F(self.uncomment_line)),
|
|
421
534
|
'select_line' : (11, self.on_linesel_begin),
|
|
422
535
|
'select_lines' : (11, self.on_linesel_next),
|
|
423
536
|
},
|
|
@@ -435,9 +548,9 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
435
548
|
},
|
|
436
549
|
})
|
|
437
550
|
|
|
438
|
-
## cf. wx.py.editwindow.EditWindow.OnUpdateUI => Check for brace matching
|
|
551
|
+
## cf. wx.py.editwindow.EditWindow.OnUpdateUI => Check for brace matching.
|
|
439
552
|
self.Bind(stc.EVT_STC_UPDATEUI,
|
|
440
|
-
lambda v: self.match_paren())
|
|
553
|
+
lambda v: self.match_paren()) # no skip
|
|
441
554
|
|
|
442
555
|
def eof(evt):
|
|
443
556
|
p = evt.Position
|
|
@@ -459,104 +572,100 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
459
572
|
ss |= set(x for x in dir(obj) if x.startswith('__'))
|
|
460
573
|
return ss
|
|
461
574
|
|
|
462
|
-
## Keyword(2) setting
|
|
575
|
+
## Keyword(2) setting.
|
|
463
576
|
self.SetLexer(stc.STC_LEX_PYTHON)
|
|
464
577
|
self.SetKeyWords(0, ' '.join(keyword.kwlist))
|
|
465
578
|
self.SetKeyWords(1, ' '.join(builtins.__dict__)
|
|
466
579
|
+ ' '.join(_dunders(type, int, float, str, bytes,
|
|
467
580
|
tuple, list, range, operator,))
|
|
468
|
-
+ ' self this')
|
|
581
|
+
+ ' self this shell')
|
|
469
582
|
|
|
470
|
-
## AutoComp setting
|
|
583
|
+
## AutoComp setting.
|
|
471
584
|
self.AutoCompSetAutoHide(False)
|
|
472
585
|
self.AutoCompSetIgnoreCase(True)
|
|
473
586
|
self.AutoCompSetMaxWidth(80)
|
|
474
587
|
self.AutoCompSetMaxHeight(10)
|
|
475
588
|
|
|
476
|
-
## To prevent @filling crash (Never access to DropTarget)
|
|
477
|
-
## [BUG 4.1.1] Don't allow DnD of text, file, whatever.
|
|
589
|
+
## To prevent @filling crash (Never access to DropTarget).
|
|
590
|
+
## [BUG ver 4.1.1] Don't allow DnD of text, file, whatever.
|
|
478
591
|
## self.SetDropTarget(None)
|
|
479
592
|
|
|
480
593
|
self.Bind(stc.EVT_STC_START_DRAG, self.OnDrag)
|
|
481
594
|
self.Bind(stc.EVT_STC_DRAG_OVER, self.OnDragging)
|
|
482
595
|
self.Bind(stc.EVT_STC_DO_DROP, self.OnDragged)
|
|
483
596
|
|
|
484
|
-
## Global style for all languages
|
|
597
|
+
## Global style for all languages.
|
|
485
598
|
## font = wx.Font(9, wx.MODERN, wx.NORMAL, wx.NORMAL, False, "MS Gothic")
|
|
486
599
|
## self.StyleSetFont(stc.STC_STYLE_DEFAULT, font)
|
|
487
|
-
|
|
488
600
|
## self.StyleClearAll()
|
|
489
|
-
##
|
|
490
|
-
## self.SetSelBackground(True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT))
|
|
601
|
+
## => STC_STYLE_DEFAULT に設定したスタイルを他のスタイルに全適用する.
|
|
491
602
|
|
|
492
|
-
## The magin style for line numbers and symbols
|
|
603
|
+
## The magin style for line numbers and symbols.
|
|
493
604
|
## [0] for markers, 10 pixels wide, mask 0b11111
|
|
494
605
|
## [1] for numbers, 32 pixels wide, mask 0x01ffffff (~stc.STC_MASK_FOLDERS)
|
|
495
606
|
## [2] for borders, 1 pixels wide, mask 0xfe000000 ( stc.STC_MASK_FOLDERS)
|
|
496
|
-
|
|
607
|
+
##
|
|
608
|
+
## cf. `EditWindow.setDisplayLineNumbers`
|
|
609
|
+
##
|
|
497
610
|
## 32 bit margin mask
|
|
498
611
|
## [0] 1111,1111,1111,1111,1111,1111,1111,1111 = -1 for all markers
|
|
499
612
|
## [1] 0000,0001,1111,1111,1111,1111,1111,1111 = 0x01ffffff for markers
|
|
500
613
|
## [2] 1111,1110,0000,0000,0000,0000,0000,0000 = 0xfe000000 for folders
|
|
501
614
|
|
|
502
615
|
self.SetMarginType(0, stc.STC_MARGIN_SYMBOL)
|
|
503
|
-
self.SetMarginMask(0, 0b00111)
|
|
616
|
+
self.SetMarginMask(0, 0b00111) # mask for markers (0,1,2)
|
|
504
617
|
self.SetMarginWidth(0, 10)
|
|
505
618
|
self.SetMarginSensitive(0, False)
|
|
506
619
|
|
|
507
620
|
self.SetMarginType(1, stc.STC_MARGIN_NUMBER)
|
|
508
|
-
self.SetMarginMask(1, 0b11000)
|
|
621
|
+
self.SetMarginMask(1, 0b11000) # mask for pointer (3,4)
|
|
509
622
|
self.SetMarginWidth(1, 32)
|
|
510
623
|
self.SetMarginSensitive(1, False)
|
|
511
624
|
|
|
512
625
|
self.SetMarginType(2, stc.STC_MARGIN_SYMBOL)
|
|
513
|
-
self.SetMarginMask(2, stc.STC_MASK_FOLDERS)
|
|
626
|
+
self.SetMarginMask(2, stc.STC_MASK_FOLDERS) # mask for folders
|
|
514
627
|
self.SetMarginWidth(2, 1)
|
|
515
628
|
self.SetMarginSensitive(2, False)
|
|
516
629
|
|
|
517
|
-
self.SetMarginLeft(2)
|
|
630
|
+
self.SetMarginLeft(2) # +1 margin at the left
|
|
518
631
|
|
|
519
|
-
self.SetFoldFlags(
|
|
632
|
+
self.SetFoldFlags(stc.STC_FOLDFLAG_LINEAFTER_CONTRACTED)
|
|
520
633
|
|
|
521
|
-
self.SetProperty('fold', '1')
|
|
634
|
+
self.SetProperty('fold', '1') # Enable folder property
|
|
522
635
|
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
|
|
526
|
-
self.Bind(stc.EVT_STC_MARGIN_RIGHT_CLICK, self.OnMarginRClick)
|
|
527
|
-
except AttributeError:
|
|
528
|
-
pass
|
|
636
|
+
self.Bind(stc.EVT_STC_MARGINCLICK, self.OnMarginClick)
|
|
637
|
+
self.Bind(stc.EVT_STC_MARGIN_RIGHT_CLICK, self.OnMarginRClick)
|
|
529
638
|
|
|
530
|
-
## Custom markers
|
|
531
|
-
self.MarkerDefine(0, stc.STC_MARK_CIRCLE, '#007ff0', '#007ff0')
|
|
532
|
-
self.MarkerDefine(1, stc.STC_MARK_ARROW, '#000000', '#ffffff')
|
|
533
|
-
self.MarkerDefine(2, stc.STC_MARK_ARROW, '#7f0000', '#ff0000')
|
|
534
|
-
self.MarkerDefine(3, stc.STC_MARK_SHORTARROW, 'blue', 'gray')
|
|
535
|
-
self.MarkerDefine(4, stc.STC_MARK_SHORTARROW, 'red', 'yellow')
|
|
639
|
+
## Custom markers.
|
|
640
|
+
self.MarkerDefine(0, stc.STC_MARK_CIRCLE, '#007ff0', '#007ff0') # o mark
|
|
641
|
+
self.MarkerDefine(1, stc.STC_MARK_ARROW, '#000000', '#ffffff') # > arrow
|
|
642
|
+
self.MarkerDefine(2, stc.STC_MARK_ARROW, '#7f0000', '#ff0000') # > red-arrow
|
|
643
|
+
self.MarkerDefine(3, stc.STC_MARK_SHORTARROW, 'blue', 'gray') # -> pointer
|
|
644
|
+
self.MarkerDefine(4, stc.STC_MARK_SHORTARROW, 'red', 'yellow') # -> red-pointer
|
|
536
645
|
|
|
537
646
|
v = ('white', 'black')
|
|
538
647
|
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPEN, stc.STC_MARK_BOXMINUS, *v)
|
|
539
648
|
self.MarkerDefine(stc.STC_MARKNUM_FOLDER, stc.STC_MARK_BOXPLUS, *v)
|
|
540
649
|
self.MarkerDefine(stc.STC_MARKNUM_FOLDERSUB, stc.STC_MARK_VLINE, *v)
|
|
541
650
|
self.MarkerDefine(stc.STC_MARKNUM_FOLDERTAIL, stc.STC_MARK_LCORNER, *v)
|
|
542
|
-
|
|
543
|
-
|
|
651
|
+
# self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_TCORNER, *v)
|
|
652
|
+
# self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_TCORNER, *v)
|
|
544
653
|
self.MarkerDefine(stc.STC_MARKNUM_FOLDEREND, stc.STC_MARK_VLINE, *v)
|
|
545
654
|
self.MarkerDefine(stc.STC_MARKNUM_FOLDEROPENMID, stc.STC_MARK_VLINE, *v)
|
|
546
655
|
self.MarkerDefine(stc.STC_MARKNUM_FOLDERMIDTAIL, stc.STC_MARK_VLINE, *v)
|
|
547
656
|
|
|
548
|
-
## Custom indicators ([BUG] indicator=1 is reset when the buffer is
|
|
657
|
+
## Custom indicators ([BUG] indicator=1 is reset when the buffer is updated).
|
|
549
658
|
## [10-11] filter_text
|
|
550
|
-
## [2] URL
|
|
659
|
+
## [2] URL
|
|
551
660
|
## [3] match_paren
|
|
552
661
|
self.IndicatorSetStyle(10, stc.STC_INDIC_TEXTFORE)
|
|
553
662
|
self.IndicatorSetForeground(10, "red")
|
|
554
663
|
|
|
555
664
|
self.IndicatorSetStyle(11, stc.STC_INDIC_STRAIGHTBOX)
|
|
556
|
-
self.IndicatorSetUnder(11, True)
|
|
557
|
-
self.IndicatorSetAlpha(11, 255)
|
|
558
|
-
self.IndicatorSetOutlineAlpha(11, 255)
|
|
559
665
|
self.IndicatorSetForeground(11, "yellow")
|
|
666
|
+
self.IndicatorSetUnder(11, True)
|
|
667
|
+
# self.IndicatorSetAlpha(11, 0xe8)
|
|
668
|
+
# self.IndicatorSetOutlineAlpha(11, 0)
|
|
560
669
|
|
|
561
670
|
self.IndicatorSetStyle(2, stc.STC_INDIC_DOTS)
|
|
562
671
|
self.IndicatorSetForeground(2, "light gray")
|
|
@@ -569,57 +678,50 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
569
678
|
self.IndicatorSetStyle(3, stc.STC_INDIC_DOTS)
|
|
570
679
|
self.IndicatorSetForeground(3, "light gray")
|
|
571
680
|
|
|
572
|
-
## Custom annotation
|
|
681
|
+
## Custom annotation.
|
|
573
682
|
self.AnnotationSetVisible(stc.STC_ANNOTATION_BOXED)
|
|
574
683
|
|
|
575
|
-
## Custom style of control-char, wrap-mode
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
684
|
+
## Custom style of control-char, wrap-mode.
|
|
685
|
+
# self.UseTabs = False
|
|
686
|
+
# self.ViewEOL = False
|
|
687
|
+
# self.ViewWhiteSpace = False
|
|
688
|
+
# self.TabWidth = 4
|
|
689
|
+
# self.EOLMode = stc.STC_EOL_CRLF
|
|
581
690
|
self.WrapMode = stc.STC_WRAP_NONE
|
|
582
691
|
self.WrapIndentMode = stc.STC_WRAPINDENT_SAME
|
|
583
692
|
self.IndentationGuides = stc.STC_IV_LOOKFORWARD
|
|
584
693
|
|
|
585
694
|
self.__mark = -1
|
|
586
695
|
self.__stylus = {}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
EditorInterface.__dnd_from = evt.EventObject
|
|
595
|
-
try:
|
|
596
|
-
EditorInterface.__dnd_flag = (evt.Position < self.bolc) # force copy
|
|
597
|
-
except AttributeError:
|
|
696
|
+
|
|
697
|
+
__dnd_flag = 0
|
|
698
|
+
|
|
699
|
+
def OnDrag(self, evt): #<wx._core.StyledTextEvent>
|
|
700
|
+
if isinstance(self, Shell):
|
|
701
|
+
EditorInterface.__dnd_flag = (evt.Position < self.bolc) # readonly
|
|
702
|
+
else:
|
|
598
703
|
EditorInterface.__dnd_flag = 0
|
|
599
704
|
evt.Skip()
|
|
600
|
-
|
|
601
|
-
def OnDragging(self, evt):
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
wx.UIActionSimulator().KeyDown(wx.WXK_CONTROL) # force copy
|
|
606
|
-
try:
|
|
607
|
-
if evt.Position < self.bolc:
|
|
608
|
-
evt.DragResult = wx.DragNone # Don't drop (as readonly)
|
|
705
|
+
|
|
706
|
+
def OnDragging(self, evt): #<wx._core.StyledTextEvent>
|
|
707
|
+
if isinstance(self, Shell):
|
|
708
|
+
if evt.Position < self.bolc: # target is readonly
|
|
709
|
+
evt.DragResult = wx.DragNone
|
|
609
710
|
elif EditorInterface.__dnd_flag:
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
711
|
+
## from shell to shell
|
|
712
|
+
evt.DragResult = wx.DragCopy if wx.GetKeyState(wx.WXK_CONTROL) else wx.DragNone
|
|
713
|
+
else:
|
|
714
|
+
if EditorInterface.__dnd_flag:
|
|
715
|
+
## from shell to buffer
|
|
716
|
+
evt.DragResult = wx.DragCopy if wx.GetKeyState(wx.WXK_CONTROL) else wx.DragNone
|
|
613
717
|
evt.Skip()
|
|
614
|
-
|
|
615
|
-
def OnDragged(self, evt):
|
|
616
|
-
EditorInterface.__dnd_from = None
|
|
718
|
+
|
|
719
|
+
def OnDragged(self, evt): #<wx._core.StyledTextEvent>
|
|
617
720
|
EditorInterface.__dnd_flag = 0
|
|
618
|
-
wx.UIActionSimulator().KeyUp(wx.WXK_CONTROL)
|
|
619
721
|
evt.Skip()
|
|
620
|
-
|
|
722
|
+
|
|
621
723
|
## --------------------------------
|
|
622
|
-
## Marker attributes of the editor
|
|
724
|
+
## Marker attributes of the editor.
|
|
623
725
|
## --------------------------------
|
|
624
726
|
marker_names = {
|
|
625
727
|
0: "mark",
|
|
@@ -628,112 +730,116 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
628
730
|
3: "pointer",
|
|
629
731
|
4: "red-pointer",
|
|
630
732
|
}
|
|
631
|
-
|
|
733
|
+
|
|
632
734
|
def get_marker(self, n):
|
|
633
735
|
return self.MarkerNext(0, 1<<n)
|
|
634
|
-
|
|
736
|
+
|
|
635
737
|
def set_marker(self, line, n):
|
|
636
738
|
if line != -1:
|
|
637
739
|
self.MarkerDeleteAll(n)
|
|
638
740
|
self.add_marker(line, n)
|
|
639
741
|
else:
|
|
640
742
|
self.del_marker(n)
|
|
641
|
-
|
|
743
|
+
|
|
642
744
|
def add_marker(self, line, n):
|
|
643
745
|
if self.MarkerAdd(line, n):
|
|
644
|
-
self.EnsureVisible(line)
|
|
746
|
+
self.EnsureVisible(line) # expand if folded
|
|
645
747
|
self.handler('{}_set'.format(self.marker_names[n]), line)
|
|
646
|
-
|
|
748
|
+
|
|
647
749
|
def del_marker(self, n):
|
|
648
750
|
line = self.MarkerNext(0, 1<<n)
|
|
649
751
|
if line != -1:
|
|
650
752
|
self.MarkerDeleteAll(n)
|
|
651
753
|
self.handler('{}_unset'.format(self.marker_names[n]), line)
|
|
652
|
-
|
|
754
|
+
|
|
653
755
|
def goto_marker(self, markerMask, selection=False):
|
|
654
756
|
line = self.MarkerNext(0, markerMask)
|
|
655
757
|
if line != -1:
|
|
656
|
-
self.EnsureVisible(line)
|
|
758
|
+
self.EnsureVisible(line) # expand if folded
|
|
657
759
|
self.goto_line(line, selection)
|
|
658
760
|
self.recenter()
|
|
659
|
-
|
|
761
|
+
|
|
660
762
|
def goto_next_marker(self, markerMask, selection=False):
|
|
661
763
|
line = self.MarkerNext(self.cline+1, markerMask)
|
|
662
764
|
if line == -1:
|
|
663
765
|
line = self.LineCount
|
|
664
766
|
self.goto_line(line, selection)
|
|
665
|
-
|
|
767
|
+
|
|
666
768
|
def goto_previous_marker(self, markerMask, selection=False):
|
|
667
769
|
line = self.MarkerPrevious(self.cline-1, markerMask)
|
|
668
770
|
if line == -1:
|
|
669
771
|
line = 0
|
|
670
772
|
self.goto_line(line, selection)
|
|
671
|
-
|
|
773
|
+
|
|
672
774
|
white_arrow = property(
|
|
673
775
|
lambda self: self.get_marker(1),
|
|
674
|
-
lambda self,v: self.set_marker(v, 1),
|
|
675
|
-
lambda self: self.del_marker(1)
|
|
676
|
-
|
|
776
|
+
lambda self, v: self.set_marker(v, 1), # [arrow_set]
|
|
777
|
+
lambda self: self.del_marker(1), # [arrow_unset]
|
|
778
|
+
doc="Arrow marker used to indicate success.")
|
|
779
|
+
|
|
677
780
|
red_arrow = property(
|
|
678
781
|
lambda self: self.get_marker(2),
|
|
679
|
-
lambda self,v: self.set_marker(v, 2),
|
|
680
|
-
lambda self: self.del_marker(2)
|
|
681
|
-
|
|
782
|
+
lambda self, v: self.set_marker(v, 2), # [red-arrow_set]
|
|
783
|
+
lambda self: self.del_marker(2), # [red-arrow_unset]
|
|
784
|
+
doc="Arrow marker used to indicate failure.")
|
|
785
|
+
|
|
682
786
|
pointer = property(
|
|
683
787
|
lambda self: self.get_marker(3),
|
|
684
|
-
lambda self,v: self.set_marker(v, 3),
|
|
685
|
-
lambda self: self.del_marker(3)
|
|
686
|
-
|
|
788
|
+
lambda self, v: self.set_marker(v, 3), # [pointer_set]
|
|
789
|
+
lambda self: self.del_marker(3), # [pointer_unset]
|
|
790
|
+
doc="Arrow marker used to indicate breakpoint.")
|
|
791
|
+
|
|
687
792
|
red_pointer = property(
|
|
688
793
|
lambda self: self.get_marker(4),
|
|
689
|
-
lambda self,v: self.set_marker(v, 4),
|
|
690
|
-
lambda self: self.del_marker(4)
|
|
691
|
-
|
|
794
|
+
lambda self, v: self.set_marker(v, 4), # [red-pointer_set]
|
|
795
|
+
lambda self: self.del_marker(4), # [red-pointer_unset]
|
|
796
|
+
doc="Arrow marker used to indicate exception.")
|
|
797
|
+
|
|
692
798
|
@property
|
|
693
799
|
def markline(self):
|
|
694
800
|
return self.MarkerNext(0, 1<<0)
|
|
695
|
-
|
|
801
|
+
|
|
696
802
|
@markline.setter
|
|
697
803
|
def markline(self, v):
|
|
698
804
|
if v != -1:
|
|
699
|
-
self.mark = self.PositionFromLine(v)
|
|
805
|
+
self.mark = self.PositionFromLine(v) # [mark_set]
|
|
700
806
|
else:
|
|
701
|
-
del self.mark
|
|
702
|
-
|
|
807
|
+
del self.mark # [mark_unset]
|
|
808
|
+
|
|
703
809
|
@markline.deleter
|
|
704
810
|
def markline(self):
|
|
705
811
|
del self.mark
|
|
706
|
-
|
|
812
|
+
|
|
707
813
|
@property
|
|
708
814
|
def mark(self):
|
|
709
815
|
return self.__mark
|
|
710
|
-
|
|
816
|
+
|
|
711
817
|
@mark.setter
|
|
712
818
|
def mark(self, v):
|
|
713
819
|
if v != -1:
|
|
714
820
|
self.__mark = v
|
|
715
821
|
ln = self.LineFromPosition(v)
|
|
716
|
-
self.set_marker(ln, 0)
|
|
822
|
+
self.set_marker(ln, 0) # [mark_set]
|
|
717
823
|
else:
|
|
718
824
|
del self.mark
|
|
719
|
-
|
|
825
|
+
|
|
720
826
|
@mark.deleter
|
|
721
827
|
def mark(self):
|
|
722
828
|
v = self.__mark
|
|
723
829
|
if v != -1:
|
|
724
830
|
self.__mark = -1
|
|
725
|
-
self.del_marker(0)
|
|
726
|
-
|
|
831
|
+
self.del_marker(0) # [mark_unset]
|
|
832
|
+
|
|
727
833
|
def set_mark(self):
|
|
728
834
|
self.mark = self.cpos
|
|
729
|
-
|
|
730
|
-
def
|
|
731
|
-
if self.pointer == self.cline:
|
|
835
|
+
|
|
836
|
+
def toggle_pointer(self):
|
|
837
|
+
if self.pointer == self.cline: # toggle
|
|
732
838
|
self.pointer = -1
|
|
733
839
|
else:
|
|
734
|
-
self.pointer = self.cline
|
|
735
|
-
|
|
736
|
-
|
|
840
|
+
self.pointer = self.cline # reset
|
|
841
|
+
self.red_pointer = -1
|
|
842
|
+
|
|
737
843
|
def exchange_point_and_mark(self):
|
|
738
844
|
p = self.cpos
|
|
739
845
|
q = self.mark
|
|
@@ -743,13 +849,13 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
743
849
|
self.mark = p
|
|
744
850
|
else:
|
|
745
851
|
self.message("No marks")
|
|
746
|
-
|
|
852
|
+
|
|
747
853
|
## --------------------------------
|
|
748
|
-
## Attributes of the editor
|
|
854
|
+
## Attributes of the editor.
|
|
749
855
|
## --------------------------------
|
|
750
856
|
py_styles = {
|
|
751
|
-
stc.STC_P_DEFAULT : 'nil',
|
|
752
|
-
stc.STC_P_OPERATOR : 'op',
|
|
857
|
+
stc.STC_P_DEFAULT : 'nil', # etc. space \r\n\\$\0 (non-identifier)
|
|
858
|
+
stc.STC_P_OPERATOR : 'op', # ops. `@=+-/*%<>&|^~!?.,:;([{<>}])
|
|
753
859
|
stc.STC_P_COMMENTLINE : 'comment',
|
|
754
860
|
stc.STC_P_COMMENTBLOCK : 'comment',
|
|
755
861
|
stc.STC_P_NUMBER : 'suji',
|
|
@@ -765,7 +871,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
765
871
|
stc.STC_P_CLASSNAME : 'class',
|
|
766
872
|
stc.STC_P_DEFNAME : 'def',
|
|
767
873
|
}
|
|
768
|
-
|
|
874
|
+
|
|
769
875
|
def get_style(self, pos):
|
|
770
876
|
c = self.get_char(pos)
|
|
771
877
|
st = self.GetStyleAt(pos)
|
|
@@ -782,11 +888,11 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
782
888
|
if c in "({[": return 'lparen'
|
|
783
889
|
if c in ")}]": return 'rparen'
|
|
784
890
|
return sty
|
|
785
|
-
|
|
891
|
+
|
|
786
892
|
def get_char(self, pos):
|
|
787
|
-
"""
|
|
893
|
+
"""Return the character at the given position."""
|
|
788
894
|
return chr(self.GetCharAt(pos))
|
|
789
|
-
|
|
895
|
+
|
|
790
896
|
def get_text(self, start, end):
|
|
791
897
|
"""Retrieve a range of text.
|
|
792
898
|
|
|
@@ -798,50 +904,53 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
798
904
|
p = max(start, 0)
|
|
799
905
|
q = min(end, self.TextLength)
|
|
800
906
|
return self.GetTextRange(p, q)
|
|
801
|
-
|
|
907
|
+
|
|
802
908
|
anchor = property(
|
|
803
909
|
lambda self: self.GetAnchor(),
|
|
804
|
-
lambda self,v: self.SetAnchor(v)
|
|
805
|
-
|
|
910
|
+
lambda self, v: self.SetAnchor(v),
|
|
911
|
+
doc="Position of the opposite end of the selection to the caret.")
|
|
912
|
+
|
|
806
913
|
cpos = property(
|
|
807
914
|
lambda self: self.GetCurrentPos(),
|
|
808
|
-
lambda self,v: self.SetCurrentPos(v)
|
|
809
|
-
|
|
915
|
+
lambda self, v: self.SetCurrentPos(v),
|
|
916
|
+
doc="Position of the caret.")
|
|
917
|
+
|
|
810
918
|
cline = property(
|
|
811
919
|
lambda self: self.GetCurrentLine(),
|
|
812
|
-
lambda self,v: self.SetCurrentPos(self.PositionFromLine(v))
|
|
813
|
-
|
|
920
|
+
lambda self, v: self.SetCurrentPos(self.PositionFromLine(v)),
|
|
921
|
+
doc="Line number of the line with the caret.")
|
|
922
|
+
|
|
814
923
|
@property
|
|
815
924
|
def bol(self):
|
|
816
925
|
"""Beginning of line."""
|
|
817
926
|
text, lp = self.CurLine
|
|
818
927
|
return self.cpos - lp
|
|
819
|
-
|
|
928
|
+
|
|
820
929
|
@property
|
|
821
930
|
def eol(self):
|
|
822
931
|
"""End of line."""
|
|
823
932
|
text, lp = self.CurLine
|
|
824
|
-
text = text.strip('\r\n')
|
|
933
|
+
text = text.strip('\r\n') # remove linesep: '\r' and '\n'
|
|
825
934
|
return (self.cpos - lp + len(text.encode()))
|
|
826
|
-
|
|
935
|
+
|
|
827
936
|
@property
|
|
828
937
|
def line_at_caret(self):
|
|
829
938
|
"""Text of the range (bol, eol) at the caret-line."""
|
|
830
939
|
return self.GetTextRange(self.bol, self.eol)
|
|
831
|
-
|
|
940
|
+
|
|
832
941
|
@property
|
|
833
942
|
def expr_at_caret(self):
|
|
834
943
|
"""A syntax unit (expression) at the caret-line."""
|
|
835
944
|
p = q = self.cpos
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if
|
|
839
|
-
|
|
945
|
+
lst = self.get_style(p-1)
|
|
946
|
+
rst = self.get_style(p)
|
|
947
|
+
if lst == rst == 'moji': # inside string
|
|
948
|
+
# styles = {'moji'}
|
|
840
949
|
return ''
|
|
841
|
-
elif
|
|
950
|
+
elif lst == 'suji' or rst == 'suji':
|
|
842
951
|
styles = {'suji'}
|
|
843
|
-
elif
|
|
844
|
-
or
|
|
952
|
+
elif lst in ('word', 'dot', 'moji', 'rparen')\
|
|
953
|
+
or rst in ('word', 'dot', 'moji', 'lparen'):
|
|
845
954
|
styles = {'word', 'dot', 'moji', 'paren'}
|
|
846
955
|
else:
|
|
847
956
|
return ''
|
|
@@ -854,7 +963,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
854
963
|
if sty not in styles:
|
|
855
964
|
break
|
|
856
965
|
return self.GetTextRange(start, end).strip()
|
|
857
|
-
|
|
966
|
+
|
|
858
967
|
@property
|
|
859
968
|
def topic_at_caret(self):
|
|
860
969
|
"""Topic word at the caret or selected substring.
|
|
@@ -865,75 +974,77 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
865
974
|
return topic
|
|
866
975
|
with self.save_excursion():
|
|
867
976
|
p = q = self.cpos
|
|
868
|
-
|
|
977
|
+
cp = self.GetTextRange(self.PositionBefore(p), p) # cf. self.get_char(p-1)
|
|
978
|
+
if cp.isidentifier() or cp.isalnum():
|
|
869
979
|
self.WordLeft()
|
|
870
980
|
p = self.cpos
|
|
871
|
-
|
|
981
|
+
cq = self.GetTextRange(q, self.PositionAfter(q)) # cf. self.get_char(q)
|
|
982
|
+
if cq.isidentifier() or cq.isalnum():
|
|
872
983
|
self.WordRightEnd()
|
|
873
984
|
q = self.cpos
|
|
874
985
|
return self.GetTextRange(p, q)
|
|
875
|
-
|
|
986
|
+
|
|
876
987
|
## --------------------------------
|
|
877
|
-
## Python syntax and indentation
|
|
988
|
+
## Python syntax and indentation.
|
|
878
989
|
## --------------------------------
|
|
879
|
-
|
|
990
|
+
|
|
880
991
|
def on_indent_line(self, evt):
|
|
881
992
|
if self.SelectedText:
|
|
882
993
|
evt.Skip()
|
|
883
994
|
else:
|
|
884
995
|
self.py_indent_line()
|
|
885
|
-
|
|
996
|
+
|
|
886
997
|
def on_outdent_line(self, evt):
|
|
887
998
|
if self.SelectedText:
|
|
888
999
|
evt.Skip()
|
|
889
1000
|
else:
|
|
890
1001
|
self.py_outdent_line()
|
|
891
|
-
|
|
892
|
-
@
|
|
1002
|
+
|
|
1003
|
+
@editable
|
|
893
1004
|
def py_indent_line(self):
|
|
894
1005
|
"""Indent the current line."""
|
|
895
|
-
text = self.line_at_caret
|
|
896
|
-
lstr = text.lstrip()
|
|
1006
|
+
text = self.line_at_caret # w/ no-prompt
|
|
1007
|
+
lstr = text.lstrip() # w/ no-indent
|
|
897
1008
|
p = self.bol + len(text) - len(lstr)
|
|
898
1009
|
offset = max(0, self.cpos - p)
|
|
899
|
-
indent = self.py_current_indent()
|
|
1010
|
+
indent = self.py_current_indent() # check current/previous line
|
|
900
1011
|
if indent >= 0:
|
|
901
1012
|
self.Replace(self.bol, p, ' '*indent)
|
|
902
1013
|
self.goto_char(self.bol + indent + offset)
|
|
903
|
-
|
|
904
|
-
@
|
|
1014
|
+
|
|
1015
|
+
@editable
|
|
905
1016
|
def py_outdent_line(self):
|
|
906
1017
|
"""Outdent the current line."""
|
|
907
|
-
text = self.line_at_caret
|
|
908
|
-
lstr = text.lstrip()
|
|
1018
|
+
text = self.line_at_caret # w/ no-prompt
|
|
1019
|
+
lstr = text.lstrip() # w/ no-indent
|
|
909
1020
|
p = self.bol + len(text) - len(lstr)
|
|
910
1021
|
offset = max(0, self.cpos - p)
|
|
911
1022
|
indent = len(text) - len(lstr) - 4
|
|
912
1023
|
if indent >= 0:
|
|
913
1024
|
self.Replace(self.bol, p, ' '*indent)
|
|
914
1025
|
self.goto_char(self.bol + indent + offset)
|
|
915
|
-
|
|
1026
|
+
|
|
916
1027
|
def py_current_indent(self):
|
|
917
1028
|
"""Calculate indent spaces from previous line."""
|
|
918
1029
|
text = self.GetLine(self.cline - 1)
|
|
919
|
-
indent = self.py_calc_indentation(text)
|
|
1030
|
+
indent = self.py_calc_indentation(text) # check previous line
|
|
920
1031
|
text = self.GetLine(self.cline)
|
|
921
|
-
lstr, _indent = self.py_strip_indents(text)
|
|
1032
|
+
lstr, _indent = self.py_strip_indents(text) # check current line
|
|
922
1033
|
if re.match(py_outdent_re, lstr):
|
|
923
1034
|
indent -= 4
|
|
924
1035
|
return indent
|
|
925
|
-
|
|
1036
|
+
|
|
926
1037
|
def py_electric_indent(self):
|
|
927
1038
|
"""Calculate indent spaces for the following line."""
|
|
928
|
-
## [BUG 4.2.0] The last char is replaced with b'\x00'.
|
|
1039
|
+
## [BUG ver 4.2.0] The last char is replaced with b'\x00'.
|
|
929
1040
|
## text, lp = self.CurLineRaw
|
|
930
1041
|
## return self.py_calc_indentation(text[:lp].decode())
|
|
931
1042
|
text, lp = self.CurLine
|
|
932
1043
|
return self.py_calc_indentation(text[:lp])
|
|
933
|
-
|
|
1044
|
+
|
|
934
1045
|
@classmethod
|
|
935
1046
|
def py_calc_indentation(self, text):
|
|
936
|
-
"""
|
|
1047
|
+
"""Return indent spaces for the command text."""
|
|
937
1048
|
text = self.py_strip_comments(text)
|
|
938
1049
|
lstr, indent = self.py_strip_indents(text)
|
|
939
1050
|
text = text.rstrip()
|
|
@@ -944,33 +1055,33 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
944
1055
|
if re.match(py_closing_re, lstr):
|
|
945
1056
|
return indent - 4
|
|
946
1057
|
return indent
|
|
947
|
-
|
|
1058
|
+
|
|
948
1059
|
@classmethod
|
|
949
1060
|
def py_strip_indents(self, text):
|
|
950
|
-
"""
|
|
951
|
-
text = self.py_strip_prompts(text)
|
|
1061
|
+
"""Return left-stripped text and the number of indent spaces."""
|
|
1062
|
+
text = self.py_strip_prompts(text) # cf. shell.lstripPrompt(text)
|
|
952
1063
|
lstr = text.lstrip(' \t')
|
|
953
1064
|
indent = len(text) - len(lstr)
|
|
954
1065
|
return lstr, indent
|
|
955
|
-
|
|
1066
|
+
|
|
956
1067
|
@classmethod
|
|
957
1068
|
def py_strip_prompts(self, text):
|
|
958
|
-
"""
|
|
1069
|
+
"""Return text without a leading prompt."""
|
|
959
1070
|
for ps in (sys.ps1, sys.ps2, sys.ps3):
|
|
960
1071
|
if text.startswith(ps):
|
|
961
1072
|
text = text[len(ps):]
|
|
962
1073
|
break
|
|
963
1074
|
return text
|
|
964
|
-
|
|
1075
|
+
|
|
965
1076
|
@classmethod
|
|
966
1077
|
def py_strip_comments(self, text):
|
|
967
|
-
"""
|
|
1078
|
+
"""Return text without comments."""
|
|
968
1079
|
return ''.join(split_tokens(text, comment=False))
|
|
969
|
-
|
|
1080
|
+
|
|
970
1081
|
## --------------------------------
|
|
971
|
-
## Fold / Unfold functions
|
|
1082
|
+
## Fold / Unfold functions.
|
|
972
1083
|
## --------------------------------
|
|
973
|
-
|
|
1084
|
+
|
|
974
1085
|
def show_folder(self, show=True):
|
|
975
1086
|
"""Show folder margin.
|
|
976
1087
|
|
|
@@ -992,8 +1103,8 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
992
1103
|
self.SetMarginSensitive(2, False)
|
|
993
1104
|
self.SetFoldMarginColour(True, 'black')
|
|
994
1105
|
self.SetFoldMarginHiColour(True, 'black')
|
|
995
|
-
|
|
996
|
-
def OnMarginClick(self, evt):
|
|
1106
|
+
|
|
1107
|
+
def OnMarginClick(self, evt): #<wx._stc.StyledTextEvent>
|
|
997
1108
|
lc = self.LineFromPosition(evt.Position)
|
|
998
1109
|
level = self.GetFoldLevel(lc) ^ stc.STC_FOLDLEVELBASE
|
|
999
1110
|
## `level` indicates indent-level number
|
|
@@ -1004,8 +1115,8 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1004
1115
|
self.handler('select_lines', evt)
|
|
1005
1116
|
else:
|
|
1006
1117
|
self.handler('select_line', evt)
|
|
1007
|
-
|
|
1008
|
-
def OnMarginRClick(self, evt):
|
|
1118
|
+
|
|
1119
|
+
def OnMarginRClick(self, evt): #<wx._stc.StyledTextEvent>
|
|
1009
1120
|
"""Popup context menu."""
|
|
1010
1121
|
def _Icon(key):
|
|
1011
1122
|
return wx.ArtProvider.GetBitmap(key, size=(16,16))
|
|
@@ -1017,26 +1128,26 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1017
1128
|
(wx.ID_UP, "&Expand ALL", _Icon(wx.ART_PLUS),
|
|
1018
1129
|
lambda v: self.FoldAll(1)),
|
|
1019
1130
|
])
|
|
1020
|
-
|
|
1131
|
+
|
|
1021
1132
|
def toggle_fold(self, lc):
|
|
1022
1133
|
"""Similar to ToggleFold, but the top header containing
|
|
1023
1134
|
the specified line switches between expanded and contracted.
|
|
1024
1135
|
"""
|
|
1025
1136
|
while 1:
|
|
1026
|
-
la = self.GetFoldParent(lc)
|
|
1137
|
+
la = self.GetFoldParent(lc) # get folding root
|
|
1027
1138
|
if la == -1:
|
|
1028
1139
|
break
|
|
1029
1140
|
lc = la
|
|
1030
1141
|
self.ToggleFold(lc)
|
|
1031
|
-
self.
|
|
1142
|
+
self.ensureLineOnScreen(lc)
|
|
1032
1143
|
return lc
|
|
1033
|
-
|
|
1034
|
-
def
|
|
1144
|
+
|
|
1145
|
+
def get_indent_region(self, line):
|
|
1035
1146
|
"""Line numbers of folding head and tail containing the line."""
|
|
1036
1147
|
lc = line
|
|
1037
1148
|
le = lc + 1
|
|
1038
1149
|
while 1:
|
|
1039
|
-
la = self.GetFoldParent(lc)
|
|
1150
|
+
la = self.GetFoldParent(lc) # get folding root
|
|
1040
1151
|
if la == -1:
|
|
1041
1152
|
break
|
|
1042
1153
|
lc = la
|
|
@@ -1046,39 +1157,40 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1046
1157
|
break
|
|
1047
1158
|
le += 1
|
|
1048
1159
|
return lc, le
|
|
1049
|
-
|
|
1160
|
+
|
|
1050
1161
|
def on_linesel_begin(self, evt):
|
|
1051
1162
|
"""Called when a line of text selection begins."""
|
|
1052
|
-
self.cpos = self.anchor = evt.Position
|
|
1163
|
+
self.cpos = self.anchor = evt.Position #<select_line>
|
|
1053
1164
|
self.CaptureMouse()
|
|
1054
1165
|
evt.Skip()
|
|
1055
|
-
|
|
1166
|
+
|
|
1056
1167
|
def on_linesel_next(self, evt):
|
|
1057
1168
|
"""Called when next line of text selection begins."""
|
|
1058
|
-
self.cpos = evt.Position
|
|
1169
|
+
self.cpos = evt.Position #<select_lines>
|
|
1059
1170
|
self.CaptureMouse()
|
|
1060
1171
|
evt.Skip()
|
|
1061
|
-
|
|
1172
|
+
|
|
1062
1173
|
def on_linesel_motion(self, evt):
|
|
1063
1174
|
"""Called when a line of text selection is changing."""
|
|
1064
1175
|
self.cpos = self.PositionFromPoint(evt.Position)
|
|
1065
1176
|
self.EnsureCaretVisible()
|
|
1066
1177
|
evt.Skip()
|
|
1067
|
-
|
|
1178
|
+
|
|
1068
1179
|
def on_linesel_end(self, evt):
|
|
1069
1180
|
"""Called when a line of text selection ends."""
|
|
1070
1181
|
if self.HasCapture():
|
|
1071
1182
|
self.ReleaseMouse()
|
|
1072
1183
|
evt.Skip()
|
|
1073
|
-
|
|
1184
|
+
|
|
1074
1185
|
## --------------------------------
|
|
1075
|
-
## Preferences / Appearance
|
|
1186
|
+
## Preferences / Appearance.
|
|
1076
1187
|
## --------------------------------
|
|
1077
|
-
|
|
1188
|
+
|
|
1078
1189
|
def get_stylus(self):
|
|
1079
1190
|
return self.__stylus
|
|
1080
|
-
|
|
1191
|
+
|
|
1081
1192
|
def set_stylus(self, spec=None, **kwargs):
|
|
1193
|
+
"""Set style spec for wx.stc.StyleSetSpec."""
|
|
1082
1194
|
spec = spec and spec.copy() or {}
|
|
1083
1195
|
spec.update(kwargs)
|
|
1084
1196
|
if not spec:
|
|
@@ -1089,33 +1201,38 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1089
1201
|
def _map(sc):
|
|
1090
1202
|
return dict(kv.partition(':')[::2] for kv in sc.split(',') if kv)
|
|
1091
1203
|
|
|
1092
|
-
## Apply the default style first
|
|
1204
|
+
## Apply the default style first.
|
|
1093
1205
|
default = spec.pop(stc.STC_STYLE_DEFAULT, '')
|
|
1094
1206
|
if default:
|
|
1095
1207
|
self.StyleSetSpec(stc.STC_STYLE_DEFAULT, default)
|
|
1096
1208
|
self.StyleClearAll()
|
|
1097
1209
|
|
|
1098
|
-
|
|
1210
|
+
self.SetSelForeground(True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHTTEXT))
|
|
1211
|
+
self.SetSelBackground(True, wx.SystemSettings.GetColour(wx.SYS_COLOUR_HIGHLIGHT))
|
|
1212
|
+
|
|
1213
|
+
## Add style to the folding margin.
|
|
1099
1214
|
item = _map(spec.get(stc.STC_STYLE_LINENUMBER, ''))
|
|
1100
1215
|
if item:
|
|
1101
|
-
## Set colors used as a chequeboard pattern
|
|
1102
|
-
## lo (back) one of the colors
|
|
1103
|
-
## hi (fore) the other color
|
|
1216
|
+
## Set colors used as a chequeboard pattern.
|
|
1217
|
+
## - lo (back) one of the colors
|
|
1218
|
+
## - hi (fore) the other color
|
|
1104
1219
|
self.BackgroundColour = item.get('back')
|
|
1105
1220
|
self.ForegroundColour = item.get('fore')
|
|
1106
1221
|
if self.GetMarginWidth(2) > 1:
|
|
1107
|
-
## 12 pixel chequeboard, fore being default colour
|
|
1222
|
+
## 12 pixel chequeboard, fore being default colour.
|
|
1108
1223
|
self.SetFoldMarginColour(True, item.get('back'))
|
|
1109
1224
|
self.SetFoldMarginHiColour(True, 'light gray')
|
|
1110
1225
|
else:
|
|
1111
|
-
##
|
|
1226
|
+
## One pixel solid line, the same colour as the line number.
|
|
1112
1227
|
self.SetFoldMarginColour(True, item.get('fore'))
|
|
1113
1228
|
self.SetFoldMarginHiColour(True, item.get('fore'))
|
|
1114
1229
|
|
|
1115
|
-
|
|
1230
|
+
self.SetCaretLineVisible(0)
|
|
1231
|
+
self.SetCaretForeground(_map(default).get('fore'))
|
|
1232
|
+
|
|
1233
|
+
## Custom style for caret and line colour.
|
|
1116
1234
|
item = _map(spec.pop(stc.STC_STYLE_CARETLINE, ''))
|
|
1117
1235
|
if item:
|
|
1118
|
-
self.SetCaretLineVisible(0)
|
|
1119
1236
|
if 'fore' in item:
|
|
1120
1237
|
self.SetCaretForeground(item['fore'])
|
|
1121
1238
|
if 'back' in item:
|
|
@@ -1127,10 +1244,10 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1127
1244
|
if 'bold' in item:
|
|
1128
1245
|
self.SetCaretStyle(stc.STC_CARETSTYLE_BLOCK)
|
|
1129
1246
|
|
|
1130
|
-
## Apply the rest of the style
|
|
1247
|
+
## Apply the rest of the style.
|
|
1131
1248
|
for key, value in spec.items():
|
|
1132
1249
|
self.StyleSetSpec(key, value)
|
|
1133
|
-
|
|
1250
|
+
|
|
1134
1251
|
def match_paren(self):
|
|
1135
1252
|
self.SetIndicatorCurrent(3)
|
|
1136
1253
|
self.IndicatorClearRange(0, self.TextLength)
|
|
@@ -1138,7 +1255,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1138
1255
|
if self.get_char(p-1) in ")}]>":
|
|
1139
1256
|
q = self.BraceMatch(p-1)
|
|
1140
1257
|
if q != -1:
|
|
1141
|
-
self.BraceHighlight(q, p-1)
|
|
1258
|
+
self.BraceHighlight(q, p-1) # matched the preceding char
|
|
1142
1259
|
self.IndicatorFillRange(q, p-q)
|
|
1143
1260
|
return q
|
|
1144
1261
|
else:
|
|
@@ -1146,20 +1263,20 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1146
1263
|
elif self.get_char(p) in "({[<":
|
|
1147
1264
|
q = self.BraceMatch(p)
|
|
1148
1265
|
if q != -1:
|
|
1149
|
-
self.BraceHighlight(p, q)
|
|
1266
|
+
self.BraceHighlight(p, q) # matched the following char
|
|
1150
1267
|
self.IndicatorFillRange(p, q-p+1)
|
|
1151
1268
|
return q
|
|
1152
1269
|
else:
|
|
1153
1270
|
self.BraceBadLight(p)
|
|
1154
1271
|
else:
|
|
1155
|
-
self.BraceHighlight(-1,-1)
|
|
1156
|
-
|
|
1272
|
+
self.BraceHighlight(-1,-1) # no highlight
|
|
1273
|
+
|
|
1157
1274
|
def over(self, mode=1):
|
|
1158
1275
|
"""Set insert or overtype.
|
|
1159
1276
|
mode in {0:insert, 1:over, None:toggle}
|
|
1160
1277
|
"""
|
|
1161
1278
|
self.Overtype = mode if mode is not None else not self.Overtype
|
|
1162
|
-
|
|
1279
|
+
|
|
1163
1280
|
def wrap(self, mode=1):
|
|
1164
1281
|
"""Set whether text is word wrapped.
|
|
1165
1282
|
|
|
@@ -1167,78 +1284,141 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1167
1284
|
3:whitespace-wrap, None:toggle}
|
|
1168
1285
|
"""
|
|
1169
1286
|
self.WrapMode = mode if mode is not None else not self.WrapMode
|
|
1170
|
-
|
|
1287
|
+
|
|
1171
1288
|
def recenter(self, ln=None):
|
|
1172
1289
|
"""Scroll the cursor line to the center of screen.
|
|
1173
1290
|
If ln=0, the cursor moves to the top of the screen.
|
|
1174
1291
|
If ln=-1 (ln=n-1), moves to the bottom
|
|
1175
1292
|
"""
|
|
1176
|
-
n = self.LinesOnScreen()
|
|
1177
|
-
m = n//2 if ln is None else ln % n if ln < n else n
|
|
1293
|
+
n = self.LinesOnScreen() # lines completely visible
|
|
1294
|
+
m = n//2 if ln is None else ln % n if ln < n else n # ln[0:n]
|
|
1178
1295
|
vl = self._calc_vline(self.cline)
|
|
1179
1296
|
self.ScrollToLine(vl - m)
|
|
1180
|
-
|
|
1297
|
+
|
|
1181
1298
|
def _calc_vline(self, line):
|
|
1182
1299
|
"""Virtual line number in the buffer window."""
|
|
1183
1300
|
pos = self.PositionFromLine(line)
|
|
1184
1301
|
w, h = self.PointFromPosition(pos)
|
|
1185
|
-
return self.FirstVisibleLine + h//self.TextHeight(
|
|
1186
|
-
|
|
1187
|
-
def
|
|
1302
|
+
return self.FirstVisibleLine + h // self.TextHeight(line)
|
|
1303
|
+
|
|
1304
|
+
def ensureLineOnScreen(self, line):
|
|
1188
1305
|
"""Ensure a particular line is visible by scrolling the buffer
|
|
1189
1306
|
without expanding any header line hiding it.
|
|
1190
1307
|
"""
|
|
1191
|
-
n = self.LinesOnScreen()
|
|
1308
|
+
n = self.LinesOnScreen() # lines completely visible
|
|
1192
1309
|
hl = self.FirstVisibleLine
|
|
1193
1310
|
vl = self._calc_vline(line)
|
|
1194
1311
|
if vl < hl:
|
|
1195
1312
|
self.ScrollToLine(vl)
|
|
1196
1313
|
elif vl > hl + n - 1:
|
|
1197
1314
|
self.ScrollToLine(vl - n + 1)
|
|
1198
|
-
|
|
1199
|
-
def
|
|
1315
|
+
|
|
1316
|
+
def ensureLineMoreOnScreen(self, line, offset=0):
|
|
1200
1317
|
"""Ensure a particular line is visible by scrolling the buffer
|
|
1201
1318
|
without expanding any header line hiding it.
|
|
1202
1319
|
If the line is at the screen edge, recenter it.
|
|
1203
1320
|
"""
|
|
1204
|
-
n = self.LinesOnScreen()
|
|
1321
|
+
n = self.LinesOnScreen() # lines completely visible
|
|
1205
1322
|
hl = self.FirstVisibleLine
|
|
1206
1323
|
vl = self._calc_vline(line)
|
|
1207
1324
|
if not hl + offset < vl < hl + n - 1 - offset:
|
|
1208
1325
|
self.ScrollToLine(vl - n//2)
|
|
1209
|
-
|
|
1326
|
+
|
|
1210
1327
|
## --------------------------------
|
|
1211
|
-
## Search functions
|
|
1328
|
+
## Search functions.
|
|
1212
1329
|
## --------------------------------
|
|
1213
|
-
|
|
1330
|
+
|
|
1331
|
+
def DoFindNext(self, findData):
|
|
1332
|
+
"""Find the search text defined in `findData`.
|
|
1333
|
+
|
|
1334
|
+
If found, selects the matched text and scrolls to its line.
|
|
1335
|
+
Typically called from `wx.EVT_FIND` event handlers.
|
|
1336
|
+
|
|
1337
|
+
(override) Enables whole word search.
|
|
1338
|
+
Returns the match position if found, -1 otherwise.
|
|
1339
|
+
"""
|
|
1340
|
+
flags = 0
|
|
1341
|
+
if findData.Flags & wx.FR_MATCHCASE: flags |= stc.STC_FIND_MATCHCASE
|
|
1342
|
+
if findData.Flags & wx.FR_WHOLEWORD: flags |= stc.STC_FIND_WHOLEWORD
|
|
1343
|
+
self.SetSearchFlags(flags)
|
|
1344
|
+
|
|
1345
|
+
backward = not (findData.Flags & wx.FR_DOWN)
|
|
1346
|
+
findstring = findData.FindString.encode()
|
|
1347
|
+
if backward:
|
|
1348
|
+
self.TargetStart = self.anchor # backward anchor
|
|
1349
|
+
self.TargetEnd = 0
|
|
1350
|
+
else:
|
|
1351
|
+
self.TargetStart = self.cpos # forward anchor
|
|
1352
|
+
self.TargetEnd = self.TextLength
|
|
1353
|
+
loc = self.SearchInTarget(findstring)
|
|
1354
|
+
|
|
1355
|
+
## If it wasn't found then restart at beginning.
|
|
1356
|
+
if loc == -1:
|
|
1357
|
+
self.TargetStart = self.TextLength if backward else 0
|
|
1358
|
+
loc = self.SearchInTarget(findstring)
|
|
1359
|
+
|
|
1360
|
+
## Was it still not found?
|
|
1361
|
+
if loc == -1:
|
|
1362
|
+
# wx.MessageBox("Unable to find the search text any more.",
|
|
1363
|
+
# "Not found!", wx.OK|wx.ICON_INFORMATION)
|
|
1364
|
+
wx.Bell()
|
|
1365
|
+
pass
|
|
1366
|
+
else:
|
|
1367
|
+
self.SetSelection(loc, loc + len(findstring))
|
|
1368
|
+
self.EnsureVisible(self.cline) # expand if folded
|
|
1369
|
+
self.EnsureCaretVisible()
|
|
1370
|
+
return loc
|
|
1371
|
+
|
|
1372
|
+
def DoReplaceNext(self, findData):
|
|
1373
|
+
if self.SelectedText == findData.FindString:
|
|
1374
|
+
if self.CanEdit():
|
|
1375
|
+
self.ReplaceSelection(findData.ReplaceString)
|
|
1376
|
+
return self.DoFindNext(findData)
|
|
1377
|
+
|
|
1378
|
+
def DoReplaceAll(self, findData):
|
|
1379
|
+
with self.save_excursion():
|
|
1380
|
+
locs = [-1]
|
|
1381
|
+
count = 0
|
|
1382
|
+
while 1:
|
|
1383
|
+
loc = self.DoFindNext(findData)
|
|
1384
|
+
if loc in locs:
|
|
1385
|
+
break
|
|
1386
|
+
locs.append(loc)
|
|
1387
|
+
self.TargetStart = self.anchor
|
|
1388
|
+
self.TargetEnd = self.cpos
|
|
1389
|
+
if self.CanEdit():
|
|
1390
|
+
self.ReplaceTarget(findData.ReplaceString)
|
|
1391
|
+
count += 1
|
|
1392
|
+
return count
|
|
1393
|
+
|
|
1214
1394
|
def get_right_paren(self, p):
|
|
1215
|
-
if self.get_char(p) in "({[<":
|
|
1395
|
+
if self.get_char(p) in "({[<": # left-parentheses, <
|
|
1216
1396
|
q = self.BraceMatch(p)
|
|
1217
1397
|
return q if q < 0 else q+1
|
|
1218
|
-
|
|
1398
|
+
|
|
1219
1399
|
def get_left_paren(self, p):
|
|
1220
|
-
if self.get_char(p-1) in ")}]>":
|
|
1400
|
+
if self.get_char(p-1) in ")}]>": # right-parentheses, >
|
|
1221
1401
|
q = self.BraceMatch(p-1)
|
|
1222
1402
|
return q
|
|
1223
|
-
|
|
1403
|
+
|
|
1224
1404
|
def get_right_quotation(self, p):
|
|
1225
1405
|
st = self.get_style(p)
|
|
1226
1406
|
if st == 'moji':
|
|
1227
|
-
if self.get_style(p-1) == 'moji':
|
|
1407
|
+
if self.get_style(p-1) == 'moji': # inside string
|
|
1228
1408
|
return
|
|
1229
1409
|
while self.get_style(p) == st and p < self.TextLength:
|
|
1230
1410
|
p += 1
|
|
1231
1411
|
return p
|
|
1232
|
-
|
|
1412
|
+
|
|
1233
1413
|
def get_left_quotation(self, p):
|
|
1234
1414
|
st = self.get_style(p-1)
|
|
1235
1415
|
if st == 'moji':
|
|
1236
|
-
if self.get_style(p) == 'moji':
|
|
1416
|
+
if self.get_style(p) == 'moji': # inside string
|
|
1237
1417
|
return
|
|
1238
1418
|
while self.get_style(p-1) == st and p > 0:
|
|
1239
1419
|
p -= 1
|
|
1240
1420
|
return p
|
|
1241
|
-
|
|
1421
|
+
|
|
1242
1422
|
def get_following_atom(self, p):
|
|
1243
1423
|
q = p
|
|
1244
1424
|
st = self.get_style(p)
|
|
@@ -1246,15 +1426,15 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1246
1426
|
q = self.BraceMatch(p)
|
|
1247
1427
|
if q == -1:
|
|
1248
1428
|
q = self.TextLength
|
|
1249
|
-
st = None
|
|
1429
|
+
st = None # no closing paren
|
|
1250
1430
|
else:
|
|
1251
1431
|
q += 1
|
|
1252
|
-
st = 'paren'
|
|
1432
|
+
st = 'paren' # closed
|
|
1253
1433
|
else:
|
|
1254
1434
|
while self.get_style(q) == st and q < self.TextLength:
|
|
1255
1435
|
q += 1
|
|
1256
1436
|
return p, q, st
|
|
1257
|
-
|
|
1437
|
+
|
|
1258
1438
|
def get_preceding_atom(self, p):
|
|
1259
1439
|
q = p
|
|
1260
1440
|
st = self.get_style(p-1)
|
|
@@ -1262,14 +1442,14 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1262
1442
|
p = self.BraceMatch(p-1)
|
|
1263
1443
|
if p == -1:
|
|
1264
1444
|
p = 0
|
|
1265
|
-
st = None
|
|
1445
|
+
st = None # no closing paren
|
|
1266
1446
|
else:
|
|
1267
|
-
st = 'paren'
|
|
1447
|
+
st = 'paren' # closed
|
|
1268
1448
|
else:
|
|
1269
1449
|
while self.get_style(p-1) == st and p > 0:
|
|
1270
1450
|
p -= 1
|
|
1271
1451
|
return p, q, st
|
|
1272
|
-
|
|
1452
|
+
|
|
1273
1453
|
def grep_forward(self, pattern, flags=re.M):
|
|
1274
1454
|
orig = self.eol if (self.markline == self.cline) else self.cpos
|
|
1275
1455
|
text = self.GetTextRange(orig, self.TextLength)
|
|
@@ -1281,7 +1461,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1281
1461
|
self.mark = self.cpos
|
|
1282
1462
|
self.EnsureVisible(self.cline)
|
|
1283
1463
|
yield err
|
|
1284
|
-
|
|
1464
|
+
|
|
1285
1465
|
def grep_backward(self, pattern, flags=re.M):
|
|
1286
1466
|
text = self.GetTextRange(0, self.cpos)
|
|
1287
1467
|
errs = re.finditer(pattern, text, flags)
|
|
@@ -1292,47 +1472,43 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1292
1472
|
self.mark = self.cpos
|
|
1293
1473
|
self.EnsureVisible(self.cline)
|
|
1294
1474
|
yield err
|
|
1295
|
-
|
|
1475
|
+
|
|
1296
1476
|
def grep(self, pattern, flags=re.M):
|
|
1297
1477
|
yield from re.finditer(pattern.encode(), self.TextRaw, flags)
|
|
1298
|
-
|
|
1299
|
-
def
|
|
1300
|
-
"""Yields raw-positions where `text` is found."""
|
|
1301
|
-
word = text.encode()
|
|
1302
|
-
raw = self.TextRaw
|
|
1303
|
-
pos = -1
|
|
1304
|
-
while 1:
|
|
1305
|
-
pos = raw.find(word, pos+1)
|
|
1306
|
-
if pos < 0:
|
|
1307
|
-
break
|
|
1308
|
-
yield pos
|
|
1309
|
-
|
|
1310
|
-
def filter_text(self, text=None):
|
|
1478
|
+
|
|
1479
|
+
def filter_text(self):
|
|
1311
1480
|
"""Show indicators for the selected text."""
|
|
1312
1481
|
self.__itextlines = []
|
|
1313
1482
|
for i in (10, 11,):
|
|
1314
1483
|
self.SetIndicatorCurrent(i)
|
|
1315
1484
|
self.IndicatorClearRange(0, self.TextLength)
|
|
1316
|
-
|
|
1317
|
-
text = self.topic_at_caret
|
|
1485
|
+
text = self.topic_at_caret
|
|
1318
1486
|
if not text:
|
|
1319
1487
|
self.message("No words")
|
|
1320
1488
|
return
|
|
1321
|
-
|
|
1322
|
-
|
|
1489
|
+
wholeword = (not self.SelectedText # Enable or disable whole word search.
|
|
1490
|
+
and text.isascii()) # whole word search is for ascii only. (単語境界が不明確のため)
|
|
1491
|
+
pattern = re.escape(text)
|
|
1492
|
+
if wholeword:
|
|
1493
|
+
pattern = rf"\b{pattern}\b"
|
|
1323
1494
|
lines = []
|
|
1324
|
-
for
|
|
1495
|
+
for m in self.grep(pattern):
|
|
1496
|
+
p, q = m.span()
|
|
1325
1497
|
lines.append(self.LineFromPosition(p))
|
|
1326
1498
|
for i in (10, 11,):
|
|
1327
1499
|
self.SetIndicatorCurrent(i)
|
|
1328
|
-
self.IndicatorFillRange(p,
|
|
1329
|
-
self.__itextlines = sorted(set(lines))
|
|
1500
|
+
self.IndicatorFillRange(p, q-p)
|
|
1501
|
+
self.__itextlines = sorted(set(lines)) # keep order, no duplication
|
|
1330
1502
|
self.message("{}: {} found".format(text, len(lines)))
|
|
1331
1503
|
try:
|
|
1332
1504
|
self.TopLevelParent.findData.FindString = text
|
|
1505
|
+
if wholeword:
|
|
1506
|
+
self.TopLevelParent.findData.Flags |= wx.FR_WHOLEWORD
|
|
1507
|
+
else:
|
|
1508
|
+
self.TopLevelParent.findData.Flags &= ~wx.FR_WHOLEWORD
|
|
1333
1509
|
except AttributeError:
|
|
1334
1510
|
pass
|
|
1335
|
-
|
|
1511
|
+
|
|
1336
1512
|
def on_itext_enter(self, evt):
|
|
1337
1513
|
"""Called when entering filter_text mode."""
|
|
1338
1514
|
if not self.__itextlines:
|
|
@@ -1342,22 +1518,22 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1342
1518
|
def _format(ln):
|
|
1343
1519
|
return "{:4d} {}".format(ln+1, self.GetLine(ln).strip())
|
|
1344
1520
|
|
|
1345
|
-
pts = self.StyleGetSize(stc.STC_STYLE_DEFAULT)
|
|
1346
|
-
self.StyleSetSize(stc.STC_STYLE_DEFAULT, pts-1)
|
|
1521
|
+
# pts = self.StyleGetSize(stc.STC_STYLE_DEFAULT)
|
|
1522
|
+
# self.StyleSetSize(stc.STC_STYLE_DEFAULT, pts-1)
|
|
1347
1523
|
|
|
1348
1524
|
self.AutoCompSetSeparator(ord('\n'))
|
|
1349
1525
|
self.AutoCompShow(0, '\n'.join(map(_format, self.__itextlines)))
|
|
1350
1526
|
self.AutoCompSelect("{:4d}".format(self.cline+1))
|
|
1351
1527
|
self.Bind(stc.EVT_STC_AUTOCOMP_SELECTION, self.on_itext_selection)
|
|
1352
1528
|
|
|
1353
|
-
self.StyleSetSize(stc.STC_STYLE_DEFAULT, pts)
|
|
1354
|
-
|
|
1529
|
+
# self.StyleSetSize(stc.STC_STYLE_DEFAULT, pts)
|
|
1530
|
+
|
|
1355
1531
|
def on_itext_exit(self, evt):
|
|
1356
1532
|
"""Called when exiting filter_text mode."""
|
|
1357
1533
|
if self.AutoCompActive():
|
|
1358
1534
|
self.AutoCompCancel()
|
|
1359
1535
|
self.Unbind(stc.EVT_STC_AUTOCOMP_SELECTION, handler=self.on_itext_selection)
|
|
1360
|
-
|
|
1536
|
+
|
|
1361
1537
|
def on_itext_selection(self, evt):
|
|
1362
1538
|
"""Called when filter_text is selected."""
|
|
1363
1539
|
i = self.AutoCompGetCurrent()
|
|
@@ -1365,15 +1541,15 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1365
1541
|
evt.Skip()
|
|
1366
1542
|
return
|
|
1367
1543
|
line = self.__itextlines[i]
|
|
1368
|
-
self.EnsureVisible(line)
|
|
1544
|
+
self.EnsureVisible(line) # expand if folded
|
|
1369
1545
|
self.goto_line(line)
|
|
1370
1546
|
self.recenter()
|
|
1371
1547
|
self.on_itext_exit(evt)
|
|
1372
|
-
|
|
1548
|
+
|
|
1373
1549
|
## --------------------------------
|
|
1374
1550
|
## goto / skip / selection / etc.
|
|
1375
1551
|
## --------------------------------
|
|
1376
|
-
|
|
1552
|
+
|
|
1377
1553
|
def goto_char(self, pos, selection=False, interactive=False):
|
|
1378
1554
|
"""Goto char position with selection."""
|
|
1379
1555
|
if pos is None or pos < 0:
|
|
@@ -1388,7 +1564,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1388
1564
|
|
|
1389
1565
|
if interactive:
|
|
1390
1566
|
## Update the caret position/status manually.
|
|
1391
|
-
## To update caret status, shake L/R w/o modifier
|
|
1567
|
+
## To update caret status, shake L/R w/o modifier.
|
|
1392
1568
|
## Don't do this if selection is active.
|
|
1393
1569
|
vk = wx.UIActionSimulator()
|
|
1394
1570
|
modkeys = [k for k in (wx.WXK_CONTROL, wx.WXK_ALT, wx.WXK_SHIFT)
|
|
@@ -1402,74 +1578,74 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1402
1578
|
vk.KeyDown(wx.WXK_LEFT)
|
|
1403
1579
|
vk.KeyDown(wx.WXK_RIGHT)
|
|
1404
1580
|
for k in modkeys:
|
|
1405
|
-
vk.KeyDown(k)
|
|
1581
|
+
vk.KeyDown(k) # restore modifier key state
|
|
1406
1582
|
return True
|
|
1407
|
-
|
|
1583
|
+
|
|
1408
1584
|
def goto_line(self, ln, selection=False):
|
|
1409
1585
|
"""Goto line with selection."""
|
|
1410
1586
|
if ln is None or ln < 0:
|
|
1411
1587
|
return
|
|
1412
|
-
|
|
1413
|
-
|
|
1588
|
+
# if ln < 0:
|
|
1589
|
+
# ln += self.LineCount
|
|
1414
1590
|
if selection:
|
|
1415
1591
|
self.cline = ln
|
|
1416
1592
|
else:
|
|
1417
1593
|
self.GotoLine(ln)
|
|
1418
1594
|
return True
|
|
1419
|
-
|
|
1595
|
+
|
|
1420
1596
|
def skip_chars_forward(self, chars):
|
|
1421
1597
|
p = self.cpos
|
|
1422
1598
|
while self.get_char(p) in chars and p < self.TextLength:
|
|
1423
1599
|
p += 1
|
|
1424
1600
|
self.goto_char(p)
|
|
1425
|
-
|
|
1601
|
+
|
|
1426
1602
|
def skip_chars_backward(self, chars):
|
|
1427
1603
|
p = self.cpos
|
|
1428
1604
|
while self.get_char(p-1) in chars and p > 0:
|
|
1429
1605
|
p -= 1
|
|
1430
1606
|
self.goto_char(p)
|
|
1431
|
-
|
|
1607
|
+
|
|
1432
1608
|
def back_to_indentation(self):
|
|
1433
|
-
text = self.line_at_caret
|
|
1434
|
-
lstr = text.lstrip()
|
|
1609
|
+
text = self.line_at_caret # w/ no-prompt
|
|
1610
|
+
lstr = text.lstrip() # w/ no-indent
|
|
1435
1611
|
p = self.bol + len(text) - len(lstr)
|
|
1436
1612
|
self.goto_char(p, interactive=True)
|
|
1437
1613
|
self.ScrollToColumn(0)
|
|
1438
|
-
|
|
1614
|
+
|
|
1439
1615
|
def beginning_of_line(self):
|
|
1440
1616
|
self.goto_char(self.bol, interactive=True)
|
|
1441
1617
|
self.ScrollToColumn(0)
|
|
1442
|
-
|
|
1618
|
+
|
|
1443
1619
|
def end_of_line(self):
|
|
1444
1620
|
self.goto_char(self.eol, interactive=True)
|
|
1445
|
-
|
|
1621
|
+
|
|
1446
1622
|
def beginning_of_buffer(self):
|
|
1447
1623
|
self.mark = self.cpos
|
|
1448
1624
|
self.goto_char(0, interactive=True)
|
|
1449
|
-
|
|
1625
|
+
|
|
1450
1626
|
def end_of_buffer(self):
|
|
1451
1627
|
self.mark = self.cpos
|
|
1452
1628
|
self.goto_char(self.TextLength, interactive=True)
|
|
1453
|
-
|
|
1629
|
+
|
|
1454
1630
|
def goto_matched_paren(self):
|
|
1455
1631
|
p = self.cpos
|
|
1456
1632
|
return (self.goto_char(self.get_left_paren(p))
|
|
1457
1633
|
or self.goto_char(self.get_right_paren(p))
|
|
1458
1634
|
or self.goto_char(self.get_left_quotation(p))
|
|
1459
1635
|
or self.goto_char(self.get_right_quotation(p)))
|
|
1460
|
-
|
|
1636
|
+
|
|
1461
1637
|
def selection_forward_word_or_paren(self):
|
|
1462
1638
|
p = self.cpos
|
|
1463
1639
|
return (self.goto_char(self.get_right_paren(p), selection=True)
|
|
1464
1640
|
or self.goto_char(self.get_right_quotation(p), selection=True)
|
|
1465
1641
|
or self.WordRightEndExtend())
|
|
1466
|
-
|
|
1642
|
+
|
|
1467
1643
|
def selection_backward_word_or_paren(self):
|
|
1468
1644
|
p = self.cpos
|
|
1469
1645
|
return (self.goto_char(self.get_left_paren(p), selection=True)
|
|
1470
1646
|
or self.goto_char(self.get_left_quotation(p), selection=True)
|
|
1471
1647
|
or self.WordLeftExtend())
|
|
1472
|
-
|
|
1648
|
+
|
|
1473
1649
|
@contextmanager
|
|
1474
1650
|
def save_excursion(self):
|
|
1475
1651
|
"""Save buffer excursion."""
|
|
@@ -1482,7 +1658,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1482
1658
|
self.GotoPos(p)
|
|
1483
1659
|
self.ScrollToLine(vpos)
|
|
1484
1660
|
self.SetXOffset(hpos)
|
|
1485
|
-
|
|
1661
|
+
|
|
1486
1662
|
@contextmanager
|
|
1487
1663
|
def pre_selection(self):
|
|
1488
1664
|
"""Save buffer cpos and anchor."""
|
|
@@ -1495,7 +1671,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1495
1671
|
self.cpos = p
|
|
1496
1672
|
else:
|
|
1497
1673
|
self.anchor = q
|
|
1498
|
-
|
|
1674
|
+
|
|
1499
1675
|
@contextmanager
|
|
1500
1676
|
def save_attributes(self, **kwargs):
|
|
1501
1677
|
"""Save buffer attributes (e.g. ReadOnly=False)."""
|
|
@@ -1507,50 +1683,48 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1507
1683
|
finally:
|
|
1508
1684
|
for k, v in kwargs.items():
|
|
1509
1685
|
setattr(self, k, v)
|
|
1510
|
-
|
|
1686
|
+
|
|
1511
1687
|
def off_readonly(self):
|
|
1512
1688
|
"""Disables buffer read-only lock temporarily."""
|
|
1513
1689
|
return self.save_attributes(ReadOnly=False)
|
|
1514
|
-
|
|
1690
|
+
|
|
1515
1691
|
def off_undocollection(self):
|
|
1516
1692
|
"""Disables buffer undo stack temporarily."""
|
|
1517
1693
|
return self.save_attributes(UndoCollection=False)
|
|
1518
|
-
|
|
1694
|
+
|
|
1519
1695
|
## --------------------------------
|
|
1520
|
-
## Edit: comment / insert / kill
|
|
1696
|
+
## Edit: comment / insert / kill.
|
|
1521
1697
|
## --------------------------------
|
|
1522
|
-
comment_prefix = "
|
|
1523
|
-
|
|
1524
|
-
@
|
|
1698
|
+
comment_prefix = "#"
|
|
1699
|
+
|
|
1700
|
+
@editable
|
|
1525
1701
|
def comment_out_selection(self, from_=None, to_=None):
|
|
1526
1702
|
"""Comment out the selected text."""
|
|
1527
1703
|
if from_ is not None: self.anchor = from_
|
|
1528
1704
|
if to_ is not None: self.cpos = to_
|
|
1529
|
-
prefix = self.comment_prefix
|
|
1530
1705
|
with self.pre_selection():
|
|
1531
|
-
text = re.sub("^",
|
|
1532
|
-
## Don't comment out the last (blank) line.
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
text = text[:-len(prefix)]
|
|
1706
|
+
text = re.sub("^", self.comment_prefix + ' ', self.SelectedText, flags=re.M)
|
|
1707
|
+
## Don't comment out the last (blank) line in a multiline selection.
|
|
1708
|
+
if '\n' in text:
|
|
1709
|
+
text = text.rstrip(self.comment_prefix + ' ')
|
|
1536
1710
|
self.ReplaceSelection(text)
|
|
1537
|
-
|
|
1538
|
-
@
|
|
1711
|
+
|
|
1712
|
+
@editable
|
|
1539
1713
|
def uncomment_selection(self, from_=None, to_=None):
|
|
1540
1714
|
"""Uncomment the selected text."""
|
|
1541
1715
|
if from_ is not None: self.anchor = from_
|
|
1542
1716
|
if to_ is not None: self.cpos = to_
|
|
1543
1717
|
with self.pre_selection():
|
|
1544
|
-
text = re.sub("
|
|
1718
|
+
text = re.sub(f"^{self.comment_prefix}+ ", "", self.SelectedText, flags=re.M)
|
|
1545
1719
|
if text != self.SelectedText:
|
|
1546
1720
|
self.ReplaceSelection(text)
|
|
1547
|
-
|
|
1548
|
-
@
|
|
1721
|
+
|
|
1722
|
+
@editable
|
|
1549
1723
|
def comment_out_line(self):
|
|
1550
1724
|
if self.SelectedText:
|
|
1551
1725
|
self.comment_out_selection()
|
|
1552
1726
|
else:
|
|
1553
|
-
##
|
|
1727
|
+
## Align with current or previous indent position.
|
|
1554
1728
|
self.back_to_indentation()
|
|
1555
1729
|
text = self.GetLine(self.cline - 1)
|
|
1556
1730
|
lstr, j = self.py_strip_indents(text)
|
|
@@ -1560,8 +1734,8 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1560
1734
|
self.goto_char(self.bol + min(j, k))
|
|
1561
1735
|
self.comment_out_selection(self.cpos, self.eol)
|
|
1562
1736
|
self.LineDown()
|
|
1563
|
-
|
|
1564
|
-
@
|
|
1737
|
+
|
|
1738
|
+
@editable
|
|
1565
1739
|
def uncomment_line(self):
|
|
1566
1740
|
if self.SelectedText:
|
|
1567
1741
|
self.uncomment_selection()
|
|
@@ -1569,36 +1743,58 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1569
1743
|
self.back_to_indentation()
|
|
1570
1744
|
self.uncomment_selection(self.cpos, self.eol)
|
|
1571
1745
|
self.LineDown()
|
|
1572
|
-
|
|
1573
|
-
@
|
|
1746
|
+
|
|
1747
|
+
@editable
|
|
1574
1748
|
def eat_white_forward(self):
|
|
1575
1749
|
p = self.cpos
|
|
1576
1750
|
self.skip_chars_forward(' \t')
|
|
1577
1751
|
self.Replace(p, self.cpos, '')
|
|
1578
|
-
|
|
1579
|
-
@
|
|
1752
|
+
|
|
1753
|
+
@editable
|
|
1580
1754
|
def eat_white_backward(self):
|
|
1581
1755
|
p = self.cpos
|
|
1582
1756
|
self.skip_chars_backward(' \t')
|
|
1583
1757
|
self.Replace(max(self.cpos, self.bol), p, '')
|
|
1584
|
-
|
|
1585
|
-
@
|
|
1758
|
+
|
|
1759
|
+
@editable
|
|
1760
|
+
def kill_word(self):
|
|
1761
|
+
if not self.SelectedText:
|
|
1762
|
+
self.WordRightEndExtend()
|
|
1763
|
+
self.ReplaceSelection('')
|
|
1764
|
+
|
|
1765
|
+
@editable
|
|
1766
|
+
def backward_kill_word(self):
|
|
1767
|
+
if not self.SelectedText:
|
|
1768
|
+
self.WordLeftExtend()
|
|
1769
|
+
self.ReplaceSelection('')
|
|
1770
|
+
|
|
1771
|
+
@editable
|
|
1586
1772
|
def kill_line(self):
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
if
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1773
|
+
if not self.SelectedText:
|
|
1774
|
+
p = self.cpos
|
|
1775
|
+
if p == self.eol:
|
|
1776
|
+
## self.WordRightEndExtend() # Select cr/lf chunks.
|
|
1777
|
+
if self.get_char(p) == '\r': p += 1
|
|
1778
|
+
if self.get_char(p) == '\n': p += 1
|
|
1779
|
+
self.cpos = p
|
|
1780
|
+
else:
|
|
1781
|
+
self.cpos = self.eol
|
|
1782
|
+
self.ReplaceSelection('')
|
|
1783
|
+
|
|
1784
|
+
@editable
|
|
1594
1785
|
def backward_kill_line(self):
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
if
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1786
|
+
if not self.SelectedText:
|
|
1787
|
+
p = self.cpos
|
|
1788
|
+
if p == self.bol:
|
|
1789
|
+
## self.WordLeftExtend() # Select cr/lf chunks.
|
|
1790
|
+
if self.get_char(p-1) == '\n': p -= 1
|
|
1791
|
+
if self.get_char(p-1) == '\r': p -= 1
|
|
1792
|
+
self.cpos = p
|
|
1793
|
+
else:
|
|
1794
|
+
self.cpos = self.bol
|
|
1795
|
+
self.ReplaceSelection('')
|
|
1796
|
+
|
|
1797
|
+
@editable
|
|
1602
1798
|
def insert_space_like_tab(self):
|
|
1603
1799
|
"""Insert half-width spaces forward as if feeling like [tab].
|
|
1604
1800
|
タブの気持ちになって半角スペースを入力する
|
|
@@ -1606,15 +1802,15 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1606
1802
|
self.eat_white_forward()
|
|
1607
1803
|
_text, lp = self.CurLine
|
|
1608
1804
|
self.WriteText(' ' * (4 - lp % 4))
|
|
1609
|
-
|
|
1610
|
-
@
|
|
1805
|
+
|
|
1806
|
+
@editable
|
|
1611
1807
|
def delete_backward_space_like_tab(self):
|
|
1612
1808
|
"""Delete half-width spaces backward as if feeling like [S-tab].
|
|
1613
1809
|
シフト+タブの気持ちになって半角スペースを消す
|
|
1614
1810
|
"""
|
|
1615
1811
|
self.eat_white_forward()
|
|
1616
1812
|
_text, lp = self.CurLine
|
|
1617
|
-
for
|
|
1813
|
+
for _i in range(lp % 4 or 4):
|
|
1618
1814
|
p = self.cpos
|
|
1619
1815
|
if p == self.bol or self.get_char(p-1) != ' ':
|
|
1620
1816
|
break
|
|
@@ -1624,78 +1820,71 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
1624
1820
|
|
|
1625
1821
|
class Buffer(EditorInterface, EditWindow):
|
|
1626
1822
|
"""Python code buffer.
|
|
1627
|
-
|
|
1628
|
-
Attributes:
|
|
1629
|
-
name : buffer-name (basename)
|
|
1630
|
-
filename : buffer-file-name
|
|
1631
|
-
code : code object
|
|
1632
1823
|
"""
|
|
1633
|
-
STYLE = {
|
|
1634
|
-
stc.STC_STYLE_DEFAULT : "fore:#7f7f7f,back:#ffffb8,size:9,face:MS Gothic",
|
|
1635
|
-
stc.STC_STYLE_LINENUMBER : "fore:#000000,back:#ffffb8,size:9",
|
|
1636
|
-
stc.STC_STYLE_BRACELIGHT : "fore:#000000,back:#ffffb8,bold",
|
|
1637
|
-
stc.STC_STYLE_BRACEBAD : "fore:#000000,back:#ff0000,bold",
|
|
1638
|
-
stc.STC_STYLE_CONTROLCHAR : "size:6",
|
|
1639
|
-
stc.STC_STYLE_CARETLINE : "fore:#000000,back:#ffff7f,size:2", # optional
|
|
1640
|
-
stc.STC_STYLE_ANNOTATION : "fore:#7f0000,back:#ff7f7f", # optional
|
|
1641
|
-
stc.STC_P_DEFAULT : "fore:#000000",
|
|
1642
|
-
stc.STC_P_OPERATOR : "fore:#000000",
|
|
1643
|
-
stc.STC_P_IDENTIFIER : "fore:#000000",
|
|
1644
|
-
stc.STC_P_COMMENTLINE : "fore:#007f7f,back:#ffcfcf",
|
|
1645
|
-
stc.STC_P_COMMENTBLOCK : "fore:#007f7f,back:#ffcfcf,eol",
|
|
1646
|
-
stc.STC_P_NUMBER : "fore:#7f0000",
|
|
1647
|
-
stc.STC_P_STRINGEOL : "fore:#000000,back:#ffcfcf",
|
|
1648
|
-
stc.STC_P_CHARACTER : "fore:#7f7f7f",
|
|
1649
|
-
stc.STC_P_STRING : "fore:#7f7f7f",
|
|
1650
|
-
stc.STC_P_TRIPLE : "fore:#7f7f7f",
|
|
1651
|
-
stc.STC_P_TRIPLEDOUBLE : "fore:#7f7f7f",
|
|
1652
|
-
stc.STC_P_CLASSNAME : "fore:#7f00ff,bold",
|
|
1653
|
-
stc.STC_P_DEFNAME : "fore:#0000ff,bold",
|
|
1654
|
-
stc.STC_P_WORD : "fore:#0000ff",
|
|
1655
|
-
stc.STC_P_WORD2 : "fore:#b8007f",
|
|
1656
|
-
stc.STC_P_DECORATOR : "fore:#e08040",
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
1824
|
@property
|
|
1660
1825
|
def message(self):
|
|
1661
1826
|
return self.parent.message
|
|
1662
|
-
|
|
1827
|
+
|
|
1663
1828
|
@property
|
|
1664
1829
|
def name(self):
|
|
1830
|
+
"""buffer-name (basename)."""
|
|
1665
1831
|
return os.path.basename(self.__filename or '')
|
|
1666
|
-
|
|
1667
|
-
Name = name
|
|
1668
|
-
|
|
1832
|
+
|
|
1833
|
+
Name = name # page.window.Name for save/loadPerspective
|
|
1834
|
+
|
|
1669
1835
|
@property
|
|
1670
1836
|
def filename(self):
|
|
1837
|
+
"""buffer-file-name."""
|
|
1671
1838
|
return self.__filename
|
|
1672
|
-
|
|
1839
|
+
|
|
1673
1840
|
def update_filestamp(self, fn):
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1841
|
+
self.__path = Path(fn)
|
|
1842
|
+
try:
|
|
1843
|
+
self.__mtime = os.path.getmtime(self.__path) # update timestamp (modified time)
|
|
1844
|
+
except FileNotFoundError:
|
|
1845
|
+
self.__mtime = False # valid path (but not found)
|
|
1846
|
+
except OSError:
|
|
1847
|
+
if re.match(url_re, fn):
|
|
1848
|
+
self.__mtime = -1
|
|
1849
|
+
else:
|
|
1850
|
+
self.__mtime = None # *invalid path*
|
|
1678
1851
|
if self.__filename != fn:
|
|
1679
1852
|
self.__filename = fn
|
|
1680
1853
|
self.update_caption()
|
|
1681
|
-
|
|
1854
|
+
|
|
1682
1855
|
@property
|
|
1683
1856
|
def mtdelta(self):
|
|
1684
1857
|
"""Timestamp delta (for checking external mod).
|
|
1685
1858
|
|
|
1686
1859
|
Returns:
|
|
1687
|
-
None
|
|
1688
|
-
= 0
|
|
1689
|
-
> 0
|
|
1690
|
-
< 0
|
|
1860
|
+
None: no file
|
|
1861
|
+
= 0: a file (even if not found)
|
|
1862
|
+
> 0: a file edited externally
|
|
1863
|
+
< 0: a url file
|
|
1691
1864
|
"""
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
return
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1865
|
+
try:
|
|
1866
|
+
mtime = os.path.getmtime(self.__path)
|
|
1867
|
+
return mtime - self.__mtime
|
|
1868
|
+
except FileNotFoundError:
|
|
1869
|
+
self.__mtime = False # valid path (but not found)
|
|
1870
|
+
except OSError:
|
|
1871
|
+
pass
|
|
1872
|
+
return self.__mtime
|
|
1873
|
+
|
|
1874
|
+
@property
|
|
1875
|
+
def need_buffer_save(self):
|
|
1876
|
+
"""Return whether the buffer should be saved.
|
|
1877
|
+
The file has been modified internally.
|
|
1878
|
+
"""
|
|
1879
|
+
return self.mtdelta is not None and self.IsModified()
|
|
1880
|
+
|
|
1881
|
+
@property
|
|
1882
|
+
def need_buffer_load(self):
|
|
1883
|
+
"""Return whether the buffer should be loaded.
|
|
1884
|
+
The file has been modified externally.
|
|
1885
|
+
"""
|
|
1886
|
+
return self.mtdelta is not None and self.mtdelta > 0
|
|
1887
|
+
|
|
1699
1888
|
@property
|
|
1700
1889
|
def caption_prefix(self):
|
|
1701
1890
|
prefix = ''
|
|
@@ -1710,7 +1899,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1710
1899
|
if prefix:
|
|
1711
1900
|
prefix += ' '
|
|
1712
1901
|
return prefix
|
|
1713
|
-
|
|
1902
|
+
|
|
1714
1903
|
def update_caption(self):
|
|
1715
1904
|
caption = self.caption_prefix + self.name
|
|
1716
1905
|
try:
|
|
@@ -1718,31 +1907,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1718
1907
|
self.parent.handler('buffer_caption_updated', self)
|
|
1719
1908
|
except AttributeError:
|
|
1720
1909
|
pass
|
|
1721
|
-
|
|
1722
|
-
@property
|
|
1723
|
-
def need_buffer_save(self):
|
|
1724
|
-
"""Returns whether the buffer should be saved.
|
|
1725
|
-
The file has been modified internally.
|
|
1726
|
-
"""
|
|
1727
|
-
return self.mtdelta is not None and self.IsModified()
|
|
1728
|
-
|
|
1729
|
-
@property
|
|
1730
|
-
def need_buffer_load(self):
|
|
1731
|
-
"""Returns whether the buffer should be loaded.
|
|
1732
|
-
The file has been modified externally.
|
|
1733
|
-
"""
|
|
1734
|
-
return self.mtdelta is not None and self.mtdelta > 0
|
|
1735
|
-
|
|
1736
|
-
def pre_command_hook(self, evt):
|
|
1737
|
-
self.parent.handler(self.handler.current_event, evt)
|
|
1738
|
-
return EditorInterface.pre_command_hook(self, evt)
|
|
1739
|
-
pre_command_hook.__name__ = str('pre_command_dispatch') # alias
|
|
1740
|
-
|
|
1741
|
-
def post_command_hook(self, evt):
|
|
1742
|
-
self.parent.handler(self.handler.current_event, evt)
|
|
1743
|
-
return EditorInterface.post_command_hook(self, evt)
|
|
1744
|
-
post_command_hook.__name__ = str('post_command_dispatch') # alias
|
|
1745
|
-
|
|
1910
|
+
|
|
1746
1911
|
def __init__(self, parent, filename, **kwargs):
|
|
1747
1912
|
EditWindow.__init__(self, parent, **kwargs)
|
|
1748
1913
|
EditorInterface.__init__(self)
|
|
@@ -1752,7 +1917,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1752
1917
|
self.update_filestamp(filename)
|
|
1753
1918
|
self.code = None
|
|
1754
1919
|
|
|
1755
|
-
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate)
|
|
1920
|
+
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate) # skip to brace matching
|
|
1756
1921
|
self.Bind(stc.EVT_STC_CALLTIP_CLICK, self.OnCallTipClick)
|
|
1757
1922
|
self.Bind(stc.EVT_STC_INDICATOR_CLICK, self.OnIndicatorClick)
|
|
1758
1923
|
|
|
@@ -1773,19 +1938,10 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1773
1938
|
|
|
1774
1939
|
def clear(evt):
|
|
1775
1940
|
## """Clear selection and message, no skip."""
|
|
1776
|
-
## *
|
|
1941
|
+
## *DO NOT* clear autocomp, so that the event can skip to AutoComp properly.
|
|
1777
1942
|
if self.CanEdit():
|
|
1778
1943
|
with self.off_undocollection():
|
|
1779
|
-
self.ReplaceSelection(
|
|
1780
|
-
self.message("")
|
|
1781
|
-
|
|
1782
|
-
def clear_autocomp(evt):
|
|
1783
|
-
## """Clear autocomp, selection, and message."""
|
|
1784
|
-
if self.AutoCompActive():
|
|
1785
|
-
self.AutoCompCancel()
|
|
1786
|
-
if self.CanEdit():
|
|
1787
|
-
with self.off_undocollection():
|
|
1788
|
-
self.ReplaceSelection("")
|
|
1944
|
+
self.ReplaceSelection('')
|
|
1789
1945
|
self.message("")
|
|
1790
1946
|
|
|
1791
1947
|
def fork(evt):
|
|
@@ -1795,16 +1951,16 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1795
1951
|
"""Fork events to the parent."""
|
|
1796
1952
|
self.parent.handler(self.handler.current_event, evt)
|
|
1797
1953
|
|
|
1798
|
-
## Note:
|
|
1954
|
+
## Note: Mouse events are not propagated from Buffer to EditorBook.
|
|
1799
1955
|
## They are explicitly dispatched from buffer.handler to editor.handler.
|
|
1800
1956
|
|
|
1801
1957
|
self.handler.update({ # DNA<Buffer>
|
|
1802
1958
|
None : {
|
|
1803
1959
|
'buffer_saved' : [ None, dispatch ],
|
|
1804
1960
|
'buffer_loaded' : [ None, dispatch ],
|
|
1805
|
-
'buffer_modified' : [ None, dispatch, self.
|
|
1806
|
-
'buffer_activated' : [ None, dispatch, self.
|
|
1807
|
-
'buffer_inactivated' : [ None, dispatch, self.
|
|
1961
|
+
'buffer_modified' : [ None, dispatch, self.on_buffer_modified ],
|
|
1962
|
+
'buffer_activated' : [ None, dispatch, self.on_buffer_activated ],
|
|
1963
|
+
'buffer_inactivated' : [ None, dispatch, self.on_buffer_inactivated ],
|
|
1808
1964
|
'buffer_region_executed' : [ None, dispatch ],
|
|
1809
1965
|
},
|
|
1810
1966
|
-1 : { # original action of the EditWindow
|
|
@@ -1815,20 +1971,24 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1815
1971
|
'*[LR]win pressed' : (-1, ),
|
|
1816
1972
|
},
|
|
1817
1973
|
0 : { # Normal mode
|
|
1818
|
-
'* pressed' : (0, skip
|
|
1974
|
+
'* pressed' : (0, skip),
|
|
1819
1975
|
'* released' : (0, skip, dispatch),
|
|
1976
|
+
'*button* pressed' : (0, skip, dispatch),
|
|
1977
|
+
'Lbutton pressed' : (0, self.on_left_down),
|
|
1820
1978
|
'escape pressed' : (-1, self.on_enter_escmap),
|
|
1821
1979
|
'C-h pressed' : (0, self.call_helpTip),
|
|
1822
1980
|
'. pressed' : (2, self.OnEnterDot),
|
|
1981
|
+
'C-. pressed' : (2, self.call_word_autocomp),
|
|
1982
|
+
'C-/ pressed' : (3, self.call_apropos_autocomp),
|
|
1823
1983
|
'M-. pressed' : (2, self.call_word_autocomp),
|
|
1824
1984
|
'M-/ pressed' : (3, self.call_apropos_autocomp),
|
|
1825
1985
|
},
|
|
1826
1986
|
2 : { # word auto completion AS-mode
|
|
1827
|
-
'quit' : (0, clear_autocomp),
|
|
1828
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
1987
|
+
'quit' : (0, self.clear_autocomp),
|
|
1988
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
1829
1989
|
'tab pressed' : (0, clear, skip),
|
|
1830
1990
|
'enter pressed' : (0, clear, skip),
|
|
1831
|
-
'escape pressed' : (0,
|
|
1991
|
+
'escape pressed' : (0, clear, skip),
|
|
1832
1992
|
'up pressed' : (2, skip, self.on_completion_backward),
|
|
1833
1993
|
'down pressed' : (2, skip, self.on_completion_forward),
|
|
1834
1994
|
'*left pressed' : (2, skip),
|
|
@@ -1843,7 +2003,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1843
2003
|
'*delete pressed' : (2, skip),
|
|
1844
2004
|
'*backspace pressed' : (2, skip),
|
|
1845
2005
|
'*backspace released' : (2, self.call_word_autocomp),
|
|
1846
|
-
|
|
2006
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
1847
2007
|
'*alt pressed' : (2, ),
|
|
1848
2008
|
'*ctrl pressed' : (2, ),
|
|
1849
2009
|
'*shift pressed' : (2, ),
|
|
@@ -1851,11 +2011,11 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1851
2011
|
'*f[0-9]* pressed' : (2, ),
|
|
1852
2012
|
},
|
|
1853
2013
|
3 : { # apropos auto completion AS-mode
|
|
1854
|
-
'quit' : (0, clear_autocomp),
|
|
1855
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
2014
|
+
'quit' : (0, self.clear_autocomp),
|
|
2015
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
1856
2016
|
'tab pressed' : (0, clear, skip),
|
|
1857
2017
|
'enter pressed' : (0, clear, skip),
|
|
1858
|
-
'escape pressed' : (0,
|
|
2018
|
+
'escape pressed' : (0, clear, skip),
|
|
1859
2019
|
'up pressed' : (3, skip, self.on_completion_backward),
|
|
1860
2020
|
'down pressed' : (3, skip, self.on_completion_forward),
|
|
1861
2021
|
'*left pressed' : (3, skip),
|
|
@@ -1870,7 +2030,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1870
2030
|
'*delete pressed' : (3, skip),
|
|
1871
2031
|
'*backspace pressed' : (3, skip),
|
|
1872
2032
|
'*backspace released' : (3, self.call_apropos_autocomp),
|
|
1873
|
-
|
|
2033
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
1874
2034
|
'*alt pressed' : (3, ),
|
|
1875
2035
|
'*ctrl pressed' : (3, ),
|
|
1876
2036
|
'*shift pressed' : (3, ),
|
|
@@ -1880,104 +2040,119 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1880
2040
|
})
|
|
1881
2041
|
|
|
1882
2042
|
self.show_folder()
|
|
1883
|
-
self.set_stylus(
|
|
1884
|
-
|
|
2043
|
+
self.set_stylus(Stylus.py_text_mode)
|
|
2044
|
+
|
|
1885
2045
|
def __contains__(self, code):
|
|
1886
2046
|
if inspect.iscode(code) and self.code:
|
|
1887
2047
|
return code is self.code\
|
|
1888
2048
|
or code in self.code.co_consts
|
|
1889
|
-
|
|
2049
|
+
|
|
1890
2050
|
def trace_position(self):
|
|
1891
2051
|
_text, lp = self.CurLine
|
|
1892
2052
|
self.message("{:>6d}:{} ({})".format(self.cline, lp, self.cpos), pane=-1)
|
|
1893
|
-
|
|
1894
|
-
def OnUpdate(self, evt):
|
|
2053
|
+
|
|
2054
|
+
def OnUpdate(self, evt): #<wx._stc.StyledTextEvent>
|
|
1895
2055
|
if evt.Updated & (stc.STC_UPDATE_SELECTION | stc.STC_UPDATE_CONTENT):
|
|
1896
2056
|
self.trace_position()
|
|
1897
2057
|
if evt.Updated & stc.STC_UPDATE_CONTENT:
|
|
1898
2058
|
self.handler('buffer_modified', self)
|
|
1899
2059
|
evt.Skip()
|
|
1900
|
-
|
|
1901
|
-
def OnCallTipClick(self, evt):
|
|
2060
|
+
|
|
2061
|
+
def OnCallTipClick(self, evt): #<wx._stc.StyledTextEvent>
|
|
1902
2062
|
if self.CallTipActive():
|
|
1903
2063
|
self.CallTipCancel()
|
|
1904
2064
|
pos, tip, more = self._calltips
|
|
1905
2065
|
if more:
|
|
1906
2066
|
self.CallTipShow(pos, tip, N=None)
|
|
1907
2067
|
evt.Skip()
|
|
1908
|
-
|
|
1909
|
-
def OnIndicatorClick(self, evt):
|
|
2068
|
+
|
|
2069
|
+
def OnIndicatorClick(self, evt): #<wx._stc.StyledTextEvent>
|
|
1910
2070
|
if self.SelectedText or not wx.GetKeyState(wx.WXK_CONTROL):
|
|
1911
|
-
## Processing text selection
|
|
2071
|
+
## Processing text selection or dragging.
|
|
1912
2072
|
evt.Skip()
|
|
1913
2073
|
return
|
|
2074
|
+
|
|
1914
2075
|
pos = evt.Position
|
|
2076
|
+
self.goto_char(pos)
|
|
1915
2077
|
i = 2
|
|
1916
|
-
if self.IndicatorValueAt(i, pos):
|
|
2078
|
+
if self.IndicatorValueAt(i, pos): # [C-indic click]
|
|
1917
2079
|
p = self.IndicatorStart(i, pos)
|
|
1918
2080
|
q = self.IndicatorEnd(i, pos)
|
|
1919
2081
|
url = self.GetTextRange(p, q).strip()
|
|
1920
|
-
|
|
1921
|
-
if wx.GetKeyState(wx.WXK_SHIFT): # [C-S-indic click]
|
|
2082
|
+
if wx.GetKeyState(wx.WXK_SHIFT): # [C-S-indic click]
|
|
1922
2083
|
import webbrowser
|
|
1923
2084
|
return webbrowser.open(url)
|
|
1924
2085
|
else:
|
|
1925
2086
|
## Note: post-call for the confirmation dialog.
|
|
1926
2087
|
wx.CallAfter(self.parent.load_file, url)
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
def on_modified(self, buf):
|
|
2088
|
+
|
|
2089
|
+
def on_buffer_modified(self, buf):
|
|
1930
2090
|
"""Called when the buffer is modified."""
|
|
1931
2091
|
self.SetIndicatorCurrent(2)
|
|
1932
2092
|
self.IndicatorClearRange(0, self.TextLength)
|
|
1933
2093
|
for m in self.grep(url_re):
|
|
1934
2094
|
p, q = m.span()
|
|
1935
2095
|
self.IndicatorFillRange(p, q-p)
|
|
1936
|
-
|
|
2096
|
+
|
|
1937
2097
|
def OnSavePointLeft(self, evt):
|
|
1938
2098
|
self.update_caption()
|
|
1939
2099
|
evt.Skip()
|
|
1940
|
-
|
|
2100
|
+
|
|
1941
2101
|
def OnSavePointReached(self, evt):
|
|
1942
2102
|
self.update_caption()
|
|
1943
2103
|
evt.Skip()
|
|
1944
|
-
|
|
2104
|
+
|
|
1945
2105
|
def OnEnterDot(self, evt):
|
|
2106
|
+
if not self.CanEdit():
|
|
2107
|
+
self.handler('quit', evt)
|
|
2108
|
+
return
|
|
1946
2109
|
p = self.cpos
|
|
1947
|
-
|
|
2110
|
+
lst = self.get_style(p-1)
|
|
1948
2111
|
rst = self.get_style(p)
|
|
1949
|
-
if
|
|
1950
|
-
self.handler('quit', evt)
|
|
2112
|
+
if lst not in ('moji', 'word', 'rparen') or rst == 'word':
|
|
2113
|
+
self.handler('quit', evt) # Don't enter autocomp
|
|
1951
2114
|
evt.Skip()
|
|
1952
|
-
|
|
1953
|
-
def
|
|
2115
|
+
|
|
2116
|
+
def on_buffer_activated(self, buf):
|
|
1954
2117
|
"""Called when the buffer is activated."""
|
|
1955
2118
|
self.update_caption()
|
|
1956
2119
|
self.trace_position()
|
|
1957
|
-
|
|
1958
|
-
def
|
|
2120
|
+
|
|
2121
|
+
def on_buffer_inactivated(self, buf):
|
|
1959
2122
|
"""Called when the buffer is inactivated."""
|
|
1960
2123
|
pass
|
|
1961
|
-
|
|
2124
|
+
|
|
2125
|
+
def on_left_down(self, evt):
|
|
2126
|
+
pos = self.PositionFromPoint(evt.Position)
|
|
2127
|
+
ln = self.LineFromPosition(pos)
|
|
2128
|
+
ann_text = self.AnnotationGetText(ln)
|
|
2129
|
+
if ann_text:
|
|
2130
|
+
if pos == self.GetLineEndPosition(ln): # Check eol (not clicked yet).
|
|
2131
|
+
if wx.TheClipboard.Open():
|
|
2132
|
+
wx.TheClipboard.SetData(wx.TextDataObject(ann_text))
|
|
2133
|
+
wx.TheClipboard.Close()
|
|
2134
|
+
self.message("Annotation copied.")
|
|
2135
|
+
self.AnnotationClearLine(ln)
|
|
2136
|
+
evt.Skip()
|
|
2137
|
+
|
|
1962
2138
|
def on_enter_escmap(self, evt):
|
|
1963
2139
|
self.message("ESC-")
|
|
1964
|
-
|
|
2140
|
+
|
|
1965
2141
|
def on_exit_escmap(self, evt):
|
|
1966
2142
|
self.message("ESC {}".format(evt.key))
|
|
1967
2143
|
self.AnnotationClearAll()
|
|
1968
|
-
|
|
2144
|
+
|
|
1969
2145
|
## --------------------------------
|
|
1970
|
-
## File I/O
|
|
2146
|
+
## File I/O.
|
|
1971
2147
|
## --------------------------------
|
|
1972
|
-
|
|
1973
|
-
def _load_textfile(self, text
|
|
2148
|
+
|
|
2149
|
+
def _load_textfile(self, text):
|
|
1974
2150
|
with self.off_readonly():
|
|
1975
2151
|
self.Text = text
|
|
1976
2152
|
self.EmptyUndoBuffer()
|
|
1977
2153
|
self.SetSavePoint()
|
|
1978
|
-
self.update_filestamp(filename)
|
|
1979
2154
|
self.handler('buffer_loaded', self)
|
|
1980
|
-
|
|
2155
|
+
|
|
1981
2156
|
def _load_file(self, filename):
|
|
1982
2157
|
"""Wrapped method of LoadFile."""
|
|
1983
2158
|
if self.LoadFile(filename):
|
|
@@ -1987,7 +2162,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1987
2162
|
self.handler('buffer_loaded', self)
|
|
1988
2163
|
return True
|
|
1989
2164
|
return False
|
|
1990
|
-
|
|
2165
|
+
|
|
1991
2166
|
def _save_file(self, filename):
|
|
1992
2167
|
"""Wrapped method of SaveFile."""
|
|
1993
2168
|
if self.SaveFile(filename):
|
|
@@ -1996,7 +2171,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1996
2171
|
self.handler('buffer_saved', self)
|
|
1997
2172
|
return True
|
|
1998
2173
|
return False
|
|
1999
|
-
|
|
2174
|
+
|
|
2000
2175
|
def LoadFile(self, filename):
|
|
2001
2176
|
"""Load the contents of file into the editor.
|
|
2002
2177
|
|
|
@@ -2006,7 +2181,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2006
2181
|
with self.off_readonly():
|
|
2007
2182
|
self.Text = i.read()
|
|
2008
2183
|
return True
|
|
2009
|
-
|
|
2184
|
+
|
|
2010
2185
|
def SaveFile(self, filename):
|
|
2011
2186
|
"""Write the contents of the editor to file.
|
|
2012
2187
|
|
|
@@ -2015,37 +2190,38 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2015
2190
|
with open(filename, "w", encoding='utf-8', newline='') as o:
|
|
2016
2191
|
o.write(self.Text)
|
|
2017
2192
|
return True
|
|
2018
|
-
|
|
2193
|
+
|
|
2019
2194
|
## --------------------------------
|
|
2020
|
-
## Python eval / exec
|
|
2195
|
+
## Python eval / exec.
|
|
2021
2196
|
## --------------------------------
|
|
2022
|
-
|
|
2197
|
+
|
|
2023
2198
|
@property
|
|
2024
|
-
def locals(self):
|
|
2199
|
+
def locals(self): # internal use only
|
|
2025
2200
|
try:
|
|
2026
2201
|
return self.parent.parent.current_shell.locals
|
|
2027
2202
|
except AttributeError:
|
|
2028
2203
|
return None
|
|
2029
|
-
|
|
2204
|
+
|
|
2030
2205
|
@property
|
|
2031
|
-
def globals(self):
|
|
2206
|
+
def globals(self): # internal use only
|
|
2032
2207
|
try:
|
|
2033
2208
|
return self.parent.parent.current_shell.globals
|
|
2034
2209
|
except AttributeError:
|
|
2035
2210
|
return None
|
|
2036
|
-
|
|
2211
|
+
|
|
2037
2212
|
def eval(self, text):
|
|
2038
|
-
return eval(text, self.globals, self.locals)
|
|
2039
|
-
|
|
2213
|
+
return eval(text, self.globals, self.locals) # using current shell namespace
|
|
2214
|
+
|
|
2040
2215
|
def exec(self, text):
|
|
2041
|
-
exec(text, self.globals, self.locals)
|
|
2216
|
+
exec(text, self.globals, self.locals) # using current shell namespace
|
|
2042
2217
|
dispatcher.send(signal='Interpreter.push',
|
|
2043
2218
|
sender=self, command=None, more=False)
|
|
2044
|
-
|
|
2045
|
-
def
|
|
2046
|
-
"""
|
|
2047
|
-
|
|
2048
|
-
|
|
2219
|
+
|
|
2220
|
+
def eval_line(self):
|
|
2221
|
+
"""Evaluate the selected word or line and show calltips."""
|
|
2222
|
+
if self.CallTipActive():
|
|
2223
|
+
self.CallTipCancel()
|
|
2224
|
+
|
|
2049
2225
|
def _gen_text():
|
|
2050
2226
|
text = self.SelectedText
|
|
2051
2227
|
if text:
|
|
@@ -2053,61 +2229,47 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2053
2229
|
else:
|
|
2054
2230
|
yield self.line_at_caret
|
|
2055
2231
|
yield self.expr_at_caret
|
|
2056
|
-
return filter(None, _gen_text())
|
|
2057
|
-
|
|
2058
|
-
def eval_line(self):
|
|
2059
|
-
self.py_eval_line(self.globals, self.locals)
|
|
2060
|
-
|
|
2061
|
-
def py_eval_line(self, globals=None, locals=None):
|
|
2062
|
-
if self.CallTipActive():
|
|
2063
|
-
self.CallTipCancel()
|
|
2064
2232
|
|
|
2065
|
-
|
|
2066
|
-
for text in self.gen_text_at_caret():
|
|
2233
|
+
for text in _gen_text():
|
|
2067
2234
|
try:
|
|
2068
|
-
obj = eval(text, globals, locals)
|
|
2235
|
+
obj = eval(text, self.globals, self.locals)
|
|
2069
2236
|
except Exception as e:
|
|
2070
|
-
|
|
2237
|
+
self.message(e)
|
|
2071
2238
|
else:
|
|
2072
2239
|
self.CallTipShow(self.cpos, pformat(obj))
|
|
2073
2240
|
self.message(text)
|
|
2074
2241
|
return
|
|
2075
|
-
|
|
2076
|
-
|
|
2242
|
+
if not text:
|
|
2243
|
+
self.message("No words")
|
|
2244
|
+
|
|
2077
2245
|
def exec_region(self):
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
def py_exec_region(self, globals=None, locals=None, filename=None):
|
|
2081
|
-
if not filename:
|
|
2082
|
-
filename = self.filename
|
|
2246
|
+
"""Execute a region of code."""
|
|
2083
2247
|
try:
|
|
2084
|
-
code = compile(self.Text, filename, "exec")
|
|
2085
|
-
exec(code, globals, locals)
|
|
2248
|
+
code = compile(self.Text, self.filename, "exec")
|
|
2249
|
+
exec(code, self.globals, self.locals)
|
|
2086
2250
|
dispatcher.send(signal='Interpreter.push',
|
|
2087
2251
|
sender=self, command=None, more=False)
|
|
2088
2252
|
except BdbQuit:
|
|
2089
|
-
self.red_pointer = self.cline
|
|
2090
2253
|
pass
|
|
2091
2254
|
except Exception as e:
|
|
2092
2255
|
msg = traceback.format_exc()
|
|
2093
|
-
err = re.findall(
|
|
2094
|
-
lines = [int(ln) for fn, ln in err if fn == filename]
|
|
2256
|
+
err = re.findall(py_trace_re, msg, re.M)
|
|
2257
|
+
lines = [int(ln) for fn, ln in err if fn == self.filename]
|
|
2095
2258
|
if lines:
|
|
2096
2259
|
lx = lines[-1] - 1
|
|
2097
2260
|
self.red_arrow = lx
|
|
2098
2261
|
self.goto_line(lx)
|
|
2099
|
-
self.EnsureVisible(lx)
|
|
2262
|
+
self.EnsureVisible(lx) # expand if folded
|
|
2100
2263
|
self.EnsureCaretVisible()
|
|
2101
2264
|
self.AnnotationSetStyle(lx, stc.STC_STYLE_ANNOTATION)
|
|
2102
2265
|
self.AnnotationSetText(lx, msg)
|
|
2103
2266
|
self.message(e)
|
|
2104
|
-
## print(msg, file=sys.__stderr__)
|
|
2105
2267
|
else:
|
|
2106
2268
|
self.code = code
|
|
2107
|
-
del self.pointer
|
|
2269
|
+
del self.pointer # Reset pointer (debugger hook point).
|
|
2108
2270
|
del self.red_arrow
|
|
2109
2271
|
self.handler('buffer_region_executed', self)
|
|
2110
|
-
self.message("Evaluated {!r} successfully.".format(filename))
|
|
2272
|
+
self.message("Evaluated {!r} successfully.".format(self.filename))
|
|
2111
2273
|
self.AnnotationClearAll()
|
|
2112
2274
|
|
|
2113
2275
|
|
|
@@ -2115,16 +2277,16 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2115
2277
|
"""Python code editor.
|
|
2116
2278
|
|
|
2117
2279
|
Args:
|
|
2118
|
-
name
|
|
2280
|
+
name: Window.Name (e.g. 'Scratch')
|
|
2119
2281
|
|
|
2120
2282
|
Attributes:
|
|
2121
|
-
default_name
|
|
2122
|
-
default_buffer
|
|
2283
|
+
default_name: default buffer name (e.g. '*scratch*')
|
|
2284
|
+
default_buffer: default buffer
|
|
2123
2285
|
"""
|
|
2124
2286
|
@property
|
|
2125
2287
|
def message(self):
|
|
2126
2288
|
return self.parent.message
|
|
2127
|
-
|
|
2289
|
+
|
|
2128
2290
|
def __init__(self, parent, name="book", **kwargs):
|
|
2129
2291
|
kwargs.setdefault('style',
|
|
2130
2292
|
(aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TOP)
|
|
@@ -2136,12 +2298,11 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2136
2298
|
## So we set the tabs' height to zero to hide them.
|
|
2137
2299
|
self.TabCtrlHeight = 0
|
|
2138
2300
|
|
|
2139
|
-
self.defaultBufferStyle =
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
self.parent = parent #: parent<ShellFrame> is not Parent<AuiNotebook>
|
|
2301
|
+
self.defaultBufferStyle = {}
|
|
2302
|
+
|
|
2303
|
+
self.parent = parent # parent<ShellFrame> is not Parent<AuiNotebook>
|
|
2143
2304
|
self.Name = name
|
|
2144
|
-
self.default_name = "*{}*".format(name.lower())
|
|
2305
|
+
self.default_name = "*{}*".format(name.lower()) # e.g. '*scratch*'
|
|
2145
2306
|
self.default_buffer = self.create_buffer(self.default_name)
|
|
2146
2307
|
|
|
2147
2308
|
self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnPageClose)
|
|
@@ -2163,29 +2324,29 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2163
2324
|
'buffer_loaded' : [ None, dispatch ],
|
|
2164
2325
|
'buffer_deleted' : [ None, dispatch ],
|
|
2165
2326
|
'buffer_modified' : [ None, dispatch ],
|
|
2166
|
-
'buffer_activated' : [ None, dispatch, self.
|
|
2167
|
-
'buffer_inactivated' : [ None, dispatch, self.
|
|
2327
|
+
'buffer_activated' : [ None, dispatch, self.on_buffer_activated ],
|
|
2328
|
+
'buffer_inactivated' : [ None, dispatch, self.on_buffer_inactivated ],
|
|
2168
2329
|
'buffer_caption_updated' : [ None, dispatch ],
|
|
2169
|
-
'*button* pressed' : [ None, dispatch, skip ],
|
|
2170
|
-
'*button* released' : [ None, dispatch, skip ],
|
|
2171
2330
|
},
|
|
2172
2331
|
0 : { # Normal mode
|
|
2332
|
+
'* pressed' : (0, skip),
|
|
2333
|
+
'* released' : (0, skip, dispatch),
|
|
2334
|
+
'*button* pressed' : (0, skip, dispatch),
|
|
2173
2335
|
'M-up pressed' : (0, _F(self.previous_buffer)),
|
|
2174
2336
|
'M-down pressed' : (0, _F(self.next_buffer)),
|
|
2175
2337
|
},
|
|
2176
2338
|
})
|
|
2177
|
-
|
|
2339
|
+
|
|
2178
2340
|
def OnDestroy(self, evt):
|
|
2179
2341
|
obj = evt.EventObject
|
|
2180
2342
|
if isinstance(obj, Buffer):
|
|
2181
2343
|
self.handler('buffer_deleted', obj)
|
|
2182
2344
|
evt.Skip()
|
|
2183
|
-
|
|
2184
|
-
def OnPageClose(self, evt):
|
|
2185
|
-
|
|
2186
|
-
buf = nb.all_buffers[evt.Selection]
|
|
2345
|
+
|
|
2346
|
+
def OnPageClose(self, evt): #<wx._aui.AuiNotebookEvent>
|
|
2347
|
+
buf = self.GetPage(evt.Selection)
|
|
2187
2348
|
if buf.need_buffer_save:
|
|
2188
|
-
if wx.MessageBox( # Confirm
|
|
2349
|
+
if wx.MessageBox( # Confirm closing the buffer.
|
|
2189
2350
|
"You are closing unsaved content.\n\n"
|
|
2190
2351
|
"The changes will be discarded.\n"
|
|
2191
2352
|
"Continue closing?",
|
|
@@ -2195,20 +2356,19 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2195
2356
|
evt.Veto()
|
|
2196
2357
|
return
|
|
2197
2358
|
evt.Skip()
|
|
2198
|
-
|
|
2199
|
-
def OnPageClosed(self, evt):
|
|
2359
|
+
|
|
2360
|
+
def OnPageClosed(self, evt): #<wx._aui.AuiNotebookEvent>
|
|
2200
2361
|
if self.PageCount == 0:
|
|
2201
2362
|
self.new_buffer()
|
|
2202
2363
|
evt.Skip()
|
|
2203
|
-
|
|
2364
|
+
|
|
2204
2365
|
def set_attributes(self, buf=None, **kwargs):
|
|
2205
2366
|
"""Set multiple properties at once to the buffer(s).
|
|
2206
2367
|
|
|
2207
2368
|
Args:
|
|
2208
|
-
buf
|
|
2369
|
+
buf: a buffer to apply (if None, applies to all buffers).
|
|
2209
2370
|
**kwargs: default style.
|
|
2210
2371
|
|
|
2211
|
-
Style = Buffer.STYLE
|
|
2212
2372
|
ReadOnly = False
|
|
2213
2373
|
UseTabs = False
|
|
2214
2374
|
ViewEOL = False
|
|
@@ -2228,29 +2388,36 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2228
2388
|
_setattribute(buf, kwargs)
|
|
2229
2389
|
else:
|
|
2230
2390
|
self.defaultBufferStyle.update(kwargs)
|
|
2231
|
-
for buf in self.
|
|
2391
|
+
for buf in self.get_all_buffers():
|
|
2232
2392
|
_setattribute(buf, self.defaultBufferStyle)
|
|
2233
|
-
|
|
2234
|
-
def
|
|
2393
|
+
|
|
2394
|
+
def on_buffer_activated(self, buf):
|
|
2235
2395
|
"""Called when the buffer is activated."""
|
|
2236
2396
|
title = "{} file: {}".format(self.Name, buf.filename)
|
|
2237
2397
|
self.parent.handler('title_window', title)
|
|
2238
|
-
|
|
2239
|
-
def
|
|
2398
|
+
|
|
2399
|
+
def on_buffer_inactivated(self, buf):
|
|
2240
2400
|
"""Called when the buffer is inactivated."""
|
|
2241
2401
|
pass
|
|
2242
|
-
|
|
2402
|
+
|
|
2243
2403
|
## --------------------------------
|
|
2244
|
-
## Buffer list controls
|
|
2404
|
+
## Buffer list controls.
|
|
2245
2405
|
## --------------------------------
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2406
|
+
|
|
2407
|
+
def get_all_buffers(self, fn=None):
|
|
2408
|
+
"""Yields all buffers with specified fn:filename or code."""
|
|
2409
|
+
if fn is None:
|
|
2410
|
+
yield from self.get_pages(Buffer)
|
|
2411
|
+
elif isinstance(fn, str):
|
|
2412
|
+
g = os.path.realpath(fn)
|
|
2413
|
+
for buf in self.get_pages(Buffer):
|
|
2414
|
+
if fn == buf.filename or g == os.path.realpath(buf.filename):
|
|
2415
|
+
yield buf
|
|
2416
|
+
else:
|
|
2417
|
+
for buf in self.get_pages(Buffer):
|
|
2418
|
+
if fn is buf or fn in buf: # check code
|
|
2419
|
+
yield buf
|
|
2420
|
+
|
|
2254
2421
|
@property
|
|
2255
2422
|
def menu(self):
|
|
2256
2423
|
"""Yields context menu."""
|
|
@@ -2260,86 +2427,91 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2260
2427
|
lambda v: buf.SetFocus(),
|
|
2261
2428
|
lambda v: v.Check(buf is self.buffer))
|
|
2262
2429
|
|
|
2263
|
-
return (_menu(j+1, x) for j, x in enumerate(self.
|
|
2264
|
-
|
|
2430
|
+
return (_menu(j+1, x) for j, x in enumerate(self.get_all_buffers()))
|
|
2431
|
+
|
|
2265
2432
|
@property
|
|
2266
2433
|
def buffer(self):
|
|
2267
|
-
"""
|
|
2434
|
+
"""Return the currently selected page or None."""
|
|
2268
2435
|
return self.CurrentPage
|
|
2269
|
-
|
|
2436
|
+
|
|
2270
2437
|
def find_buffer(self, fn):
|
|
2271
|
-
"""Find buffer with specified fn:filename or code."""
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
for buf in self.all_buffers:
|
|
2275
|
-
if fn == buf.filename or g == os.path.realpath(buf.filename):
|
|
2276
|
-
return buf
|
|
2277
|
-
else:
|
|
2278
|
-
for buf in self.all_buffers:
|
|
2279
|
-
if fn is buf or fn in buf: # check code
|
|
2280
|
-
return buf
|
|
2281
|
-
|
|
2438
|
+
"""Find a buffer with specified fn:filename or code."""
|
|
2439
|
+
return next(self.get_all_buffers(fn), None)
|
|
2440
|
+
|
|
2282
2441
|
def swap_buffer(self, buf, lineno=0):
|
|
2283
2442
|
self.swap_page(buf)
|
|
2284
2443
|
if lineno:
|
|
2285
2444
|
buf.markline = lineno - 1
|
|
2286
2445
|
buf.goto_marker(1)
|
|
2287
|
-
|
|
2446
|
+
|
|
2288
2447
|
def create_buffer(self, filename, index=None):
|
|
2289
2448
|
"""Create a new buffer (internal use only)."""
|
|
2290
|
-
|
|
2291
|
-
self.Freeze()
|
|
2449
|
+
with wx.FrozenWindow(self):
|
|
2292
2450
|
buf = Buffer(self, filename, style=wx.BORDER_DEFAULT)
|
|
2293
2451
|
self.set_attributes(buf, **self.defaultBufferStyle)
|
|
2294
2452
|
if index is None:
|
|
2295
2453
|
index = self.PageCount
|
|
2296
|
-
self.InsertPage(index, buf, buf.name)
|
|
2454
|
+
self.InsertPage(index, buf, buf.name) # => [buffer_activated]
|
|
2297
2455
|
self.handler('buffer_new', buf)
|
|
2298
2456
|
return buf
|
|
2299
|
-
|
|
2300
|
-
self.Thaw()
|
|
2301
|
-
|
|
2457
|
+
|
|
2302
2458
|
def new_buffer(self):
|
|
2303
2459
|
"""Create a new default buffer."""
|
|
2304
2460
|
buf = self.default_buffer
|
|
2305
|
-
if not buf or buf.mtdelta is not None:
|
|
2306
|
-
buf = self.create_buffer(self.default_name
|
|
2461
|
+
if not buf or buf.mtdelta is not None: # is saved?
|
|
2462
|
+
buf = self.create_buffer(self.default_name)
|
|
2307
2463
|
self.default_buffer = buf
|
|
2308
2464
|
else:
|
|
2309
2465
|
buf.ClearAll()
|
|
2310
|
-
|
|
2466
|
+
# buf.EmptyUndoBuffer()
|
|
2311
2467
|
buf.SetFocus()
|
|
2312
2468
|
return buf
|
|
2313
|
-
|
|
2469
|
+
|
|
2314
2470
|
def delete_buffer(self, buf=None):
|
|
2315
2471
|
"""Pop the current buffer from the buffer list."""
|
|
2316
2472
|
if not buf:
|
|
2317
2473
|
buf = self.buffer
|
|
2318
2474
|
j = self.GetPageIndex(buf)
|
|
2319
2475
|
if j != -1:
|
|
2320
|
-
self.DeletePage(j)
|
|
2321
|
-
if not self.buffer:
|
|
2322
|
-
self.new_buffer
|
|
2323
|
-
|
|
2476
|
+
self.DeletePage(j) # the focus moves
|
|
2477
|
+
if not self.buffer: # no buffers
|
|
2478
|
+
wx.CallAfter(self.new_buffer) # Note: post-call to avoid a crash.
|
|
2479
|
+
|
|
2324
2480
|
def delete_all_buffers(self):
|
|
2325
2481
|
"""Initialize list of buffers."""
|
|
2326
2482
|
self.DeleteAllPages()
|
|
2327
|
-
self.new_buffer
|
|
2328
|
-
|
|
2483
|
+
wx.CallAfter(self.new_buffer) # Note: post-call to avoid a crash.
|
|
2484
|
+
|
|
2329
2485
|
def next_buffer(self):
|
|
2330
|
-
self.Selection
|
|
2331
|
-
|
|
2486
|
+
if self.Selection < self.PageCount - 1:
|
|
2487
|
+
self.Selection += 1
|
|
2488
|
+
else:
|
|
2489
|
+
books = list(self.Parent.get_pages(type(self)))
|
|
2490
|
+
k = books.index(self)
|
|
2491
|
+
if k < len(books) - 1:
|
|
2492
|
+
other_editor = books[k+1]
|
|
2493
|
+
other_editor.Selection = 0
|
|
2494
|
+
other_editor.CurrentPage.SetFocus()
|
|
2495
|
+
|
|
2332
2496
|
def previous_buffer(self):
|
|
2333
|
-
self.Selection
|
|
2334
|
-
|
|
2497
|
+
if self.Selection > 0:
|
|
2498
|
+
self.Selection -= 1
|
|
2499
|
+
else:
|
|
2500
|
+
books = list(self.Parent.get_pages(type(self)))
|
|
2501
|
+
k = books.index(self)
|
|
2502
|
+
if k > 0:
|
|
2503
|
+
other_editor = books[k-1]
|
|
2504
|
+
other_editor.Selection = other_editor.PageCount - 1
|
|
2505
|
+
other_editor.CurrentPage.SetFocus()
|
|
2506
|
+
|
|
2335
2507
|
## --------------------------------
|
|
2336
|
-
## File I/O
|
|
2508
|
+
## File I/O.
|
|
2337
2509
|
## --------------------------------
|
|
2338
2510
|
wildcards = [
|
|
2339
2511
|
"PY files (*.py)|*.py",
|
|
2340
2512
|
"ALL files (*.*)|*.*",
|
|
2341
2513
|
]
|
|
2342
|
-
|
|
2514
|
+
|
|
2343
2515
|
def load_cache(self, filename, lineno=0):
|
|
2344
2516
|
"""Load a file from cache using linecache.
|
|
2345
2517
|
Note:
|
|
@@ -2355,12 +2527,13 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2355
2527
|
elif not buf.need_buffer_load:
|
|
2356
2528
|
self.swap_buffer(buf, lineno)
|
|
2357
2529
|
return True
|
|
2358
|
-
buf._load_textfile(''.join(lines)
|
|
2530
|
+
buf._load_textfile(''.join(lines))
|
|
2531
|
+
buf.update_filestamp(filename)
|
|
2359
2532
|
self.swap_buffer(buf, lineno)
|
|
2360
2533
|
return True
|
|
2361
2534
|
return False
|
|
2362
|
-
|
|
2363
|
-
def load_file(self, filename, lineno=0, verbose=True):
|
|
2535
|
+
|
|
2536
|
+
def load_file(self, filename, lineno=0, verbose=True, **kwargs):
|
|
2364
2537
|
"""Load a file into an existing or new buffer.
|
|
2365
2538
|
The requests module is required to use URL extension.
|
|
2366
2539
|
"""
|
|
@@ -2368,7 +2541,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2368
2541
|
if not buf:
|
|
2369
2542
|
buf = self.create_buffer("*temp file*")
|
|
2370
2543
|
elif buf.need_buffer_save and verbose:
|
|
2371
|
-
if wx.MessageBox( # Confirm
|
|
2544
|
+
if wx.MessageBox( # Confirm loading the buffer.
|
|
2372
2545
|
"You are leaving unsaved content.\n\n"
|
|
2373
2546
|
"The changes will be discarded.\n"
|
|
2374
2547
|
"Continue loading?",
|
|
@@ -2382,22 +2555,23 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2382
2555
|
try:
|
|
2383
2556
|
if re.match(url_re, filename):
|
|
2384
2557
|
import requests
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2558
|
+
kwargs.setdefault('timeout', 3.0)
|
|
2559
|
+
res = requests.get(filename, **kwargs)
|
|
2560
|
+
if res.status_code == requests.codes.OK:
|
|
2561
|
+
buf._load_textfile(res.text)
|
|
2562
|
+
buf.update_filestamp(filename)
|
|
2388
2563
|
self.swap_buffer(buf, lineno)
|
|
2389
2564
|
return True
|
|
2390
|
-
|
|
2391
|
-
raise Exception("The requested URL was not found")
|
|
2565
|
+
res.raise_for_status() # raise HTTP error; don't catch here.
|
|
2392
2566
|
if buf._load_file(filename):
|
|
2393
2567
|
self.swap_buffer(buf, lineno)
|
|
2394
2568
|
return True
|
|
2395
2569
|
return False
|
|
2396
|
-
except
|
|
2397
|
-
self.post_message(
|
|
2570
|
+
except (OSError, UnicodeDecodeError, ModuleNotFoundError) as e:
|
|
2571
|
+
self.post_message("Failed to load:", e)
|
|
2398
2572
|
self.delete_buffer(buf)
|
|
2399
2573
|
return False
|
|
2400
|
-
|
|
2574
|
+
|
|
2401
2575
|
def find_file(self, filename=None):
|
|
2402
2576
|
"""Open the specified file."""
|
|
2403
2577
|
if not filename:
|
|
@@ -2406,21 +2580,22 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2406
2580
|
wildcard='|'.join(self.wildcards),
|
|
2407
2581
|
style=wx.FD_OPEN|wx.FD_MULTIPLE) as dlg:
|
|
2408
2582
|
if dlg.ShowModal() == wx.ID_OK:
|
|
2409
|
-
for fn in dlg.Paths
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
if not
|
|
2583
|
+
return all([self.find_file(fn) for fn in dlg.Paths])
|
|
2584
|
+
return None
|
|
2585
|
+
retval = self.load_file(filename)
|
|
2586
|
+
if retval == False: # noqa # to check if not None
|
|
2413
2587
|
buf = self.create_buffer(filename)
|
|
2414
|
-
buf._Buffer__mtime = 0 # => need_buffer_save
|
|
2415
2588
|
self.swap_buffer(buf)
|
|
2416
|
-
|
|
2589
|
+
self.post_message("New file.")
|
|
2590
|
+
return retval
|
|
2591
|
+
|
|
2417
2592
|
def save_file(self, filename, buf=None, verbose=True):
|
|
2418
2593
|
"""Save the current buffer to a file.
|
|
2419
2594
|
"""
|
|
2420
2595
|
buf = buf or self.buffer
|
|
2421
2596
|
if buf.need_buffer_load and verbose:
|
|
2422
2597
|
self.swap_buffer(buf)
|
|
2423
|
-
if wx.MessageBox( # Confirm
|
|
2598
|
+
if wx.MessageBox( # Confirm saving the buffer.
|
|
2424
2599
|
"The file has been modified externally.\n\n"
|
|
2425
2600
|
"The contents of the file will be overwritten.\n"
|
|
2426
2601
|
"Continue saving?",
|
|
@@ -2434,10 +2609,10 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2434
2609
|
self.default_buffer = None
|
|
2435
2610
|
return True
|
|
2436
2611
|
return False
|
|
2437
|
-
except
|
|
2438
|
-
self.post_message(
|
|
2612
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
2613
|
+
self.post_message("Failed to save:", e)
|
|
2439
2614
|
return False
|
|
2440
|
-
|
|
2615
|
+
|
|
2441
2616
|
def load_buffer(self, buf=None):
|
|
2442
2617
|
"""Confirm the load with the dialog."""
|
|
2443
2618
|
buf = buf or self.buffer
|
|
@@ -2450,7 +2625,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2450
2625
|
return None
|
|
2451
2626
|
else:
|
|
2452
2627
|
return self.load_file(buf.filename, buf.markline+1)
|
|
2453
|
-
|
|
2628
|
+
|
|
2454
2629
|
def save_buffer(self, buf=None):
|
|
2455
2630
|
"""Confirm the save with the dialog."""
|
|
2456
2631
|
buf = buf or self.buffer
|
|
@@ -2463,28 +2638,28 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2463
2638
|
return None
|
|
2464
2639
|
else:
|
|
2465
2640
|
return self.save_file(buf.filename, buf)
|
|
2466
|
-
|
|
2641
|
+
|
|
2467
2642
|
def save_buffer_as(self, buf=None):
|
|
2468
2643
|
"""Confirm the saveas with the dialog."""
|
|
2469
2644
|
buf = buf or self.buffer
|
|
2470
2645
|
with wx.FileDialog(self, "Save buffer as",
|
|
2471
2646
|
defaultDir=os.path.dirname(self.buffer.filename),
|
|
2472
|
-
defaultFile=
|
|
2647
|
+
defaultFile=fix_fnchars(buf.name),
|
|
2473
2648
|
wildcard='|'.join(self.wildcards),
|
|
2474
2649
|
style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dlg:
|
|
2475
2650
|
if dlg.ShowModal() == wx.ID_OK:
|
|
2476
2651
|
return self.save_file(dlg.Path, buf)
|
|
2477
|
-
|
|
2652
|
+
|
|
2478
2653
|
def save_all_buffers(self):
|
|
2479
|
-
for buf in self.
|
|
2654
|
+
for buf in self.get_all_buffers():
|
|
2480
2655
|
if buf.need_buffer_save:
|
|
2481
2656
|
self.save_buffer(buf)
|
|
2482
|
-
|
|
2657
|
+
|
|
2483
2658
|
def kill_buffer(self, buf=None):
|
|
2484
2659
|
"""Confirm the close with the dialog."""
|
|
2485
2660
|
buf = buf or self.buffer
|
|
2486
2661
|
if buf.need_buffer_save:
|
|
2487
|
-
if wx.MessageBox( # Confirm
|
|
2662
|
+
if wx.MessageBox( # Confirm closing the buffer.
|
|
2488
2663
|
"You are closing unsaved content.\n\n"
|
|
2489
2664
|
"The changes will be discarded.\n"
|
|
2490
2665
|
"Continue closing?",
|
|
@@ -2492,12 +2667,12 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2492
2667
|
style=wx.YES_NO|wx.ICON_INFORMATION) != wx.YES:
|
|
2493
2668
|
self.post_message("The close has been canceled.")
|
|
2494
2669
|
return None
|
|
2495
|
-
|
|
2496
|
-
|
|
2670
|
+
self.delete_buffer(buf)
|
|
2671
|
+
|
|
2497
2672
|
def kill_all_buffers(self):
|
|
2498
|
-
for buf in self.
|
|
2673
|
+
for buf in self.get_all_buffers():
|
|
2499
2674
|
if buf.need_buffer_save:
|
|
2500
|
-
if wx.MessageBox( # Confirm
|
|
2675
|
+
if wx.MessageBox( # Confirm closing the buffer.
|
|
2501
2676
|
"You are closing unsaved content.\n\n"
|
|
2502
2677
|
"The changes will be discarded.\n"
|
|
2503
2678
|
"Continue closing?",
|
|
@@ -2505,7 +2680,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2505
2680
|
style=wx.YES_NO|wx.ICON_INFORMATION) != wx.YES:
|
|
2506
2681
|
self.post_message("The close has been canceled.")
|
|
2507
2682
|
return None
|
|
2508
|
-
|
|
2683
|
+
self.delete_all_buffers()
|
|
2509
2684
|
|
|
2510
2685
|
|
|
2511
2686
|
class Interpreter(interpreter.Interpreter):
|
|
@@ -2516,7 +2691,7 @@ class Interpreter(interpreter.Interpreter):
|
|
|
2516
2691
|
|
|
2517
2692
|
self.parent = interpShell
|
|
2518
2693
|
self.globals = self.locals
|
|
2519
|
-
|
|
2694
|
+
|
|
2520
2695
|
def runcode(self, code):
|
|
2521
2696
|
"""Execute a code object.
|
|
2522
2697
|
|
|
@@ -2529,10 +2704,10 @@ class Interpreter(interpreter.Interpreter):
|
|
|
2529
2704
|
except Exception:
|
|
2530
2705
|
self.showtraceback()
|
|
2531
2706
|
finally:
|
|
2532
|
-
## ex. KeyboardInterrupt
|
|
2707
|
+
## ex. KeyboardInterrupt
|
|
2533
2708
|
if wx.IsBusy():
|
|
2534
2709
|
wx.EndBusyCursor()
|
|
2535
|
-
|
|
2710
|
+
|
|
2536
2711
|
def showtraceback(self):
|
|
2537
2712
|
"""Display the exception that just occurred.
|
|
2538
2713
|
|
|
@@ -2541,41 +2716,27 @@ class Interpreter(interpreter.Interpreter):
|
|
|
2541
2716
|
interpreter.Interpreter.showtraceback(self)
|
|
2542
2717
|
|
|
2543
2718
|
t, v, tb = sys.exc_info()
|
|
2544
|
-
|
|
2545
|
-
|
|
2719
|
+
while tb.tb_next:
|
|
2720
|
+
tb = tb.tb_next
|
|
2721
|
+
v.lineno = tb.tb_lineno
|
|
2722
|
+
v.filename = tb.tb_frame.f_code.co_filename
|
|
2546
2723
|
try:
|
|
2547
2724
|
self.parent.handler('interp_error', v)
|
|
2548
2725
|
except AttributeError:
|
|
2549
2726
|
pass
|
|
2550
|
-
|
|
2551
|
-
def showsyntaxerror(self, filename=None):
|
|
2727
|
+
|
|
2728
|
+
def showsyntaxerror(self, filename=None, **kwargs):
|
|
2552
2729
|
"""Display the syntax error that just occurred.
|
|
2553
2730
|
|
|
2554
2731
|
(override) Pass the syntax error info to the parent:shell.
|
|
2555
2732
|
"""
|
|
2556
|
-
interpreter.Interpreter.showsyntaxerror(self, filename)
|
|
2733
|
+
interpreter.Interpreter.showsyntaxerror(self, filename, **kwargs)
|
|
2557
2734
|
|
|
2558
2735
|
t, v, tb = sys.exc_info()
|
|
2559
2736
|
try:
|
|
2560
2737
|
self.parent.handler('interp_error', v)
|
|
2561
2738
|
except AttributeError:
|
|
2562
2739
|
pass
|
|
2563
|
-
|
|
2564
|
-
@ignore(DeprecationWarning)
|
|
2565
|
-
def getCallTip(self, command='', *args, **kwargs):
|
|
2566
|
-
"""Return call tip text for a command.
|
|
2567
|
-
|
|
2568
|
-
(override) Ignore DeprecationWarning: for function,
|
|
2569
|
-
`formatargspec` is deprecated since Python 3.5.
|
|
2570
|
-
(override) Ignore ValueError: no signature found for builtin
|
|
2571
|
-
if the unwrapped function is a builtin function.
|
|
2572
|
-
"""
|
|
2573
|
-
## In 4.2.1, DeprecationWarning was fixed.
|
|
2574
|
-
## In 4.2.2, ValueError was fixed.
|
|
2575
|
-
try:
|
|
2576
|
-
return interpreter.Interpreter.getCallTip(self, command, *args, **kwargs)
|
|
2577
|
-
except ValueError:
|
|
2578
|
-
return interpreter.Interpreter.getCallTip(self) # dummy
|
|
2579
2740
|
|
|
2580
2741
|
|
|
2581
2742
|
class Nautilus(EditorInterface, Shell):
|
|
@@ -2597,10 +2758,9 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2597
2758
|
|
|
2598
2759
|
- quoteback : x`y --> y=x | x`y`z --> z=y=x
|
|
2599
2760
|
- pullback : x@y --> y(x) | x@y@z --> z(y(x))
|
|
2600
|
-
- apropos : x.y? [not] p --> shows
|
|
2601
|
-
equiv. apropos(x, y [,ignorecase ?:True,??:False] [,pred=p])
|
|
2602
|
-
``y`` can contain regular expressions
|
|
2603
|
-
``y`` can contain abbreviations: \\a:[a-z], \\A:[A-Z] .
|
|
2761
|
+
- apropos : x.y? [not] p --> shows items (not) matched by predicate p
|
|
2762
|
+
equiv. apropos(x, y [,ignorecase ?:True,??:False] [,pred=p]).
|
|
2763
|
+
``y`` can contain regular expressions, but no dots or backslashes.
|
|
2604
2764
|
``p`` can be atom, callable, type (e.g., int, str, ...),
|
|
2605
2765
|
and any predicates such as inspect.isclass.
|
|
2606
2766
|
|
|
@@ -2615,15 +2775,15 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2615
2775
|
|
|
2616
2776
|
C-up : [0] retrieve previous history
|
|
2617
2777
|
C-down : [0] retrieve next history
|
|
2618
|
-
C-j
|
|
2619
|
-
C-h
|
|
2778
|
+
C-j : [0] tooltip of eval (for the selected or focused word)
|
|
2779
|
+
C-h M-h : [0] calltip of help (for the selected or focused func)
|
|
2620
2780
|
TAB : [1] history-comp
|
|
2621
2781
|
M-p : [1] retrieve previous history in history-comp mode
|
|
2622
2782
|
M-n : [1] retrieve next history in history-comp mode
|
|
2623
|
-
M-.
|
|
2624
|
-
M-/
|
|
2625
|
-
M-,
|
|
2626
|
-
M-m
|
|
2783
|
+
M-. C-. : [2] word-comp
|
|
2784
|
+
M-/ C-/ : [3] apropos-comp
|
|
2785
|
+
M-, C-, : [4] text-comp
|
|
2786
|
+
M-m C-m : [5] module-comp
|
|
2627
2787
|
|
|
2628
2788
|
Autocomps are incremental when pressed any alnums,
|
|
2629
2789
|
and decremental when backspace.
|
|
@@ -2645,46 +2805,20 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2645
2805
|
Half-baked by Patrik K. O'Brien,
|
|
2646
2806
|
and this other half by K. O'moto.
|
|
2647
2807
|
"""
|
|
2648
|
-
STYLE = {
|
|
2649
|
-
stc.STC_STYLE_DEFAULT : "fore:#7f7f7f,back:#202020,size:9,face:MS Gothic",
|
|
2650
|
-
stc.STC_STYLE_LINENUMBER : "fore:#000000,back:#f0f0f0,size:9",
|
|
2651
|
-
stc.STC_STYLE_BRACELIGHT : "fore:#ffffff,back:#202020,bold",
|
|
2652
|
-
stc.STC_STYLE_BRACEBAD : "fore:#ffffff,back:#ff0000,bold",
|
|
2653
|
-
stc.STC_STYLE_CONTROLCHAR : "size:6",
|
|
2654
|
-
stc.STC_STYLE_CARETLINE : "fore:#ffffff,back:#123460,size:2", # optional
|
|
2655
|
-
stc.STC_STYLE_ANNOTATION : "fore:#7f0000,back:#ff7f7f", # optional
|
|
2656
|
-
stc.STC_P_DEFAULT : "fore:#cccccc",
|
|
2657
|
-
stc.STC_P_OPERATOR : "fore:#cccccc",
|
|
2658
|
-
stc.STC_P_IDENTIFIER : "fore:#cccccc",
|
|
2659
|
-
stc.STC_P_COMMENTLINE : "fore:#42c18c,back:#004040",
|
|
2660
|
-
stc.STC_P_COMMENTBLOCK : "fore:#42c18c,back:#004040,eol",
|
|
2661
|
-
stc.STC_P_NUMBER : "fore:#ffc080",
|
|
2662
|
-
stc.STC_P_STRINGEOL : "fore:#cccccc,back:#004040,eol",
|
|
2663
|
-
stc.STC_P_CHARACTER : "fore:#a0a0a0",
|
|
2664
|
-
stc.STC_P_STRING : "fore:#a0a0a0",
|
|
2665
|
-
stc.STC_P_TRIPLE : "fore:#a0a0a0,back:#004040",
|
|
2666
|
-
stc.STC_P_TRIPLEDOUBLE : "fore:#a0a0a0,back:#004040",
|
|
2667
|
-
stc.STC_P_CLASSNAME : "fore:#61d6d6,bold",
|
|
2668
|
-
stc.STC_P_DEFNAME : "fore:#3a96ff,bold",
|
|
2669
|
-
stc.STC_P_WORD : "fore:#80c0ff",
|
|
2670
|
-
stc.STC_P_WORD2 : "fore:#ff80ff",
|
|
2671
|
-
stc.STC_P_DECORATOR : "fore:#ff8040",
|
|
2672
|
-
}
|
|
2673
|
-
|
|
2674
2808
|
@property
|
|
2675
2809
|
def message(self):
|
|
2676
2810
|
return self.parent.message
|
|
2677
|
-
|
|
2811
|
+
|
|
2678
2812
|
@property
|
|
2679
2813
|
def target(self):
|
|
2680
2814
|
return self.__target
|
|
2681
|
-
|
|
2815
|
+
|
|
2682
2816
|
@target.setter
|
|
2683
2817
|
def target(self, obj):
|
|
2684
2818
|
"""Reset the shell target object; Rename the parent title.
|
|
2685
2819
|
"""
|
|
2686
2820
|
if not hasattr(obj, '__dict__'):
|
|
2687
|
-
raise TypeError("
|
|
2821
|
+
raise TypeError("invalid target")
|
|
2688
2822
|
|
|
2689
2823
|
self.__target = obj
|
|
2690
2824
|
self.locals = obj.__dict__
|
|
@@ -2693,39 +2827,39 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2693
2827
|
try:
|
|
2694
2828
|
obj.self = obj
|
|
2695
2829
|
obj.this = inspect.getmodule(obj)
|
|
2696
|
-
obj.shell = self
|
|
2830
|
+
obj.shell = self # Overwrite the facade <wx.py.shell.ShellFacade>.
|
|
2831
|
+
## obj.__name__ = typename(obj) # A namespace for ghost in the shell. cf. exec_region
|
|
2697
2832
|
except AttributeError:
|
|
2698
|
-
## print("- cannot overwrite target vars:", e)
|
|
2699
2833
|
pass
|
|
2700
2834
|
self.parent.handler('title_window', obj)
|
|
2701
|
-
|
|
2835
|
+
|
|
2702
2836
|
@property
|
|
2703
2837
|
def locals(self):
|
|
2704
2838
|
return self.interp.locals
|
|
2705
|
-
|
|
2839
|
+
|
|
2706
2840
|
@locals.setter
|
|
2707
|
-
def locals(self, v):
|
|
2841
|
+
def locals(self, v): # internal use only
|
|
2708
2842
|
self.interp.locals = v
|
|
2709
|
-
|
|
2843
|
+
|
|
2710
2844
|
@locals.deleter
|
|
2711
|
-
def locals(self):
|
|
2845
|
+
def locals(self): # internal use only
|
|
2712
2846
|
self.interp.locals = self.__target.__dict__
|
|
2713
|
-
|
|
2847
|
+
|
|
2714
2848
|
@property
|
|
2715
2849
|
def globals(self):
|
|
2716
2850
|
return self.interp.globals
|
|
2717
|
-
|
|
2851
|
+
|
|
2718
2852
|
@globals.setter
|
|
2719
|
-
def globals(self, v):
|
|
2853
|
+
def globals(self, v): # internal use only
|
|
2720
2854
|
self.interp.globals = v
|
|
2721
|
-
|
|
2855
|
+
|
|
2722
2856
|
@globals.deleter
|
|
2723
|
-
def globals(self):
|
|
2857
|
+
def globals(self): # internal use only
|
|
2724
2858
|
self.interp.globals = self.__target.__dict__
|
|
2725
2859
|
self.interp.globals.update(self.__globals)
|
|
2726
|
-
|
|
2860
|
+
|
|
2727
2861
|
__globals = {}
|
|
2728
|
-
|
|
2862
|
+
|
|
2729
2863
|
def __init__(self, parent, target, name="root",
|
|
2730
2864
|
introText=None,
|
|
2731
2865
|
startupScript=None,
|
|
@@ -2733,22 +2867,22 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2733
2867
|
**kwargs):
|
|
2734
2868
|
Shell.__init__(self, parent,
|
|
2735
2869
|
locals=target.__dict__,
|
|
2736
|
-
interpShell=self,
|
|
2870
|
+
interpShell=self, # **kwds of InterpClass
|
|
2737
2871
|
InterpClass=Interpreter,
|
|
2738
2872
|
introText=introText,
|
|
2739
2873
|
startupScript=startupScript,
|
|
2740
|
-
execStartupScript=execStartupScript,
|
|
2874
|
+
execStartupScript=execStartupScript, # executes ~/.py
|
|
2741
2875
|
**kwargs)
|
|
2742
2876
|
EditorInterface.__init__(self)
|
|
2743
2877
|
|
|
2744
|
-
self.parent = parent
|
|
2878
|
+
self.parent = parent # parent<ShellFrame> is not Parent<AuiNotebook>
|
|
2745
2879
|
self.target = target
|
|
2746
2880
|
self.Name = name
|
|
2747
2881
|
|
|
2748
2882
|
wx.py.shell.USE_MAGIC = True
|
|
2749
|
-
wx.py.shell.magic = self.magic
|
|
2883
|
+
wx.py.shell.magic = self.magic # called when USE_MAGIC
|
|
2750
2884
|
|
|
2751
|
-
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate)
|
|
2885
|
+
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate) # skip to brace matching
|
|
2752
2886
|
self.Bind(stc.EVT_STC_CALLTIP_CLICK, self.OnCallTipClick)
|
|
2753
2887
|
|
|
2754
2888
|
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
|
|
@@ -2767,19 +2901,10 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2767
2901
|
|
|
2768
2902
|
def clear(evt):
|
|
2769
2903
|
## """Clear selection and message, no skip."""
|
|
2770
|
-
## *
|
|
2771
|
-
if self.CanEdit():
|
|
2772
|
-
with self.off_undocollection():
|
|
2773
|
-
self.ReplaceSelection("")
|
|
2774
|
-
self.message("")
|
|
2775
|
-
|
|
2776
|
-
def clear_autocomp(evt):
|
|
2777
|
-
## """Clear autocomp, selection, and message."""
|
|
2778
|
-
if self.AutoCompActive():
|
|
2779
|
-
self.AutoCompCancel()
|
|
2904
|
+
## *DO NOT* clear autocomp, so that the event can skip to AutoComp properly.
|
|
2780
2905
|
if self.CanEdit():
|
|
2781
2906
|
with self.off_undocollection():
|
|
2782
|
-
self.ReplaceSelection(
|
|
2907
|
+
self.ReplaceSelection('')
|
|
2783
2908
|
self.message("")
|
|
2784
2909
|
|
|
2785
2910
|
def fork(evt):
|
|
@@ -2792,12 +2917,10 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2792
2917
|
self.handler.update({ # DNA<Nautilus>
|
|
2793
2918
|
None : {
|
|
2794
2919
|
'interp_error' : [ None, self.on_interp_error ],
|
|
2795
|
-
'shell_deleted' : [ None, dispatch, self.
|
|
2920
|
+
'shell_deleted' : [ None, dispatch, self.on_shell_deleted ],
|
|
2796
2921
|
'shell_modified' : [ None, dispatch ],
|
|
2797
|
-
'shell_activated' : [ None, dispatch, self.
|
|
2798
|
-
'shell_inactivated' : [ None, dispatch, self.
|
|
2799
|
-
'*button* pressed' : [ None, dispatch, skip ],
|
|
2800
|
-
'*button* released' : [ None, dispatch, skip ],
|
|
2922
|
+
'shell_activated' : [ None, dispatch, self.on_shell_activated ],
|
|
2923
|
+
'shell_inactivated' : [ None, dispatch, self.on_shell_inactivated ],
|
|
2801
2924
|
},
|
|
2802
2925
|
-1 : { # original action of the wx.py.shell
|
|
2803
2926
|
'* pressed' : (0, skip, self.on_exit_escmap),
|
|
@@ -2814,36 +2937,39 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2814
2937
|
},
|
|
2815
2938
|
0 : { # Normal mode
|
|
2816
2939
|
'* pressed' : (0, skip),
|
|
2817
|
-
'* released' : (0, skip),
|
|
2940
|
+
'* released' : (0, skip, dispatch),
|
|
2941
|
+
'*button* pressed' : (0, skip, dispatch),
|
|
2818
2942
|
'escape pressed' : (-1, self.on_enter_escmap),
|
|
2819
2943
|
'space pressed' : (0, self.OnSpace),
|
|
2820
2944
|
'*backspace pressed' : (0, self.OnBackspace),
|
|
2821
|
-
'C-backspace pressed' : (0, _F(self.backward_kill_word)),
|
|
2822
|
-
'S-backspace pressed' : (0, _F(self.backward_kill_line)),
|
|
2823
2945
|
'enter pressed' : (0, self.OnEnter),
|
|
2824
2946
|
'C-enter pressed' : (0, _F(self.insertLineBreak)),
|
|
2825
2947
|
'C-S-enter pressed' : (0, _F(self.insertLineBreak)),
|
|
2826
|
-
'*enter pressed' : (0, ),
|
|
2948
|
+
'*enter pressed' : (0, ), # -> OnShowCompHistory 無効
|
|
2827
2949
|
'C-[ pressed' : (0, _F(self.goto_previous_mark_arrow)),
|
|
2828
2950
|
'C-S-[ pressed' : (0, _F(self.goto_previous_mark_arrow, selection=1)),
|
|
2829
2951
|
'C-] pressed' : (0, _F(self.goto_next_mark_arrow)),
|
|
2830
2952
|
'C-S-] pressed' : (0, _F(self.goto_next_mark_arrow, selection=1)),
|
|
2831
2953
|
'M-up pressed' : (0, _F(self.goto_previous_white_arrow)),
|
|
2832
2954
|
'M-down pressed' : (0, _F(self.goto_next_white_arrow)),
|
|
2833
|
-
# 'C-c pressed' : (0, skip),
|
|
2834
|
-
'C-S-c pressed' : (0, skip),
|
|
2955
|
+
# 'C-c pressed' : (0, skip), # -> spec-map
|
|
2956
|
+
'C-S-c pressed' : (0, skip), # -> Copy selected text, retaining prompts.
|
|
2835
2957
|
'C-v pressed' : (0, _F(self.Paste)),
|
|
2836
2958
|
'C-S-v pressed' : (0, _F(self.Paste, rectangle=1)),
|
|
2837
2959
|
'S-insert pressed' : (0, _F(self.Paste)),
|
|
2838
2960
|
'C-S-insert pressed' : (0, _F(self.Paste, rectangle=1)),
|
|
2839
|
-
'C-j pressed' : (0, self.eval_line),
|
|
2840
|
-
'M-j pressed' : (0, self.exec_region),
|
|
2961
|
+
'C-j pressed' : (0, _F(self.eval_line)),
|
|
2962
|
+
'M-j pressed' : (0, _F(self.exec_region)),
|
|
2841
2963
|
'C-h pressed' : (0, self.call_helpTip),
|
|
2842
2964
|
'M-h pressed' : (0, self.call_helpDoc),
|
|
2843
|
-
'. pressed' : (2, self.OnEnterDot),
|
|
2844
2965
|
'tab pressed' : (1, self.call_history_comp),
|
|
2845
2966
|
'M-p pressed' : (1, self.call_history_comp),
|
|
2846
2967
|
'M-n pressed' : (1, self.call_history_comp),
|
|
2968
|
+
'. pressed' : (2, self.OnEnterDot),
|
|
2969
|
+
'C-. pressed' : (2, self.call_word_autocomp),
|
|
2970
|
+
'C-/ pressed' : (3, self.call_apropos_autocomp),
|
|
2971
|
+
'C-, pressed' : (4, self.call_text_autocomp),
|
|
2972
|
+
'C-m pressed' : (5, self.call_module_autocomp),
|
|
2847
2973
|
'M-. pressed' : (2, self.call_word_autocomp),
|
|
2848
2974
|
'M-/ pressed' : (3, self.call_apropos_autocomp),
|
|
2849
2975
|
'M-, pressed' : (4, self.call_text_autocomp),
|
|
@@ -2859,10 +2985,10 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2859
2985
|
'S-left released' : (1, self.call_history_comp),
|
|
2860
2986
|
'S-right pressed' : (1, skip),
|
|
2861
2987
|
'S-right released' : (1, self.call_history_comp),
|
|
2862
|
-
'tab pressed' : (1, _F(self._on_completion,
|
|
2863
|
-
'S-tab pressed' : (1, _F(self._on_completion,
|
|
2864
|
-
'M-p pressed' : (1, _F(self._on_completion,
|
|
2865
|
-
'M-n pressed' : (1, _F(self._on_completion,
|
|
2988
|
+
'tab pressed' : (1, _F(self._on_completion, step=1)), # 古いヒストリへ進む
|
|
2989
|
+
'S-tab pressed' : (1, _F(self._on_completion, step=-1)), # 新しいヒストリへ戻る
|
|
2990
|
+
'M-p pressed' : (1, _F(self._on_completion, step=1)),
|
|
2991
|
+
'M-n pressed' : (1, _F(self._on_completion, step=-1)),
|
|
2866
2992
|
'[a-z0-9_] pressed' : (1, skip),
|
|
2867
2993
|
'[a-z0-9_] released' : (1, self.call_history_comp),
|
|
2868
2994
|
'S-[a-z\\] pressed' : (1, skip),
|
|
@@ -2875,11 +3001,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2875
3001
|
'*f[0-9]* pressed' : (1, ),
|
|
2876
3002
|
},
|
|
2877
3003
|
2 : { # word auto completion AS-mode
|
|
2878
|
-
'quit' : (0, clear_autocomp),
|
|
2879
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3004
|
+
'quit' : (0, self.clear_autocomp),
|
|
3005
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2880
3006
|
'tab pressed' : (0, clear, skip),
|
|
2881
3007
|
'enter pressed' : (0, clear, skip),
|
|
2882
|
-
'escape pressed' : (0,
|
|
3008
|
+
'escape pressed' : (0, clear, skip),
|
|
2883
3009
|
'up pressed' : (2, skip, self.on_completion_backward),
|
|
2884
3010
|
'down pressed' : (2, skip, self.on_completion_forward),
|
|
2885
3011
|
'*left pressed' : (2, skip),
|
|
@@ -2894,8 +3020,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2894
3020
|
'*delete pressed' : (2, skip),
|
|
2895
3021
|
'*backspace pressed' : (2, self.OnBackspace),
|
|
2896
3022
|
'*backspace released' : (2, self.call_word_autocomp),
|
|
2897
|
-
|
|
2898
|
-
'C-j pressed' : (2, self.eval_line),
|
|
3023
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
3024
|
+
'C-j pressed' : (2, _F(self.eval_line)),
|
|
2899
3025
|
'*alt pressed' : (2, ),
|
|
2900
3026
|
'*ctrl pressed' : (2, ),
|
|
2901
3027
|
'*shift pressed' : (2, ),
|
|
@@ -2903,11 +3029,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2903
3029
|
'*f[0-9]* pressed' : (2, ),
|
|
2904
3030
|
},
|
|
2905
3031
|
3 : { # apropos auto completion AS-mode
|
|
2906
|
-
'quit' : (0, clear_autocomp),
|
|
2907
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3032
|
+
'quit' : (0, self.clear_autocomp),
|
|
3033
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2908
3034
|
'tab pressed' : (0, clear, skip),
|
|
2909
3035
|
'enter pressed' : (0, clear, skip),
|
|
2910
|
-
'escape pressed' : (0,
|
|
3036
|
+
'escape pressed' : (0, clear, skip),
|
|
2911
3037
|
'up pressed' : (3, skip, self.on_completion_backward),
|
|
2912
3038
|
'down pressed' : (3, skip, self.on_completion_forward),
|
|
2913
3039
|
'*left pressed' : (3, skip),
|
|
@@ -2922,8 +3048,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2922
3048
|
'*delete pressed' : (3, skip),
|
|
2923
3049
|
'*backspace pressed' : (3, self.OnBackspace),
|
|
2924
3050
|
'*backspace released' : (3, self.call_apropos_autocomp),
|
|
2925
|
-
|
|
2926
|
-
'C-j pressed' : (3, self.eval_line),
|
|
3051
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
3052
|
+
'C-j pressed' : (3, _F(self.eval_line)),
|
|
2927
3053
|
'*alt pressed' : (3, ),
|
|
2928
3054
|
'*ctrl pressed' : (3, ),
|
|
2929
3055
|
'*shift pressed' : (3, ),
|
|
@@ -2931,11 +3057,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2931
3057
|
'*f[0-9]* pressed' : (3, ),
|
|
2932
3058
|
},
|
|
2933
3059
|
4 : { # text auto completion AS-mode
|
|
2934
|
-
'quit' : (0, clear_autocomp),
|
|
2935
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3060
|
+
'quit' : (0, self.clear_autocomp),
|
|
3061
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2936
3062
|
'tab pressed' : (0, clear, skip),
|
|
2937
3063
|
'enter pressed' : (0, clear, skip),
|
|
2938
|
-
'escape pressed' : (0,
|
|
3064
|
+
'escape pressed' : (0, clear, skip),
|
|
2939
3065
|
'up pressed' : (4, skip, self.on_completion_backward),
|
|
2940
3066
|
'down pressed' : (4, skip, self.on_completion_forward),
|
|
2941
3067
|
'*left pressed' : (4, skip),
|
|
@@ -2950,8 +3076,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2950
3076
|
'*delete pressed' : (4, skip),
|
|
2951
3077
|
'*backspace pressed' : (4, self.OnBackspace),
|
|
2952
3078
|
'*backspace released' : (4, self.call_text_autocomp),
|
|
2953
|
-
|
|
2954
|
-
'C-j pressed' : (4, self.eval_line),
|
|
3079
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
3080
|
+
'C-j pressed' : (4, _F(self.eval_line)),
|
|
2955
3081
|
'*alt pressed' : (4, ),
|
|
2956
3082
|
'*ctrl pressed' : (4, ),
|
|
2957
3083
|
'*shift pressed' : (4, ),
|
|
@@ -2959,11 +3085,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2959
3085
|
'*f[0-9]* pressed' : (4, ),
|
|
2960
3086
|
},
|
|
2961
3087
|
5 : { # module auto completion AS-mode
|
|
2962
|
-
'quit' : (0, clear_autocomp),
|
|
2963
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3088
|
+
'quit' : (0, self.clear_autocomp),
|
|
3089
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2964
3090
|
'tab pressed' : (0, clear, skip),
|
|
2965
3091
|
'enter pressed' : (0, clear, skip),
|
|
2966
|
-
'escape pressed' : (0,
|
|
3092
|
+
'escape pressed' : (0, clear, skip),
|
|
2967
3093
|
'up pressed' : (5, skip, self.on_completion_backward),
|
|
2968
3094
|
'down pressed' : (5, skip, self.on_completion_forward),
|
|
2969
3095
|
'*left pressed' : (5, skip),
|
|
@@ -2975,11 +3101,12 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2975
3101
|
'S-[a-z\\] pressed' : (5, skip),
|
|
2976
3102
|
'S-[a-z\\] released' : (5, self.call_module_autocomp),
|
|
2977
3103
|
'\\ released' : (5, self.call_module_autocomp),
|
|
3104
|
+
'C-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
|
|
2978
3105
|
'M-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
|
|
2979
3106
|
'*delete pressed' : (5, skip),
|
|
2980
3107
|
'*backspace pressed' : (5, self.OnBackspace),
|
|
2981
3108
|
'*backspace released' : (5, self.call_module_autocomp),
|
|
2982
|
-
|
|
3109
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
2983
3110
|
'*alt pressed' : (5, ),
|
|
2984
3111
|
'*ctrl pressed' : (5, ),
|
|
2985
3112
|
'*shift pressed' : (5, ),
|
|
@@ -2990,24 +3117,24 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2990
3117
|
|
|
2991
3118
|
self.wrap(0)
|
|
2992
3119
|
self.show_folder()
|
|
2993
|
-
self.set_stylus(
|
|
3120
|
+
self.set_stylus(Stylus.py_shell_mode)
|
|
2994
3121
|
|
|
2995
|
-
##
|
|
3122
|
+
## Delete unnecessary arrows at startup.
|
|
2996
3123
|
del self.white_arrow
|
|
2997
3124
|
del self.red_arrow
|
|
2998
3125
|
|
|
2999
3126
|
self.__text = ''
|
|
3000
|
-
|
|
3127
|
+
|
|
3001
3128
|
def trace_position(self):
|
|
3002
3129
|
_text, lp = self.CurLine
|
|
3003
3130
|
self.message("{:>6d}:{} ({})".format(self.cline, lp, self.cpos), pane=-1)
|
|
3004
|
-
|
|
3131
|
+
|
|
3005
3132
|
def OnDestroy(self, evt):
|
|
3006
3133
|
if evt.EventObject is self:
|
|
3007
3134
|
self.handler('shell_deleted', self)
|
|
3008
3135
|
evt.Skip()
|
|
3009
|
-
|
|
3010
|
-
def OnUpdate(self, evt):
|
|
3136
|
+
|
|
3137
|
+
def OnUpdate(self, evt): #<wx._stc.StyledTextEvent>
|
|
3011
3138
|
if evt.Updated & (stc.STC_UPDATE_SELECTION | stc.STC_UPDATE_CONTENT):
|
|
3012
3139
|
self.trace_position()
|
|
3013
3140
|
if self.handler.current_state == 0:
|
|
@@ -3016,18 +3143,18 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3016
3143
|
name, argspec, tip = self.interp.getCallTip(text)
|
|
3017
3144
|
if tip:
|
|
3018
3145
|
tip = tip.splitlines()[0]
|
|
3019
|
-
self.message(tip)
|
|
3146
|
+
self.message(tip) # clear if no tip
|
|
3020
3147
|
self.__text = text
|
|
3021
3148
|
if evt.Updated & stc.STC_UPDATE_CONTENT:
|
|
3022
3149
|
self.handler('shell_modified', self)
|
|
3023
3150
|
evt.Skip()
|
|
3024
|
-
|
|
3151
|
+
|
|
3025
3152
|
def OnCallTipClick(self, evt):
|
|
3026
3153
|
if self.CallTipActive():
|
|
3027
3154
|
self.CallTipCancel()
|
|
3028
3155
|
self.parent.handler('add_help', self._calltips[1])
|
|
3029
3156
|
evt.Skip()
|
|
3030
|
-
|
|
3157
|
+
|
|
3031
3158
|
def OnSpace(self, evt):
|
|
3032
3159
|
"""Called when space pressed."""
|
|
3033
3160
|
if not self.CanEdit():
|
|
@@ -3037,133 +3164,142 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3037
3164
|
or re.match(r"from\s*$", cmdl)\
|
|
3038
3165
|
or re.match(r"from\s+([\w.]+)\s+import\s*", cmdl):
|
|
3039
3166
|
self.ReplaceSelection(' ')
|
|
3040
|
-
self.handler('M-m pressed', None)
|
|
3167
|
+
self.handler('M-m pressed', None) # => call_module_autocomp
|
|
3041
3168
|
return
|
|
3042
3169
|
evt.Skip()
|
|
3043
|
-
|
|
3170
|
+
|
|
3044
3171
|
def OnBackspace(self, evt):
|
|
3045
3172
|
"""Called when backspace pressed.
|
|
3046
3173
|
Backspace-guard from autocomp eating over a prompt whitespace.
|
|
3047
3174
|
"""
|
|
3048
3175
|
if self.cpos == self.bolc:
|
|
3049
|
-
self.handler('quit', evt)
|
|
3176
|
+
self.handler('quit', evt) # Don't eat backward prompt
|
|
3050
3177
|
return
|
|
3051
3178
|
evt.Skip()
|
|
3052
|
-
|
|
3053
|
-
@
|
|
3054
|
-
def
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
self.
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
|
|
3076
|
-
|
|
3179
|
+
|
|
3180
|
+
@editable
|
|
3181
|
+
def backward_kill_word(self): # (override)
|
|
3182
|
+
if not self.SelectedText:
|
|
3183
|
+
if self.cpos <= self.bolc:
|
|
3184
|
+
return
|
|
3185
|
+
text, lp = self.CurLine
|
|
3186
|
+
if text[:lp] == sys.ps2:
|
|
3187
|
+
self.cpos -= lp # Select ps2:prompt
|
|
3188
|
+
self.WordLeftExtend() # Select cr/lf chunks
|
|
3189
|
+
else:
|
|
3190
|
+
self.WordLeftExtend()
|
|
3191
|
+
q = max(self.bol, self.bolc) # for debugger mode: bol <= bolc
|
|
3192
|
+
if self.cpos < q:
|
|
3193
|
+
self.cpos = q # Don't skip back ps2:prompt
|
|
3194
|
+
self.ReplaceSelection('')
|
|
3195
|
+
|
|
3196
|
+
@editable
|
|
3197
|
+
def backward_kill_line(self): # (override)
|
|
3198
|
+
if not self.SelectedText:
|
|
3199
|
+
if self.cpos <= self.bolc:
|
|
3200
|
+
return
|
|
3201
|
+
text, lp = self.CurLine
|
|
3202
|
+
if text[:lp] == sys.ps2:
|
|
3203
|
+
self.cpos -= lp # Select ps2:prompt
|
|
3204
|
+
self.WordLeftExtend() # Select cr/lf chunks
|
|
3205
|
+
else:
|
|
3206
|
+
q = max(self.bol, self.bolc) # for debugger mode: bol <= bolc
|
|
3207
|
+
if self.cpos > q:
|
|
3208
|
+
self.cpos = q
|
|
3209
|
+
else:
|
|
3210
|
+
self.WordLeftExtend() # Select cr/lf chunks
|
|
3211
|
+
self.ReplaceSelection('')
|
|
3212
|
+
|
|
3077
3213
|
def OnEnter(self, evt):
|
|
3078
3214
|
"""Called when enter pressed."""
|
|
3079
3215
|
if not self.CanEdit():
|
|
3080
|
-
self.goto_char(self.eolc)
|
|
3216
|
+
self.goto_char(self.eolc) # go to end of command line
|
|
3081
3217
|
return
|
|
3082
|
-
if self.AutoCompActive():
|
|
3218
|
+
if self.AutoCompActive(): # skip to auto completion
|
|
3083
3219
|
evt.Skip()
|
|
3084
3220
|
return
|
|
3085
3221
|
if self.CallTipActive():
|
|
3086
3222
|
self.CallTipCancel()
|
|
3087
3223
|
|
|
3088
|
-
##
|
|
3224
|
+
## Skip to wx.py.magic if text begins with !(sx), ?(info), and ??(help).
|
|
3089
3225
|
text = self.cmdline
|
|
3090
3226
|
if not text or text[0] in '!?':
|
|
3091
3227
|
evt.Skip()
|
|
3092
3228
|
return
|
|
3093
3229
|
|
|
3094
|
-
##
|
|
3230
|
+
## Cast magic for `@? (Note: PY35 supports @(matmul)-operator).
|
|
3095
3231
|
tokens = list(split_words(text))
|
|
3096
3232
|
if any(x in tokens for x in '`@?$'):
|
|
3097
3233
|
cmd = self.magic_interpret(tokens)
|
|
3098
3234
|
if '\n' in cmd:
|
|
3099
|
-
self.Execute(cmd)
|
|
3235
|
+
self.Execute(cmd) # => multi-line commands
|
|
3100
3236
|
else:
|
|
3101
|
-
self.run(cmd, verbose=0, prompt=0)
|
|
3237
|
+
self.run(cmd, verbose=0, prompt=0) # => push(cmd)
|
|
3102
3238
|
return
|
|
3103
3239
|
|
|
3104
3240
|
self.exec_cmdline()
|
|
3105
|
-
|
|
3106
|
-
|
|
3241
|
+
# evt.Skip() # => processLine
|
|
3242
|
+
|
|
3107
3243
|
def OnEnterDot(self, evt):
|
|
3108
3244
|
"""Called when dot [.] pressed."""
|
|
3109
3245
|
if not self.CanEdit():
|
|
3110
3246
|
self.handler('quit', evt)
|
|
3111
3247
|
return
|
|
3112
3248
|
p = self.cpos
|
|
3113
|
-
|
|
3249
|
+
lst = self.get_style(p-1)
|
|
3114
3250
|
rst = self.get_style(p)
|
|
3115
3251
|
if p == self.bolc:
|
|
3116
|
-
self.ReplaceSelection('self')
|
|
3117
|
-
elif
|
|
3252
|
+
self.ReplaceSelection('self') # replace [.] --> [self.]
|
|
3253
|
+
elif lst in ('space', 'sep', 'lparen'):
|
|
3118
3254
|
self.ReplaceSelection('self')
|
|
3119
|
-
elif
|
|
3120
|
-
self.handler('quit', evt)
|
|
3255
|
+
elif lst not in ('moji', 'word', 'rparen') or rst == 'word':
|
|
3256
|
+
self.handler('quit', evt) # Don't enter autocomp
|
|
3121
3257
|
evt.Skip()
|
|
3122
|
-
|
|
3258
|
+
|
|
3123
3259
|
def on_enter_escmap(self, evt):
|
|
3124
3260
|
self.__caret_mode = self.CaretPeriod
|
|
3125
3261
|
self.CaretPeriod = 0
|
|
3126
3262
|
self.message("ESC-")
|
|
3127
|
-
|
|
3263
|
+
|
|
3128
3264
|
def on_exit_escmap(self, evt):
|
|
3129
3265
|
self.CaretPeriod = self.__caret_mode
|
|
3130
3266
|
self.message("ESC {}".format(evt.key))
|
|
3131
|
-
if self.eolc < self.bolc:
|
|
3267
|
+
if self.eolc < self.bolc: # Check if prompt is in valid state.
|
|
3132
3268
|
self.goto_char(self.eolc)
|
|
3133
|
-
self.promptPosEnd = 0
|
|
3269
|
+
self.promptPosEnd = 0 # Enabale write(prompt).
|
|
3134
3270
|
self.prompt()
|
|
3135
3271
|
self.AnnotationClearAll()
|
|
3136
|
-
|
|
3272
|
+
|
|
3137
3273
|
def on_enter_notemode(self, evt):
|
|
3138
3274
|
self.noteMode = True
|
|
3139
3275
|
self.__caret_mode = self.CaretForeground
|
|
3140
3276
|
self.CaretForeground = 'red'
|
|
3141
3277
|
self.message("Note mode")
|
|
3142
|
-
|
|
3278
|
+
|
|
3143
3279
|
def on_exit_notemode(self, evt):
|
|
3144
3280
|
self.noteMode = False
|
|
3145
3281
|
self.CaretForeground = self.__caret_mode
|
|
3146
3282
|
self.goto_char(self.eolc)
|
|
3147
|
-
self.promptPosEnd = 0
|
|
3283
|
+
self.promptPosEnd = 0 # Enabale write(prompt).
|
|
3148
3284
|
self.prompt()
|
|
3149
3285
|
self.message("")
|
|
3150
|
-
|
|
3286
|
+
|
|
3151
3287
|
def goto_next_white_arrow(self):
|
|
3152
|
-
self.goto_next_marker(0b010)
|
|
3153
|
-
|
|
3288
|
+
self.goto_next_marker(0b010) # next white-arrow
|
|
3289
|
+
|
|
3154
3290
|
def goto_previous_white_arrow(self):
|
|
3155
|
-
self.goto_previous_marker(0b010)
|
|
3156
|
-
|
|
3291
|
+
self.goto_previous_marker(0b010) # previous white-arrow
|
|
3292
|
+
|
|
3157
3293
|
def goto_next_mark_arrow(self, selection=False):
|
|
3158
|
-
self.goto_next_marker(0b110, selection)
|
|
3159
|
-
|
|
3294
|
+
self.goto_next_marker(0b110, selection) # next white/red-arrow
|
|
3295
|
+
|
|
3160
3296
|
def goto_previous_mark_arrow(self, selection=False):
|
|
3161
|
-
self.goto_previous_marker(0b110, selection)
|
|
3162
|
-
|
|
3297
|
+
self.goto_previous_marker(0b110, selection) # previous white/red-arrow
|
|
3298
|
+
|
|
3163
3299
|
## --------------------------------
|
|
3164
|
-
## Magic caster of the shell
|
|
3300
|
+
## Magic caster of the shell.
|
|
3165
3301
|
## --------------------------------
|
|
3166
|
-
|
|
3302
|
+
|
|
3167
3303
|
@classmethod
|
|
3168
3304
|
def magic(self, cmd):
|
|
3169
3305
|
"""Called before command pushed.
|
|
@@ -3175,7 +3311,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3175
3311
|
elif cmd[0] == '?': cmd = 'info({})'.format(cmd[1:])
|
|
3176
3312
|
elif cmd[0] == '!': cmd = 'sx({!r})'.format(cmd[1:])
|
|
3177
3313
|
return cmd
|
|
3178
|
-
|
|
3314
|
+
|
|
3179
3315
|
@classmethod
|
|
3180
3316
|
def magic_interpret(self, tokens):
|
|
3181
3317
|
"""Called when [Enter] command, or eval-time for tooltip.
|
|
@@ -3190,8 +3326,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3190
3326
|
Note:
|
|
3191
3327
|
This is called before run, execute, and original magic.
|
|
3192
3328
|
"""
|
|
3193
|
-
sep1 = "
|
|
3194
|
-
sep2 = "`@=+-/*%<>&|^~,; \t#"
|
|
3329
|
+
sep1 = "`@=;\r\n#" # [`] no ops, no spaces, no comma
|
|
3330
|
+
sep2 = "`@=+-/*%<>&|^~,; \t\r\n#" # [@] ops, delims, and whitespaces
|
|
3195
3331
|
|
|
3196
3332
|
def _popiter(ls, f):
|
|
3197
3333
|
pred = f if callable(f) else re.compile(f).match
|
|
@@ -3207,7 +3343,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3207
3343
|
for i, c in enumerate(tokens):
|
|
3208
3344
|
rest = tokens[i+1:]
|
|
3209
3345
|
|
|
3210
|
-
if c == '@' and not lhs.strip() and '\n' in rest:
|
|
3346
|
+
if c == '@' and not lhs.strip() and '\n' in rest: # @decor
|
|
3211
3347
|
pass
|
|
3212
3348
|
|
|
3213
3349
|
elif c == '@':
|
|
@@ -3217,16 +3353,21 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3217
3353
|
## func(a,b,c) @debug --> func,a,b,c @debug
|
|
3218
3354
|
if rhs in ("debug", "profile", "timeit"):
|
|
3219
3355
|
if lhs[-1] in ')':
|
|
3220
|
-
|
|
3356
|
+
R = next(split_parts(lhs, reverse=1))
|
|
3357
|
+
L = lhs[:-len(R)]
|
|
3221
3358
|
if not L:
|
|
3222
3359
|
lhs = "{!r}".format(R[1:-1])
|
|
3223
|
-
|
|
3360
|
+
else:
|
|
3224
3361
|
lhs = "{}, {}".format(L, R[1:-1])
|
|
3225
3362
|
|
|
3226
3363
|
## @(y1,,,yn) --> @partial(y1,,,yn)
|
|
3227
3364
|
elif rhs.startswith('('):
|
|
3228
3365
|
rhs = re.sub(r"^\((.*)\)", r"partial(\1)", rhs, flags=re.S)
|
|
3229
3366
|
|
|
3367
|
+
## obj @.method --> (obj).method
|
|
3368
|
+
elif rhs.startswith('.'):
|
|
3369
|
+
return self.magic_interpret([f"({lhs}){rhs}"] + rest)
|
|
3370
|
+
|
|
3230
3371
|
return self.magic_interpret([f"{rhs}({lhs})"] + rest)
|
|
3231
3372
|
|
|
3232
3373
|
if c == '`':
|
|
@@ -3245,40 +3386,44 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3245
3386
|
return lhs + c + self.magic_interpret(rest)
|
|
3246
3387
|
|
|
3247
3388
|
if c == sys.ps2.strip():
|
|
3248
|
-
rhs = ''.join(_popiter(rest, "[ \t\r\n]"))
|
|
3389
|
+
rhs = ''.join(_popiter(rest, "[ \t\r\n]")) # feed
|
|
3249
3390
|
return lhs + c + rhs + self.magic_interpret(rest)
|
|
3250
3391
|
|
|
3251
3392
|
if c.startswith('#'):
|
|
3252
|
-
rhs = ''.join(_popiter(rest, "[^\r\n]"))
|
|
3393
|
+
rhs = ''.join(_popiter(rest, "[^\r\n]")) # skip comment
|
|
3253
3394
|
return lhs + c + rhs + self.magic_interpret(rest)
|
|
3254
3395
|
|
|
3255
|
-
lhs += c
|
|
3396
|
+
lhs += c # store in lhs; no more processing
|
|
3256
3397
|
return lhs
|
|
3257
|
-
|
|
3258
|
-
def
|
|
3398
|
+
|
|
3399
|
+
def on_shell_deleted(self, shell):
|
|
3259
3400
|
"""Called before shell:self is killed.
|
|
3260
3401
|
Delete target shell to prevent referencing the dead shell.
|
|
3261
3402
|
"""
|
|
3262
3403
|
def _del():
|
|
3404
|
+
obj = self.target
|
|
3263
3405
|
try:
|
|
3264
|
-
if not
|
|
3265
|
-
del
|
|
3406
|
+
if not obj.shell:
|
|
3407
|
+
del obj.shell # delete the facade <wx.py.shell.ShellFacade>
|
|
3266
3408
|
except AttributeError:
|
|
3267
3409
|
pass
|
|
3268
3410
|
wx.CallAfter(_del)
|
|
3269
|
-
|
|
3270
|
-
def
|
|
3411
|
+
|
|
3412
|
+
def on_shell_activated(self, shell):
|
|
3271
3413
|
"""Called when the shell:self is activated.
|
|
3272
|
-
Reset localvars assigned for the shell target.
|
|
3414
|
+
Reset localvars assigned for the shell target. cf. target.setter.
|
|
3273
3415
|
"""
|
|
3274
3416
|
self.trace_position()
|
|
3275
|
-
|
|
3417
|
+
obj = self.target
|
|
3276
3418
|
try:
|
|
3277
|
-
self
|
|
3419
|
+
obj.self = obj
|
|
3420
|
+
obj.this = inspect.getmodule(obj)
|
|
3421
|
+
obj.shell = self # Overwrite the facade <wx.py.shell.ShellFacade>
|
|
3278
3422
|
except AttributeError:
|
|
3279
3423
|
pass
|
|
3280
|
-
|
|
3281
|
-
|
|
3424
|
+
self.parent.handler('title_window', obj)
|
|
3425
|
+
|
|
3426
|
+
def on_shell_inactivated(self, shell):
|
|
3282
3427
|
"""Called when shell:self is inactivated.
|
|
3283
3428
|
Remove target localvars assigned for the shell target.
|
|
3284
3429
|
"""
|
|
@@ -3286,7 +3431,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3286
3431
|
self.AutoCompCancel()
|
|
3287
3432
|
if self.CallTipActive():
|
|
3288
3433
|
self.CallTipCancel()
|
|
3289
|
-
|
|
3434
|
+
|
|
3290
3435
|
def on_text_input(self, text):
|
|
3291
3436
|
"""Called when [Enter] text (before push).
|
|
3292
3437
|
Mark points, reset history point, etc.
|
|
@@ -3297,7 +3442,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3297
3442
|
if text.rstrip():
|
|
3298
3443
|
self.__eolc_mark = self.eolc
|
|
3299
3444
|
self.historyIndex = -1
|
|
3300
|
-
|
|
3445
|
+
|
|
3301
3446
|
def on_text_output(self, text):
|
|
3302
3447
|
"""Called when [Enter] text (after push).
|
|
3303
3448
|
Set markers at the last command line.
|
|
@@ -3306,28 +3451,28 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3306
3451
|
Argument `text` is raw output:str with no magic cast.
|
|
3307
3452
|
"""
|
|
3308
3453
|
ln = self.LineFromPosition(self.bolc)
|
|
3309
|
-
err = re.findall(
|
|
3310
|
-
self.add_marker(ln, 1 if not err else 2)
|
|
3454
|
+
err = re.findall(py_trace_re, text, re.M)
|
|
3455
|
+
self.add_marker(ln, 1 if not err else 2) # 1:white-arrow 2:red-arrow
|
|
3311
3456
|
return (not err)
|
|
3312
|
-
|
|
3457
|
+
|
|
3313
3458
|
def on_interp_error(self, e):
|
|
3314
3459
|
ln = self.LineFromPosition(self.bolc)
|
|
3315
|
-
self.
|
|
3316
|
-
|
|
3460
|
+
self.red_pointer = ln + e.lineno - 1
|
|
3461
|
+
|
|
3317
3462
|
## --------------------------------
|
|
3318
|
-
## Attributes of the shell
|
|
3463
|
+
## Attributes of the shell.
|
|
3319
3464
|
## --------------------------------
|
|
3320
|
-
|
|
3465
|
+
|
|
3321
3466
|
@property
|
|
3322
3467
|
def bolc(self):
|
|
3323
|
-
"Beginning of command-line."
|
|
3468
|
+
"""Beginning of command-line."""
|
|
3324
3469
|
return self.promptPosEnd
|
|
3325
|
-
|
|
3470
|
+
|
|
3326
3471
|
@property
|
|
3327
3472
|
def eolc(self):
|
|
3328
|
-
"End of command-line."
|
|
3473
|
+
"""End of command-line."""
|
|
3329
3474
|
return self.TextLength
|
|
3330
|
-
|
|
3475
|
+
|
|
3331
3476
|
@property
|
|
3332
3477
|
def bol(self):
|
|
3333
3478
|
"""Beginning of line (override) excluding prompt."""
|
|
@@ -3337,30 +3482,35 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3337
3482
|
lp -= len(ps)
|
|
3338
3483
|
break
|
|
3339
3484
|
return self.cpos - lp
|
|
3340
|
-
|
|
3485
|
+
|
|
3341
3486
|
@property
|
|
3342
3487
|
def cmdline(self):
|
|
3343
3488
|
"""Full multi-line command in the current prompt."""
|
|
3344
3489
|
return self.GetTextRange(self.bolc, self.eolc)
|
|
3345
|
-
|
|
3490
|
+
|
|
3346
3491
|
## cf. getCommand() -> caret-line that starts with a prompt
|
|
3347
3492
|
## cf. getMultilineCommand() -> caret-multi-line that starts with a prompt
|
|
3348
|
-
## [BUG 4.1.1] Don't use for current prompt --> Fixed in 4.2.0.
|
|
3349
|
-
|
|
3350
|
-
def getMultilineCommand(self):
|
|
3493
|
+
## [BUG ver 4.1.1] Don't use for current prompt --> Fixed in wx ver 4.2.0.
|
|
3494
|
+
|
|
3495
|
+
def getMultilineCommand(self, rstrip=True):
|
|
3351
3496
|
"""Extract a multi-line command which starts with a prompt.
|
|
3352
3497
|
|
|
3353
3498
|
(override) Don't remove trailing ps2 + spaces.
|
|
3354
3499
|
Don't invoke ``GotoLine``.
|
|
3355
3500
|
"""
|
|
3356
|
-
region = self.
|
|
3501
|
+
region = self.get_command_region(self.cline)
|
|
3357
3502
|
if region:
|
|
3358
3503
|
p, q = (self.PositionFromLine(x) for x in region)
|
|
3359
3504
|
p += len(sys.ps1)
|
|
3360
|
-
|
|
3505
|
+
command = self.GetTextRange(p, q).rstrip(os.linesep) # remove the last cr/lf
|
|
3506
|
+
if rstrip:
|
|
3507
|
+
command = command.replace(os.linesep + sys.ps2, '\n')
|
|
3508
|
+
command = command.rstrip()
|
|
3509
|
+
command = command.replace('\n', os.linesep + sys.ps2)
|
|
3510
|
+
return command
|
|
3361
3511
|
return ''
|
|
3362
|
-
|
|
3363
|
-
def
|
|
3512
|
+
|
|
3513
|
+
def get_command_region(self, line):
|
|
3364
3514
|
"""Line numbers of prompt head and tail containing the line."""
|
|
3365
3515
|
lc = line
|
|
3366
3516
|
le = lc + 1
|
|
@@ -3369,7 +3519,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3369
3519
|
if not text.startswith(sys.ps2):
|
|
3370
3520
|
break
|
|
3371
3521
|
lc -= 1
|
|
3372
|
-
if not text.startswith(sys.ps1):
|
|
3522
|
+
if not text.startswith(sys.ps1): # bad region
|
|
3373
3523
|
return None
|
|
3374
3524
|
while le < self.LineCount:
|
|
3375
3525
|
text = self.GetLine(le)
|
|
@@ -3377,11 +3527,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3377
3527
|
break
|
|
3378
3528
|
le += 1
|
|
3379
3529
|
return lc, le
|
|
3380
|
-
|
|
3530
|
+
|
|
3381
3531
|
## --------------------------------
|
|
3382
|
-
## Execution methods of the shell
|
|
3532
|
+
## Execution methods of the shell.
|
|
3383
3533
|
## --------------------------------
|
|
3384
|
-
|
|
3534
|
+
|
|
3385
3535
|
def push(self, command, **kwargs):
|
|
3386
3536
|
"""Send command to the interpreter for execution.
|
|
3387
3537
|
|
|
@@ -3389,7 +3539,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3389
3539
|
"""
|
|
3390
3540
|
self.on_text_input(command)
|
|
3391
3541
|
Shell.push(self, command, **kwargs)
|
|
3392
|
-
|
|
3542
|
+
|
|
3393
3543
|
def addHistory(self, command):
|
|
3394
3544
|
"""Add command to the command history.
|
|
3395
3545
|
|
|
@@ -3399,7 +3549,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3399
3549
|
if not command:
|
|
3400
3550
|
return
|
|
3401
3551
|
|
|
3402
|
-
## この段階では push
|
|
3552
|
+
## この段階では push された直後で,次のようになっている.
|
|
3403
3553
|
## bolc : beginning of command-line
|
|
3404
3554
|
## eolc : end of the output-buffer
|
|
3405
3555
|
try:
|
|
@@ -3407,7 +3557,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3407
3557
|
output = self.GetTextRange(self.__eolc_mark, self.eolc)
|
|
3408
3558
|
|
|
3409
3559
|
input = self.regulate_cmd(input)
|
|
3410
|
-
Shell.addHistory(self, input)
|
|
3560
|
+
Shell.addHistory(self, input) # => self.history
|
|
3411
3561
|
|
|
3412
3562
|
noerr = self.on_text_output(output)
|
|
3413
3563
|
if noerr:
|
|
@@ -3416,18 +3566,30 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3416
3566
|
command = self.fixLineEndings(command)
|
|
3417
3567
|
self.parent.handler('add_log', command + os.linesep, noerr)
|
|
3418
3568
|
except AttributeError:
|
|
3419
|
-
## execStartupScript 実行時は出力先 (owner)
|
|
3420
|
-
## shell.__init__
|
|
3569
|
+
## execStartupScript 実行時は出力先 (owner) が存在しない.
|
|
3570
|
+
## shell.__init__ よりも先に実行される.
|
|
3421
3571
|
pass
|
|
3422
|
-
|
|
3572
|
+
|
|
3573
|
+
def setBuiltinKeywords(self):
|
|
3574
|
+
"""Create pseudo keywords as part of builtins.
|
|
3575
|
+
|
|
3576
|
+
(override) Don't add `close`, `exit` and `quit` to builtins.
|
|
3577
|
+
"""
|
|
3578
|
+
from wx.py.path import ls, cd, pwd, sx
|
|
3579
|
+
builtins.cd = cd
|
|
3580
|
+
builtins.ls = ls
|
|
3581
|
+
builtins.pwd = pwd
|
|
3582
|
+
builtins.sx = sx
|
|
3583
|
+
|
|
3423
3584
|
def execStartupScript(self, su):
|
|
3424
3585
|
"""Execute the user's PYTHONSTARTUP script if they have one.
|
|
3425
3586
|
|
|
3426
3587
|
(override) Add globals when executing su:startupScript.
|
|
3427
|
-
|
|
3588
|
+
Don't add '_f' to globals when executing su:startupScript.
|
|
3589
|
+
Don't add intro text in the history.
|
|
3428
3590
|
"""
|
|
3429
|
-
keys = set(self.locals.keys())
|
|
3430
|
-
self.promptPosEnd = self.TextLength
|
|
3591
|
+
keys = set(self.locals.keys()) # to check for locals map changes.
|
|
3592
|
+
self.promptPosEnd = self.TextLength # Fix history point
|
|
3431
3593
|
if su and os.path.isfile(su):
|
|
3432
3594
|
self.push("print('Startup script executed:', {0!r})\n".format(su))
|
|
3433
3595
|
self.push("with open({0!r}) as _f: exec(_f.read())\n".format(su))
|
|
@@ -3437,31 +3599,31 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3437
3599
|
self.push("")
|
|
3438
3600
|
self.interp.startupScript = None
|
|
3439
3601
|
self.__globals = {k: self.locals[k] for k in (self.locals.keys() - keys)}
|
|
3440
|
-
|
|
3602
|
+
|
|
3441
3603
|
def Paste(self, rectangle=False):
|
|
3442
3604
|
"""Replace selection with clipboard contents.
|
|
3443
3605
|
|
|
3444
3606
|
(override) Remove ps1 and ps2 from the multi-line command to paste.
|
|
3445
3607
|
Add offset in paste-rectangle mode.
|
|
3446
|
-
Don't relplace the last
|
|
3608
|
+
Don't relplace the last cr/lf to ps.
|
|
3447
3609
|
"""
|
|
3448
3610
|
if self.CanPaste() and wx.TheClipboard.Open():
|
|
3449
3611
|
data = wx.TextDataObject()
|
|
3450
3612
|
if wx.TheClipboard.GetData(data):
|
|
3451
3613
|
command = data.GetText()
|
|
3452
|
-
|
|
3614
|
+
# command = command.rstrip()
|
|
3453
3615
|
command = self.fixLineEndings(command)
|
|
3454
3616
|
command = self.regulate_cmd(command)
|
|
3455
3617
|
ps = sys.ps2
|
|
3456
3618
|
_text, lp = self.CurLine
|
|
3457
3619
|
if rectangle:
|
|
3458
|
-
ps += ' ' * (lp - len(ps))
|
|
3620
|
+
ps += ' ' * (lp - len(ps)) # add offset
|
|
3459
3621
|
if lp == 0:
|
|
3460
|
-
command = ps + command
|
|
3622
|
+
command = ps + command # paste-line
|
|
3461
3623
|
command = command.replace('\n', os.linesep + ps)
|
|
3462
3624
|
self.ReplaceSelection(command)
|
|
3463
3625
|
wx.TheClipboard.Close()
|
|
3464
|
-
|
|
3626
|
+
|
|
3465
3627
|
def regulate_cmd(self, text):
|
|
3466
3628
|
"""Regulate text to executable command.
|
|
3467
3629
|
|
|
@@ -3470,53 +3632,53 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3470
3632
|
The eol-code (cr/lf) is not fixed.
|
|
3471
3633
|
Call self.fixLineEndings in advance as necessary.
|
|
3472
3634
|
"""
|
|
3473
|
-
text = self.lstripPrompt(text)
|
|
3635
|
+
text = self.lstripPrompt(text) # strip a leading prompt
|
|
3474
3636
|
lf = '\n'
|
|
3475
3637
|
return (text.replace(os.linesep + sys.ps1, lf)
|
|
3476
3638
|
.replace(os.linesep + sys.ps2, lf)
|
|
3477
3639
|
.replace(os.linesep, lf))
|
|
3478
|
-
|
|
3640
|
+
|
|
3479
3641
|
def clear(self):
|
|
3480
3642
|
"""Delete all text (override) put new prompt."""
|
|
3481
3643
|
self.ClearAll()
|
|
3482
3644
|
self.promptPosStart = 0
|
|
3483
3645
|
self.promptPosEnd = 0
|
|
3484
3646
|
self.prompt()
|
|
3485
|
-
|
|
3647
|
+
|
|
3486
3648
|
def write(self, text, pos=None):
|
|
3487
3649
|
"""Display text in the shell.
|
|
3488
3650
|
|
|
3489
|
-
(override) Append text if it is writable at the position.
|
|
3651
|
+
(override) Append text if it is writable at the given position.
|
|
3490
3652
|
"""
|
|
3491
3653
|
if pos is not None:
|
|
3492
3654
|
if pos < 0:
|
|
3493
|
-
pos += self.TextLength + 1
|
|
3655
|
+
pos += self.TextLength + 1 # Counts end-of-buffer (+1:\0)
|
|
3494
3656
|
self.goto_char(pos)
|
|
3495
3657
|
if self.CanEdit():
|
|
3496
|
-
Shell.write(self, text)
|
|
3497
|
-
|
|
3658
|
+
Shell.write(self, text) # => AddText
|
|
3659
|
+
|
|
3498
3660
|
## input = classmethod(Shell.ask)
|
|
3499
|
-
|
|
3661
|
+
|
|
3500
3662
|
def info(self, obj):
|
|
3501
3663
|
"""Short information."""
|
|
3502
3664
|
doc = inspect.getdoc(obj)\
|
|
3503
3665
|
or "No information about {}".format(obj)
|
|
3504
|
-
self.parent.handler('add_help', doc) or print(doc)
|
|
3505
|
-
|
|
3666
|
+
self.parent.handler('add_help', doc, typename(obj)) or print(doc)
|
|
3667
|
+
|
|
3506
3668
|
def help(self, obj):
|
|
3507
3669
|
"""Full description."""
|
|
3508
3670
|
doc = pydoc.plain(pydoc.render_doc(obj))\
|
|
3509
3671
|
or "No description about {}".format(obj)
|
|
3510
|
-
self.parent.handler('add_help', doc) or print(doc)
|
|
3511
|
-
|
|
3672
|
+
self.parent.handler('add_help', doc, typename(obj)) or print(doc)
|
|
3673
|
+
|
|
3512
3674
|
def eval(self, text):
|
|
3513
3675
|
return eval(text, self.globals, self.locals)
|
|
3514
|
-
|
|
3676
|
+
|
|
3515
3677
|
def exec(self, text):
|
|
3516
3678
|
exec(text, self.globals, self.locals)
|
|
3517
3679
|
dispatcher.send(signal='Interpreter.push',
|
|
3518
3680
|
sender=self, command=None, more=False)
|
|
3519
|
-
|
|
3681
|
+
|
|
3520
3682
|
def exec_cmdline(self):
|
|
3521
3683
|
"""Execute command-line directly.
|
|
3522
3684
|
|
|
@@ -3537,14 +3699,14 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3537
3699
|
continue
|
|
3538
3700
|
line = self.lstripPrompt(lines)
|
|
3539
3701
|
lstr = line.lstrip()
|
|
3540
|
-
if (lstr and lstr == line
|
|
3541
|
-
and not lstr.startswith('#')
|
|
3542
|
-
and not re.match(py_outdent_re, lstr)):
|
|
3702
|
+
if (lstr and lstr == line # no indent
|
|
3703
|
+
and not lstr.startswith('#') # no comment
|
|
3704
|
+
and not re.match(py_outdent_re, lstr)): # no outdent pattern
|
|
3543
3705
|
if cmd:
|
|
3544
|
-
commands.append(cmd)
|
|
3706
|
+
commands.append(cmd) # Add stacked commands to the list
|
|
3545
3707
|
cmd = line
|
|
3546
3708
|
else:
|
|
3547
|
-
cmd += lines
|
|
3709
|
+
cmd += lines # multi-line command
|
|
3548
3710
|
lines = ''
|
|
3549
3711
|
commands.append(cmd + lines)
|
|
3550
3712
|
|
|
@@ -3568,56 +3730,37 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3568
3730
|
for cmd in commands:
|
|
3569
3731
|
self.write(cmd)
|
|
3570
3732
|
self.processLine()
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
def autoCallTipShow(self, command, insertcalltip=True, forceCallTip=False):
|
|
3577
|
-
"""Display argument spec and docstring in a popup window.
|
|
3733
|
+
|
|
3734
|
+
def eval_line(self):
|
|
3735
|
+
"""Evaluate the selected word or line and show calltips."""
|
|
3736
|
+
if self.CallTipActive():
|
|
3737
|
+
self.CallTipCancel()
|
|
3578
3738
|
|
|
3579
|
-
(override) Swap anchors to not scroll to the end of the line.
|
|
3580
|
-
"""
|
|
3581
|
-
Shell.autoCallTipShow(self, command, insertcalltip, forceCallTip)
|
|
3582
|
-
self.cpos, self.anchor = self.anchor, self.cpos
|
|
3583
|
-
self.EnsureCaretVisible()
|
|
3584
|
-
|
|
3585
|
-
def gen_text_at_caret(self):
|
|
3586
|
-
"""Generates the selected text,
|
|
3587
|
-
otherwise the line or expression at the caret.
|
|
3588
|
-
(override) Generates command line (that starts with a prompt).
|
|
3589
|
-
"""
|
|
3590
3739
|
def _gen_text():
|
|
3591
3740
|
text = self.SelectedText
|
|
3592
3741
|
if text:
|
|
3593
3742
|
yield text
|
|
3594
3743
|
else:
|
|
3595
|
-
yield self.
|
|
3744
|
+
yield self.line_at_caret
|
|
3596
3745
|
yield self.expr_at_caret
|
|
3597
|
-
return filter(None, _gen_text())
|
|
3598
|
-
|
|
3599
|
-
def eval_line(self, evt):
|
|
3600
|
-
"""Evaluate the selected word or line."""
|
|
3601
|
-
if self.CallTipActive():
|
|
3602
|
-
self.CallTipCancel()
|
|
3603
3746
|
|
|
3604
|
-
|
|
3605
|
-
for text in self.gen_text_at_caret():
|
|
3747
|
+
for text in _gen_text():
|
|
3606
3748
|
tokens = split_words(text)
|
|
3607
3749
|
try:
|
|
3608
3750
|
cmd = self.magic_interpret(tokens)
|
|
3609
3751
|
cmd = self.regulate_cmd(cmd)
|
|
3610
3752
|
obj = self.eval(cmd)
|
|
3611
3753
|
except Exception as e:
|
|
3612
|
-
|
|
3754
|
+
self.message(e)
|
|
3613
3755
|
else:
|
|
3614
3756
|
self.CallTipShow(self.cpos, pformat(obj))
|
|
3615
3757
|
self.message(cmd)
|
|
3616
3758
|
return
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3759
|
+
if not text:
|
|
3760
|
+
self.message("No words")
|
|
3761
|
+
|
|
3762
|
+
def exec_region(self):
|
|
3763
|
+
"""Execute a region of code."""
|
|
3621
3764
|
if self.CallTipActive():
|
|
3622
3765
|
self.CallTipCancel()
|
|
3623
3766
|
|
|
@@ -3632,15 +3775,15 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3632
3775
|
self.exec(code)
|
|
3633
3776
|
except Exception as e:
|
|
3634
3777
|
msg = traceback.format_exc()
|
|
3635
|
-
err = re.findall(
|
|
3778
|
+
err = re.findall(py_trace_re, msg, re.M)
|
|
3636
3779
|
lines = [int(ln) for fn, ln in err if fn == filename]
|
|
3637
3780
|
if lines:
|
|
3638
|
-
region = self.
|
|
3639
|
-
|
|
3781
|
+
region = self.get_command_region(self.cline)
|
|
3782
|
+
lx = region[0] + lines[-1] - 1
|
|
3783
|
+
self.red_pointer = lx
|
|
3640
3784
|
self.message(e)
|
|
3641
|
-
## print(msg, file=sys.__stderr__)
|
|
3642
3785
|
else:
|
|
3643
|
-
del self.
|
|
3786
|
+
del self.red_pointer
|
|
3644
3787
|
self.message("Evaluated {!r} successfully.".format(filename))
|
|
3645
3788
|
else:
|
|
3646
3789
|
self.message("No region")
|