mwxlib 0.97.7__py3-none-any.whl → 0.98.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of mwxlib might be problematic. Click here for more details.

mwx/framework.py CHANGED
@@ -1,7 +1,7 @@
1
1
  #! python3
2
2
  """mwxlib framework.
3
3
  """
4
- __version__ = "0.97.7"
4
+ __version__ = "0.98.0"
5
5
  __author__ = "Kazuya O'moto <komoto@jeol.co.jp>"
6
6
 
7
7
  from contextlib import contextmanager
@@ -78,11 +78,12 @@ def postcall(f):
78
78
 
79
79
  @contextmanager
80
80
  def save_focus_excursion():
81
+ """Save window focus excursion."""
81
82
  wnd = wx.Window.FindFocus() # original focus
82
83
  try:
83
84
  yield wnd
84
85
  finally:
85
- if wnd:
86
+ if wnd and wnd.IsShownOnScreen():
86
87
  wnd.SetFocus() # restore focus
87
88
 
88
89
 
mwx/nutshell.py CHANGED
@@ -71,11 +71,289 @@ def ask(f, prompt="Enter value", type=str):
71
71
  return _f
72
72
 
73
73
 
74
+ class AutoCompInterfaceMixin:
75
+ """Auto completion interface.
76
+
77
+ Note:
78
+ This class is mixed-in ``wx.py.editwindow.EditWindow``.
79
+ """
80
+ modules = set() # to be used in module-comp mode
81
+
82
+ fragmwords = set(keyword.kwlist + dir(builtins)) # to be used in text-comp mode
83
+
84
+ def __init__(self):
85
+ ## cf. sys.modules
86
+ if not self.modules:
87
+ force = wx.GetKeyState(wx.WXK_CONTROL)\
88
+ & wx.GetKeyState(wx.WXK_SHIFT)
89
+ AutoCompInterfaceMixin.modules = set(find_modules(force))
90
+
91
+ def CallTipShow(self, pos, tip, N=11):
92
+ """Show a call tip containing a definition near position pos.
93
+
94
+ (override) Snip the tip of max N lines if it is too long.
95
+ Keep the tip for calltip-click event.
96
+ """
97
+ self._calltip_pos = pos
98
+ self._calltip = tip
99
+ lines = tip.splitlines()
100
+ if len(lines) > N > 0:
101
+ lines[N+1:] = ["\n...(snip) This tips are too long... "
102
+ "Click to show more details."]
103
+ tip = '\n'.join(lines)
104
+ super().CallTipShow(pos, tip)
105
+
106
+ def autoCallTipShow(self, command, insertcalltip=True):
107
+ """Display argument spec and docstring in a popup window."""
108
+ if self.CallTipActive():
109
+ self.CallTipCancel()
110
+
111
+ name, argspec, tip = introspect.getCallTip(command, locals=self.locals)
112
+ if tip:
113
+ dispatcher.send(signal='Shell.calltip', sender=self, calltip=tip)
114
+ p = self.cpos
115
+ if argspec and insertcalltip:
116
+ self.AddText(argspec + ')') # 挿入後のカーソル位置は変化しない
117
+ self.SetSelection(self.cpos, p) # selection back
118
+ if tip:
119
+ ## In case there isn't enough room, only go back to bol fallback.
120
+ tippos = max(self.bol, p - len(name) - 1)
121
+ self.CallTipShow(tippos, tip)
122
+
123
+ def call_helpDoc(self, evt):
124
+ """Show help:str for the selected topic."""
125
+ if self.CallTipActive():
126
+ self.CallTipCancel()
127
+
128
+ text = next(self.gen_text_at_caret(), None)
129
+ if text:
130
+ text = introspect.getRoot(text, terminator='(')
131
+ try:
132
+ obj = self.eval(text)
133
+ self.help(obj)
134
+ except Exception as e:
135
+ self.message("- {} : {!r}".format(e, text))
136
+
137
+ def call_helpTip(self, evt):
138
+ """Show tooltips for the selected topic."""
139
+ if self.CallTipActive():
140
+ self.CallTipCancel()
141
+
142
+ text = next(self.gen_text_at_caret(), None)
143
+ if text:
144
+ p = self.cpos
145
+ self.autoCallTipShow(text,
146
+ p == self.eol and self.get_char(p-1) == '(') # => CallTipShow
147
+
148
+ def on_completion_forward(self, evt):
149
+ if not self.AutoCompActive():
150
+ self.handler('quit', evt)
151
+ return
152
+ self._on_completion(1)
153
+
154
+ def on_completion_backward(self, evt):
155
+ if not self.AutoCompActive():
156
+ self.handler('quit', evt)
157
+ return
158
+ self._on_completion(-1)
159
+
160
+ def _on_completion(self, step=0):
161
+ """Show completion with selection."""
162
+ try:
163
+ N = len(self.__comp_words)
164
+ j = self.__comp_ind + step
165
+ j = 0 if j < 0 else j if j < N else N-1
166
+ word = self.__comp_words[j]
167
+ n = len(self.__comp_hint)
168
+ p = self.cpos
169
+ if not self.SelectedText:
170
+ p, self.anchor, sty = self.get_following_atom(p) # word-right-selection
171
+ self.ReplaceSelection(word[n:]) # Modify (or insert) the selected range
172
+ self.cpos = p # backward selection to the point
173
+ self.__comp_ind = j
174
+ except IndexError:
175
+ self.message("No completion words")
176
+
177
+ def _gen_autocomp(self, j, hint, words, sep=' ', mode=True):
178
+ ## Prepare on_completion_forward/backward
179
+ self.__comp_ind = j
180
+ self.__comp_hint = hint
181
+ self.__comp_words = words
182
+ if not mode:
183
+ self.anchor = self.eolc # selection to eol
184
+ self._on_completion() # show completion always
185
+ elif words:
186
+ self.AutoCompSetSeparator(ord(sep))
187
+ self.AutoCompShow(len(hint), sep.join(words))
188
+
189
+ def _get_words_hint(self):
190
+ cmdl = self.GetTextRange(self.bol, self.cpos)
191
+ text = next(split_words(cmdl, reverse=1), '')
192
+ return text.rpartition('.') # -> text, sep, hint
193
+
194
+ def call_history_comp(self, evt):
195
+ """Called when history-comp mode."""
196
+ if not self.CanEdit():
197
+ self.handler('quit', evt)
198
+ return
199
+
200
+ cmdl = self.GetTextRange(self.bol, self.cpos)
201
+ if cmdl.isspace() or self.bol != self.bolc:
202
+ self.handler('skip', evt) # [tab pressed] => on_indent_line
203
+ return
204
+
205
+ hint = cmdl.strip()
206
+ ls = [x.replace('\n', os.linesep + sys.ps2)
207
+ for x in self.history if x.startswith(hint)] # case-sensitive match
208
+ words = sorted(set(ls), key=ls.index, reverse=0) # keep order, no duplication
209
+
210
+ ## the latest history stacks in the head of the list (time-descending)
211
+ self._gen_autocomp(0, hint, words, mode=False)
212
+ self.message("[history] {} candidates matched"
213
+ " with {!r}".format(len(words), hint))
214
+
215
+ def call_text_autocomp(self, evt):
216
+ """Called when text-comp mode."""
217
+ if not self.CanEdit():
218
+ self.handler('quit', evt)
219
+ return
220
+
221
+ cmdl = self.GetTextRange(self.bol, self.cpos)
222
+ hint = re.search(r"[\w.]*$", cmdl).group(0) # extract the last word
223
+
224
+ ls = [x for x in self.fragmwords if x.startswith(hint)] # case-sensitive match
225
+ words = sorted(ls, key=lambda s:s.upper())
226
+
227
+ self._gen_autocomp(0, hint, words)
228
+ self.message("[text] {} candidates matched"
229
+ " with {!r}".format(len(words), hint))
230
+
231
+ def call_module_autocomp(self, evt, force=False):
232
+ """Called when module-comp mode."""
233
+ if not self.CanEdit():
234
+ self.handler('quit', evt)
235
+ return
236
+
237
+ def _continue(hints):
238
+ if not hints.endswith(' '):
239
+ h = hints.strip()
240
+ if not h.endswith(','):
241
+ lh = h.split(',')[-1].strip() # 'x, y, z|' last hint after ','
242
+ if ' ' not in lh: # 'x, y as|' contains no spaces.
243
+ return lh
244
+
245
+ cmdl = self.GetTextRange(self.bol, self.cpos)
246
+ hint = re.search(r"[\w.]*$", cmdl).group(0) # extract the last word
247
+ try:
248
+ if (m := re.match(r"from\s+([\w.]+)\s+import\s+(.*)", cmdl)):
249
+ text, hints = m.groups()
250
+ if not _continue(hints) and not force:
251
+ self.message("[module]>>> waiting for key input...")
252
+ return
253
+ elif '.' in hints:
254
+ self.message("[module] invalid syntax.")
255
+ return
256
+ try:
257
+ self.message("[module]>>> loading {}...".format(text))
258
+ modules = set(dir(import_module(text)))
259
+ except ImportError as e:
260
+ self.message("\b failed:", e)
261
+ return
262
+ else:
263
+ ## Add unimported module names.
264
+ p = "{}.{}".format(text, hint)
265
+ keys = [x[len(text)+1:] for x in self.modules if x.startswith(p)]
266
+ modules.update(k for k in keys if '.' not in k)
267
+
268
+ elif (m := re.match(r"(import|from)\s+(.*)", cmdl)):
269
+ text, hints = m.groups()
270
+ if not _continue(hints) and not force:
271
+ self.message("[module]>>> waiting for key input...")
272
+ return
273
+ modules = self.modules
274
+ else:
275
+ text, sep, hint = self._get_words_hint()
276
+ obj = self.eval(text)
277
+ modules = set(k for k, v in vars(obj).items() if inspect.ismodule(v))
278
+
279
+ P = re.compile(hint)
280
+ p = re.compile(hint, re.I)
281
+ words = sorted([x for x in modules if p.match(x)], key=lambda s:s.upper())
282
+
283
+ j = next((k for k, w in enumerate(words) if P.match(w)),
284
+ next((k for k, w in enumerate(words) if p.match(w)), -1))
285
+
286
+ self._gen_autocomp(j, hint, words)
287
+ self.message("[module] {} candidates matched"
288
+ " with {!r} in {}".format(len(words), hint, text))
289
+ except re.error as e:
290
+ self.message("- re:miss compilation {!r} : {!r}".format(e, hint))
291
+ except SyntaxError as e:
292
+ self.handler('quit', evt)
293
+ self.message("- {} : {!r}".format(e, text))
294
+ except Exception as e:
295
+ self.message("- {} : {!r}".format(e, text))
296
+
297
+ def call_word_autocomp(self, evt):
298
+ """Called when word-comp mode."""
299
+ if not self.CanEdit():
300
+ self.handler('quit', evt)
301
+ return
302
+ try:
303
+ text, sep, hint = self._get_words_hint()
304
+ obj = self.eval(text)
305
+
306
+ P = re.compile(hint)
307
+ p = re.compile(hint, re.I)
308
+ words = sorted([x for x in dir(obj) if p.match(x)], key=lambda s:s.upper())
309
+
310
+ j = next((k for k, w in enumerate(words) if P.match(w)),
311
+ next((k for k, w in enumerate(words) if p.match(w)), -1))
312
+
313
+ self._gen_autocomp(j, hint, words)
314
+ self.message("[word] {} candidates matched"
315
+ " with {!r} in {}".format(len(words), hint, text))
316
+ except re.error as e:
317
+ self.message("- re:miss compilation {!r} : {!r}".format(e, hint))
318
+ except SyntaxError as e:
319
+ self.handler('quit', evt)
320
+ self.message("- {} : {!r}".format(e, text))
321
+ except Exception as e:
322
+ self.message("- {} : {!r}".format(e, text))
323
+
324
+ def call_apropos_autocomp(self, evt):
325
+ """Called when apropos mode."""
326
+ if not self.CanEdit():
327
+ self.handler('quit', evt)
328
+ return
329
+ try:
330
+ text, sep, hint = self._get_words_hint()
331
+ obj = self.eval(text)
332
+
333
+ P = re.compile(hint)
334
+ p = re.compile(hint, re.I)
335
+ words = sorted([x for x in dir(obj) if p.search(x)], key=lambda s:s.upper())
336
+
337
+ j = next((k for k, w in enumerate(words) if P.match(w)),
338
+ next((k for k, w in enumerate(words) if p.match(w)), -1))
339
+
340
+ self._gen_autocomp(j, hint, words)
341
+ self.message("[apropos] {} candidates matched"
342
+ " with {!r} in {}".format(len(words), hint, text))
343
+ except re.error as e:
344
+ self.message("- re:miss compilation {!r} : {!r}".format(e, hint))
345
+ except SyntaxError as e:
346
+ self.handler('quit', evt)
347
+ self.message("- {} : {!r}".format(e, text))
348
+ except Exception as e:
349
+ self.message("- {} : {!r}".format(e, text))
350
+
351
+
74
352
  class EditorInterface(CtrlInterface):
