mwxlib 0.99.0__py3-none-any.whl → 1.7.13__py3-none-any.whl

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