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