75
353
  """Interface of Python code editor.
76
354
 
77
355
  Note:
78
- This class should be mixed-in `wx.stc.StyledTextCtrl`
356
+ This class is mixed-in ``wx.stc.StyledTextCtrl``.
79
357
  """
80
358
  def __init__(self):
81
359
  CtrlInterface.__init__(self)
@@ -538,7 +816,7 @@ class EditorInterface(CtrlInterface):
538
816
  return (self.cpos - lp + len(text.encode()))
539
817
 
540
818
  @property
541
- def caretline(self):
819
+ def line_at_caret(self):
542
820
  """Text of the range (bol, eol) at the caret-line."""
543
821
  return self.GetTextRange(self.bol, self.eol)
544
822
 
@@ -586,6 +864,19 @@ class EditorInterface(CtrlInterface):
586
864
  q = self.cpos
587
865
  return self.GetTextRange(p, q)
588
866
 
867
+ def gen_text_at_caret(self):
868
+ """Generates the selected text,
869
+ otherwise the line or expression at the caret.
870
+ """
871
+ def _gen_text():
872
+ text = self.SelectedText
873
+ if text:
874
+ yield text
875
+ else:
876
+ yield self.line_at_caret
877
+ yield self.expr_at_caret
878
+ return filter(None, _gen_text())
879
+
589
880
  ## --------------------------------
590
881
  ## Python syntax and indentation
591
882
  ## --------------------------------
@@ -604,24 +895,26 @@ class EditorInterface(CtrlInterface):
604
895
  @can_edit
605
896
  def py_indent_line(self):
606
897
  """Indent the current line."""
607
- text = self.caretline # w/ no-prompt
608
- lstr = text.lstrip() # w/ no-indent
609
- p = self.bol + len(text) - len(lstr) # for multi-byte string
898
+ text = self.line_at_caret # w/ no-prompt
899
+ lstr = text.lstrip() # w/ no-indent
900
+ p = self.bol + len(text) - len(lstr)
610
901
  offset = max(0, self.cpos - p)
611
902
  indent = self.py_current_indent() # check current/previous line
612
- self.Replace(self.bol, p, ' '*indent)
613
- self.goto_char(self.bol + indent + offset)
903
+ if indent >= 0:
904
+ self.Replace(self.bol, p, ' '*indent)
905
+ self.goto_char(self.bol + indent + offset)
614
906
 
615
907
  @can_edit
616
908
  def py_outdent_line(self):
617
909
  """Outdent the current line."""
618
- text = self.caretline # w/ no-prompt
619
- lstr = text.lstrip() # w/ no-indent
620
- p = self.bol + len(text) - len(lstr) # for multi-byte string
910
+ text = self.line_at_caret # w/ no-prompt
911
+ lstr = text.lstrip() # w/ no-indent
912
+ p = self.bol + len(text) - len(lstr)
621
913
  offset = max(0, self.cpos - p)
