##// END OF EJS Templates
Usability tweaks. Better auto tab completion. Terminal size more...
Gael Varoquaux -
Show More
@@ -1,421 +1,418 b''
1 1 # encoding: utf-8
2 2 """
3 3 A Wx widget to act as a console and input commands.
4 4
5 5 This widget deals with prompts and provides an edit buffer
6 6 restricted to after the last prompt.
7 7 """
8 8
9 9 __docformat__ = "restructuredtext en"
10 10
11 11 #-------------------------------------------------------------------------------
12 12 # Copyright (C) 2008 The IPython Development Team
13 13 #
14 14 # Distributed under the terms of the BSD License. The full license is
15 15 # in the file COPYING, distributed as part of this software.
16 16 #-------------------------------------------------------------------------------
17 17
18 18 #-------------------------------------------------------------------------------
19 19 # Imports
20 20 #-------------------------------------------------------------------------------
21 21
22 22 import wx
23 23 import wx.stc as stc
24 24
25 25 from wx.py import editwindow
26 26
27 27 import re
28 28
29 29 # FIXME: Need to provide an API for non user-generated display on the
30 30 # screen: this should not be editable by the user.
31 31
32 32 if wx.Platform == '__WXMSW__':
33 33 _DEFAULT_SIZE = 80
34 34 else:
35 35 _DEFAULT_SIZE = 10
36 36
37 37 _DEFAULT_STYLE = {
38 38 'stdout' : 'fore:#0000FF',
39 39 'stderr' : 'fore:#007f00',
40 40 'trace' : 'fore:#FF0000',
41 41
42 42 'default' : 'size:%d' % _DEFAULT_SIZE,
43 43 'bracegood' : 'fore:#FFFFFF,back:#0000FF,bold',
44 44 'bracebad' : 'fore:#000000,back:#FF0000,bold',
45 45
46 46 # properties for the various Python lexer styles
47 47 'comment' : 'fore:#007F00',
48 48 'number' : 'fore:#007F7F',
49 49 'string' : 'fore:#7F007F,italic',
50 50 'char' : 'fore:#7F007F,italic',
51 51 'keyword' : 'fore:#00007F,bold',
52 52 'triple' : 'fore:#7F0000',
53 53 'tripledouble' : 'fore:#7F0000',
54 54 'class' : 'fore:#0000FF,bold,underline',
55 55 'def' : 'fore:#007F7F,bold',
56 56 'operator' : 'bold'
57 57 }
58 58
59 59 # new style numbers
60 60 _STDOUT_STYLE = 15
61 61 _STDERR_STYLE = 16
62 62 _TRACE_STYLE = 17
63 63
64 64
65 65 # system colors
66 SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
66 #SYS_COLOUR_BACKGROUND = wx.SystemSettings.GetColour(wx.SYS_COLOUR_BACKGROUND)
67 67
68 68 #-------------------------------------------------------------------------------
69 69 # The console widget class
70 70 #-------------------------------------------------------------------------------
71 71 class ConsoleWidget(editwindow.EditWindow):
72 72 """ Specialized styled text control view for console-like workflow.
73 73
74 74 This widget is mainly interested in dealing with the prompt and
75 75 keeping the cursor inside the editing line.
76 76 """
77 77
78 78 title = 'Console'
79 79
80 80 style = _DEFAULT_STYLE.copy()
81 81
82 82 # Translation table from ANSI escape sequences to color. Override
83 83 # this to specify your colors.
84 84 ANSI_STYLES = {'0;30': [0, 'BLACK'], '0;31': [1, 'RED'],
85 85 '0;32': [2, 'GREEN'], '0;33': [3, 'BROWN'],
86 86 '0;34': [4, 'BLUE'], '0;35': [5, 'PURPLE'],
87 87 '0;36': [6, 'CYAN'], '0;37': [7, 'LIGHT GREY'],
88 88 '1;30': [8, 'DARK GREY'], '1;31': [9, 'RED'],
89 89 '1;32': [10, 'SEA GREEN'], '1;33': [11, 'YELLOW'],
90 90 '1;34': [12, 'LIGHT BLUE'], '1;35':
91 91 [13, 'MEDIUM VIOLET RED'],
92 92 '1;36': [14, 'LIGHT STEEL BLUE'], '1;37': [15, 'YELLOW']}
93 93
94 94 # The color of the carret (call _apply_style() after setting)
95 95 carret_color = 'BLACK'
96 96
97 97
98 98 #--------------------------------------------------------------------------
99 99 # Public API
100 100 #--------------------------------------------------------------------------
101 101
102 102 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
103 size=wx.DefaultSize, style=0,
104 autocomplete_mode='popup'):
105 """ Autocomplete_mode: Can be 'popup' or 'text'
106 'text' show autocompletion in the text buffer
107 'popup' show it with a dropdown popup
108 """
103 size=wx.DefaultSize, style=0, ):
109 104 editwindow.EditWindow.__init__(self, parent, id, pos, size, style)
110 105 self.configure_scintilla()
111 106
112 107 # FIXME: we need to retrieve this from the interpreter.
113 108 self.prompt = \
114 109 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02%i\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
115 110 self.new_prompt(self.prompt % 1)
116 111
117 self.autocomplete_mode = autocomplete_mode
118
119 112 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
120 113 self.Bind(wx.EVT_KEY_UP, self._on_key_up)
121 114
122 115
123 116 def configure_scintilla(self):
124 117 # Ctrl"+" or Ctrl "-" can be used to zoomin/zoomout the text inside
125 118 # the widget
126 119 self.CmdKeyAssign(ord('+'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMIN)
127 120 self.CmdKeyAssign(ord('-'), stc.STC_SCMOD_CTRL, stc.STC_CMD_ZOOMOUT)
128 121 # Also allow Ctrl Shift "=" for poor non US keyboard users.
129 122 self.CmdKeyAssign(ord('='), stc.STC_SCMOD_CTRL|stc.STC_SCMOD_SHIFT,
130 123 stc.STC_CMD_ZOOMIN)
131 124
132 125 #self.CmdKeyAssign(stc.STC_KEY_PRIOR, stc.STC_SCMOD_SHIFT,
133 126 # stc.STC_CMD_PAGEUP)
134 127
135 128 #self.CmdKeyAssign(stc.STC_KEY_NEXT, stc.STC_SCMOD_SHIFT,
136 129 # stc.STC_CMD_PAGEDOWN)
137 130
138 131 # Keys: we need to clear some of the keys the that don't play
139 132 # well with a console.
140 133 self.CmdKeyClear(ord('D'), stc.STC_SCMOD_CTRL)
141 134 self.CmdKeyClear(ord('L'), stc.STC_SCMOD_CTRL)
142 135 self.CmdKeyClear(ord('T'), stc.STC_SCMOD_CTRL)
143 136
144 137
145 138 self.SetEOLMode(stc.STC_EOL_CRLF)
146 139 self.SetWrapMode(stc.STC_WRAP_CHAR)
147 140 self.SetWrapMode(stc.STC_WRAP_WORD)
148 141 self.SetBufferedDraw(True)
149 142 self.SetUseAntiAliasing(True)
150 143 self.SetLayoutCache(stc.STC_CACHE_PAGE)
151 144 self.SetUndoCollection(False)
152 145 self.SetUseTabs(True)
153 146 self.SetIndent(4)
154 147 self.SetTabWidth(4)
155 148
156 149 self.EnsureCaretVisible()
157 # Tell autocompletion to choose automaticaly out of a single
158 # choice list
159 self.AutoCompSetChooseSingle(True)
150 # we don't want scintilla's autocompletion to choose
151 # automaticaly out of a single choice list, as we pop it up
152 # automaticaly
153 self.AutoCompSetChooseSingle(False)
160 154 self.AutoCompSetMaxHeight(10)
161 155
162 156 self.SetMargins(3, 3) #text is moved away from border with 3px
163 157 # Suppressing Scintilla margins
164 158 self.SetMarginWidth(0, 0)
165 159 self.SetMarginWidth(1, 0)
166 160 self.SetMarginWidth(2, 0)
167 161
168 162 self._apply_style()
169 163
170 164 # Xterm escape sequences
171 165 self.color_pat = re.compile('\x01?\x1b\[(.*?)m\x02?')
172 166 self.title_pat = re.compile('\x1b]0;(.*?)\x07')
173 167
174 168 #self.SetEdgeMode(stc.STC_EDGE_LINE)
175 169 #self.SetEdgeColumn(80)
176 170
177 171 # styles
178 172 p = self.style
179 173 self.StyleSetSpec(stc.STC_STYLE_DEFAULT, p['default'])
180 174 self.StyleClearAll()
181 175 self.StyleSetSpec(_STDOUT_STYLE, p['stdout'])
182 176 self.StyleSetSpec(_STDERR_STYLE, p['stderr'])
183 177 self.StyleSetSpec(_TRACE_STYLE, p['trace'])
184 178
185 179 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT, p['bracegood'])
186 180 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD, p['bracebad'])
187 181 self.StyleSetSpec(stc.STC_P_COMMENTLINE, p['comment'])
188 182 self.StyleSetSpec(stc.STC_P_NUMBER, p['number'])
189 183 self.StyleSetSpec(stc.STC_P_STRING, p['string'])
190 184 self.StyleSetSpec(stc.STC_P_CHARACTER, p['char'])
191 185 self.StyleSetSpec(stc.STC_P_WORD, p['keyword'])
192 186 self.StyleSetSpec(stc.STC_P_WORD2, p['keyword'])
193 187 self.StyleSetSpec(stc.STC_P_TRIPLE, p['triple'])
194 188 self.StyleSetSpec(stc.STC_P_TRIPLEDOUBLE, p['tripledouble'])
195 189 self.StyleSetSpec(stc.STC_P_CLASSNAME, p['class'])
196 190 self.StyleSetSpec(stc.STC_P_DEFNAME, p['def'])
197 191 self.StyleSetSpec(stc.STC_P_OPERATOR, p['operator'])
198 192 self.StyleSetSpec(stc.STC_P_COMMENTBLOCK, p['comment'])
199 193
200 194
201 195 def write(self, text):
202 196 """ Write given text to buffer, while translating the ansi escape
203 197 sequences.
204 198 """
205 199 title = self.title_pat.split(text)
206 200 if len(title)>0:
207 201 self.title = title[-1]
208 202
209 203 text = self.title_pat.sub('', text)
210 204 segments = self.color_pat.split(text)
211 205 segment = segments.pop(0)
212 206 self.StartStyling(self.GetLength(), 0xFF)
213 207 self.AppendText(segment)
214 208
215 209 if segments:
216 210 ansi_tags = self.color_pat.findall(text)
217 211
218 212 for tag in ansi_tags:
219 213 i = segments.index(tag)
220 214 self.StartStyling(self.GetLength(), 0xFF)
221 215 self.AppendText(segments[i+1])
222 216
223 217 if tag != '0':
224 218 self.SetStyling(len(segments[i+1]),
225 219 self.ANSI_STYLES[tag][0])
226 220
227 221 segments.pop(i)
228 222
229 223 self.GotoPos(self.GetLength())
230 224
231 225
232 226 def new_prompt(self, prompt):
233 227 """ Prints a prompt at start of line, and move the start of the
234 228 current block there.
235 229
236 230 The prompt can be give with ascii escape sequences.
237 231 """
238 232 self.write(prompt)
239 233 # now we update our cursor giving end of prompt
240 234 self.current_prompt_pos = self.GetLength()
241 235 self.current_prompt_line = self.GetCurrentLine()
242 236 wx.Yield()
243 237 self.EnsureCaretVisible()
244 238
245 239
246 240 def replace_current_edit_buffer(self, text):
247 241 """ Replace currently entered command line with given text.
248 242 """
249 243 self.SetSelection(self.current_prompt_pos, self.GetLength())
250 244 self.ReplaceSelection(text)
251 245 self.GotoPos(self.GetLength())
252 246
253 247
254 248 def get_current_edit_buffer(self):
255 249 """ Returns the text in current edit buffer.
256 250 """
257 251 return self.GetTextRange(self.current_prompt_pos,
258 252 self.GetLength())
259 253
260 254
261 255 #--------------------------------------------------------------------------
262 256 # Private API
263 257 #--------------------------------------------------------------------------
264 258
265 259 def _apply_style(self):
266 260 """ Applies the colors for the different text elements and the
267 261 carret.
268 262 """
269 263 self.SetCaretForeground(self.carret_color)
270 264
271 265 #self.StyleClearAll()
272 266 self.StyleSetSpec(stc.STC_STYLE_BRACELIGHT,
273 267 "fore:#FF0000,back:#0000FF,bold")
274 268 self.StyleSetSpec(stc.STC_STYLE_BRACEBAD,
275 269 "fore:#000000,back:#FF0000,bold")
276 270
277 271 for style in self.ANSI_STYLES.values():
278 272 self.StyleSetSpec(style[0], "bold,fore:%s" % style[1])
279 273
280 274
281 def write_completion(self, possibilities, mode=None):
282 if mode=='text' or self.autocomplete_mode == 'text':
275 def write_completion(self, possibilities):
283 276 # FIXME: This is non Wx specific and needs to be moved into
284 277 # the base class.
285 278 current_buffer = self.get_current_edit_buffer()
286 279
287 280 self.write('\n')
288 max_len = len(max(possibilities, key=len))
281 max_len = len(max(possibilities, key=len)) + 1
289 282
290 283 #now we check how much symbol we can put on a line...
291 284 chars_per_line = self.GetSize()[0]/self.GetCharWidth()
292 285 symbols_per_line = max(1, chars_per_line/max_len)
293 286
294 287 pos = 1
295 288 buf = []
296 289 for symbol in possibilities:
297 290 if pos < symbols_per_line:
298 291 buf.append(symbol.ljust(max_len))
299 292 pos += 1
300 293 else:
301 294 buf.append(symbol.rstrip() +'\n')
302 295 pos = 1
303 296 self.write(''.join(buf))
304 297 self.new_prompt(self.prompt % (self.last_result['number'] + 1))
305 298 self.replace_current_edit_buffer(current_buffer)
306 299
307 else:
300
301 def pop_completion(self, possibilities, offset=0):
302 """ Pops up an autocompletion menu. Offset is the offset
303 in characters of the position at which the menu should
304 appear, relativ to the cursor.
305 """
308 306 self.AutoCompSetIgnoreCase(False)
309 307 self.AutoCompSetAutoHide(False)
310 # compute the length ot the last word
311 separators = [' ', '(', '[', '{', '\n', '\t', '.']
312 symbol = self.get_current_edit_buffer()
313 for separator in separators:
314 symbol = symbol.split(separator)[-1]
315 308 self.AutoCompSetMaxHeight(len(possibilities))
316 self.AutoCompShow(len(symbol), " ".join(possibilities))
309 self.AutoCompShow(offset, " ".join(possibilities))
317 310
318 311
319 312 def scroll_to_bottom(self):
320 313 maxrange = self.GetScrollRange(wx.VERTICAL)
321 314 self.ScrollLines(maxrange)
322 315
323 316
324 317 def _on_enter(self):
325 318 """ Called when the return key is hit.
326 319 """
327 320 pass
328 321
329 322
330 323 def _on_key_down(self, event, skip=True):
331 324 """ Key press callback used for correcting behavior for
332 325 console-like interfaces: the cursor is constraint to be after
333 326 the last prompt.
334 327
335 328 Return True if event as been catched.
336 329 """
337 330 catched = True
338 331 # Intercept some specific keys.
339 332 if event.KeyCode == ord('L') and event.ControlDown() :
340 333 self.scroll_to_bottom()
341 334 elif event.KeyCode == ord('K') and event.ControlDown() :
342 335 self.replace_current_edit_buffer('')
343 336 elif event.KeyCode == wx.WXK_PAGEUP and event.ShiftDown():
344 337 self.ScrollPages(-1)
345 338 elif event.KeyCode == wx.WXK_PAGEDOWN and event.ShiftDown():
346 339 self.ScrollPages(1)
340 elif event.KeyCode == wx.WXK_UP and event.ShiftDown():
341 self.ScrollLines(-1)
342 elif event.KeyCode == wx.WXK_DOWN and event.ShiftDown():
343 self.ScrollLinees(1)
347 344 else:
348 345 catched = False
349 346
350 347 if self.AutoCompActive():
351 348 event.Skip()
352 349 else:
353 350 if event.KeyCode in (13, wx.WXK_NUMPAD_ENTER) and \
354 351 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
355 352 catched = True
356 353 self.write('\n')
357 354 self._on_enter()
358 355
359 356 elif event.KeyCode == wx.WXK_HOME:
360 357 if event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN):
361 358 self.GotoPos(self.current_prompt_pos)
362 359 catched = True
363 360
364 361 elif event.Modifiers in (wx.MOD_SHIFT, wx.MOD_WIN) :
365 362 # FIXME: This behavior is not ideal: if the selection
366 363 # is already started, it will jump.
367 364 self.SetSelectionStart(self.current_prompt_pos)
368 365 self.SetSelectionEnd(self.GetCurrentPos())
369 366 catched = True
370 367
371 368 elif event.KeyCode == wx.WXK_UP:
372 369 if self.GetCurrentLine() > self.current_prompt_line:
373 370 if self.GetCurrentLine() == self.current_prompt_line + 1 \
374 371 and self.GetColumn(self.GetCurrentPos()) < \
375 372 self.GetColumn(self.current_prompt_pos):
376 373 self.GotoPos(self.current_prompt_pos)
377 374 else:
378 375 event.Skip()
379 376 catched = True
380 377
381 378 elif event.KeyCode in (wx.WXK_LEFT, wx.WXK_BACK):
382 379 if self.GetCurrentPos() > self.current_prompt_pos:
383 380 event.Skip()
384 381 catched = True
385 382
386 383 if skip and not catched:
387 384 event.Skip()
388 385
389 386 return catched
390 387
391 388
392 389 def _on_key_up(self, event, skip=True):
393 390 """ If cursor is outside the editing region, put it back.
394 391 """
395 392 event.Skip()
396 393 if self.GetCurrentPos() < self.current_prompt_pos:
397 394 self.GotoPos(self.current_prompt_pos)
398 395
399 396
400 397
401 398
402 399 if __name__ == '__main__':
403 400 # Some simple code to test the console widget.
404 401 class MainWindow(wx.Frame):
405 402 def __init__(self, parent, id, title):
406 403 wx.Frame.__init__(self, parent, id, title, size=(300,250))
407 404 self._sizer = wx.BoxSizer(wx.VERTICAL)
408 405 self.console_widget = ConsoleWidget(self)
409 406 self._sizer.Add(self.console_widget, 1, wx.EXPAND)
410 407 self.SetSizer(self._sizer)
411 408 self.SetAutoLayout(1)
412 409 self.Show(True)
413 410
414 411 app = wx.PySimpleApp()
415 412 w = MainWindow(None, wx.ID_ANY, 'ConsoleWidget')
416 413 w.SetSize((780, 460))
417 414 w.Show()
418 415
419 416 app.MainLoop()
420 417
421 418
@@ -1,202 +1,215 b''
1 1 # encoding: utf-8 -*- test-case-name:
2 2 # FIXME: Need to add tests.
3 3 # ipython1.frontend.cocoa.tests.test_cocoa_frontend -*-
4 4
5 5 """Classes to provide a Wx frontend to the
6 6 IPython.kernel.core.interpreter.
7 7
8 8 """
9 9
10 10 __docformat__ = "restructuredtext en"
11 11
12 12 #-------------------------------------------------------------------------------
13 13 # Copyright (C) 2008 The IPython Development Team
14 14 #
15 15 # Distributed under the terms of the BSD License. The full license is in
16 16 # the file COPYING, distributed as part of this software.
17 17 #-------------------------------------------------------------------------------
18 18
19 19 #-------------------------------------------------------------------------------
20 20 # Imports
21 21 #-------------------------------------------------------------------------------
22 22
23 23
24 24 import wx
25 25 import re
26 26 from wx import stc
27 27 from console_widget import ConsoleWidget
28 28 import __builtin__
29 29
30 30 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
31 31
32 32 #_COMMAND_BG = '#FAFAF1' # Nice green
33 33 _RUNNING_BUFFER_BG = '#FDFFD3' # Nice yellow
34 34
35 35 _RUNNING_BUFFER_MARKER = 31
36 36
37 37
38 38 #-------------------------------------------------------------------------------
39 39 # Classes to implement the Wx frontend
40 40 #-------------------------------------------------------------------------------
41 41 class IPythonWxController(PrefilterFrontEnd, ConsoleWidget):
42 42
43 43 output_prompt = \
44 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
44 '\n\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02%i\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
45 45
46 46 #--------------------------------------------------------------------------
47 47 # Public API
48 48 #--------------------------------------------------------------------------
49 49
50 50 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
51 51 size=wx.DefaultSize, style=wx.CLIP_CHILDREN,
52 52 *args, **kwds):
53 53 """ Create Shell instance.
54 54 """
55 55 ConsoleWidget.__init__(self, parent, id, pos, size, style)
56 56 PrefilterFrontEnd.__init__(self)
57 57
58 58 # Capture Character keys
59 59 self.Bind(wx.EVT_KEY_DOWN, self._on_key_down)
60 60
61 61 # Marker for running buffer.
62 62 self.MarkerDefine(_RUNNING_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
63 63 background=_RUNNING_BUFFER_BG)
64 64
65 65
66 66
67 def do_completion(self, mode=None):
67 def do_completion(self):
68 68 """ Do code completion.
69 mode can be 'text', 'popup' or 'none' to use default.
70 69 """
71 70 line = self.get_current_edit_buffer()
72 71 new_line, completions = self.complete(line)
73 72 if len(completions)>1:
74 self.write_completion(completions, mode=mode)
73 self.write_completion(completions)
75 74 self.replace_current_edit_buffer(new_line)
76 75
77 76
78 77 def do_calltip(self):
79 78 separators = [' ', '(', '[', '{', '\n', '\t']
80 79 symbol = self.get_current_edit_buffer()
81 80 for separator in separators:
82 81 symbol_string = symbol.split(separator)[-1]
83 82 base_symbol_string = symbol_string.split('.')[0]
84 83 if base_symbol_string in self.shell.user_ns:
85 84 symbol = self.shell.user_ns[base_symbol_string]
86 85 elif base_symbol_string in self.shell.user_global_ns:
87 86 symbol = self.shell.user_global_ns[base_symbol_string]
88 87 elif base_symbol_string in __builtin__.__dict__:
89 88 symbol = __builtin__.__dict__[base_symbol_string]
90 89 else:
91 90 return False
92 91 for name in base_symbol_string.split('.')[1:] + ['__doc__']:
93 92 symbol = getattr(symbol, name)
93 try:
94 94 self.CallTipShow(self.GetCurrentPos(), symbol)
95 except TypeError:
96 # The retrieve symbol couldn't be converted to a string
97 pass
95 98
96 99
97 def update_completion(self):
100 def popup_completion(self, create=False):
101 """ Updates the popup completion menu if it exists. If create is
102 true, open the menu.
103 """
98 104 line = self.get_current_edit_buffer()
99 if self.AutoCompActive() and not line[-1] == '.':
100 line = line[:-1]
101 completions = self.complete(line)
102 choose_single = self.AutoCompGetChooseSingle()
103 self.AutoCompSetChooseSingle(False)
104 self.write_completion(completions, mode='popup')
105 self.AutoCompSetChooseSingle(choose_single)
105 if (self.AutoCompActive() and not line[-1] == '.') \
106 or create==True:
107 suggestion, completions = self.complete(line)
108 offset=0
109 if completions:
110 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ]')
111 residual = complete_sep.split(line)[-1]
112 offset = len(residual)
113 self.pop_completion(completions, offset=offset)
106 114
107 115
108 116 def execute(self, python_string, raw_string=None):
117 self.CallTipCancel()
109 118 self._cursor = wx.BusyCursor()
110 119 if raw_string is None:
111 120 raw_string = python_string
112 121 end_line = self.current_prompt_line \
113 122 + max(1, len(raw_string.split('\n'))-1)
114 123 for i in range(self.current_prompt_line, end_line):
115 124 self.MarkerAdd(i, 31)
125 # Remove the trailing "\n" for cleaner display
126 self.SetSelection(self.GetLength()-1, self.GetLength())
127 self.ReplaceSelection('')
128 self.GotoPos(self.GetLength())
116 129 PrefilterFrontEnd.execute(self, python_string, raw_string=raw_string)
117 130
118 131
119 132 def after_execute(self):
120 133 PrefilterFrontEnd.after_execute(self)
121 134 if hasattr(self, '_cursor'):
122 135 del self._cursor
123 136
124 137 #--------------------------------------------------------------------------
125 138 # Private API
126 139 #--------------------------------------------------------------------------
127 140
128 141
129 142 def _on_key_down(self, event, skip=True):
130 143 """ Capture the character events, let the parent
131 144 widget handle them, and put our logic afterward.
132 145 """
133 146 current_line_number = self.GetCurrentLine()
134 147 if self.AutoCompActive():
135 148 event.Skip()
136 149 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
137 wx.CallAfter(self.do_completion)
150 wx.CallAfter(self.popup_completion)
138 151 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
139 152 wx.WXK_RIGHT):
140 wx.CallAfter(self.update_completion)
153 wx.CallAfter(self.popup_completion)
141 154 else:
142 155 # Up history
143 156 if event.KeyCode == wx.WXK_UP and (
144 157 ( current_line_number == self.current_prompt_line and
145 158 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
146 159 or event.ControlDown() ):
147 160 new_buffer = self.get_history_previous(
148 161 self.get_current_edit_buffer())
149 162 if new_buffer is not None:
150 163 self.replace_current_edit_buffer(new_buffer)
151 164 if self.GetCurrentLine() > self.current_prompt_line:
152 165 # Go to first line, for seemless history up.
153 166 self.GotoPos(self.current_prompt_pos)
154 167 # Down history
155 168 elif event.KeyCode == wx.WXK_DOWN and (
156 169 ( current_line_number == self.LineCount -1 and
157 170 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
158 171 or event.ControlDown() ):
159 172 new_buffer = self.get_history_next()
160 173 if new_buffer is not None:
161 174 self.replace_current_edit_buffer(new_buffer)
162 175 elif event.KeyCode == ord('\t'):
163 176 last_line = self.get_current_edit_buffer().split('\n')[-1]
164 177 if not re.match(r'^\s*$', last_line):
165 self.do_completion(mode='text')
178 self.do_completion()
166 179 else:
167 180 event.Skip()
168 181 elif event.KeyCode == ord('('):
169 182 event.Skip()
170 183 self.do_calltip()
171 184 else:
172 185 ConsoleWidget._on_key_down(self, event, skip=skip)
173 186
174 187
175 188 def _on_key_up(self, event, skip=True):
176 189 if event.KeyCode == 59:
177 190 # Intercepting '.'
178 191 event.Skip()
179 #self.do_completion(mode='popup')
192 self.popup_completion(create=True)
180 193 else:
181 194 ConsoleWidget._on_key_up(self, event, skip=skip)
182 195
183 196
184 197 if __name__ == '__main__':
185 198 class MainWindow(wx.Frame):
186 199 def __init__(self, parent, id, title):
187 200 wx.Frame.__init__(self, parent, id, title, size=(300,250))
188 201 self._sizer = wx.BoxSizer(wx.VERTICAL)
189 202 self.shell = IPythonWxController(self)
190 203 self._sizer.Add(self.shell, 1, wx.EXPAND)
191 204 self.SetSizer(self._sizer)
192 205 self.SetAutoLayout(1)
193 206 self.Show(True)
194 207
195 208 app = wx.PySimpleApp()
196 209 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
197 210 frame.shell.SetFocus()
198 frame.SetSize((660, 460))
211 frame.SetSize((680, 460))
199 212 self = frame.shell
200 213
201 214 app.MainLoop()
202 215
General Comments 0
You need to be logged in to leave comments. Login now