mwxlib 1.0.0__py3-none-any.whl → 1.7.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mwx/__init__.py +6 -4
- mwx/bookshelf.py +133 -57
- mwx/controls.py +441 -375
- mwx/framework.py +531 -513
- mwx/graphman.py +683 -677
- mwx/images.py +16 -0
- mwx/matplot2.py +225 -216
- mwx/matplot2g.py +735 -652
- mwx/matplot2lg.py +192 -183
- mwx/mgplt.py +20 -22
- mwx/nutshell.py +1063 -939
- mwx/plugins/ffmpeg_view.py +74 -75
- mwx/plugins/fft_view.py +13 -15
- mwx/plugins/frame_listview.py +55 -52
- mwx/plugins/line_profile.py +1 -1
- mwx/py/filling.py +9 -8
- mwx/testsuite.py +38 -0
- mwx/utilus.py +273 -210
- mwx/wxmon.py +39 -38
- mwx/wxpdb.py +81 -83
- mwx/wxwil.py +18 -18
- mwx/wxwit.py +32 -35
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.dist-info}/METADATA +12 -5
- mwxlib-1.7.13.dist-info/RECORD +28 -0
- {mwxlib-1.0.0.dist-info → mwxlib-1.7.13.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.7.13.dist-info}/top_level.txt +0 -0
mwx/nutshell.py
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#! python3
|
|
2
2
|
"""mwxlib Nautilus in the shell.
|
|
3
3
|
"""
|
|
4
|
+
from contextlib import contextmanager
|
|
4
5
|
from functools import wraps
|
|
5
6
|
from importlib import import_module
|
|
6
|
-
from
|
|
7
|
+
from pathlib import Path
|
|
7
8
|
from pprint import pformat
|
|
8
9
|
from bdb import BdbQuit
|
|
9
10
|
import traceback
|
|
@@ -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:
|
|
@@ -354,17 +372,18 @@ class AutoCompInterfaceMixin:
|
|
|
354
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."""
|
|
@@ -468,7 +497,7 @@ class EditorInterface(AutoCompInterfaceMixin, CtrlInterface):
|
|
|
468
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,71 @@ 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
|
-
return
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1865
|
+
try:
|
|
1866
|
+
mtime = os.path.getmtime(self.__path)
|
|
1867
|
+
return mtime - self.__mtime
|
|
1868
|
+
except FileNotFoundError:
|
|
1869
|
+
self.__mtime = False # valid path (but not found)
|
|
1870
|
+
except OSError:
|
|
1871
|
+
pass
|
|
1872
|
+
return self.__mtime
|
|
1873
|
+
|
|
1874
|
+
@property
|
|
1875
|
+
def need_buffer_save(self):
|
|
1876
|
+
"""Return whether the buffer should be saved.
|
|
1877
|
+
The file has been modified internally.
|
|
1878
|
+
"""
|
|
1879
|
+
return self.mtdelta is not None and self.IsModified()
|
|
1880
|
+
|
|
1881
|
+
@property
|
|
1882
|
+
def need_buffer_load(self):
|
|
1883
|
+
"""Return whether the buffer should be loaded.
|
|
1884
|
+
The file has been modified externally.
|
|
1885
|
+
"""
|
|
1886
|
+
return self.mtdelta is not None and self.mtdelta > 0
|
|
1887
|
+
|
|
1749
1888
|
@property
|
|
1750
1889
|
def caption_prefix(self):
|
|
1751
1890
|
prefix = ''
|
|
@@ -1760,7 +1899,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1760
1899
|
if prefix:
|
|
1761
1900
|
prefix += ' '
|
|
1762
1901
|
return prefix
|
|
1763
|
-
|
|
1902
|
+
|
|
1764
1903
|
def update_caption(self):
|
|
1765
1904
|
caption = self.caption_prefix + self.name
|
|
1766
1905
|
try:
|
|
@@ -1768,31 +1907,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1768
1907
|
self.parent.handler('buffer_caption_updated', self)
|
|
1769
1908
|
except AttributeError:
|
|
1770
1909
|
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
|
-
|
|
1910
|
+
|
|
1796
1911
|
def __init__(self, parent, filename, **kwargs):
|
|
1797
1912
|
EditWindow.__init__(self, parent, **kwargs)
|
|
1798
1913
|
EditorInterface.__init__(self)
|
|
@@ -1802,7 +1917,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1802
1917
|
self.update_filestamp(filename)
|
|
1803
1918
|
self.code = None
|
|
1804
1919
|
|
|
1805
|
-
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate)
|
|
1920
|
+
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate) # skip to brace matching
|
|
1806
1921
|
self.Bind(stc.EVT_STC_CALLTIP_CLICK, self.OnCallTipClick)
|
|
1807
1922
|
self.Bind(stc.EVT_STC_INDICATOR_CLICK, self.OnIndicatorClick)
|
|
1808
1923
|
|
|
@@ -1823,19 +1938,10 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1823
1938
|
|
|
1824
1939
|
def clear(evt):
|
|
1825
1940
|
## """Clear selection and message, no skip."""
|
|
1826
|
-
## *
|
|
1827
|
-
if self.CanEdit():
|
|
1828
|
-
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()
|
|
1941
|
+
## *DO NOT* clear autocomp, so that the event can skip to AutoComp properly.
|
|
1836
1942
|
if self.CanEdit():
|
|
1837
1943
|
with self.off_undocollection():
|
|
1838
|
-
self.ReplaceSelection(
|
|
1944
|
+
self.ReplaceSelection('')
|
|
1839
1945
|
self.message("")
|
|
1840
1946
|
|
|
1841
1947
|
def fork(evt):
|
|
@@ -1845,16 +1951,16 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1845
1951
|
"""Fork events to the parent."""
|
|
1846
1952
|
self.parent.handler(self.handler.current_event, evt)
|
|
1847
1953
|
|
|
1848
|
-
## Note:
|
|
1954
|
+
## Note: Mouse events are not propagated from Buffer to EditorBook.
|
|
1849
1955
|
## They are explicitly dispatched from buffer.handler to editor.handler.
|
|
1850
1956
|
|
|
1851
1957
|
self.handler.update({ # DNA<Buffer>
|
|
1852
1958
|
None : {
|
|
1853
1959
|
'buffer_saved' : [ None, dispatch ],
|
|
1854
1960
|
'buffer_loaded' : [ None, dispatch ],
|
|
1855
|
-
'buffer_modified' : [ None, dispatch, self.
|
|
1856
|
-
'buffer_activated' : [ None, dispatch, self.
|
|
1857
|
-
'buffer_inactivated' : [ None, dispatch, self.
|
|
1961
|
+
'buffer_modified' : [ None, dispatch, self.on_buffer_modified ],
|
|
1962
|
+
'buffer_activated' : [ None, dispatch, self.on_buffer_activated ],
|
|
1963
|
+
'buffer_inactivated' : [ None, dispatch, self.on_buffer_inactivated ],
|
|
1858
1964
|
'buffer_region_executed' : [ None, dispatch ],
|
|
1859
1965
|
},
|
|
1860
1966
|
-1 : { # original action of the EditWindow
|
|
@@ -1865,20 +1971,24 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1865
1971
|
'*[LR]win pressed' : (-1, ),
|
|
1866
1972
|
},
|
|
1867
1973
|
0 : { # Normal mode
|
|
1868
|
-
'* pressed' : (0, skip
|
|
1974
|
+
'* pressed' : (0, skip),
|
|
1869
1975
|
'* released' : (0, skip, dispatch),
|
|
1976
|
+
'*button* pressed' : (0, skip, dispatch),
|
|
1977
|
+
'Lbutton pressed' : (0, self.on_left_down),
|
|
1870
1978
|
'escape pressed' : (-1, self.on_enter_escmap),
|
|
1871
1979
|
'C-h pressed' : (0, self.call_helpTip),
|
|
1872
1980
|
'. pressed' : (2, self.OnEnterDot),
|
|
1981
|
+
'C-. pressed' : (2, self.call_word_autocomp),
|
|
1982
|
+
'C-/ pressed' : (3, self.call_apropos_autocomp),
|
|
1873
1983
|
'M-. pressed' : (2, self.call_word_autocomp),
|
|
1874
1984
|
'M-/ pressed' : (3, self.call_apropos_autocomp),
|
|
1875
1985
|
},
|
|
1876
1986
|
2 : { # word auto completion AS-mode
|
|
1877
|
-
'quit' : (0, clear_autocomp),
|
|
1878
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
1987
|
+
'quit' : (0, self.clear_autocomp),
|
|
1988
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
1879
1989
|
'tab pressed' : (0, clear, skip),
|
|
1880
1990
|
'enter pressed' : (0, clear, skip),
|
|
1881
|
-
'escape pressed' : (0,
|
|
1991
|
+
'escape pressed' : (0, clear, skip),
|
|
1882
1992
|
'up pressed' : (2, skip, self.on_completion_backward),
|
|
1883
1993
|
'down pressed' : (2, skip, self.on_completion_forward),
|
|
1884
1994
|
'*left pressed' : (2, skip),
|
|
@@ -1893,7 +2003,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1893
2003
|
'*delete pressed' : (2, skip),
|
|
1894
2004
|
'*backspace pressed' : (2, skip),
|
|
1895
2005
|
'*backspace released' : (2, self.call_word_autocomp),
|
|
1896
|
-
|
|
2006
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
1897
2007
|
'*alt pressed' : (2, ),
|
|
1898
2008
|
'*ctrl pressed' : (2, ),
|
|
1899
2009
|
'*shift pressed' : (2, ),
|
|
@@ -1901,11 +2011,11 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1901
2011
|
'*f[0-9]* pressed' : (2, ),
|
|
1902
2012
|
},
|
|
1903
2013
|
3 : { # apropos auto completion AS-mode
|
|
1904
|
-
'quit' : (0, clear_autocomp),
|
|
1905
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
2014
|
+
'quit' : (0, self.clear_autocomp),
|
|
2015
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
1906
2016
|
'tab pressed' : (0, clear, skip),
|
|
1907
2017
|
'enter pressed' : (0, clear, skip),
|
|
1908
|
-
'escape pressed' : (0,
|
|
2018
|
+
'escape pressed' : (0, clear, skip),
|
|
1909
2019
|
'up pressed' : (3, skip, self.on_completion_backward),
|
|
1910
2020
|
'down pressed' : (3, skip, self.on_completion_forward),
|
|
1911
2021
|
'*left pressed' : (3, skip),
|
|
@@ -1920,7 +2030,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1920
2030
|
'*delete pressed' : (3, skip),
|
|
1921
2031
|
'*backspace pressed' : (3, skip),
|
|
1922
2032
|
'*backspace released' : (3, self.call_apropos_autocomp),
|
|
1923
|
-
|
|
2033
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
1924
2034
|
'*alt pressed' : (3, ),
|
|
1925
2035
|
'*ctrl pressed' : (3, ),
|
|
1926
2036
|
'*shift pressed' : (3, ),
|
|
@@ -1931,103 +2041,118 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
1931
2041
|
|
|
1932
2042
|
self.show_folder()
|
|
1933
2043
|
self.set_stylus(Stylus.py_text_mode)
|
|
1934
|
-
|
|
2044
|
+
|
|
1935
2045
|
def __contains__(self, code):
|
|
1936
2046
|
if inspect.iscode(code) and self.code:
|
|
1937
2047
|
return code is self.code\
|
|
1938
2048
|
or code in self.code.co_consts
|
|
1939
|
-
|
|
2049
|
+
|
|
1940
2050
|
def trace_position(self):
|
|
1941
2051
|
_text, lp = self.CurLine
|
|
1942
2052
|
self.message("{:>6d}:{} ({})".format(self.cline, lp, self.cpos), pane=-1)
|
|
1943
|
-
|
|
1944
|
-
def OnUpdate(self, evt):
|
|
2053
|
+
|
|
2054
|
+
def OnUpdate(self, evt): #<wx._stc.StyledTextEvent>
|
|
1945
2055
|
if evt.Updated & (stc.STC_UPDATE_SELECTION | stc.STC_UPDATE_CONTENT):
|
|
1946
2056
|
self.trace_position()
|
|
1947
2057
|
if evt.Updated & stc.STC_UPDATE_CONTENT:
|
|
1948
2058
|
self.handler('buffer_modified', self)
|
|
1949
2059
|
evt.Skip()
|
|
1950
|
-
|
|
1951
|
-
def OnCallTipClick(self, evt):
|
|
2060
|
+
|
|
2061
|
+
def OnCallTipClick(self, evt): #<wx._stc.StyledTextEvent>
|
|
1952
2062
|
if self.CallTipActive():
|
|
1953
2063
|
self.CallTipCancel()
|
|
1954
2064
|
pos, tip, more = self._calltips
|
|
1955
2065
|
if more:
|
|
1956
2066
|
self.CallTipShow(pos, tip, N=None)
|
|
1957
2067
|
evt.Skip()
|
|
1958
|
-
|
|
1959
|
-
def OnIndicatorClick(self, evt):
|
|
2068
|
+
|
|
2069
|
+
def OnIndicatorClick(self, evt): #<wx._stc.StyledTextEvent>
|
|
1960
2070
|
if self.SelectedText or not wx.GetKeyState(wx.WXK_CONTROL):
|
|
1961
|
-
## Processing text selection
|
|
2071
|
+
## Processing text selection or dragging.
|
|
1962
2072
|
evt.Skip()
|
|
1963
2073
|
return
|
|
2074
|
+
|
|
1964
2075
|
pos = evt.Position
|
|
2076
|
+
self.goto_char(pos)
|
|
1965
2077
|
i = 2
|
|
1966
|
-
if self.IndicatorValueAt(i, pos):
|
|
2078
|
+
if self.IndicatorValueAt(i, pos): # [C-indic click]
|
|
1967
2079
|
p = self.IndicatorStart(i, pos)
|
|
1968
2080
|
q = self.IndicatorEnd(i, pos)
|
|
1969
2081
|
url = self.GetTextRange(p, q).strip()
|
|
1970
|
-
|
|
1971
|
-
if wx.GetKeyState(wx.WXK_SHIFT): # [C-S-indic click]
|
|
2082
|
+
if wx.GetKeyState(wx.WXK_SHIFT): # [C-S-indic click]
|
|
1972
2083
|
import webbrowser
|
|
1973
2084
|
return webbrowser.open(url)
|
|
1974
2085
|
else:
|
|
1975
2086
|
## Note: post-call for the confirmation dialog.
|
|
1976
2087
|
wx.CallAfter(self.parent.load_file, url)
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
def on_modified(self, buf):
|
|
2088
|
+
|
|
2089
|
+
def on_buffer_modified(self, buf):
|
|
1980
2090
|
"""Called when the buffer is modified."""
|
|
1981
2091
|
self.SetIndicatorCurrent(2)
|
|
1982
2092
|
self.IndicatorClearRange(0, self.TextLength)
|
|
1983
2093
|
for m in self.grep(url_re):
|
|
1984
2094
|
p, q = m.span()
|
|
1985
2095
|
self.IndicatorFillRange(p, q-p)
|
|
1986
|
-
|
|
2096
|
+
|
|
1987
2097
|
def OnSavePointLeft(self, evt):
|
|
1988
2098
|
self.update_caption()
|
|
1989
2099
|
evt.Skip()
|
|
1990
|
-
|
|
2100
|
+
|
|
1991
2101
|
def OnSavePointReached(self, evt):
|
|
1992
2102
|
self.update_caption()
|
|
1993
2103
|
evt.Skip()
|
|
1994
|
-
|
|
2104
|
+
|
|
1995
2105
|
def OnEnterDot(self, evt):
|
|
2106
|
+
if not self.CanEdit():
|
|
2107
|
+
self.handler('quit', evt)
|
|
2108
|
+
return
|
|
1996
2109
|
p = self.cpos
|
|
1997
|
-
|
|
2110
|
+
lst = self.get_style(p-1)
|
|
1998
2111
|
rst = self.get_style(p)
|
|
1999
|
-
if
|
|
2000
|
-
self.handler('quit', evt)
|
|
2112
|
+
if lst not in ('moji', 'word', 'rparen') or rst == 'word':
|
|
2113
|
+
self.handler('quit', evt) # Don't enter autocomp
|
|
2001
2114
|
evt.Skip()
|
|
2002
|
-
|
|
2003
|
-
def
|
|
2115
|
+
|
|
2116
|
+
def on_buffer_activated(self, buf):
|
|
2004
2117
|
"""Called when the buffer is activated."""
|
|
2005
2118
|
self.update_caption()
|
|
2006
2119
|
self.trace_position()
|
|
2007
|
-
|
|
2008
|
-
def
|
|
2120
|
+
|
|
2121
|
+
def on_buffer_inactivated(self, buf):
|
|
2009
2122
|
"""Called when the buffer is inactivated."""
|
|
2010
2123
|
pass
|
|
2011
|
-
|
|
2124
|
+
|
|
2125
|
+
def on_left_down(self, evt):
|
|
2126
|
+
pos = self.PositionFromPoint(evt.Position)
|
|
2127
|
+
ln = self.LineFromPosition(pos)
|
|
2128
|
+
ann_text = self.AnnotationGetText(ln)
|
|
2129
|
+
if ann_text:
|
|
2130
|
+
if pos == self.GetLineEndPosition(ln): # Check eol (not clicked yet).
|
|
2131
|
+
if wx.TheClipboard.Open():
|
|
2132
|
+
wx.TheClipboard.SetData(wx.TextDataObject(ann_text))
|
|
2133
|
+
wx.TheClipboard.Close()
|
|
2134
|
+
self.message("Annotation copied.")
|
|
2135
|
+
self.AnnotationClearLine(ln)
|
|
2136
|
+
evt.Skip()
|
|
2137
|
+
|
|
2012
2138
|
def on_enter_escmap(self, evt):
|
|
2013
2139
|
self.message("ESC-")
|
|
2014
|
-
|
|
2140
|
+
|
|
2015
2141
|
def on_exit_escmap(self, evt):
|
|
2016
2142
|
self.message("ESC {}".format(evt.key))
|
|
2017
2143
|
self.AnnotationClearAll()
|
|
2018
|
-
|
|
2144
|
+
|
|
2019
2145
|
## --------------------------------
|
|
2020
|
-
## File I/O
|
|
2146
|
+
## File I/O.
|
|
2021
2147
|
## --------------------------------
|
|
2022
|
-
|
|
2023
|
-
def _load_textfile(self, text
|
|
2148
|
+
|
|
2149
|
+
def _load_textfile(self, text):
|
|
2024
2150
|
with self.off_readonly():
|
|
2025
2151
|
self.Text = text
|
|
2026
2152
|
self.EmptyUndoBuffer()
|
|
2027
2153
|
self.SetSavePoint()
|
|
2028
|
-
self.update_filestamp(filename)
|
|
2029
2154
|
self.handler('buffer_loaded', self)
|
|
2030
|
-
|
|
2155
|
+
|
|
2031
2156
|
def _load_file(self, filename):
|
|
2032
2157
|
"""Wrapped method of LoadFile."""
|
|
2033
2158
|
if self.LoadFile(filename):
|
|
@@ -2037,7 +2162,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2037
2162
|
self.handler('buffer_loaded', self)
|
|
2038
2163
|
return True
|
|
2039
2164
|
return False
|
|
2040
|
-
|
|
2165
|
+
|
|
2041
2166
|
def _save_file(self, filename):
|
|
2042
2167
|
"""Wrapped method of SaveFile."""
|
|
2043
2168
|
if self.SaveFile(filename):
|
|
@@ -2046,7 +2171,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2046
2171
|
self.handler('buffer_saved', self)
|
|
2047
2172
|
return True
|
|
2048
2173
|
return False
|
|
2049
|
-
|
|
2174
|
+
|
|
2050
2175
|
def LoadFile(self, filename):
|
|
2051
2176
|
"""Load the contents of file into the editor.
|
|
2052
2177
|
|
|
@@ -2056,7 +2181,7 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2056
2181
|
with self.off_readonly():
|
|
2057
2182
|
self.Text = i.read()
|
|
2058
2183
|
return True
|
|
2059
|
-
|
|
2184
|
+
|
|
2060
2185
|
def SaveFile(self, filename):
|
|
2061
2186
|
"""Write the contents of the editor to file.
|
|
2062
2187
|
|
|
@@ -2065,37 +2190,38 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2065
2190
|
with open(filename, "w", encoding='utf-8', newline='') as o:
|
|
2066
2191
|
o.write(self.Text)
|
|
2067
2192
|
return True
|
|
2068
|
-
|
|
2193
|
+
|
|
2069
2194
|
## --------------------------------
|
|
2070
|
-
## Python eval / exec
|
|
2195
|
+
## Python eval / exec.
|
|
2071
2196
|
## --------------------------------
|
|
2072
|
-
|
|
2197
|
+
|
|
2073
2198
|
@property
|
|
2074
|
-
def locals(self):
|
|
2199
|
+
def locals(self): # internal use only
|
|
2075
2200
|
try:
|
|
2076
2201
|
return self.parent.parent.current_shell.locals
|
|
2077
2202
|
except AttributeError:
|
|
2078
2203
|
return None
|
|
2079
|
-
|
|
2204
|
+
|
|
2080
2205
|
@property
|
|
2081
|
-
def globals(self):
|
|
2206
|
+
def globals(self): # internal use only
|
|
2082
2207
|
try:
|
|
2083
2208
|
return self.parent.parent.current_shell.globals
|
|
2084
2209
|
except AttributeError:
|
|
2085
2210
|
return None
|
|
2086
|
-
|
|
2211
|
+
|
|
2087
2212
|
def eval(self, text):
|
|
2088
|
-
return eval(text, self.globals, self.locals)
|
|
2089
|
-
|
|
2213
|
+
return eval(text, self.globals, self.locals) # using current shell namespace
|
|
2214
|
+
|
|
2090
2215
|
def exec(self, text):
|
|
2091
|
-
exec(text, self.globals, self.locals)
|
|
2216
|
+
exec(text, self.globals, self.locals) # using current shell namespace
|
|
2092
2217
|
dispatcher.send(signal='Interpreter.push',
|
|
2093
2218
|
sender=self, command=None, more=False)
|
|
2094
|
-
|
|
2095
|
-
def
|
|
2096
|
-
"""
|
|
2097
|
-
|
|
2098
|
-
|
|
2219
|
+
|
|
2220
|
+
def eval_line(self):
|
|
2221
|
+
"""Evaluate the selected word or line and show calltips."""
|
|
2222
|
+
if self.CallTipActive():
|
|
2223
|
+
self.CallTipCancel()
|
|
2224
|
+
|
|
2099
2225
|
def _gen_text():
|
|
2100
2226
|
text = self.SelectedText
|
|
2101
2227
|
if text:
|
|
@@ -2103,49 +2229,44 @@ class Buffer(EditorInterface, EditWindow):
|
|
|
2103
2229
|
else:
|
|
2104
2230
|
yield self.line_at_caret
|
|
2105
2231
|
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
2232
|
|
|
2112
|
-
|
|
2113
|
-
for text in self.gen_text_at_caret():
|
|
2233
|
+
for text in _gen_text():
|
|
2114
2234
|
try:
|
|
2115
2235
|
obj = eval(text, self.globals, self.locals)
|
|
2116
2236
|
except Exception as e:
|
|
2117
|
-
|
|
2237
|
+
self.message(e)
|
|
2118
2238
|
else:
|
|
2119
2239
|
self.CallTipShow(self.cpos, pformat(obj))
|
|
2120
2240
|
self.message(text)
|
|
2121
2241
|
return
|
|
2122
|
-
|
|
2123
|
-
|
|
2242
|
+
if not text:
|
|
2243
|
+
self.message("No words")
|
|
2244
|
+
|
|
2124
2245
|
def exec_region(self):
|
|
2246
|
+
"""Execute a region of code."""
|
|
2125
2247
|
try:
|
|
2126
2248
|
code = compile(self.Text, self.filename, "exec")
|
|
2127
2249
|
exec(code, self.globals, self.locals)
|
|
2128
2250
|
dispatcher.send(signal='Interpreter.push',
|
|
2129
2251
|
sender=self, command=None, more=False)
|
|
2130
2252
|
except BdbQuit:
|
|
2131
|
-
self.red_pointer = self.cline
|
|
2132
2253
|
pass
|
|
2133
2254
|
except Exception as e:
|
|
2134
2255
|
msg = traceback.format_exc()
|
|
2135
|
-
err = re.findall(
|
|
2256
|
+
err = re.findall(py_trace_re, msg, re.M)
|
|
2136
2257
|
lines = [int(ln) for fn, ln in err if fn == self.filename]
|
|
2137
2258
|
if lines:
|
|
2138
2259
|
lx = lines[-1] - 1
|
|
2139
2260
|
self.red_arrow = lx
|
|
2140
2261
|
self.goto_line(lx)
|
|
2141
|
-
self.EnsureVisible(lx)
|
|
2262
|
+
self.EnsureVisible(lx) # expand if folded
|
|
2142
2263
|
self.EnsureCaretVisible()
|
|
2143
2264
|
self.AnnotationSetStyle(lx, stc.STC_STYLE_ANNOTATION)
|
|
2144
2265
|
self.AnnotationSetText(lx, msg)
|
|
2145
2266
|
self.message(e)
|
|
2146
2267
|
else:
|
|
2147
2268
|
self.code = code
|
|
2148
|
-
del self.pointer
|
|
2269
|
+
del self.pointer # Reset pointer (debugger hook point).
|
|
2149
2270
|
del self.red_arrow
|
|
2150
2271
|
self.handler('buffer_region_executed', self)
|
|
2151
2272
|
self.message("Evaluated {!r} successfully.".format(self.filename))
|
|
@@ -2156,16 +2277,16 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2156
2277
|
"""Python code editor.
|
|
2157
2278
|
|
|
2158
2279
|
Args:
|
|
2159
|
-
name
|
|
2280
|
+
name: Window.Name (e.g. 'Scratch')
|
|
2160
2281
|
|
|
2161
2282
|
Attributes:
|
|
2162
|
-
default_name
|
|
2163
|
-
default_buffer
|
|
2283
|
+
default_name: default buffer name (e.g. '*scratch*')
|
|
2284
|
+
default_buffer: default buffer
|
|
2164
2285
|
"""
|
|
2165
2286
|
@property
|
|
2166
2287
|
def message(self):
|
|
2167
2288
|
return self.parent.message
|
|
2168
|
-
|
|
2289
|
+
|
|
2169
2290
|
def __init__(self, parent, name="book", **kwargs):
|
|
2170
2291
|
kwargs.setdefault('style',
|
|
2171
2292
|
(aui.AUI_NB_DEFAULT_STYLE | aui.AUI_NB_TOP)
|
|
@@ -2177,12 +2298,11 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2177
2298
|
## So we set the tabs' height to zero to hide them.
|
|
2178
2299
|
self.TabCtrlHeight = 0
|
|
2179
2300
|
|
|
2180
|
-
self.defaultBufferStyle =
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
self.parent = parent #: parent<ShellFrame> is not Parent<AuiNotebook>
|
|
2301
|
+
self.defaultBufferStyle = {}
|
|
2302
|
+
|
|
2303
|
+
self.parent = parent # parent<ShellFrame> is not Parent<AuiNotebook>
|
|
2184
2304
|
self.Name = name
|
|
2185
|
-
self.default_name = "*{}*".format(name.lower())
|
|
2305
|
+
self.default_name = "*{}*".format(name.lower()) # e.g. '*scratch*'
|
|
2186
2306
|
self.default_buffer = self.create_buffer(self.default_name)
|
|
2187
2307
|
|
|
2188
2308
|
self.Bind(aui.EVT_AUINOTEBOOK_PAGE_CLOSE, self.OnPageClose)
|
|
@@ -2204,29 +2324,29 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2204
2324
|
'buffer_loaded' : [ None, dispatch ],
|
|
2205
2325
|
'buffer_deleted' : [ None, dispatch ],
|
|
2206
2326
|
'buffer_modified' : [ None, dispatch ],
|
|
2207
|
-
'buffer_activated' : [ None, dispatch, self.
|
|
2208
|
-
'buffer_inactivated' : [ None, dispatch, self.
|
|
2327
|
+
'buffer_activated' : [ None, dispatch, self.on_buffer_activated ],
|
|
2328
|
+
'buffer_inactivated' : [ None, dispatch, self.on_buffer_inactivated ],
|
|
2209
2329
|
'buffer_caption_updated' : [ None, dispatch ],
|
|
2210
|
-
'*button* pressed' : [ None, dispatch, skip ],
|
|
2211
|
-
'*button* released' : [ None, dispatch, skip ],
|
|
2212
2330
|
},
|
|
2213
2331
|
0 : { # Normal mode
|
|
2332
|
+
'* pressed' : (0, skip),
|
|
2333
|
+
'* released' : (0, skip, dispatch),
|
|
2334
|
+
'*button* pressed' : (0, skip, dispatch),
|
|
2214
2335
|
'M-up pressed' : (0, _F(self.previous_buffer)),
|
|
2215
2336
|
'M-down pressed' : (0, _F(self.next_buffer)),
|
|
2216
2337
|
},
|
|
2217
2338
|
})
|
|
2218
|
-
|
|
2339
|
+
|
|
2219
2340
|
def OnDestroy(self, evt):
|
|
2220
2341
|
obj = evt.EventObject
|
|
2221
2342
|
if isinstance(obj, Buffer):
|
|
2222
2343
|
self.handler('buffer_deleted', obj)
|
|
2223
2344
|
evt.Skip()
|
|
2224
|
-
|
|
2225
|
-
def OnPageClose(self, evt):
|
|
2226
|
-
|
|
2227
|
-
buf = nb.all_buffers[evt.Selection]
|
|
2345
|
+
|
|
2346
|
+
def OnPageClose(self, evt): #<wx._aui.AuiNotebookEvent>
|
|
2347
|
+
buf = self.GetPage(evt.Selection)
|
|
2228
2348
|
if buf.need_buffer_save:
|
|
2229
|
-
if wx.MessageBox( # Confirm
|
|
2349
|
+
if wx.MessageBox( # Confirm closing the buffer.
|
|
2230
2350
|
"You are closing unsaved content.\n\n"
|
|
2231
2351
|
"The changes will be discarded.\n"
|
|
2232
2352
|
"Continue closing?",
|
|
@@ -2236,17 +2356,17 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2236
2356
|
evt.Veto()
|
|
2237
2357
|
return
|
|
2238
2358
|
evt.Skip()
|
|
2239
|
-
|
|
2240
|
-
def OnPageClosed(self, evt):
|
|
2359
|
+
|
|
2360
|
+
def OnPageClosed(self, evt): #<wx._aui.AuiNotebookEvent>
|
|
2241
2361
|
if self.PageCount == 0:
|
|
2242
2362
|
self.new_buffer()
|
|
2243
2363
|
evt.Skip()
|
|
2244
|
-
|
|
2364
|
+
|
|
2245
2365
|
def set_attributes(self, buf=None, **kwargs):
|
|
2246
2366
|
"""Set multiple properties at once to the buffer(s).
|
|
2247
2367
|
|
|
2248
2368
|
Args:
|
|
2249
|
-
buf
|
|
2369
|
+
buf: a buffer to apply (if None, applies to all buffers).
|
|
2250
2370
|
**kwargs: default style.
|
|
2251
2371
|
|
|
2252
2372
|
ReadOnly = False
|
|
@@ -2268,29 +2388,36 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2268
2388
|
_setattribute(buf, kwargs)
|
|
2269
2389
|
else:
|
|
2270
2390
|
self.defaultBufferStyle.update(kwargs)
|
|
2271
|
-
for buf in self.
|
|
2391
|
+
for buf in self.get_all_buffers():
|
|
2272
2392
|
_setattribute(buf, self.defaultBufferStyle)
|
|
2273
|
-
|
|
2274
|
-
def
|
|
2393
|
+
|
|
2394
|
+
def on_buffer_activated(self, buf):
|
|
2275
2395
|
"""Called when the buffer is activated."""
|
|
2276
2396
|
title = "{} file: {}".format(self.Name, buf.filename)
|
|
2277
2397
|
self.parent.handler('title_window', title)
|
|
2278
|
-
|
|
2279
|
-
def
|
|
2398
|
+
|
|
2399
|
+
def on_buffer_inactivated(self, buf):
|
|
2280
2400
|
"""Called when the buffer is inactivated."""
|
|
2281
2401
|
pass
|
|
2282
|
-
|
|
2402
|
+
|
|
2283
2403
|
## --------------------------------
|
|
2284
|
-
## Buffer list controls
|
|
2404
|
+
## Buffer list controls.
|
|
2285
2405
|
## --------------------------------
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2406
|
+
|
|
2407
|
+
def get_all_buffers(self, fn=None):
|
|
2408
|
+
"""Yields all buffers with specified fn:filename or code."""
|
|
2409
|
+
if fn is None:
|
|
2410
|
+
yield from self.get_pages(Buffer)
|
|
2411
|
+
elif isinstance(fn, str):
|
|
2412
|
+
g = os.path.realpath(fn)
|
|
2413
|
+
for buf in self.get_pages(Buffer):
|
|
2414
|
+
if fn == buf.filename or g == os.path.realpath(buf.filename):
|
|
2415
|
+
yield buf
|
|
2416
|
+
else:
|
|
2417
|
+
for buf in self.get_pages(Buffer):
|
|
2418
|
+
if fn is buf or fn in buf: # check code
|
|
2419
|
+
yield buf
|
|
2420
|
+
|
|
2294
2421
|
@property
|
|
2295
2422
|
def menu(self):
|
|
2296
2423
|
"""Yields context menu."""
|
|
@@ -2300,86 +2427,91 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2300
2427
|
lambda v: buf.SetFocus(),
|
|
2301
2428
|
lambda v: v.Check(buf is self.buffer))
|
|
2302
2429
|
|
|
2303
|
-
return (_menu(j+1, x) for j, x in enumerate(self.
|
|
2304
|
-
|
|
2430
|
+
return (_menu(j+1, x) for j, x in enumerate(self.get_all_buffers()))
|
|
2431
|
+
|
|
2305
2432
|
@property
|
|
2306
2433
|
def buffer(self):
|
|
2307
|
-
"""
|
|
2434
|
+
"""Return the currently selected page or None."""
|
|
2308
2435
|
return self.CurrentPage
|
|
2309
|
-
|
|
2436
|
+
|
|
2310
2437
|
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
|
-
|
|
2438
|
+
"""Find a buffer with specified fn:filename or code."""
|
|
2439
|
+
return next(self.get_all_buffers(fn), None)
|
|
2440
|
+
|
|
2322
2441
|
def swap_buffer(self, buf, lineno=0):
|
|
2323
2442
|
self.swap_page(buf)
|
|
2324
2443
|
if lineno:
|
|
2325
2444
|
buf.markline = lineno - 1
|
|
2326
2445
|
buf.goto_marker(1)
|
|
2327
|
-
|
|
2446
|
+
|
|
2328
2447
|
def create_buffer(self, filename, index=None):
|
|
2329
2448
|
"""Create a new buffer (internal use only)."""
|
|
2330
|
-
|
|
2331
|
-
self.Freeze()
|
|
2449
|
+
with wx.FrozenWindow(self):
|
|
2332
2450
|
buf = Buffer(self, filename, style=wx.BORDER_DEFAULT)
|
|
2333
2451
|
self.set_attributes(buf, **self.defaultBufferStyle)
|
|
2334
2452
|
if index is None:
|
|
2335
2453
|
index = self.PageCount
|
|
2336
|
-
self.InsertPage(index, buf, buf.name)
|
|
2454
|
+
self.InsertPage(index, buf, buf.name) # => [buffer_activated]
|
|
2337
2455
|
self.handler('buffer_new', buf)
|
|
2338
2456
|
return buf
|
|
2339
|
-
|
|
2340
|
-
self.Thaw()
|
|
2341
|
-
|
|
2457
|
+
|
|
2342
2458
|
def new_buffer(self):
|
|
2343
2459
|
"""Create a new default buffer."""
|
|
2344
2460
|
buf = self.default_buffer
|
|
2345
|
-
if not buf or buf.mtdelta is not None:
|
|
2346
|
-
buf = self.create_buffer(self.default_name
|
|
2461
|
+
if not buf or buf.mtdelta is not None: # is saved?
|
|
2462
|
+
buf = self.create_buffer(self.default_name)
|
|
2347
2463
|
self.default_buffer = buf
|
|
2348
2464
|
else:
|
|
2349
2465
|
buf.ClearAll()
|
|
2350
|
-
|
|
2466
|
+
# buf.EmptyUndoBuffer()
|
|
2351
2467
|
buf.SetFocus()
|
|
2352
2468
|
return buf
|
|
2353
|
-
|
|
2469
|
+
|
|
2354
2470
|
def delete_buffer(self, buf=None):
|
|
2355
2471
|
"""Pop the current buffer from the buffer list."""
|
|
2356
2472
|
if not buf:
|
|
2357
2473
|
buf = self.buffer
|
|
2358
2474
|
j = self.GetPageIndex(buf)
|
|
2359
2475
|
if j != -1:
|
|
2360
|
-
self.DeletePage(j)
|
|
2361
|
-
if not self.buffer:
|
|
2362
|
-
self.new_buffer
|
|
2363
|
-
|
|
2476
|
+
self.DeletePage(j) # the focus moves
|
|
2477
|
+
if not self.buffer: # no buffers
|
|
2478
|
+
wx.CallAfter(self.new_buffer) # Note: post-call to avoid a crash.
|
|
2479
|
+
|
|
2364
2480
|
def delete_all_buffers(self):
|
|
2365
2481
|
"""Initialize list of buffers."""
|
|
2366
2482
|
self.DeleteAllPages()
|
|
2367
|
-
self.new_buffer
|
|
2368
|
-
|
|
2483
|
+
wx.CallAfter(self.new_buffer) # Note: post-call to avoid a crash.
|
|
2484
|
+
|
|
2369
2485
|
def next_buffer(self):
|
|
2370
|
-
self.Selection
|
|
2371
|
-
|
|
2486
|
+
if self.Selection < self.PageCount - 1:
|
|
2487
|
+
self.Selection += 1
|
|
2488
|
+
else:
|
|
2489
|
+
books = list(self.Parent.get_pages(type(self)))
|
|
2490
|
+
k = books.index(self)
|
|
2491
|
+
if k < len(books) - 1:
|
|
2492
|
+
other_editor = books[k+1]
|
|
2493
|
+
other_editor.Selection = 0
|
|
2494
|
+
other_editor.CurrentPage.SetFocus()
|
|
2495
|
+
|
|
2372
2496
|
def previous_buffer(self):
|
|
2373
|
-
self.Selection
|
|
2374
|
-
|
|
2497
|
+
if self.Selection > 0:
|
|
2498
|
+
self.Selection -= 1
|
|
2499
|
+
else:
|
|
2500
|
+
books = list(self.Parent.get_pages(type(self)))
|
|
2501
|
+
k = books.index(self)
|
|
2502
|
+
if k > 0:
|
|
2503
|
+
other_editor = books[k-1]
|
|
2504
|
+
other_editor.Selection = other_editor.PageCount - 1
|
|
2505
|
+
other_editor.CurrentPage.SetFocus()
|
|
2506
|
+
|
|
2375
2507
|
## --------------------------------
|
|
2376
|
-
## File I/O
|
|
2508
|
+
## File I/O.
|
|
2377
2509
|
## --------------------------------
|
|
2378
2510
|
wildcards = [
|
|
2379
2511
|
"PY files (*.py)|*.py",
|
|
2380
2512
|
"ALL files (*.*)|*.*",
|
|
2381
2513
|
]
|
|
2382
|
-
|
|
2514
|
+
|
|
2383
2515
|
def load_cache(self, filename, lineno=0):
|
|
2384
2516
|
"""Load a file from cache using linecache.
|
|
2385
2517
|
Note:
|
|
@@ -2395,12 +2527,13 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2395
2527
|
elif not buf.need_buffer_load:
|
|
2396
2528
|
self.swap_buffer(buf, lineno)
|
|
2397
2529
|
return True
|
|
2398
|
-
buf._load_textfile(''.join(lines)
|
|
2530
|
+
buf._load_textfile(''.join(lines))
|
|
2531
|
+
buf.update_filestamp(filename)
|
|
2399
2532
|
self.swap_buffer(buf, lineno)
|
|
2400
2533
|
return True
|
|
2401
2534
|
return False
|
|
2402
|
-
|
|
2403
|
-
def load_file(self, filename, lineno=0, verbose=True):
|
|
2535
|
+
|
|
2536
|
+
def load_file(self, filename, lineno=0, verbose=True, **kwargs):
|
|
2404
2537
|
"""Load a file into an existing or new buffer.
|
|
2405
2538
|
The requests module is required to use URL extension.
|
|
2406
2539
|
"""
|
|
@@ -2408,7 +2541,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2408
2541
|
if not buf:
|
|
2409
2542
|
buf = self.create_buffer("*temp file*")
|
|
2410
2543
|
elif buf.need_buffer_save and verbose:
|
|
2411
|
-
if wx.MessageBox( # Confirm
|
|
2544
|
+
if wx.MessageBox( # Confirm loading the buffer.
|
|
2412
2545
|
"You are leaving unsaved content.\n\n"
|
|
2413
2546
|
"The changes will be discarded.\n"
|
|
2414
2547
|
"Continue loading?",
|
|
@@ -2422,22 +2555,23 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2422
2555
|
try:
|
|
2423
2556
|
if re.match(url_re, filename):
|
|
2424
2557
|
import requests
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2558
|
+
kwargs.setdefault('timeout', 3.0)
|
|
2559
|
+
res = requests.get(filename, **kwargs)
|
|
2560
|
+
if res.status_code == requests.codes.OK:
|
|
2561
|
+
buf._load_textfile(res.text)
|
|
2562
|
+
buf.update_filestamp(filename)
|
|
2428
2563
|
self.swap_buffer(buf, lineno)
|
|
2429
2564
|
return True
|
|
2430
|
-
|
|
2431
|
-
raise Exception("The requested URL was not found")
|
|
2565
|
+
res.raise_for_status() # raise HTTP error; don't catch here.
|
|
2432
2566
|
if buf._load_file(filename):
|
|
2433
2567
|
self.swap_buffer(buf, lineno)
|
|
2434
2568
|
return True
|
|
2435
2569
|
return False
|
|
2436
|
-
except
|
|
2437
|
-
self.post_message(
|
|
2570
|
+
except (OSError, UnicodeDecodeError, ModuleNotFoundError) as e:
|
|
2571
|
+
self.post_message("Failed to load:", e)
|
|
2438
2572
|
self.delete_buffer(buf)
|
|
2439
2573
|
return False
|
|
2440
|
-
|
|
2574
|
+
|
|
2441
2575
|
def find_file(self, filename=None):
|
|
2442
2576
|
"""Open the specified file."""
|
|
2443
2577
|
if not filename:
|
|
@@ -2446,21 +2580,22 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2446
2580
|
wildcard='|'.join(self.wildcards),
|
|
2447
2581
|
style=wx.FD_OPEN|wx.FD_MULTIPLE) as dlg:
|
|
2448
2582
|
if dlg.ShowModal() == wx.ID_OK:
|
|
2449
|
-
for fn in dlg.Paths
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
if not
|
|
2583
|
+
return all([self.find_file(fn) for fn in dlg.Paths])
|
|
2584
|
+
return None
|
|
2585
|
+
retval = self.load_file(filename)
|
|
2586
|
+
if retval == False: # noqa # to check if not None
|
|
2453
2587
|
buf = self.create_buffer(filename)
|
|
2454
|
-
buf._Buffer__mtime = 0 # => need_buffer_save
|
|
2455
2588
|
self.swap_buffer(buf)
|
|
2456
|
-
|
|
2589
|
+
self.post_message("New file.")
|
|
2590
|
+
return retval
|
|
2591
|
+
|
|
2457
2592
|
def save_file(self, filename, buf=None, verbose=True):
|
|
2458
2593
|
"""Save the current buffer to a file.
|
|
2459
2594
|
"""
|
|
2460
2595
|
buf = buf or self.buffer
|
|
2461
2596
|
if buf.need_buffer_load and verbose:
|
|
2462
2597
|
self.swap_buffer(buf)
|
|
2463
|
-
if wx.MessageBox( # Confirm
|
|
2598
|
+
if wx.MessageBox( # Confirm saving the buffer.
|
|
2464
2599
|
"The file has been modified externally.\n\n"
|
|
2465
2600
|
"The contents of the file will be overwritten.\n"
|
|
2466
2601
|
"Continue saving?",
|
|
@@ -2474,10 +2609,10 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2474
2609
|
self.default_buffer = None
|
|
2475
2610
|
return True
|
|
2476
2611
|
return False
|
|
2477
|
-
except
|
|
2478
|
-
self.post_message(
|
|
2612
|
+
except (OSError, UnicodeDecodeError) as e:
|
|
2613
|
+
self.post_message("Failed to save:", e)
|
|
2479
2614
|
return False
|
|
2480
|
-
|
|
2615
|
+
|
|
2481
2616
|
def load_buffer(self, buf=None):
|
|
2482
2617
|
"""Confirm the load with the dialog."""
|
|
2483
2618
|
buf = buf or self.buffer
|
|
@@ -2490,7 +2625,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2490
2625
|
return None
|
|
2491
2626
|
else:
|
|
2492
2627
|
return self.load_file(buf.filename, buf.markline+1)
|
|
2493
|
-
|
|
2628
|
+
|
|
2494
2629
|
def save_buffer(self, buf=None):
|
|
2495
2630
|
"""Confirm the save with the dialog."""
|
|
2496
2631
|
buf = buf or self.buffer
|
|
@@ -2503,28 +2638,28 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2503
2638
|
return None
|
|
2504
2639
|
else:
|
|
2505
2640
|
return self.save_file(buf.filename, buf)
|
|
2506
|
-
|
|
2641
|
+
|
|
2507
2642
|
def save_buffer_as(self, buf=None):
|
|
2508
2643
|
"""Confirm the saveas with the dialog."""
|
|
2509
2644
|
buf = buf or self.buffer
|
|
2510
2645
|
with wx.FileDialog(self, "Save buffer as",
|
|
2511
2646
|
defaultDir=os.path.dirname(self.buffer.filename),
|
|
2512
|
-
defaultFile=
|
|
2647
|
+
defaultFile=fix_fnchars(buf.name),
|
|
2513
2648
|
wildcard='|'.join(self.wildcards),
|
|
2514
2649
|
style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT) as dlg:
|
|
2515
2650
|
if dlg.ShowModal() == wx.ID_OK:
|
|
2516
2651
|
return self.save_file(dlg.Path, buf)
|
|
2517
|
-
|
|
2652
|
+
|
|
2518
2653
|
def save_all_buffers(self):
|
|
2519
|
-
for buf in self.
|
|
2654
|
+
for buf in self.get_all_buffers():
|
|
2520
2655
|
if buf.need_buffer_save:
|
|
2521
2656
|
self.save_buffer(buf)
|
|
2522
|
-
|
|
2657
|
+
|
|
2523
2658
|
def kill_buffer(self, buf=None):
|
|
2524
2659
|
"""Confirm the close with the dialog."""
|
|
2525
2660
|
buf = buf or self.buffer
|
|
2526
2661
|
if buf.need_buffer_save:
|
|
2527
|
-
if wx.MessageBox( # Confirm
|
|
2662
|
+
if wx.MessageBox( # Confirm closing the buffer.
|
|
2528
2663
|
"You are closing unsaved content.\n\n"
|
|
2529
2664
|
"The changes will be discarded.\n"
|
|
2530
2665
|
"Continue closing?",
|
|
@@ -2532,12 +2667,12 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2532
2667
|
style=wx.YES_NO|wx.ICON_INFORMATION) != wx.YES:
|
|
2533
2668
|
self.post_message("The close has been canceled.")
|
|
2534
2669
|
return None
|
|
2535
|
-
|
|
2536
|
-
|
|
2670
|
+
self.delete_buffer(buf)
|
|
2671
|
+
|
|
2537
2672
|
def kill_all_buffers(self):
|
|
2538
|
-
for buf in self.
|
|
2673
|
+
for buf in self.get_all_buffers():
|
|
2539
2674
|
if buf.need_buffer_save:
|
|
2540
|
-
if wx.MessageBox( # Confirm
|
|
2675
|
+
if wx.MessageBox( # Confirm closing the buffer.
|
|
2541
2676
|
"You are closing unsaved content.\n\n"
|
|
2542
2677
|
"The changes will be discarded.\n"
|
|
2543
2678
|
"Continue closing?",
|
|
@@ -2545,7 +2680,7 @@ class EditorBook(AuiNotebook, CtrlInterface):
|
|
|
2545
2680
|
style=wx.YES_NO|wx.ICON_INFORMATION) != wx.YES:
|
|
2546
2681
|
self.post_message("The close has been canceled.")
|
|
2547
2682
|
return None
|
|
2548
|
-
|
|
2683
|
+
self.delete_all_buffers()
|
|
2549
2684
|
|
|
2550
2685
|
|
|
2551
2686
|
class Interpreter(interpreter.Interpreter):
|
|
@@ -2556,7 +2691,7 @@ class Interpreter(interpreter.Interpreter):
|
|
|
2556
2691
|
|
|
2557
2692
|
self.parent = interpShell
|
|
2558
2693
|
self.globals = self.locals
|
|
2559
|
-
|
|
2694
|
+
|
|
2560
2695
|
def runcode(self, code):
|
|
2561
2696
|
"""Execute a code object.
|
|
2562
2697
|
|
|
@@ -2569,10 +2704,10 @@ class Interpreter(interpreter.Interpreter):
|
|
|
2569
2704
|
except Exception:
|
|
2570
2705
|
self.showtraceback()
|
|
2571
2706
|
finally:
|
|
2572
|
-
## ex. KeyboardInterrupt
|
|
2707
|
+
## ex. KeyboardInterrupt
|
|
2573
2708
|
if wx.IsBusy():
|
|
2574
2709
|
wx.EndBusyCursor()
|
|
2575
|
-
|
|
2710
|
+
|
|
2576
2711
|
def showtraceback(self):
|
|
2577
2712
|
"""Display the exception that just occurred.
|
|
2578
2713
|
|
|
@@ -2589,7 +2724,7 @@ class Interpreter(interpreter.Interpreter):
|
|
|
2589
2724
|
self.parent.handler('interp_error', v)
|
|
2590
2725
|
except AttributeError:
|
|
2591
2726
|
pass
|
|
2592
|
-
|
|
2727
|
+
|
|
2593
2728
|
def showsyntaxerror(self, filename=None, **kwargs):
|
|
2594
2729
|
"""Display the syntax error that just occurred.
|
|
2595
2730
|
|
|
@@ -2602,22 +2737,6 @@ class Interpreter(interpreter.Interpreter):
|
|
|
2602
2737
|
self.parent.handler('interp_error', v)
|
|
2603
2738
|
except AttributeError:
|
|
2604
2739
|
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
2740
|
|
|
2622
2741
|
|
|
2623
2742
|
class Nautilus(EditorInterface, Shell):
|
|
@@ -2639,10 +2758,9 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2639
2758
|
|
|
2640
2759
|
- quoteback : x`y --> y=x | x`y`z --> z=y=x
|
|
2641
2760
|
- 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] .
|
|
2761
|
+
- apropos : x.y? [not] p --> shows items (not) matched by predicate p
|
|
2762
|
+
equiv. apropos(x, y [,ignorecase ?:True,??:False] [,pred=p]).
|
|
2763
|
+
``y`` can contain regular expressions, but no dots or backslashes.
|
|
2646
2764
|
``p`` can be atom, callable, type (e.g., int, str, ...),
|
|
2647
2765
|
and any predicates such as inspect.isclass.
|
|
2648
2766
|
|
|
@@ -2657,15 +2775,15 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2657
2775
|
|
|
2658
2776
|
C-up : [0] retrieve previous history
|
|
2659
2777
|
C-down : [0] retrieve next history
|
|
2660
|
-
C-j
|
|
2661
|
-
C-h
|
|
2778
|
+
C-j : [0] tooltip of eval (for the selected or focused word)
|
|
2779
|
+
C-h M-h : [0] calltip of help (for the selected or focused func)
|
|
2662
2780
|
TAB : [1] history-comp
|
|
2663
2781
|
M-p : [1] retrieve previous history in history-comp mode
|
|
2664
2782
|
M-n : [1] retrieve next history in history-comp mode
|
|
2665
|
-
M-.
|
|
2666
|
-
M-/
|
|
2667
|
-
M-,
|
|
2668
|
-
M-m
|
|
2783
|
+
M-. C-. : [2] word-comp
|
|
2784
|
+
M-/ C-/ : [3] apropos-comp
|
|
2785
|
+
M-, C-, : [4] text-comp
|
|
2786
|
+
M-m C-m : [5] module-comp
|
|
2669
2787
|
|
|
2670
2788
|
Autocomps are incremental when pressed any alnums,
|
|
2671
2789
|
and decremental when backspace.
|
|
@@ -2690,17 +2808,17 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2690
2808
|
@property
|
|
2691
2809
|
def message(self):
|
|
2692
2810
|
return self.parent.message
|
|
2693
|
-
|
|
2811
|
+
|
|
2694
2812
|
@property
|
|
2695
2813
|
def target(self):
|
|
2696
2814
|
return self.__target
|
|
2697
|
-
|
|
2815
|
+
|
|
2698
2816
|
@target.setter
|
|
2699
2817
|
def target(self, obj):
|
|
2700
2818
|
"""Reset the shell target object; Rename the parent title.
|
|
2701
2819
|
"""
|
|
2702
2820
|
if not hasattr(obj, '__dict__'):
|
|
2703
|
-
raise TypeError("
|
|
2821
|
+
raise TypeError("invalid target")
|
|
2704
2822
|
|
|
2705
2823
|
self.__target = obj
|
|
2706
2824
|
self.locals = obj.__dict__
|
|
@@ -2709,38 +2827,39 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2709
2827
|
try:
|
|
2710
2828
|
obj.self = obj
|
|
2711
2829
|
obj.this = inspect.getmodule(obj)
|
|
2712
|
-
obj.shell = self
|
|
2830
|
+
obj.shell = self # Overwrite the facade <wx.py.shell.ShellFacade>.
|
|
2831
|
+
## obj.__name__ = typename(obj) # A namespace for ghost in the shell. cf. exec_region
|
|
2713
2832
|
except AttributeError:
|
|
2714
2833
|
pass
|
|
2715
2834
|
self.parent.handler('title_window', obj)
|
|
2716
|
-
|
|
2835
|
+
|
|
2717
2836
|
@property
|
|
2718
2837
|
def locals(self):
|
|
2719
2838
|
return self.interp.locals
|
|
2720
|
-
|
|
2839
|
+
|
|
2721
2840
|
@locals.setter
|
|
2722
|
-
def locals(self, v):
|
|
2841
|
+
def locals(self, v): # internal use only
|
|
2723
2842
|
self.interp.locals = v
|
|
2724
|
-
|
|
2843
|
+
|
|
2725
2844
|
@locals.deleter
|
|
2726
|
-
def locals(self):
|
|
2845
|
+
def locals(self): # internal use only
|
|
2727
2846
|
self.interp.locals = self.__target.__dict__
|
|
2728
|
-
|
|
2847
|
+
|
|
2729
2848
|
@property
|
|
2730
2849
|
def globals(self):
|
|
2731
2850
|
return self.interp.globals
|
|
2732
|
-
|
|
2851
|
+
|
|
2733
2852
|
@globals.setter
|
|
2734
|
-
def globals(self, v):
|
|
2853
|
+
def globals(self, v): # internal use only
|
|
2735
2854
|
self.interp.globals = v
|
|
2736
|
-
|
|
2855
|
+
|
|
2737
2856
|
@globals.deleter
|
|
2738
|
-
def globals(self):
|
|
2857
|
+
def globals(self): # internal use only
|
|
2739
2858
|
self.interp.globals = self.__target.__dict__
|
|
2740
2859
|
self.interp.globals.update(self.__globals)
|
|
2741
|
-
|
|
2860
|
+
|
|
2742
2861
|
__globals = {}
|
|
2743
|
-
|
|
2862
|
+
|
|
2744
2863
|
def __init__(self, parent, target, name="root",
|
|
2745
2864
|
introText=None,
|
|
2746
2865
|
startupScript=None,
|
|
@@ -2748,22 +2867,22 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2748
2867
|
**kwargs):
|
|
2749
2868
|
Shell.__init__(self, parent,
|
|
2750
2869
|
locals=target.__dict__,
|
|
2751
|
-
interpShell=self,
|
|
2870
|
+
interpShell=self, # **kwds of InterpClass
|
|
2752
2871
|
InterpClass=Interpreter,
|
|
2753
2872
|
introText=introText,
|
|
2754
2873
|
startupScript=startupScript,
|
|
2755
|
-
execStartupScript=execStartupScript,
|
|
2874
|
+
execStartupScript=execStartupScript, # executes ~/.py
|
|
2756
2875
|
**kwargs)
|
|
2757
2876
|
EditorInterface.__init__(self)
|
|
2758
2877
|
|
|
2759
|
-
self.parent = parent
|
|
2878
|
+
self.parent = parent # parent<ShellFrame> is not Parent<AuiNotebook>
|
|
2760
2879
|
self.target = target
|
|
2761
2880
|
self.Name = name
|
|
2762
2881
|
|
|
2763
2882
|
wx.py.shell.USE_MAGIC = True
|
|
2764
|
-
wx.py.shell.magic = self.magic
|
|
2883
|
+
wx.py.shell.magic = self.magic # called when USE_MAGIC
|
|
2765
2884
|
|
|
2766
|
-
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate)
|
|
2885
|
+
self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate) # skip to brace matching
|
|
2767
2886
|
self.Bind(stc.EVT_STC_CALLTIP_CLICK, self.OnCallTipClick)
|
|
2768
2887
|
|
|
2769
2888
|
self.Bind(wx.EVT_WINDOW_DESTROY, self.OnDestroy)
|
|
@@ -2782,19 +2901,10 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2782
2901
|
|
|
2783
2902
|
def clear(evt):
|
|
2784
2903
|
## """Clear selection and message, no skip."""
|
|
2785
|
-
## *
|
|
2786
|
-
if self.CanEdit():
|
|
2787
|
-
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()
|
|
2904
|
+
## *DO NOT* clear autocomp, so that the event can skip to AutoComp properly.
|
|
2795
2905
|
if self.CanEdit():
|
|
2796
2906
|
with self.off_undocollection():
|
|
2797
|
-
self.ReplaceSelection(
|
|
2907
|
+
self.ReplaceSelection('')
|
|
2798
2908
|
self.message("")
|
|
2799
2909
|
|
|
2800
2910
|
def fork(evt):
|
|
@@ -2807,12 +2917,10 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2807
2917
|
self.handler.update({ # DNA<Nautilus>
|
|
2808
2918
|
None : {
|
|
2809
2919
|
'interp_error' : [ None, self.on_interp_error ],
|
|
2810
|
-
'shell_deleted' : [ None, dispatch, self.
|
|
2920
|
+
'shell_deleted' : [ None, dispatch, self.on_shell_deleted ],
|
|
2811
2921
|
'shell_modified' : [ None, dispatch ],
|
|
2812
|
-
'shell_activated' : [ None, dispatch, self.
|
|
2813
|
-
'shell_inactivated' : [ None, dispatch, self.
|
|
2814
|
-
'*button* pressed' : [ None, dispatch, skip ],
|
|
2815
|
-
'*button* released' : [ None, dispatch, skip ],
|
|
2922
|
+
'shell_activated' : [ None, dispatch, self.on_shell_activated ],
|
|
2923
|
+
'shell_inactivated' : [ None, dispatch, self.on_shell_inactivated ],
|
|
2816
2924
|
},
|
|
2817
2925
|
-1 : { # original action of the wx.py.shell
|
|
2818
2926
|
'* pressed' : (0, skip, self.on_exit_escmap),
|
|
@@ -2829,36 +2937,39 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2829
2937
|
},
|
|
2830
2938
|
0 : { # Normal mode
|
|
2831
2939
|
'* pressed' : (0, skip),
|
|
2832
|
-
'* released' : (0, skip),
|
|
2940
|
+
'* released' : (0, skip, dispatch),
|
|
2941
|
+
'*button* pressed' : (0, skip, dispatch),
|
|
2833
2942
|
'escape pressed' : (-1, self.on_enter_escmap),
|
|
2834
2943
|
'space pressed' : (0, self.OnSpace),
|
|
2835
2944
|
'*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
2945
|
'enter pressed' : (0, self.OnEnter),
|
|
2839
2946
|
'C-enter pressed' : (0, _F(self.insertLineBreak)),
|
|
2840
2947
|
'C-S-enter pressed' : (0, _F(self.insertLineBreak)),
|
|
2841
|
-
'*enter pressed' : (0, ),
|
|
2948
|
+
'*enter pressed' : (0, ), # -> OnShowCompHistory 無効
|
|
2842
2949
|
'C-[ pressed' : (0, _F(self.goto_previous_mark_arrow)),
|
|
2843
2950
|
'C-S-[ pressed' : (0, _F(self.goto_previous_mark_arrow, selection=1)),
|
|
2844
2951
|
'C-] pressed' : (0, _F(self.goto_next_mark_arrow)),
|
|
2845
2952
|
'C-S-] pressed' : (0, _F(self.goto_next_mark_arrow, selection=1)),
|
|
2846
2953
|
'M-up pressed' : (0, _F(self.goto_previous_white_arrow)),
|
|
2847
2954
|
'M-down pressed' : (0, _F(self.goto_next_white_arrow)),
|
|
2848
|
-
# 'C-c pressed' : (0, skip),
|
|
2849
|
-
'C-S-c pressed' : (0, skip),
|
|
2955
|
+
# 'C-c pressed' : (0, skip), # -> spec-map
|
|
2956
|
+
'C-S-c pressed' : (0, skip), # -> Copy selected text, retaining prompts.
|
|
2850
2957
|
'C-v pressed' : (0, _F(self.Paste)),
|
|
2851
2958
|
'C-S-v pressed' : (0, _F(self.Paste, rectangle=1)),
|
|
2852
2959
|
'S-insert pressed' : (0, _F(self.Paste)),
|
|
2853
2960
|
'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),
|
|
2961
|
+
'C-j pressed' : (0, _F(self.eval_line)),
|
|
2962
|
+
'M-j pressed' : (0, _F(self.exec_region)),
|
|
2856
2963
|
'C-h pressed' : (0, self.call_helpTip),
|
|
2857
2964
|
'M-h pressed' : (0, self.call_helpDoc),
|
|
2858
|
-
'. pressed' : (2, self.OnEnterDot),
|
|
2859
2965
|
'tab pressed' : (1, self.call_history_comp),
|
|
2860
2966
|
'M-p pressed' : (1, self.call_history_comp),
|
|
2861
2967
|
'M-n pressed' : (1, self.call_history_comp),
|
|
2968
|
+
'. pressed' : (2, self.OnEnterDot),
|
|
2969
|
+
'C-. pressed' : (2, self.call_word_autocomp),
|
|
2970
|
+
'C-/ pressed' : (3, self.call_apropos_autocomp),
|
|
2971
|
+
'C-, pressed' : (4, self.call_text_autocomp),
|
|
2972
|
+
'C-m pressed' : (5, self.call_module_autocomp),
|
|
2862
2973
|
'M-. pressed' : (2, self.call_word_autocomp),
|
|
2863
2974
|
'M-/ pressed' : (3, self.call_apropos_autocomp),
|
|
2864
2975
|
'M-, pressed' : (4, self.call_text_autocomp),
|
|
@@ -2874,10 +2985,10 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2874
2985
|
'S-left released' : (1, self.call_history_comp),
|
|
2875
2986
|
'S-right pressed' : (1, skip),
|
|
2876
2987
|
'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,
|
|
2988
|
+
'tab pressed' : (1, _F(self._on_completion, step=1)), # 古いヒストリへ進む
|
|
2989
|
+
'S-tab pressed' : (1, _F(self._on_completion, step=-1)), # 新しいヒストリへ戻る
|
|
2990
|
+
'M-p pressed' : (1, _F(self._on_completion, step=1)),
|
|
2991
|
+
'M-n pressed' : (1, _F(self._on_completion, step=-1)),
|
|
2881
2992
|
'[a-z0-9_] pressed' : (1, skip),
|
|
2882
2993
|
'[a-z0-9_] released' : (1, self.call_history_comp),
|
|
2883
2994
|
'S-[a-z\\] pressed' : (1, skip),
|
|
@@ -2890,11 +3001,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2890
3001
|
'*f[0-9]* pressed' : (1, ),
|
|
2891
3002
|
},
|
|
2892
3003
|
2 : { # word auto completion AS-mode
|
|
2893
|
-
'quit' : (0, clear_autocomp),
|
|
2894
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3004
|
+
'quit' : (0, self.clear_autocomp),
|
|
3005
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2895
3006
|
'tab pressed' : (0, clear, skip),
|
|
2896
3007
|
'enter pressed' : (0, clear, skip),
|
|
2897
|
-
'escape pressed' : (0,
|
|
3008
|
+
'escape pressed' : (0, clear, skip),
|
|
2898
3009
|
'up pressed' : (2, skip, self.on_completion_backward),
|
|
2899
3010
|
'down pressed' : (2, skip, self.on_completion_forward),
|
|
2900
3011
|
'*left pressed' : (2, skip),
|
|
@@ -2909,8 +3020,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2909
3020
|
'*delete pressed' : (2, skip),
|
|
2910
3021
|
'*backspace pressed' : (2, self.OnBackspace),
|
|
2911
3022
|
'*backspace released' : (2, self.call_word_autocomp),
|
|
2912
|
-
|
|
2913
|
-
'C-j pressed' : (2, self.eval_line),
|
|
3023
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
3024
|
+
'C-j pressed' : (2, _F(self.eval_line)),
|
|
2914
3025
|
'*alt pressed' : (2, ),
|
|
2915
3026
|
'*ctrl pressed' : (2, ),
|
|
2916
3027
|
'*shift pressed' : (2, ),
|
|
@@ -2918,11 +3029,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2918
3029
|
'*f[0-9]* pressed' : (2, ),
|
|
2919
3030
|
},
|
|
2920
3031
|
3 : { # apropos auto completion AS-mode
|
|
2921
|
-
'quit' : (0, clear_autocomp),
|
|
2922
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3032
|
+
'quit' : (0, self.clear_autocomp),
|
|
3033
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2923
3034
|
'tab pressed' : (0, clear, skip),
|
|
2924
3035
|
'enter pressed' : (0, clear, skip),
|
|
2925
|
-
'escape pressed' : (0,
|
|
3036
|
+
'escape pressed' : (0, clear, skip),
|
|
2926
3037
|
'up pressed' : (3, skip, self.on_completion_backward),
|
|
2927
3038
|
'down pressed' : (3, skip, self.on_completion_forward),
|
|
2928
3039
|
'*left pressed' : (3, skip),
|
|
@@ -2937,8 +3048,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2937
3048
|
'*delete pressed' : (3, skip),
|
|
2938
3049
|
'*backspace pressed' : (3, self.OnBackspace),
|
|
2939
3050
|
'*backspace released' : (3, self.call_apropos_autocomp),
|
|
2940
|
-
|
|
2941
|
-
'C-j pressed' : (3, self.eval_line),
|
|
3051
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
3052
|
+
'C-j pressed' : (3, _F(self.eval_line)),
|
|
2942
3053
|
'*alt pressed' : (3, ),
|
|
2943
3054
|
'*ctrl pressed' : (3, ),
|
|
2944
3055
|
'*shift pressed' : (3, ),
|
|
@@ -2946,11 +3057,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2946
3057
|
'*f[0-9]* pressed' : (3, ),
|
|
2947
3058
|
},
|
|
2948
3059
|
4 : { # text auto completion AS-mode
|
|
2949
|
-
'quit' : (0, clear_autocomp),
|
|
2950
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3060
|
+
'quit' : (0, self.clear_autocomp),
|
|
3061
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2951
3062
|
'tab pressed' : (0, clear, skip),
|
|
2952
3063
|
'enter pressed' : (0, clear, skip),
|
|
2953
|
-
'escape pressed' : (0,
|
|
3064
|
+
'escape pressed' : (0, clear, skip),
|
|
2954
3065
|
'up pressed' : (4, skip, self.on_completion_backward),
|
|
2955
3066
|
'down pressed' : (4, skip, self.on_completion_forward),
|
|
2956
3067
|
'*left pressed' : (4, skip),
|
|
@@ -2965,8 +3076,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2965
3076
|
'*delete pressed' : (4, skip),
|
|
2966
3077
|
'*backspace pressed' : (4, self.OnBackspace),
|
|
2967
3078
|
'*backspace released' : (4, self.call_text_autocomp),
|
|
2968
|
-
|
|
2969
|
-
'C-j pressed' : (4, self.eval_line),
|
|
3079
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
3080
|
+
'C-j pressed' : (4, _F(self.eval_line)),
|
|
2970
3081
|
'*alt pressed' : (4, ),
|
|
2971
3082
|
'*ctrl pressed' : (4, ),
|
|
2972
3083
|
'*shift pressed' : (4, ),
|
|
@@ -2974,11 +3085,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2974
3085
|
'*f[0-9]* pressed' : (4, ),
|
|
2975
3086
|
},
|
|
2976
3087
|
5 : { # module auto completion AS-mode
|
|
2977
|
-
'quit' : (0, clear_autocomp),
|
|
2978
|
-
'* pressed' : (0, clear_autocomp, fork),
|
|
3088
|
+
'quit' : (0, self.clear_autocomp),
|
|
3089
|
+
'* pressed' : (0, self.clear_autocomp, fork),
|
|
2979
3090
|
'tab pressed' : (0, clear, skip),
|
|
2980
3091
|
'enter pressed' : (0, clear, skip),
|
|
2981
|
-
'escape pressed' : (0,
|
|
3092
|
+
'escape pressed' : (0, clear, skip),
|
|
2982
3093
|
'up pressed' : (5, skip, self.on_completion_backward),
|
|
2983
3094
|
'down pressed' : (5, skip, self.on_completion_forward),
|
|
2984
3095
|
'*left pressed' : (5, skip),
|
|
@@ -2990,11 +3101,12 @@ class Nautilus(EditorInterface, Shell):
|
|
|
2990
3101
|
'S-[a-z\\] pressed' : (5, skip),
|
|
2991
3102
|
'S-[a-z\\] released' : (5, self.call_module_autocomp),
|
|
2992
3103
|
'\\ released' : (5, self.call_module_autocomp),
|
|
3104
|
+
'C-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
|
|
2993
3105
|
'M-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
|
|
2994
3106
|
'*delete pressed' : (5, skip),
|
|
2995
3107
|
'*backspace pressed' : (5, self.OnBackspace),
|
|
2996
3108
|
'*backspace released' : (5, self.call_module_autocomp),
|
|
2997
|
-
|
|
3109
|
+
'*S-backspace pressed' : (0, self.clear_autocomp, fork),
|
|
2998
3110
|
'*alt pressed' : (5, ),
|
|
2999
3111
|
'*ctrl pressed' : (5, ),
|
|
3000
3112
|
'*shift pressed' : (5, ),
|
|
@@ -3007,22 +3119,22 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3007
3119
|
self.show_folder()
|
|
3008
3120
|
self.set_stylus(Stylus.py_shell_mode)
|
|
3009
3121
|
|
|
3010
|
-
##
|
|
3122
|
+
## Delete unnecessary arrows at startup.
|
|
3011
3123
|
del self.white_arrow
|
|
3012
3124
|
del self.red_arrow
|
|
3013
3125
|
|
|
3014
3126
|
self.__text = ''
|
|
3015
|
-
|
|
3127
|
+
|
|
3016
3128
|
def trace_position(self):
|
|
3017
3129
|
_text, lp = self.CurLine
|
|
3018
3130
|
self.message("{:>6d}:{} ({})".format(self.cline, lp, self.cpos), pane=-1)
|
|
3019
|
-
|
|
3131
|
+
|
|
3020
3132
|
def OnDestroy(self, evt):
|
|
3021
3133
|
if evt.EventObject is self:
|
|
3022
3134
|
self.handler('shell_deleted', self)
|
|
3023
3135
|
evt.Skip()
|
|
3024
|
-
|
|
3025
|
-
def OnUpdate(self, evt):
|
|
3136
|
+
|
|
3137
|
+
def OnUpdate(self, evt): #<wx._stc.StyledTextEvent>
|
|
3026
3138
|
if evt.Updated & (stc.STC_UPDATE_SELECTION | stc.STC_UPDATE_CONTENT):
|
|
3027
3139
|
self.trace_position()
|
|
3028
3140
|
if self.handler.current_state == 0:
|
|
@@ -3031,18 +3143,18 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3031
3143
|
name, argspec, tip = self.interp.getCallTip(text)
|
|
3032
3144
|
if tip:
|
|
3033
3145
|
tip = tip.splitlines()[0]
|
|
3034
|
-
self.message(tip)
|
|
3146
|
+
self.message(tip) # clear if no tip
|
|
3035
3147
|
self.__text = text
|
|
3036
3148
|
if evt.Updated & stc.STC_UPDATE_CONTENT:
|
|
3037
3149
|
self.handler('shell_modified', self)
|
|
3038
3150
|
evt.Skip()
|
|
3039
|
-
|
|
3151
|
+
|
|
3040
3152
|
def OnCallTipClick(self, evt):
|
|
3041
3153
|
if self.CallTipActive():
|
|
3042
3154
|
self.CallTipCancel()
|
|
3043
3155
|
self.parent.handler('add_help', self._calltips[1])
|
|
3044
3156
|
evt.Skip()
|
|
3045
|
-
|
|
3157
|
+
|
|
3046
3158
|
def OnSpace(self, evt):
|
|
3047
3159
|
"""Called when space pressed."""
|
|
3048
3160
|
if not self.CanEdit():
|
|
@@ -3052,133 +3164,142 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3052
3164
|
or re.match(r"from\s*$", cmdl)\
|
|
3053
3165
|
or re.match(r"from\s+([\w.]+)\s+import\s*", cmdl):
|
|
3054
3166
|
self.ReplaceSelection(' ')
|
|
3055
|
-
self.handler('M-m pressed', None)
|
|
3167
|
+
self.handler('M-m pressed', None) # => call_module_autocomp
|
|
3056
3168
|
return
|
|
3057
3169
|
evt.Skip()
|
|
3058
|
-
|
|
3170
|
+
|
|
3059
3171
|
def OnBackspace(self, evt):
|
|
3060
3172
|
"""Called when backspace pressed.
|
|
3061
3173
|
Backspace-guard from autocomp eating over a prompt whitespace.
|
|
3062
3174
|
"""
|
|
3063
3175
|
if self.cpos == self.bolc:
|
|
3064
|
-
self.handler('quit', evt)
|
|
3176
|
+
self.handler('quit', evt) # Don't eat backward prompt
|
|
3065
3177
|
return
|
|
3066
3178
|
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
|
-
|
|
3179
|
+
|
|
3180
|
+
@editable
|
|
3181
|
+
def backward_kill_word(self): # (override)
|
|
3182
|
+
if not self.SelectedText:
|
|
3183
|
+
if self.cpos <= self.bolc:
|
|
3184
|
+
return
|
|
3185
|
+
text, lp = self.CurLine
|
|
3186
|
+
if text[:lp] == sys.ps2:
|
|
3187
|
+
self.cpos -= lp # Select ps2:prompt
|
|
3188
|
+
self.WordLeftExtend() # Select cr/lf chunks
|
|
3189
|
+
else:
|
|
3190
|
+
self.WordLeftExtend()
|
|
3191
|
+
q = max(self.bol, self.bolc) # for debugger mode: bol <= bolc
|
|
3192
|
+
if self.cpos < q:
|
|
3193
|
+
self.cpos = q # Don't skip back ps2:prompt
|
|
3194
|
+
self.ReplaceSelection('')
|
|
3195
|
+
|
|
3196
|
+
@editable
|
|
3197
|
+
def backward_kill_line(self): # (override)
|
|
3198
|
+
if not self.SelectedText:
|
|
3199
|
+
if self.cpos <= self.bolc:
|
|
3200
|
+
return
|
|
3201
|
+
text, lp = self.CurLine
|
|
3202
|
+
if text[:lp] == sys.ps2:
|
|
3203
|
+
self.cpos -= lp # Select ps2:prompt
|
|
3204
|
+
self.WordLeftExtend() # Select cr/lf chunks
|
|
3205
|
+
else:
|
|
3206
|
+
q = max(self.bol, self.bolc) # for debugger mode: bol <= bolc
|
|
3207
|
+
if self.cpos > q:
|
|
3208
|
+
self.cpos = q
|
|
3209
|
+
else:
|
|
3210
|
+
self.WordLeftExtend() # Select cr/lf chunks
|
|
3211
|
+
self.ReplaceSelection('')
|
|
3212
|
+
|
|
3092
3213
|
def OnEnter(self, evt):
|
|
3093
3214
|
"""Called when enter pressed."""
|
|
3094
3215
|
if not self.CanEdit():
|
|
3095
|
-
self.goto_char(self.eolc)
|
|
3216
|
+
self.goto_char(self.eolc) # go to end of command line
|
|
3096
3217
|
return
|
|
3097
|
-
if self.AutoCompActive():
|
|
3218
|
+
if self.AutoCompActive(): # skip to auto completion
|
|
3098
3219
|
evt.Skip()
|
|
3099
3220
|
return
|
|
3100
3221
|
if self.CallTipActive():
|
|
3101
3222
|
self.CallTipCancel()
|
|
3102
3223
|
|
|
3103
|
-
##
|
|
3224
|
+
## Skip to wx.py.magic if text begins with !(sx), ?(info), and ??(help).
|
|
3104
3225
|
text = self.cmdline
|
|
3105
3226
|
if not text or text[0] in '!?':
|
|
3106
3227
|
evt.Skip()
|
|
3107
3228
|
return
|
|
3108
3229
|
|
|
3109
|
-
##
|
|
3230
|
+
## Cast magic for `@? (Note: PY35 supports @(matmul)-operator).
|
|
3110
3231
|
tokens = list(split_words(text))
|
|
3111
3232
|
if any(x in tokens for x in '`@?$'):
|
|
3112
3233
|
cmd = self.magic_interpret(tokens)
|
|
3113
3234
|
if '\n' in cmd:
|
|
3114
|
-
self.Execute(cmd)
|
|
3235
|
+
self.Execute(cmd) # => multi-line commands
|
|
3115
3236
|
else:
|
|
3116
|
-
self.run(cmd, verbose=0, prompt=0)
|
|
3237
|
+
self.run(cmd, verbose=0, prompt=0) # => push(cmd)
|
|
3117
3238
|
return
|
|
3118
3239
|
|
|
3119
3240
|
self.exec_cmdline()
|
|
3120
|
-
|
|
3121
|
-
|
|
3241
|
+
# evt.Skip() # => processLine
|
|
3242
|
+
|
|
3122
3243
|
def OnEnterDot(self, evt):
|
|
3123
3244
|
"""Called when dot [.] pressed."""
|
|
3124
3245
|
if not self.CanEdit():
|
|
3125
3246
|
self.handler('quit', evt)
|
|
3126
3247
|
return
|
|
3127
3248
|
p = self.cpos
|
|
3128
|
-
|
|
3249
|
+
lst = self.get_style(p-1)
|
|
3129
3250
|
rst = self.get_style(p)
|
|
3130
3251
|
if p == self.bolc:
|
|
3131
|
-
self.ReplaceSelection('self')
|
|
3132
|
-
elif
|
|
3252
|
+
self.ReplaceSelection('self') # replace [.] --> [self.]
|
|
3253
|
+
elif lst in ('space', 'sep', 'lparen'):
|
|
3133
3254
|
self.ReplaceSelection('self')
|
|
3134
|
-
elif
|
|
3135
|
-
self.handler('quit', evt)
|
|
3255
|
+
elif lst not in ('moji', 'word', 'rparen') or rst == 'word':
|
|
3256
|
+
self.handler('quit', evt) # Don't enter autocomp
|
|
3136
3257
|
evt.Skip()
|
|
3137
|
-
|
|
3258
|
+
|
|
3138
3259
|
def on_enter_escmap(self, evt):
|
|
3139
3260
|
self.__caret_mode = self.CaretPeriod
|
|
3140
3261
|
self.CaretPeriod = 0
|
|
3141
3262
|
self.message("ESC-")
|
|
3142
|
-
|
|
3263
|
+
|
|
3143
3264
|
def on_exit_escmap(self, evt):
|
|
3144
3265
|
self.CaretPeriod = self.__caret_mode
|
|
3145
3266
|
self.message("ESC {}".format(evt.key))
|
|
3146
|
-
if self.eolc < self.bolc:
|
|
3267
|
+
if self.eolc < self.bolc: # Check if prompt is in valid state.
|
|
3147
3268
|
self.goto_char(self.eolc)
|
|
3148
|
-
self.promptPosEnd = 0
|
|
3269
|
+
self.promptPosEnd = 0 # Enabale write(prompt).
|
|
3149
3270
|
self.prompt()
|
|
3150
3271
|
self.AnnotationClearAll()
|
|
3151
|
-
|
|
3272
|
+
|
|
3152
3273
|
def on_enter_notemode(self, evt):
|
|
3153
3274
|
self.noteMode = True
|
|
3154
3275
|
self.__caret_mode = self.CaretForeground
|
|
3155
3276
|
self.CaretForeground = 'red'
|
|
3156
3277
|
self.message("Note mode")
|
|
3157
|
-
|
|
3278
|
+
|
|
3158
3279
|
def on_exit_notemode(self, evt):
|
|
3159
3280
|
self.noteMode = False
|
|
3160
3281
|
self.CaretForeground = self.__caret_mode
|
|
3161
3282
|
self.goto_char(self.eolc)
|
|
3162
|
-
self.promptPosEnd = 0
|
|
3283
|
+
self.promptPosEnd = 0 # Enabale write(prompt).
|
|
3163
3284
|
self.prompt()
|
|
3164
3285
|
self.message("")
|
|
3165
|
-
|
|
3286
|
+
|
|
3166
3287
|
def goto_next_white_arrow(self):
|
|
3167
|
-
self.goto_next_marker(0b010)
|
|
3168
|
-
|
|
3288
|
+
self.goto_next_marker(0b010) # next white-arrow
|
|
3289
|
+
|
|
3169
3290
|
def goto_previous_white_arrow(self):
|
|
3170
|
-
self.goto_previous_marker(0b010)
|
|
3171
|
-
|
|
3291
|
+
self.goto_previous_marker(0b010) # previous white-arrow
|
|
3292
|
+
|
|
3172
3293
|
def goto_next_mark_arrow(self, selection=False):
|
|
3173
|
-
self.goto_next_marker(0b110, selection)
|
|
3174
|
-
|
|
3294
|
+
self.goto_next_marker(0b110, selection) # next white/red-arrow
|
|
3295
|
+
|
|
3175
3296
|
def goto_previous_mark_arrow(self, selection=False):
|
|
3176
|
-
self.goto_previous_marker(0b110, selection)
|
|
3177
|
-
|
|
3297
|
+
self.goto_previous_marker(0b110, selection) # previous white/red-arrow
|
|
3298
|
+
|
|
3178
3299
|
## --------------------------------
|
|
3179
|
-
## Magic caster of the shell
|
|
3300
|
+
## Magic caster of the shell.
|
|
3180
3301
|
## --------------------------------
|
|
3181
|
-
|
|
3302
|
+
|
|
3182
3303
|
@classmethod
|
|
3183
3304
|
def magic(self, cmd):
|
|
3184
3305
|
"""Called before command pushed.
|
|
@@ -3190,7 +3311,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3190
3311
|
elif cmd[0] == '?': cmd = 'info({})'.format(cmd[1:])
|
|
3191
3312
|
elif cmd[0] == '!': cmd = 'sx({!r})'.format(cmd[1:])
|
|
3192
3313
|
return cmd
|
|
3193
|
-
|
|
3314
|
+
|
|
3194
3315
|
@classmethod
|
|
3195
3316
|
def magic_interpret(self, tokens):
|
|
3196
3317
|
"""Called when [Enter] command, or eval-time for tooltip.
|
|
@@ -3205,8 +3326,8 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3205
3326
|
Note:
|
|
3206
3327
|
This is called before run, execute, and original magic.
|
|
3207
3328
|
"""
|
|
3208
|
-
sep1 = "`@=;\r\n#"
|
|
3209
|
-
sep2 = "`@=+-/*%<>&|^~,; \t\r\n#"
|
|
3329
|
+
sep1 = "`@=;\r\n#" # [`] no ops, no spaces, no comma
|
|
3330
|
+
sep2 = "`@=+-/*%<>&|^~,; \t\r\n#" # [@] ops, delims, and whitespaces
|
|
3210
3331
|
|
|
3211
3332
|
def _popiter(ls, f):
|
|
3212
3333
|
pred = f if callable(f) else re.compile(f).match
|
|
@@ -3222,7 +3343,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3222
3343
|
for i, c in enumerate(tokens):
|
|
3223
3344
|
rest = tokens[i+1:]
|
|
3224
3345
|
|
|
3225
|
-
if c == '@' and not lhs.strip() and '\n' in rest:
|
|
3346
|
+
if c == '@' and not lhs.strip() and '\n' in rest: # @decor
|
|
3226
3347
|
pass
|
|
3227
3348
|
|
|
3228
3349
|
elif c == '@':
|
|
@@ -3232,16 +3353,21 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3232
3353
|
## func(a,b,c) @debug --> func,a,b,c @debug
|
|
3233
3354
|
if rhs in ("debug", "profile", "timeit"):
|
|
3234
3355
|
if lhs[-1] in ')':
|
|
3235
|
-
|
|
3356
|
+
R = next(split_parts(lhs, reverse=1))
|
|
3357
|
+
L = lhs[:-len(R)]
|
|
3236
3358
|
if not L:
|
|
3237
3359
|
lhs = "{!r}".format(R[1:-1])
|
|
3238
|
-
|
|
3360
|
+
else:
|
|
3239
3361
|
lhs = "{}, {}".format(L, R[1:-1])
|
|
3240
3362
|
|
|
3241
3363
|
## @(y1,,,yn) --> @partial(y1,,,yn)
|
|
3242
3364
|
elif rhs.startswith('('):
|
|
3243
3365
|
rhs = re.sub(r"^\((.*)\)", r"partial(\1)", rhs, flags=re.S)
|
|
3244
3366
|
|
|
3367
|
+
## obj @.method --> (obj).method
|
|
3368
|
+
elif rhs.startswith('.'):
|
|
3369
|
+
return self.magic_interpret([f"({lhs}){rhs}"] + rest)
|
|
3370
|
+
|
|
3245
3371
|
return self.magic_interpret([f"{rhs}({lhs})"] + rest)
|
|
3246
3372
|
|
|
3247
3373
|
if c == '`':
|
|
@@ -3260,17 +3386,17 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3260
3386
|
return lhs + c + self.magic_interpret(rest)
|
|
3261
3387
|
|
|
3262
3388
|
if c == sys.ps2.strip():
|
|
3263
|
-
rhs = ''.join(_popiter(rest, "[ \t\r\n]"))
|
|
3389
|
+
rhs = ''.join(_popiter(rest, "[ \t\r\n]")) # feed
|
|
3264
3390
|
return lhs + c + rhs + self.magic_interpret(rest)
|
|
3265
3391
|
|
|
3266
3392
|
if c.startswith('#'):
|
|
3267
|
-
rhs = ''.join(_popiter(rest, "[^\r\n]"))
|
|
3393
|
+
rhs = ''.join(_popiter(rest, "[^\r\n]")) # skip comment
|
|
3268
3394
|
return lhs + c + rhs + self.magic_interpret(rest)
|
|
3269
3395
|
|
|
3270
|
-
lhs += c
|
|
3396
|
+
lhs += c # store in lhs; no more processing
|
|
3271
3397
|
return lhs
|
|
3272
|
-
|
|
3273
|
-
def
|
|
3398
|
+
|
|
3399
|
+
def on_shell_deleted(self, shell):
|
|
3274
3400
|
"""Called before shell:self is killed.
|
|
3275
3401
|
Delete target shell to prevent referencing the dead shell.
|
|
3276
3402
|
"""
|
|
@@ -3278,26 +3404,26 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3278
3404
|
obj = self.target
|
|
3279
3405
|
try:
|
|
3280
3406
|
if not obj.shell:
|
|
3281
|
-
del obj.shell
|
|
3407
|
+
del obj.shell # delete the facade <wx.py.shell.ShellFacade>
|
|
3282
3408
|
except AttributeError:
|
|
3283
3409
|
pass
|
|
3284
3410
|
wx.CallAfter(_del)
|
|
3285
|
-
|
|
3286
|
-
def
|
|
3411
|
+
|
|
3412
|
+
def on_shell_activated(self, shell):
|
|
3287
3413
|
"""Called when the shell:self is activated.
|
|
3288
|
-
Reset localvars assigned for the shell target.
|
|
3414
|
+
Reset localvars assigned for the shell target. cf. target.setter.
|
|
3289
3415
|
"""
|
|
3290
3416
|
self.trace_position()
|
|
3291
3417
|
obj = self.target
|
|
3292
3418
|
try:
|
|
3293
3419
|
obj.self = obj
|
|
3294
3420
|
obj.this = inspect.getmodule(obj)
|
|
3295
|
-
obj.shell = self
|
|
3421
|
+
obj.shell = self # Overwrite the facade <wx.py.shell.ShellFacade>
|
|
3296
3422
|
except AttributeError:
|
|
3297
3423
|
pass
|
|
3298
3424
|
self.parent.handler('title_window', obj)
|
|
3299
|
-
|
|
3300
|
-
def
|
|
3425
|
+
|
|
3426
|
+
def on_shell_inactivated(self, shell):
|
|
3301
3427
|
"""Called when shell:self is inactivated.
|
|
3302
3428
|
Remove target localvars assigned for the shell target.
|
|
3303
3429
|
"""
|
|
@@ -3305,7 +3431,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3305
3431
|
self.AutoCompCancel()
|
|
3306
3432
|
if self.CallTipActive():
|
|
3307
3433
|
self.CallTipCancel()
|
|
3308
|
-
|
|
3434
|
+
|
|
3309
3435
|
def on_text_input(self, text):
|
|
3310
3436
|
"""Called when [Enter] text (before push).
|
|
3311
3437
|
Mark points, reset history point, etc.
|
|
@@ -3316,7 +3442,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3316
3442
|
if text.rstrip():
|
|
3317
3443
|
self.__eolc_mark = self.eolc
|
|
3318
3444
|
self.historyIndex = -1
|
|
3319
|
-
|
|
3445
|
+
|
|
3320
3446
|
def on_text_output(self, text):
|
|
3321
3447
|
"""Called when [Enter] text (after push).
|
|
3322
3448
|
Set markers at the last command line.
|
|
@@ -3325,28 +3451,28 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3325
3451
|
Argument `text` is raw output:str with no magic cast.
|
|
3326
3452
|
"""
|
|
3327
3453
|
ln = self.LineFromPosition(self.bolc)
|
|
3328
|
-
err = re.findall(
|
|
3329
|
-
self.add_marker(ln, 1 if not err else 2)
|
|
3454
|
+
err = re.findall(py_trace_re, text, re.M)
|
|
3455
|
+
self.add_marker(ln, 1 if not err else 2) # 1:white-arrow 2:red-arrow
|
|
3330
3456
|
return (not err)
|
|
3331
|
-
|
|
3457
|
+
|
|
3332
3458
|
def on_interp_error(self, e):
|
|
3333
3459
|
ln = self.LineFromPosition(self.bolc)
|
|
3334
3460
|
self.red_pointer = ln + e.lineno - 1
|
|
3335
|
-
|
|
3461
|
+
|
|
3336
3462
|
## --------------------------------
|
|
3337
|
-
## Attributes of the shell
|
|
3463
|
+
## Attributes of the shell.
|
|
3338
3464
|
## --------------------------------
|
|
3339
|
-
|
|
3465
|
+
|
|
3340
3466
|
@property
|
|
3341
3467
|
def bolc(self):
|
|
3342
|
-
"Beginning of command-line."
|
|
3468
|
+
"""Beginning of command-line."""
|
|
3343
3469
|
return self.promptPosEnd
|
|
3344
|
-
|
|
3470
|
+
|
|
3345
3471
|
@property
|
|
3346
3472
|
def eolc(self):
|
|
3347
|
-
"End of command-line."
|
|
3473
|
+
"""End of command-line."""
|
|
3348
3474
|
return self.TextLength
|
|
3349
|
-
|
|
3475
|
+
|
|
3350
3476
|
@property
|
|
3351
3477
|
def bol(self):
|
|
3352
3478
|
"""Beginning of line (override) excluding prompt."""
|
|
@@ -3356,30 +3482,35 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3356
3482
|
lp -= len(ps)
|
|
3357
3483
|
break
|
|
3358
3484
|
return self.cpos - lp
|
|
3359
|
-
|
|
3485
|
+
|
|
3360
3486
|
@property
|
|
3361
3487
|
def cmdline(self):
|
|
3362
3488
|
"""Full multi-line command in the current prompt."""
|
|
3363
3489
|
return self.GetTextRange(self.bolc, self.eolc)
|
|
3364
|
-
|
|
3490
|
+
|
|
3365
3491
|
## cf. getCommand() -> caret-line that starts with a prompt
|
|
3366
3492
|
## 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):
|
|
3493
|
+
## [BUG ver 4.1.1] Don't use for current prompt --> Fixed in wx ver 4.2.0.
|
|
3494
|
+
|
|
3495
|
+
def getMultilineCommand(self, rstrip=True):
|
|
3370
3496
|
"""Extract a multi-line command which starts with a prompt.
|
|
3371
3497
|
|
|
3372
3498
|
(override) Don't remove trailing ps2 + spaces.
|
|
3373
3499
|
Don't invoke ``GotoLine``.
|
|
3374
3500
|
"""
|
|
3375
|
-
region = self.
|
|
3501
|
+
region = self.get_command_region(self.cline)
|
|
3376
3502
|
if region:
|
|
3377
3503
|
p, q = (self.PositionFromLine(x) for x in region)
|
|
3378
3504
|
p += len(sys.ps1)
|
|
3379
|
-
|
|
3505
|
+
command = self.GetTextRange(p, q).rstrip(os.linesep) # remove the last cr/lf
|
|
3506
|
+
if rstrip:
|
|
3507
|
+
command = command.replace(os.linesep + sys.ps2, '\n')
|
|
3508
|
+
command = command.rstrip()
|
|
3509
|
+
command = command.replace('\n', os.linesep + sys.ps2)
|
|
3510
|
+
return command
|
|
3380
3511
|
return ''
|
|
3381
|
-
|
|
3382
|
-
def
|
|
3512
|
+
|
|
3513
|
+
def get_command_region(self, line):
|
|
3383
3514
|
"""Line numbers of prompt head and tail containing the line."""
|
|
3384
3515
|
lc = line
|
|
3385
3516
|
le = lc + 1
|
|
@@ -3388,7 +3519,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3388
3519
|
if not text.startswith(sys.ps2):
|
|
3389
3520
|
break
|
|
3390
3521
|
lc -= 1
|
|
3391
|
-
if not text.startswith(sys.ps1):
|
|
3522
|
+
if not text.startswith(sys.ps1): # bad region
|
|
3392
3523
|
return None
|
|
3393
3524
|
while le < self.LineCount:
|
|
3394
3525
|
text = self.GetLine(le)
|
|
@@ -3396,11 +3527,11 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3396
3527
|
break
|
|
3397
3528
|
le += 1
|
|
3398
3529
|
return lc, le
|
|
3399
|
-
|
|
3530
|
+
|
|
3400
3531
|
## --------------------------------
|
|
3401
|
-
## Execution methods of the shell
|
|
3532
|
+
## Execution methods of the shell.
|
|
3402
3533
|
## --------------------------------
|
|
3403
|
-
|
|
3534
|
+
|
|
3404
3535
|
def push(self, command, **kwargs):
|
|
3405
3536
|
"""Send command to the interpreter for execution.
|
|
3406
3537
|
|
|
@@ -3408,7 +3539,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3408
3539
|
"""
|
|
3409
3540
|
self.on_text_input(command)
|
|
3410
3541
|
Shell.push(self, command, **kwargs)
|
|
3411
|
-
|
|
3542
|
+
|
|
3412
3543
|
def addHistory(self, command):
|
|
3413
3544
|
"""Add command to the command history.
|
|
3414
3545
|
|
|
@@ -3418,7 +3549,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3418
3549
|
if not command:
|
|
3419
3550
|
return
|
|
3420
3551
|
|
|
3421
|
-
## この段階では push
|
|
3552
|
+
## この段階では push された直後で,次のようになっている.
|
|
3422
3553
|
## bolc : beginning of command-line
|
|
3423
3554
|
## eolc : end of the output-buffer
|
|
3424
3555
|
try:
|
|
@@ -3426,7 +3557,7 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3426
3557
|
output = self.GetTextRange(self.__eolc_mark, self.eolc)
|
|
3427
3558
|
|
|
3428
3559
|
input = self.regulate_cmd(input)
|
|
3429
|
-
Shell.addHistory(self, input)
|
|
3560
|
+
Shell.addHistory(self, input) # => self.history
|
|
3430
3561
|
|
|
3431
3562
|
noerr = self.on_text_output(output)
|
|
3432
3563
|
if noerr:
|
|
@@ -3435,18 +3566,30 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3435
3566
|
command = self.fixLineEndings(command)
|
|
3436
3567
|
self.parent.handler('add_log', command + os.linesep, noerr)
|
|
3437
3568
|
except AttributeError:
|
|
3438
|
-
## execStartupScript 実行時は出力先 (owner)
|
|
3439
|
-
## shell.__init__
|
|
3569
|
+
## execStartupScript 実行時は出力先 (owner) が存在しない.
|
|
3570
|
+
## shell.__init__ よりも先に実行される.
|
|
3440
3571
|
pass
|
|
3441
|
-
|
|
3572
|
+
|
|
3573
|
+
def setBuiltinKeywords(self):
|
|
3574
|
+
"""Create pseudo keywords as part of builtins.
|
|
3575
|
+
|
|
3576
|
+
(override) Don't add `close`, `exit` and `quit` to builtins.
|
|
3577
|
+
"""
|
|
3578
|
+
from wx.py.path import ls, cd, pwd, sx
|
|
3579
|
+
builtins.cd = cd
|
|
3580
|
+
builtins.ls = ls
|
|
3581
|
+
builtins.pwd = pwd
|
|
3582
|
+
builtins.sx = sx
|
|
3583
|
+
|
|
3442
3584
|
def execStartupScript(self, su):
|
|
3443
3585
|
"""Execute the user's PYTHONSTARTUP script if they have one.
|
|
3444
3586
|
|
|
3445
3587
|
(override) Add globals when executing su:startupScript.
|
|
3446
|
-
|
|
3588
|
+
Don't add '_f' to globals when executing su:startupScript.
|
|
3589
|
+
Don't add intro text in the history.
|
|
3447
3590
|
"""
|
|
3448
|
-
keys = set(self.locals.keys())
|
|
3449
|
-
self.promptPosEnd = self.TextLength
|
|
3591
|
+
keys = set(self.locals.keys()) # to check for locals map changes.
|
|
3592
|
+
self.promptPosEnd = self.TextLength # Fix history point
|
|
3450
3593
|
if su and os.path.isfile(su):
|
|
3451
3594
|
self.push("print('Startup script executed:', {0!r})\n".format(su))
|
|
3452
3595
|
self.push("with open({0!r}) as _f: exec(_f.read())\n".format(su))
|
|
@@ -3456,31 +3599,31 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3456
3599
|
self.push("")
|
|
3457
3600
|
self.interp.startupScript = None
|
|
3458
3601
|
self.__globals = {k: self.locals[k] for k in (self.locals.keys() - keys)}
|
|
3459
|
-
|
|
3602
|
+
|
|
3460
3603
|
def Paste(self, rectangle=False):
|
|
3461
3604
|
"""Replace selection with clipboard contents.
|
|
3462
3605
|
|
|
3463
3606
|
(override) Remove ps1 and ps2 from the multi-line command to paste.
|
|
3464
3607
|
Add offset in paste-rectangle mode.
|
|
3465
|
-
Don't relplace the last
|
|
3608
|
+
Don't relplace the last cr/lf to ps.
|
|
3466
3609
|
"""
|
|
3467
3610
|
if self.CanPaste() and wx.TheClipboard.Open():
|
|
3468
3611
|
data = wx.TextDataObject()
|
|
3469
3612
|
if wx.TheClipboard.GetData(data):
|
|
3470
3613
|
command = data.GetText()
|
|
3471
|
-
|
|
3614
|
+
# command = command.rstrip()
|
|
3472
3615
|
command = self.fixLineEndings(command)
|
|
3473
3616
|
command = self.regulate_cmd(command)
|
|
3474
3617
|
ps = sys.ps2
|
|
3475
3618
|
_text, lp = self.CurLine
|
|
3476
3619
|
if rectangle:
|
|
3477
|
-
ps += ' ' * (lp - len(ps))
|
|
3620
|
+
ps += ' ' * (lp - len(ps)) # add offset
|
|
3478
3621
|
if lp == 0:
|
|
3479
|
-
command = ps + command
|
|
3622
|
+
command = ps + command # paste-line
|
|
3480
3623
|
command = command.replace('\n', os.linesep + ps)
|
|
3481
3624
|
self.ReplaceSelection(command)
|
|
3482
3625
|
wx.TheClipboard.Close()
|
|
3483
|
-
|
|
3626
|
+
|
|
3484
3627
|
def regulate_cmd(self, text):
|
|
3485
3628
|
"""Regulate text to executable command.
|
|
3486
3629
|
|
|
@@ -3489,53 +3632,53 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3489
3632
|
The eol-code (cr/lf) is not fixed.
|
|
3490
3633
|
Call self.fixLineEndings in advance as necessary.
|
|
3491
3634
|
"""
|
|
3492
|
-
text = self.lstripPrompt(text)
|
|
3635
|
+
text = self.lstripPrompt(text) # strip a leading prompt
|
|
3493
3636
|
lf = '\n'
|
|
3494
3637
|
return (text.replace(os.linesep + sys.ps1, lf)
|
|
3495
3638
|
.replace(os.linesep + sys.ps2, lf)
|
|
3496
3639
|
.replace(os.linesep, lf))
|
|
3497
|
-
|
|
3640
|
+
|
|
3498
3641
|
def clear(self):
|
|
3499
3642
|
"""Delete all text (override) put new prompt."""
|
|
3500
3643
|
self.ClearAll()
|
|
3501
3644
|
self.promptPosStart = 0
|
|
3502
3645
|
self.promptPosEnd = 0
|
|
3503
3646
|
self.prompt()
|
|
3504
|
-
|
|
3647
|
+
|
|
3505
3648
|
def write(self, text, pos=None):
|
|
3506
3649
|
"""Display text in the shell.
|
|
3507
3650
|
|
|
3508
|
-
(override) Append text if it is writable at the position.
|
|
3651
|
+
(override) Append text if it is writable at the given position.
|
|
3509
3652
|
"""
|
|
3510
3653
|
if pos is not None:
|
|
3511
3654
|
if pos < 0:
|
|
3512
|
-
pos += self.TextLength + 1
|
|
3655
|
+
pos += self.TextLength + 1 # Counts end-of-buffer (+1:\0)
|
|
3513
3656
|
self.goto_char(pos)
|
|
3514
3657
|
if self.CanEdit():
|
|
3515
|
-
Shell.write(self, text)
|
|
3516
|
-
|
|
3658
|
+
Shell.write(self, text) # => AddText
|
|
3659
|
+
|
|
3517
3660
|
## input = classmethod(Shell.ask)
|
|
3518
|
-
|
|
3661
|
+
|
|
3519
3662
|
def info(self, obj):
|
|
3520
3663
|
"""Short information."""
|
|
3521
3664
|
doc = inspect.getdoc(obj)\
|
|
3522
3665
|
or "No information about {}".format(obj)
|
|
3523
|
-
self.parent.handler('add_help', doc) or print(doc)
|
|
3524
|
-
|
|
3666
|
+
self.parent.handler('add_help', doc, typename(obj)) or print(doc)
|
|
3667
|
+
|
|
3525
3668
|
def help(self, obj):
|
|
3526
3669
|
"""Full description."""
|
|
3527
3670
|
doc = pydoc.plain(pydoc.render_doc(obj))\
|
|
3528
3671
|
or "No description about {}".format(obj)
|
|
3529
|
-
self.parent.handler('add_help', doc) or print(doc)
|
|
3530
|
-
|
|
3672
|
+
self.parent.handler('add_help', doc, typename(obj)) or print(doc)
|
|
3673
|
+
|
|
3531
3674
|
def eval(self, text):
|
|
3532
3675
|
return eval(text, self.globals, self.locals)
|
|
3533
|
-
|
|
3676
|
+
|
|
3534
3677
|
def exec(self, text):
|
|
3535
3678
|
exec(text, self.globals, self.locals)
|
|
3536
3679
|
dispatcher.send(signal='Interpreter.push',
|
|
3537
3680
|
sender=self, command=None, more=False)
|
|
3538
|
-
|
|
3681
|
+
|
|
3539
3682
|
def exec_cmdline(self):
|
|
3540
3683
|
"""Execute command-line directly.
|
|
3541
3684
|
|
|
@@ -3556,14 +3699,14 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3556
3699
|
continue
|
|
3557
3700
|
line = self.lstripPrompt(lines)
|
|
3558
3701
|
lstr = line.lstrip()
|
|
3559
|
-
if (lstr and lstr == line
|
|
3560
|
-
and not lstr.startswith('#')
|
|
3561
|
-
and not re.match(py_outdent_re, lstr)):
|
|
3702
|
+
if (lstr and lstr == line # no indent
|
|
3703
|
+
and not lstr.startswith('#') # no comment
|
|
3704
|
+
and not re.match(py_outdent_re, lstr)): # no outdent pattern
|
|
3562
3705
|
if cmd:
|
|
3563
|
-
commands.append(cmd)
|
|
3706
|
+
commands.append(cmd) # Add stacked commands to the list
|
|
3564
3707
|
cmd = line
|
|
3565
3708
|
else:
|
|
3566
|
-
cmd += lines
|
|
3709
|
+
cmd += lines # multi-line command
|
|
3567
3710
|
lines = ''
|
|
3568
3711
|
commands.append(cmd + lines)
|
|
3569
3712
|
|
|
@@ -3587,56 +3730,37 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3587
3730
|
for cmd in commands:
|
|
3588
3731
|
self.write(cmd)
|
|
3589
3732
|
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.
|
|
3733
|
+
|
|
3734
|
+
def eval_line(self):
|
|
3735
|
+
"""Evaluate the selected word or line and show calltips."""
|
|
3736
|
+
if self.CallTipActive():
|
|
3737
|
+
self.CallTipCancel()
|
|
3597
3738
|
|
|
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
3739
|
def _gen_text():
|
|
3610
3740
|
text = self.SelectedText
|
|
3611
3741
|
if text:
|
|
3612
3742
|
yield text
|
|
3613
3743
|
else:
|
|
3614
|
-
yield self.
|
|
3744
|
+
yield self.line_at_caret
|
|
3615
3745
|
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
3746
|
|
|
3623
|
-
|
|
3624
|
-
for text in self.gen_text_at_caret():
|
|
3747
|
+
for text in _gen_text():
|
|
3625
3748
|
tokens = split_words(text)
|
|
3626
3749
|
try:
|
|
3627
3750
|
cmd = self.magic_interpret(tokens)
|
|
3628
3751
|
cmd = self.regulate_cmd(cmd)
|
|
3629
3752
|
obj = self.eval(cmd)
|
|
3630
3753
|
except Exception as e:
|
|
3631
|
-
|
|
3754
|
+
self.message(e)
|
|
3632
3755
|
else:
|
|
3633
3756
|
self.CallTipShow(self.cpos, pformat(obj))
|
|
3634
3757
|
self.message(cmd)
|
|
3635
3758
|
return
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3759
|
+
if not text:
|
|
3760
|
+
self.message("No words")
|
|
3761
|
+
|
|
3762
|
+
def exec_region(self):
|
|
3763
|
+
"""Execute a region of code."""
|
|
3640
3764
|
if self.CallTipActive():
|
|
3641
3765
|
self.CallTipCancel()
|
|
3642
3766
|
|
|
@@ -3651,10 +3775,10 @@ class Nautilus(EditorInterface, Shell):
|
|
|
3651
3775
|
self.exec(code)
|
|
3652
3776
|
except Exception as e:
|
|
3653
3777
|
msg = traceback.format_exc()
|
|
3654
|
-
err = re.findall(
|
|
3778
|
+
err = re.findall(py_trace_re, msg, re.M)
|
|
3655
3779
|
lines = [int(ln) for fn, ln in err if fn == filename]
|
|
3656
3780
|
if lines:
|
|
3657
|
-
region = self.
|
|
3781
|
+
region = self.get_command_region(self.cline)
|
|
3658
3782
|
lx = region[0] + lines[-1] - 1
|
|
3659
3783
|
self.red_pointer = lx
|
|
3660
3784
|
self.message(e)
|