622
914
  indent = len(text) - len(lstr) - 4
623
- self.Replace(self.bol, p, ' '*indent)
624
- self.goto_char(self.bol + indent + offset)
915
+ if indent >= 0:
916
+ self.Replace(self.bol, p, ' '*indent)
917
+ self.goto_char(self.bol + indent + offset)
625
918
 
626
919
  def py_current_indent(self):
627
920
  """Calculate indent spaces from previous line."""
@@ -1146,9 +1439,9 @@ class EditorInterface(CtrlInterface):
1146
1439
  self.goto_char(p)
1147
1440
 
1148
1441
  def back_to_indentation(self):
1149
- text = self.caretline # w/ no-prompt
1150
- lstr = text.lstrip() # w/ no-indent
1151
- p = self.bol + len(text) - len(lstr) # for multi-byte string
1442
+ text = self.line_at_caret # w/ no-prompt
1443
+ lstr = text.lstrip() # w/ no-indent
1444
+ p = self.bol + len(text) - len(lstr)
1152
1445
  self.goto_char(p, interactive=True)
1153
1446
  self.ScrollToColumn(0)
1154
1447
 
@@ -1191,13 +1484,11 @@ class EditorInterface(CtrlInterface):
1191
1484
  """Save buffer excursion."""
1192
1485
  try:
1193
1486
  p = self.cpos
1194
- q = self.anchor
1195
1487
  vpos = self.GetScrollPos(wx.VERTICAL)
1196
1488
  hpos = self.GetScrollPos(wx.HORIZONTAL)
1197
1489
  yield
1198
1490
  finally:
1199
1491
  self.GotoPos(p)
1200
- self.SetAnchor(q)
1201
1492
  self.ScrollToLine(vpos)
1202
1493
  self.SetXOffset(hpos)
1203
1494
 
@@ -1336,13 +1627,13 @@ class EditorInterface(CtrlInterface):
1336
1627
  _text, lp = self.CurLine
1337
1628
  for i in range(lp % 4 or 4):
1338
1629
  p = self.cpos
1339
- if self.get_char(p-1) != ' ' or p == self.bol:
1630
+ if p == self.bol or self.get_char(p-1) != ' ':
1340
1631
  break
1341
1632
  self.cpos = p-1
1342
1633
  self.ReplaceSelection('')
1343
1634
 
1344
1635
 
1345
- class Buffer(EditWindow, EditorInterface):
1636
+ class Buffer(AutoCompInterfaceMixin, EditorInterface, EditWindow):
1346
1637
  """Python code buffer.
1347
1638
 
1348
1639
  Attributes:
@@ -1468,6 +1759,7 @@ class Buffer(EditWindow, EditorInterface):
1468
1759
  def __init__(self, parent, filename=None, **kwargs):
1469
1760
  EditWindow.__init__(self, parent, **kwargs)
1470
1761
  EditorInterface.__init__(self)
1762
+ AutoCompInterfaceMixin.__init__(self)
1471
1763
 
1472
1764
  self.parent = parent
1473
1765
  self.__filename = filename
@@ -1475,7 +1767,7 @@ class Buffer(EditWindow, EditorInterface):
1475
1767
  self.code = None
1476
1768
 
1477
1769
  self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate) # skip to brace matching
1478
-
1770
+ self.Bind(stc.EVT_STC_CALLTIP_CLICK, self.OnCallTipClick)
1479
1771
  self.Bind(stc.EVT_STC_INDICATOR_CLICK, self.OnIndicatorClick)
1480
1772
 
1481
1773
  self.Bind(stc.EVT_STC_SAVEPOINTLEFT, self.OnSavePointLeft)
@@ -1493,6 +1785,26 @@ class Buffer(EditWindow, EditorInterface):
1493
1785
  evt.Skip()
1494
1786
  self.Bind(wx.EVT_KILL_FOCUS, inactivate)
1495
1787
 
1788
+ def clear(evt):
1789
+ ## """Clear selection and message, no skip."""
1790
+ ## *do not* clear autocomp, so that the event can skip to AutoComp properly.
1791
+ ## if self.AutoCompActive():
1792
+ ## self.AutoCompCancel() # may delete selection
1793
+ if self.CanEdit():
1794
+ self.ReplaceSelection("")
1795
+ self.message("")
1796
+
1797
+ def clear_autocomp(evt):
1798
+ ## """Clear Autocomp, selection, and message."""
1799
+ if self.AutoCompActive():
1800
+ self.AutoCompCancel()
1801
+ if self.CanEdit():
1802
+ self.ReplaceSelection("")
1803
+ self.message("")
1804
+
1805
+ def fork(evt):
1806
+ self.handler.fork(self.handler.current_event, evt)
1807
+
1496
1808
  def dispatch(evt):
1497
1809
  """Fork events to the parent."""
1498
1810
  self.parent.handler(self.handler.current_event, evt)
@@ -1520,6 +1832,93 @@ class Buffer(EditWindow, EditorInterface):
1520
1832
  '* pressed' : (0, skip, dispatch),
1521
1833
  '* released' : (0, skip, dispatch),
1522
1834
  'escape pressed' : (-1, self.on_enter_escmap),
1835
+ 'C-h pressed' : (0, self.call_helpTip),
1836
+ '. pressed' : (2, skip),
1837
+ 'M-. pressed' : (2, self.call_word_autocomp),
1838
+ 'M-/ pressed' : (3, self.call_apropos_autocomp),
1839
+ 'M-m pressed' : (5, self.call_module_autocomp),
1840
+ },
1841
+ 2 : { # word auto completion AS-mode
1842
+ 'quit' : (0, clear_autocomp),
1843
+ '* pressed' : (0, clear_autocomp, fork),
1844
+ 'tab pressed' : (0, clear, skip),
1845
+ 'enter pressed' : (0, clear, skip),
1846
+ 'escape pressed' : (0, clear_autocomp),
1847
+ 'up pressed' : (2, skip, self.on_completion_backward),
1848
+ 'down pressed' : (2, skip, self.on_completion_forward),
1849
+ '*left pressed' : (2, skip),
1850
+ '*left released' : (2, self.call_word_autocomp),
1851
+ '*right pressed' : (2, skip),
1852
+ '*right released' : (2, self.call_word_autocomp),
1853
+ '[a-z0-9_.] pressed' : (2, skip),
1854
+ '[a-z0-9_.] released' : (2, self.call_word_autocomp),
1855
+ 'S-[a-z\\] pressed' : (2, skip),
1856
+ 'S-[a-z\\] released' : (2, self.call_word_autocomp),
1857
+ '\\ released' : (2, self.call_word_autocomp),
1858
+ '*delete pressed' : (2, skip),
1859
+ '*backspace pressed' : (2, skip),
1860
+ '*backspace released' : (2, self.call_word_autocomp),
1861
+ 'C-S-backspace pressed' : (2, ),
1862
+ '*alt pressed' : (2, ),
1863
+ '*ctrl pressed' : (2, ),
1864
+ '*shift pressed' : (2, ),
1865
+ '*[LR]win pressed' : (2, ),
1866
+ '*f[0-9]* pressed' : (2, ),
1867
+ },
1868
+ 3 : { # apropos auto completion AS-mode
1869
+ 'quit' : (0, clear_autocomp),
1870
+ '* pressed' : (0, clear_autocomp, fork),
1871
+ 'tab pressed' : (0, clear, skip),
1872
+ 'enter pressed' : (0, clear, skip),
1873
+ 'escape pressed' : (0, clear_autocomp),
1874
+ 'up pressed' : (3, skip, self.on_completion_backward),
1875
+ 'down pressed' : (3, skip, self.on_completion_forward),
1876
+ '*left pressed' : (3, skip),
1877
+ '*left released' : (3, self.call_apropos_autocomp),
1878
+ '*right pressed' : (3, skip),
1879
+ '*right released' : (3, self.call_apropos_autocomp),
1880
+ '[a-z0-9_.] pressed' : (3, skip),
1881
+ '[a-z0-9_.] released' : (3, self.call_apropos_autocomp),
1882
+ 'S-[a-z\\] pressed' : (3, skip),
1883
+ 'S-[a-z\\] released' : (3, self.call_apropos_autocomp),
1884
+ '\\ released' : (3, self.call_apropos_autocomp),
1885
+ '*delete pressed' : (3, skip),
1886
+ '*backspace pressed' : (3, skip),
1887
+ '*backspace released' : (3, self.call_apropos_autocomp),
1888
+ 'C-S-backspace pressed' : (3, ),
1889
+ '*alt pressed' : (3, ),
1890
+ '*ctrl pressed' : (3, ),
1891
+ '*shift pressed' : (3, ),
1892
+ '*[LR]win pressed' : (3, ),
1893
+ '*f[0-9]* pressed' : (3, ),
1894
+ },
1895
+ 5 : { # module auto completion AS-mode
1896
+ 'quit' : (0, clear_autocomp),
1897
+ '* pressed' : (0, clear_autocomp, fork),
1898
+ 'tab pressed' : (0, clear, skip),
1899
+ 'enter pressed' : (0, clear, skip),
1900
+ 'escape pressed' : (0, clear_autocomp),
1901
+ 'up pressed' : (5, skip, self.on_completion_backward),
1902
+ 'down pressed' : (5, skip, self.on_completion_forward),
1903
+ '*left pressed' : (5, skip),
1904
+ '*left released' : (5, self.call_module_autocomp),
1905
+ '*right pressed' : (5, skip),
1906
+ '*right released' : (5, self.call_module_autocomp),
1907
+ '[a-z0-9_.,] pressed' : (5, skip),
1908
+ '[a-z0-9_.,] released' : (5, self.call_module_autocomp),
1909
+ 'S-[a-z\\] pressed' : (5, skip),
1910
+ 'S-[a-z\\] released' : (5, self.call_module_autocomp),
1911
+ '\\ released' : (5, self.call_module_autocomp),
1912
+ 'M-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
1913
+ '*delete pressed' : (5, skip),
1914
+ '*backspace pressed' : (5, skip),
1915
+ '*backspace released' : (5, self.call_module_autocomp),
1916
+ 'C-S-backspace pressed' : (5, ),
1917
+ '*alt pressed' : (5, ),
1918
+ '*ctrl pressed' : (5, ),
1919
+ '*shift pressed' : (5, ),
1920
+ '*[LR]win pressed' : (5, ),
1921
+ '*f[0-9]* pressed' : (5, ),
1523
1922
  },
1524
1923
  })
1525
1924
 
@@ -1542,6 +1941,12 @@ class Buffer(EditWindow, EditorInterface):
1542
1941
  self.handler('buffer_modified', self)
1543
1942
  evt.Skip()
1544
1943
 
1944
+ def OnCallTipClick(self, evt):
1945
+ if self.CallTipActive():
1946
+ self.CallTipCancel()
1947
+ self.CallTipShow(self._calltip_pos, self._calltip, N=-1)
1948
+ evt.Skip()
1949
+
1545
1950
  def OnIndicatorClick(self, evt):
1546
1951
  if self.SelectedText or not wx.GetKeyState(wx.WXK_CONTROL):
1547
1952
  ## Processing text selection, dragging, or dragging+
@@ -1647,24 +2052,38 @@ class Buffer(EditWindow, EditorInterface):
1647
2052
  ## Python eval / exec
1648
2053
  ## --------------------------------
1649
2054
 
2055
+ @property
2056
+ def locals(self): # internal use only
2057
+ try:
2058
+ return self.parent.parent.current_shell.locals
2059
+ except AttributeError:
2060
+ return None
2061
+
2062
+ @property
2063
+ def globals(self): # internal use only
2064
+ try:
2065
+ return self.parent.parent.current_shell.globals
2066
+ except AttributeError:
2067
+ return None
2068
+
2069
+ def eval(self, text):
2070
+ return eval(text, self.globals, self.locals) # using current shell namespace
2071
+
2072
+ def exec(self, text):
2073
+ exec(text, self.globals, self.locals) # using current shell namespace
2074
+ dispatcher.send(signal='Interpreter.push',
2075
+ sender=self, command=None, more=False)
2076
+
1650
2077
  def py_eval_line(self, globals=None, locals=None):
1651
2078
  if self.CallTipActive():
1652
2079
  self.CallTipCancel()
1653
2080
 
1654
- def _gen_text():
1655
- text = self.SelectedText
1656
- if text:
1657
- yield text
1658
- else:
1659
- yield self.caretline
1660
- yield self.expr_at_caret
1661
-
1662
2081
  status = "No words"
1663
- for text in filter(None, _gen_text()):
2082
+ for text in self.gen_text_at_caret():
1664
2083
  try:
1665
2084
  obj = eval(text, globals, locals)
1666
2085
  except Exception as e:
1667
- status = "- {}: {!r}".format(e, text)
2086
+ status = "- {} : {!r}".format(e, text)
1668
2087
  else:
1669
2088
  self.CallTipShow(self.cpos, pformat(obj))
1670
2089
  self.message(text)
@@ -2182,13 +2601,15 @@ class Interpreter(interpreter.Interpreter):
2182
2601
  (override) Ignore ValueError: no signature found for builtin
2183
2602
  if the unwrapped function is a builtin function.
2184
2603
  """
2604
+ ## In 4.2.1, DeprecationWarning was fixed.
2605
+ ## In 4.2.2, ValueError was fixed.
2185
2606
  try:
2186
2607
  return interpreter.Interpreter.getCallTip(self, command, *args, **kwargs)
2187
2608
  except ValueError:
2188
2609
  return interpreter.Interpreter.getCallTip(self) # dummy
2189
2610
 
2190
2611
 
2191
- class Nautilus(Shell, EditorInterface):
2612
+ class Nautilus(AutoCompInterfaceMixin, EditorInterface, Shell):
2192
2613
  """Nautilus in the Shell.
2193
2614
 
2194
2615
  Facade objects for accessing the APIs:
@@ -2332,8 +2753,6 @@ class Nautilus(Shell, EditorInterface):
2332
2753
  def globals(self): # internal use only
2333
2754
  self.interp.globals = self.__target.__dict__
2334
2755
 
2335
- modules = None
2336
-
2337
2756
  ## (override)
2338
2757
  wrap = EditorInterface.wrap
2339
2758
 
@@ -2351,6 +2770,7 @@ class Nautilus(Shell, EditorInterface):
2351
2770
  execStartupScript=execStartupScript, # if True, executes ~/.py
2352
2771
  **kwargs)
2353
2772
  EditorInterface.__init__(self)
2773
+ AutoCompInterfaceMixin.__init__(self)
2354
2774
 
2355
2775
  self.parent = parent #: parent<ShellFrame> is not Parent<AuiNotebook>
2356
2776
  self.target = target
@@ -2359,12 +2779,6 @@ class Nautilus(Shell, EditorInterface):
2359
2779
  wx.py.shell.USE_MAGIC = True
2360
2780
  wx.py.shell.magic = self.magic # called when USE_MAGIC
2361
2781
 
2362
- ## cf. sys.modules
2363
- if not self.modules:
2364
- force = wx.GetKeyState(wx.WXK_CONTROL)\
2365
- & wx.GetKeyState(wx.WXK_SHIFT)
2366
- Nautilus.modules = set(find_modules(force))
2367
-
2368
2782
  self.Bind(stc.EVT_STC_UPDATEUI, self.OnUpdate) # skip to brace matching
2369
2783
  self.Bind(stc.EVT_STC_CALLTIP_CLICK, self.OnCallTipClick)
2370
2784
  self.Bind(stc.EVT_STC_START_DRAG, self.OnDrag)
@@ -2469,8 +2883,8 @@ class Nautilus(Shell, EditorInterface):
2469
2883
  'M-j pressed' : (0, self.exec_region),
2470
2884
  'C-S-j pressed' : (0, self.exec_region),
2471
2885
  'C-h pressed' : (0, self.call_helpTip),
2472
- 'M-h pressed' : (0, self.call_helpTip2),
2473
- 'C-S-h pressed' : (0, self.call_helpTip2),
2886
+ 'M-h pressed' : (0, self.call_helpDoc),
2887
+ 'C-S-h pressed' : (0, self.call_helpDoc),
2474
2888
  '. pressed' : (2, self.OnEnterDot),
2475
2889
  'tab pressed' : (1, self.call_history_comp),
2476
2890
  'M-p pressed' : (1, self.call_history_comp),
@@ -2482,7 +2896,7 @@ class Nautilus(Shell, EditorInterface):
2482
2896
  },
2483
2897
  1 : { # history auto completion S-mode
2484
2898
  'quit' : (0, clear),
2485
- 'fork' : (0, self.on_indent_line),
2899
+ 'skip' : (0, self.on_indent_line),
2486
2900
  '* pressed' : (0, fork),
2487
2901
  'enter pressed' : (0, lambda v: self.goto_char(self.eolc)),
2488
2902
  'escape pressed' : (0, clear),
@@ -2490,10 +2904,10 @@ class Nautilus(Shell, EditorInterface):
2490
2904
  'S-left released' : (1, self.call_history_comp),
2491
2905
  'S-right pressed' : (1, skip),
2492
2906
  'S-right released' : (1, self.call_history_comp),
2493
- 'tab pressed' : (1, self.on_completion_forward_history),
2494
- 'S-tab pressed' : (1, self.on_completion_backward_history),
2495
- 'M-p pressed' : (1, self.on_completion_forward_history),
2496
- 'M-n pressed' : (1, self.on_completion_backward_history),
2907
+ 'tab pressed' : (1, _F(self._on_completion, 1)), # 古いヒストリへ進む
2908
+ 'S-tab pressed' : (1, _F(self._on_completion, -1)), # 新しいヒストリへ戻る
2909
+ 'M-p pressed' : (1, _F(self._on_completion, 1)),
2910
+ 'M-n pressed' : (1, _F(self._on_completion, -1)),
2497
2911
  '[a-z0-9_] pressed' : (1, skip),
2498
2912
  '[a-z0-9_] released' : (1, self.call_history_comp),
2499
2913
  'S-[a-z\\] pressed' : (1, skip),
@@ -2509,7 +2923,7 @@ class Nautilus(Shell, EditorInterface):
2509
2923
  'quit' : (0, clear_autocomp),
2510
2924
  '* pressed' : (0, clear_autocomp, fork),
2511
2925
  'tab pressed' : (0, clear, skip),
2512
- 'enter pressed' : (0, clear, fork),
2926
+ 'enter pressed' : (0, clear, skip),
2513
2927
  'escape pressed' : (0, clear_autocomp),
2514
2928
  'up pressed' : (2, skip, self.on_completion_backward),
2515
2929
  'down pressed' : (2, skip, self.on_completion_forward),
@@ -2527,11 +2941,6 @@ class Nautilus(Shell, EditorInterface):
2527
2941
  '*backspace released' : (2, self.call_word_autocomp),
2528
2942
  'C-S-backspace pressed' : (2, ),
2529
2943
  'C-j pressed' : (2, self.eval_line),
2530
- 'M-j pressed' : (2, self.exec_region),
2531
- 'C-S-j pressed' : (2, self.exec_region),
2532
- 'C-h pressed' : (2, self.call_helpTip),
2533
- 'M-h pressed' : (2, self.call_helpTip2),
2534
- 'C-S-h pressed' : (2, self.call_helpTip2),
2535
2944
  '*alt pressed' : (2, ),
2536
2945
  '*ctrl pressed' : (2, ),
2537
2946
  '*shift pressed' : (2, ),
@@ -2542,7 +2951,7 @@ class Nautilus(Shell, EditorInterface):
2542
2951
  'quit' : (0, clear_autocomp),
2543
2952
  '* pressed' : (0, clear_autocomp, fork),
2544
2953
  'tab pressed' : (0, clear, skip),
2545
- 'enter pressed' : (0, clear, fork),
2954
+ 'enter pressed' : (0, clear, skip),
2546
2955
  'escape pressed' : (0, clear_autocomp),
2547
2956
  'up pressed' : (3, skip, self.on_completion_backward),
2548
2957
  'down pressed' : (3, skip, self.on_completion_forward),
@@ -2560,11 +2969,6 @@ class Nautilus(Shell, EditorInterface):
2560
2969
  '*backspace released' : (3, self.call_apropos_autocomp),
2561
2970
  'C-S-backspace pressed' : (3, ),
2562
2971
  'C-j pressed' : (3, self.eval_line),
2563
- 'M-j pressed' : (3, self.exec_region),
2564
- 'C-S-j pressed' : (3, self.exec_region),
2565
- 'C-h pressed' : (3, self.call_helpTip),
2566
- 'M-h pressed' : (3, self.call_helpTip2),
2567
- 'C-S-h pressed' : (3, self.call_helpTip2),
2568
2972
  '*alt pressed' : (3, ),
2569
2973
  '*ctrl pressed' : (3, ),
2570
2974
  '*shift pressed' : (3, ),
@@ -2575,7 +2979,7 @@ class Nautilus(Shell, EditorInterface):
2575
2979
  'quit' : (0, clear_autocomp),
2576
2980
  '* pressed' : (0, clear_autocomp, fork),
2577
2981
  'tab pressed' : (0, clear, skip),
2578
- 'enter pressed' : (0, clear, fork),
2982
+ 'enter pressed' : (0, clear, skip),
2579
2983
  'escape pressed' : (0, clear_autocomp),
2580
2984
  'up pressed' : (4, skip, self.on_completion_backward),
2581
2985
  'down pressed' : (4, skip, self.on_completion_forward),
@@ -2593,11 +2997,6 @@ class Nautilus(Shell, EditorInterface):
2593
2997
  '*backspace released' : (4, self.call_text_autocomp),
2594
2998
  'C-S-backspace pressed' : (4, ),
2595
2999
  'C-j pressed' : (4, self.eval_line),
2596
- 'M-j pressed' : (4, self.exec_region),
2597
- 'C-S-j pressed' : (4, self.exec_region),
2598
- 'C-h pressed' : (4, self.call_helpTip),
2599
- 'M-h pressed' : (4, self.call_helpTip2),
2600
- 'C-S-h pressed' : (4, self.call_helpTip2),
2601
3000
  '*alt pressed' : (4, ),
2602
3001
  '*ctrl pressed' : (4, ),
2603
3002
  '*shift pressed' : (4, ),
@@ -2608,7 +3007,7 @@ class Nautilus(Shell, EditorInterface):
2608
3007
  'quit' : (0, clear_autocomp),
2609
3008
  '* pressed' : (0, clear_autocomp, fork),
2610
3009
  'tab pressed' : (0, clear, skip),
2611
- 'enter pressed' : (0, clear, fork),
3010
+ 'enter pressed' : (0, clear, skip),
2612
3011
  'escape pressed' : (0, clear_autocomp),
2613
3012
  'up pressed' : (5, skip, self.on_completion_backward),
2614
3013
  'down pressed' : (5, skip, self.on_completion_forward),
@@ -2621,7 +3020,7 @@ class Nautilus(Shell, EditorInterface):
2621
3020
  'S-[a-z\\] pressed' : (5, skip),
2622
3021
  'S-[a-z\\] released' : (5, self.call_module_autocomp),
2623
3022
  '\\ released' : (5, self.call_module_autocomp),
2624
- 'M-m released' : (5, _F(self.call_module_autocomp, force=1)),
3023
+ 'M-m pressed' : (5, _F(self.call_module_autocomp, force=1)),
2625
3024
  '*delete pressed' : (5, skip),
2626
3025
  '*backspace pressed' : (5, skip_autocomp),
2627
3026
  '*backspace released' : (5, self.call_module_autocomp),
@@ -2669,9 +3068,9 @@ class Nautilus(Shell, EditorInterface):
2669
3068
  evt.Skip()
2670
3069
 
2671
3070
  def OnCallTipClick(self, evt):
2672
- self.parent.handler('add_help', self.__calltip)
2673
3071
  if self.CallTipActive():
2674
3072
  self.CallTipCancel()
3073
+ self.parent.handler('add_help', self._calltip)
2675
3074
  evt.Skip()
2676
3075
 
2677
3076
  def OnDrag(self, evt): #<wx._core.StyledTextEvent>
@@ -2689,7 +3088,7 @@ class Nautilus(Shell, EditorInterface):
2689
3088
  """Called when space pressed."""
2690
3089
  if not self.CanEdit():
2691
3090
  return
2692
- cmdl = self.cmdlc
3091
+ cmdl = self.GetTextRange(self.bol, self.cpos)
2693
3092
  if re.match(r"import\s*", cmdl)\
2694
3093
  or re.match(r"from\s*$", cmdl)\
2695
3094
  or re.match(r"from\s+([\w.]+)\s+import\s*", cmdl):
@@ -2963,22 +3362,18 @@ class Nautilus(Shell, EditorInterface):
2963
3362
  Note:
2964
3363
  Argument `text` is raw output:str with no magic cast.
2965
3364
  """
2966
- ln = self.cmdline_region[0]
3365
+ ln = self.LineFromPosition(self.bolc)
2967
3366
  err = re.findall(py_error_re, text, re.M)
2968
3367
  self.add_marker(ln, 1 if not err else 2) # 1:white-arrow 2:red-arrow
2969
3368
  return (not err)
2970
3369
 
2971
3370
  def on_interp_error(self, e):
2972
- self.pointer = self.cmdline_region[0] + e.lineno - 1
3371
+ ln = self.LineFromPosition(self.bolc)
3372
+ self.pointer = ln + e.lineno - 1
2973
3373
 
2974
3374
  ## --------------------------------
2975
3375
  ## Attributes of the shell
2976
3376
  ## --------------------------------
2977
- fragmwords = set(keyword.kwlist + dir(builtins)) # to be used in text-comp
2978
-
2979
- ## shell.history is an instance variable of the Shell.
2980
- ## If del shell.history, the history of the class variable is used
2981
- history = []
2982
3377
 
2983
3378
  @property
2984
3379
  def bolc(self):
@@ -3000,37 +3395,20 @@ class Nautilus(Shell, EditorInterface):
3000
3395
  break
3001
3396
  return self.cpos - lp
3002
3397
 
3003
- @property
3004
- def cmdlc(self):
3005
- """Cull command-line (excluding ps1:prompt)."""
3006
- return self.GetTextRange(self.bol, self.cpos)
3007
-
3008
3398
  @property
3009
3399
  def cmdline(self):
3010
- """Full command-(multi-)line (excluding ps1:prompt)."""
3400
+ """Full multi-line command in the current prompt."""
3011
3401
  return self.GetTextRange(self.bolc, self.eolc)
3012
3402
 
3013
- @property
3014
- def cmdline_region(self):
3015
- lc = self.LineFromPosition(self.bolc)
3016
- le = self.LineCount
3017
- return lc, le
3018
-
3019
3403
  ## cf. getCommand() -> caret-line that starts with a prompt
3020
3404
  ## cf. getMultilineCommand() -> caret-multi-line that starts with a prompt
3021
3405
  ## [BUG 4.1.1] Don't use for current prompt --> Fixed in 4.2.0.
3022
3406
 
3023
- @property
3024
- def Command(self):
3025
- """Extract a command from the editor."""
3026
- return self.getCommand(rstrip=False)
3027
-
3028
- @property
3029
- def MultilineCommand(self):
3030
- """Extract a multi-line command from the editor.
3407
+ def getMultilineCommand(self):
3408
+ """Extract a multi-line command which starts with a prompt.
3031
3409
 
3032
- Similar to getMultilineCommand(), but does not exclude
3033
- a trailing ps2 + blank command.
3410
+ (override) Don't remove trailing ps2 + spaces.
3411
+ Don't invoke ``GotoLine``.
3034
3412
  """
3035
3413
  region = self.get_region(self.cline)
3036
3414
  if region:
@@ -3086,13 +3464,13 @@ class Nautilus(Shell, EditorInterface):
3086
3464
  output = self.GetTextRange(self.__eolc_mark, self.eolc)
3087
3465
 
3088
3466
  input = self.regulate_cmd(input)
3089
- Shell.addHistory(self, input)
3467
+ Shell.addHistory(self, input) # => self.history
3090
3468
 
3091
3469
  noerr = self.on_text_output(output)
3092
3470
  if noerr:
3093
3471
  words = re.findall(r"\b[a-zA-Z_][\w.]+", input + output)
3094
3472
  self.fragmwords |= set(words)
3095
- command = self.fixLineEndings(command)
3473
+ command = self.fixLineEndings(command)
3096
3474
  self.parent.handler('add_log', command + os.linesep, noerr)
3097
3475
  except AttributeError:
3098
3476
  ## execStartupScript 実行時は出力先 (owner) が存在しない
@@ -3254,30 +3632,11 @@ class Nautilus(Shell, EditorInterface):
3254
3632
  def autoCallTipShow(self, command, insertcalltip=True, forceCallTip=False):
3255
3633
  """Display argument spec and docstring in a popup window.
3256
3634
 
3257
- (override) Swap anchors to not scroll to the end of the line,
3258
- and display a long hint at the insertion position.
3635
+ (override) Swap anchors to not scroll to the end of the line.
3259
3636
  """
3260
- vpos = self.GetScrollPos(wx.VERTICAL)
3261
- hpos = self.GetScrollPos(wx.HORIZONTAL)
3262
3637
  Shell.autoCallTipShow(self, command, insertcalltip, forceCallTip)
3263
3638
  self.cpos, self.anchor = self.anchor, self.cpos
3264
- ## self.EnsureCaretVisible()
3265
- self.ScrollToLine(vpos)
3266
- self.SetXOffset(hpos)
3267
-
3268
- def CallTipShow(self, pos, tip, N=11):
3269
- """Show a call tip containing a definition near position pos.
3270
-
3271
- (override) Snip the tip of max N lines if it is too long.
3272
- Keep the tip for calltip-click event.
3273
- """
3274
- self.__calltip = tip
3275
- lines = tip.splitlines()
3276
- if len(lines) > N:
3277
- lines[N+1:] = ["\n...(snip) This tips are too long... "
3278
- "Click to show more details."
3279
- ]
3280
- Shell.CallTipShow(self, pos, '\n'.join(lines))
3639
+ self.EnsureCaretVisible()
3281
3640
 
3282
3641
  def eval_line(self, evt):
3283
3642
  """Evaluate the selected word or line.
@@ -3285,24 +3644,15 @@ class Nautilus(Shell, EditorInterface):
3285
3644
  if self.CallTipActive():
3286
3645
  self.CallTipCancel()
3287
3646
 
3288
- def _gen_text():
3289
- text = self.SelectedText
3290
- if text:
3291
- yield text
3292
- else:
3293
- yield self.Command
3294
- yield self.expr_at_caret
3295
- yield self.MultilineCommand
3296
-
3297
3647
  status = "No words"
3298
- for text in filter(None, _gen_text()):
3648
+ for text in self.gen_text_at_caret():
3299
3649
  tokens = split_words(text)
3300
3650
  try:
3301
3651
  cmd = self.magic_interpret(tokens)
3302
3652
  cmd = self.regulate_cmd(cmd)
3303
3653
  obj = self.eval(cmd)
3304
3654
  except Exception as e:
3305
- status = "- {}: {!r}".format(e, text)
3655
+ status = "- {} : {!r}".format(e, text)
3306
3656
  else:
3307
3657
  self.CallTipShow(self.cpos, pformat(obj))
3308
3658
  self.message(cmd)
@@ -3315,7 +3665,7 @@ class Nautilus(Shell, EditorInterface):
3315
3665
  self.CallTipCancel()
3316
3666
 
3317
3667
  filename = "<input>"
3318
- text = self.MultilineCommand
3668
+ text = self.getMultilineCommand()
3319
3669
  if text:
3320
3670
  tokens = split_words(text)
3321
3671
  try:
@@ -3337,253 +3687,3 @@ class Nautilus(Shell, EditorInterface):
3337
3687
  self.message("Evaluated {!r} successfully.".format(filename))
3338
3688
  else:
3339
3689
  self.message("No region")
3340
-
3341
- def call_helpTip2(self, evt):
3342
- """Show help:str for the selected topic."""
3343
- if self.CallTipActive():
3344
- self.CallTipCancel()
3345
-
3346
- text = self.SelectedText or self.Command or self.expr_at_caret
3347
- if text:
3348
- text = introspect.getRoot(text, terminator='(')
3349
- try:
3350
- obj = self.eval(text)
3351
- self.help(obj)
3352
- except Exception as e:
3353
- self.message("- {} : {!r}".format(e, text))
3354
-
3355
- def call_helpTip(self, evt):
3356
- """Show tooltips for the selected topic."""
3357
- if self.CallTipActive():
3358
- self.CallTipCancel()
3359
-
3360
- text = self.SelectedText or self.Command or self.expr_at_caret
3361
- if text:
3362
- try:
3363
- p = self.cpos
3364
- c = self.get_char(p-1)
3365
- self.autoCallTipShow(text,
3366
- c == '(' and p == self.eol) # => CallTipShow
3367
- except Exception as e:
3368
- self.message("- {} : {!r}".format(e, text))
3369
-
3370
- def on_completion_forward(self, evt):
3371
- if self.AutoCompActive():
3372
- self.on_completion(evt, 1)
3373
- else:
3374
- self.handler('quit', evt)
3375
-
3376
- def on_completion_backward(self, evt):
3377
- if self.AutoCompActive():
3378
- self.on_completion(evt, -1)
3379
- else:
3380
- self.handler('quit', evt)
3381
-
3382
- def on_completion_forward_history(self, evt):
3383
- self.on_completion(evt, 1) # 古いヒストリへ進む
3384
-
3385
- def on_completion_backward_history(self, evt):
3386
- self.on_completion(evt, -1) # 新しいヒストリへ戻る
3387
-
3388
- def on_completion(self, evt, step=0):
3389
- """Show completion with selection."""
3390
- try:
3391
- N = len(self.__comp_words)
3392
- j = self.__comp_ind + step
3393
- j = 0 if j < 0 else j if j < N else N-1
3394
- word = self.__comp_words[j]
3395
- n = len(self.__comp_hint)
3396
- p = self.cpos
3397
- if not self.SelectedText:
3398
- p, self.anchor, sty = self.get_following_atom(p) # word-right-selection
3399
- self.ReplaceSelection(word[n:]) # Modify (or insert) the selected range
3400
- self.cpos = p # backward selection to the point
3401
- self.__comp_ind = j
3402
- except IndexError:
3403
- self.message("No completion words")
3404
-
3405
- def _gen_autocomp(self, j, hint, words, sep=' '):
3406
- """Call AutoCompShow for the specified words and sep."""
3407
- ## Prepare on_completion_forward/backward
3408
- self.__comp_ind = j
3409
- self.__comp_hint = hint
3410
- self.__comp_words = words
3411
- if words:
3412
- self.AutoCompSetSeparator(ord(sep))
3413
- self.AutoCompShow(len(hint), sep.join(words))
3414
-
3415
- @staticmethod
3416
- def _get_last_hint(cmdl):
3417
- return re.search(r"[\w.]*$", cmdl).group(0) # or ''
3418
-
3419
- @staticmethod
3420
- def _get_words_hint(cmdl):
3421
- text = next(split_words(cmdl, reverse=1), '')
3422
- return text.rpartition('.') # -> text, sep, hint
3423
-
3424
- def call_history_comp(self, evt):
3425
- """Called when history-comp mode."""
3426
- if not self.CanEdit():
3427
- self.handler('quit', evt)
3428
- return
3429
- try:
3430
- cmdl = self.cmdlc
3431
- if cmdl.isspace() or self.bol != self.bolc:
3432
- self.handler('fork', evt) # fork [tab pressed] => on_indent_line
3433
- return
3434
-
3435
- hint = cmdl.strip()
3436
- ls = [x.replace('\n', os.linesep + sys.ps2)
3437
- for x in self.history if x.startswith(hint)] # case-sensitive match
3438
- words = sorted(set(ls), key=ls.index, reverse=0) # keep order, no duplication
3439
-
3440
- self.__comp_ind = 0
3441
- self.__comp_hint = hint
3442
- self.__comp_words = words
3443
-
3444
- self.anchor = self.eolc # selection to eol
3445
- self.on_completion(evt) # show completion always
3446
-
3447
- ## the latest history stacks in the head of the list (time-descending)
3448
- self.message("[history] {} candidates matched"
3449
- " with {!r}".format(len(words), hint))
3450
- except Exception:
3451
- raise
3452
-
3453
- def call_text_autocomp(self, evt):
3454
- """Called when text-comp mode."""
3455
- if not self.CanEdit():
3456
- self.handler('quit', evt)
3457
- return
3458
- try:
3459
- cmdl = self.cmdlc
3460
- hint = self._get_last_hint(cmdl)
3461
-
3462
- ls = [x for x in self.fragmwords if x.startswith(hint)] # case-sensitive match
3463
- words = sorted(ls, key=lambda s:s.upper())
3464
-
3465
- self._gen_autocomp(0, hint, words)
3466
- self.message("[text] {} candidates matched"
3467
- " with {!r}".format(len(words), hint))
3468
- except Exception:
3469
- raise
3470
-
3471
- def call_module_autocomp(self, evt, force=False):
3472
- """Called when module-comp mode."""
3473
- if not self.CanEdit():
3474
- self.handler('quit', evt)
3475
- return
3476
-
3477
- def _continue(hints):
3478
- if not hints.endswith(' '):
3479
- h = hints.strip()
3480
- if not h.endswith(','):
3481
- lh = h.split(',')[-1].strip() # 'x, y, z|' last hint after ','
3482
- if ' ' not in lh: # 'x, y as|' contains no spaces.
3483
- return lh
3484
- try:
3485
- cmdl = self.cmdlc
3486
- hint = self._get_last_hint(cmdl)
3487
-
3488
- if (m := re.match(r"from\s+([\w.]+)\s+import\s+(.*)", cmdl)):
3489
- text, hints = m.groups()
3490
- if not _continue(hints) and not force:
3491
- self.message("[module]>>> waiting for key input...")
3492
- return
3493
- elif hints.endswith('.'):
3494
- self.message("[module] invalid import syntax.")
3495
- return
3496
- if text not in sys.modules:
3497
- self.message("[module]>>> loading {}...".format(text))
3498
- try:
3499
- modules = set(dir(import_module(text)))
3500
- except ImportError as e:
3501
- self.message("\b failed:", e)
3502
- return
3503
- ## Add unimported module names.
3504
- p = "{}.{}".format(text, hint)
3505
- keys = [x[len(text)+1:] for x in self.modules if x.startswith(p)]
3506
- modules.update(k for k in keys if '.' not in k)
3507
-
3508
- elif (m := re.match(r"(import|from)\s+(.*)", cmdl)):
3509
- text, hints = m.groups()
3510
- if not _continue(hints) and not force:
3511
- self.message("[module]>>> waiting for key input...")
3512
- return
3513
- modules = self.modules
3514
- else:
3515
- text, sep, hint = self._get_words_hint(cmdl)
3516
- obj = self.eval(text)
3517
- modules = set(k for k, v in vars(obj).items() if inspect.ismodule(v))
3518
-
3519
- P = re.compile(hint)
3520
- p = re.compile(hint, re.I)
3521
- words = sorted([x for x in modules if p.match(x)], key=lambda s:s.upper())
3522
-
3523
- j = next((k for k, w in enumerate(words) if P.match(w)),
3524
- next((k for k, w in enumerate(words) if p.match(w)), -1))
3525
-
3526
- self._gen_autocomp(j, hint, words)
3527
- self.message("[module] {} candidates matched"
3528
- " with {!r} in {}".format(len(words), hint, text))
3529
- except re.error as e:
3530
- self.message("- re:miss compilation {!r} : {!r}".format(e, hint))
3531
- except SyntaxError as e:
3532
- self.handler('quit', evt)
3533
- self.message("- {} : {!r}".format(e, text))
3534
- except Exception as e:
3535
- self.message("- {} : {!r}".format(e, text))
3536
-
3537
- def call_word_autocomp(self, evt):
3538
- """Called when word-comp mode."""
3539
- if not self.CanEdit():
3540
- self.handler('quit', evt)
3541
- return
3542
- try:
3543
- text, sep, hint = self._get_words_hint(self.cmdlc)
3544
- obj = self.eval(text)
3545
-
3546
- P = re.compile(hint)
3547
- p = re.compile(hint, re.I)
3548
- words = sorted([x for x in dir(obj) if p.match(x)], key=lambda s:s.upper())
3549
-
3550
- j = next((k for k, w in enumerate(words) if P.match(w)),
3551
- next((k for k, w in enumerate(words) if p.match(w)), -1))
3552
-
3553
- self._gen_autocomp(j, hint, words)
3554
- self.message("[word] {} candidates matched"
3555
- " with {!r} in {}".format(len(words), hint, text))
3556
- except re.error as e:
3557
- self.message("- re:miss compilation {!r} : {!r}".format(e, hint))
3558
- except SyntaxError as e:
3559
- self.handler('quit', evt)
3560
- self.message("- {} : {!r}".format(e, text))
3561
- except Exception as e:
3562
- self.message("- {} : {!r}".format(e, text))
3563
-
3564
- def call_apropos_autocomp(self, evt):
3565
- """Called when apropos mode."""
3566
- if not self.CanEdit():
3567
- self.handler('quit', evt)
3568
- return
3569
- try:
3570
- text, sep, hint = self._get_words_hint(self.cmdlc)
3571
- obj = self.eval(text)
3572
-
3573
- P = re.compile(hint)
3574
- p = re.compile(hint, re.I)
3575
- words = sorted([x for x in dir(obj) if p.search(x)], key=lambda s:s.upper())
3576
-
3577
- j = next((k for k, w in enumerate(words) if P.match(w)),
3578
- next((k for k, w in enumerate(words) if p.match(w)), -1))
3579
-
3580
- self._gen_autocomp(j, hint, words)
3581
- self.message("[apropos] {} candidates matched"
3582
- " with {!r} in {}".format(len(words), hint, text))
3583
- except re.error as e:
3584
- self.message("- re:miss compilation {!r} : {!r}".format(e, hint))
3585
- except SyntaxError as e:
3586
- self.handler('quit', evt)
3587
- self.message("- {} : {!r}".format(e, text))
3588
- except Exception as e:
3589
- self.message("- {} : {!r}".format(e, text))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mwxlib
3
- Version: 0.97.7
3
+ Version: 0.98.0
4
4
  Summary: A wrapper of matplotlib and wxPython (phoenix)
5
5
  Home-page: https://github.com/komoto48g/mwxlib
6
6
  Author: Kazuya O'moto
@@ -1,14 +1,14 @@
1
1
  mwx/__init__.py,sha256=nN62CGTWjME7Zz2h-jIRB8MxwuErIkHPGrlBzydkF0o,643
2
2
  mwx/bookshelf.py,sha256=Y4xI2SrEO22DrI1hyyfFx7DfFZA8znOzX9RWMPsA2BE,5137
3
3
  mwx/controls.py,sha256=K4GJB81TXv5q0faTELQgbUmtMQBRIM9yINJdTI0xA3g,47556
4
- mwx/framework.py,sha256=muvp-MrmS8TGwpRx7S18X2u-nhS9YumUHA1K_MRKCwA,75443
4
+ mwx/framework.py,sha256=kKG4rcBiq-PD5v1sHLtI1XRTve0wugG6JwwNbMv5Pa8,75509
5
5
  mwx/graphman.py,sha256=Cyhl_Da_HGBfk721gu1r5iwSH9L3yPEG8Fzmc2gx-EU,70462
6
6
  mwx/images.py,sha256=_-Eh3xF7Khu42ivkYp97NXIzSNGbjcidqtWjZQFGtqE,47827
7
7
  mwx/matplot2.py,sha256=xCJ_ZzdDEWmzctpPaOrzTnwXyHINP4nfFHweoTZa6ug,32899
8
8
  mwx/matplot2g.py,sha256=wiZFDFuQe3ax71fmyeR_9hvAmgT-4nVfZ30UByv8Nv8,64379
9
9
  mwx/matplot2lg.py,sha256=JRWjWnLJUytbSq6wxs4P0gbVUr3xoLSF6Wwqd5V_pJI,27404
10
10
  mwx/mgplt.py,sha256=ITzxA97yDwr_35BUk5OqnyskSuKVDbpf2AQCKY1jHTI,5671
11
- mwx/nutshell.py,sha256=Zwlh_1zFRCmlTBFsw_0xHBLsofKSmzd8K62cWBov1FQ,137580
11
+ mwx/nutshell.py,sha256=iVJGFh45ZtXs_g8LGi1UNI6H-3jI2pDkYNc2eF3T-7k,142853
12
12
  mwx/utilus.py,sha256=mmqB4P_3mTi7SrFleMiN1599Jm0Us0XKnNA6v2xglSs,37333
13
13
  mwx/wxmon.py,sha256=f3V24EF7kdMlYF7usLYK9QE5KU6fSu0jVqsvwAiA-Ag,12647
14
14
  mwx/wxpdb.py,sha256=lLowkkAgMhPFHAfklD7wZHq0qbSMjRxnBFtSajmVgME,19133
@@ -21,8 +21,8 @@ mwx/plugins/frame_listview.py,sha256=hbApzZWa9-BmQthu7uZBlBbGbtf4iJ_prO8IhxoGMs8
21
21
  mwx/plugins/line_profile.py,sha256=--9NIc3x5EfRB3L59JvD7rzENQHyiYfu7wWJo6AuMkA,820
22
22
  mwx/py/__init__.py,sha256=xykgfOytOwNuvXsfkLoumFZSTN-iBsHOjczYXngjmUE,12
23
23
  mwx/py/filling.py,sha256=fumUG1F5M9TL-Dfqni4G85uk7TmvnUunTbdcPDV0vfo,16857
24
- mwxlib-0.97.7.dist-info/LICENSE,sha256=PGtRKCaTkmUDlBQwpptJAxJtdqxIUtAmdBsaT9nUVkA,1091
25
- mwxlib-0.97.7.dist-info/METADATA,sha256=ITXJ8FrL_C8Zp4tjs-dwINs4TrpykIZxNe-hot0NIP0,1880
26
- mwxlib-0.97.7.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
27
- mwxlib-0.97.7.dist-info/top_level.txt,sha256=SI1Mh118AstnUFGPNq5aMNKiAnVNmZk1S9Ij-OwAEpY,4
28
- mwxlib-0.97.7.dist-info/RECORD,,
24
+ mwxlib-0.98.0.dist-info/LICENSE,sha256=PGtRKCaTkmUDlBQwpptJAxJtdqxIUtAmdBsaT9nUVkA,1091
25
+ mwxlib-0.98.0.dist-info/METADATA,sha256=qAaHXqyArN_ZNCNoeukt6YxYGPQ-n8h9JIgfAcKuBnU,1880
26
+ mwxlib-0.98.0.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
27
+ mwxlib-0.98.0.dist-info/top_level.txt,sha256=SI1Mh118AstnUFGPNq5aMNKiAnVNmZk1S9Ij-OwAEpY,4
28
+ mwxlib-0.98.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (71.1.0)
2
+ Generator: setuptools (72.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5