##// END OF EJS Templates
Fix tab-completion on lines other than the last one.
gvaroquaux -
Show More
@@ -1,527 +1,527 b''
1 # encoding: utf-8 -*- test-case-name:
1 # encoding: utf-8 -*- test-case-name:
2 # FIXME: Need to add tests.
2 # FIXME: Need to add tests.
3 # ipython1.frontend.wx.tests.test_wx_frontend -*-
3 # ipython1.frontend.wx.tests.test_wx_frontend -*-
4
4
5 """Classes to provide a Wx frontend to the
5 """Classes to provide a Wx frontend to the
6 IPython.kernel.core.interpreter.
6 IPython.kernel.core.interpreter.
7
7
8 This class inherits from ConsoleWidget, that provides a console-like
8 This class inherits from ConsoleWidget, that provides a console-like
9 widget to provide a text-rendering widget suitable for a terminal.
9 widget to provide a text-rendering widget suitable for a terminal.
10 """
10 """
11
11
12 __docformat__ = "restructuredtext en"
12 __docformat__ = "restructuredtext en"
13
13
14 #-------------------------------------------------------------------------------
14 #-------------------------------------------------------------------------------
15 # Copyright (C) 2008 The IPython Development Team
15 # Copyright (C) 2008 The IPython Development Team
16 #
16 #
17 # Distributed under the terms of the BSD License. The full license is in
17 # Distributed under the terms of the BSD License. The full license is in
18 # the file COPYING, distributed as part of this software.
18 # the file COPYING, distributed as part of this software.
19 #-------------------------------------------------------------------------------
19 #-------------------------------------------------------------------------------
20
20
21 #-------------------------------------------------------------------------------
21 #-------------------------------------------------------------------------------
22 # Imports
22 # Imports
23 #-------------------------------------------------------------------------------
23 #-------------------------------------------------------------------------------
24
24
25 # Major library imports
25 # Major library imports
26 import re
26 import re
27 import __builtin__
27 import __builtin__
28 from time import sleep
28 from time import sleep
29 import sys
29 import sys
30 from threading import Lock
30 from threading import Lock
31 import string
31 import string
32
32
33 import wx
33 import wx
34 from wx import stc
34 from wx import stc
35
35
36 # Ipython-specific imports.
36 # Ipython-specific imports.
37 from IPython.frontend._process import PipedProcess
37 from IPython.frontend._process import PipedProcess
38 from console_widget import ConsoleWidget
38 from console_widget import ConsoleWidget
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
39 from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
40
40
41 #-------------------------------------------------------------------------------
41 #-------------------------------------------------------------------------------
42 # Constants
42 # Constants
43 #-------------------------------------------------------------------------------
43 #-------------------------------------------------------------------------------
44
44
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
45 _COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
46 _INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47 _ERROR_BG = '#FFF1F1' # Nice red
47 _ERROR_BG = '#FFF1F1' # Nice red
48
48
49 _COMPLETE_BUFFER_MARKER = 31
49 _COMPLETE_BUFFER_MARKER = 31
50 _ERROR_MARKER = 30
50 _ERROR_MARKER = 30
51 _INPUT_MARKER = 29
51 _INPUT_MARKER = 29
52
52
53 prompt_in1 = \
53 prompt_in1 = \
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
54 '\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\x02]: \x01\x1b[0m\x02'
55
55
56 prompt_out = \
56 prompt_out = \
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
57 '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58
58
59 #-------------------------------------------------------------------------------
59 #-------------------------------------------------------------------------------
60 # Classes to implement the Wx frontend
60 # Classes to implement the Wx frontend
61 #-------------------------------------------------------------------------------
61 #-------------------------------------------------------------------------------
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
62 class WxController(ConsoleWidget, PrefilterFrontEnd):
63 """Classes to provide a Wx frontend to the
63 """Classes to provide a Wx frontend to the
64 IPython.kernel.core.interpreter.
64 IPython.kernel.core.interpreter.
65
65
66 This class inherits from ConsoleWidget, that provides a console-like
66 This class inherits from ConsoleWidget, that provides a console-like
67 widget to provide a text-rendering widget suitable for a terminal.
67 widget to provide a text-rendering widget suitable for a terminal.
68 """
68 """
69
69
70 output_prompt_template = string.Template(prompt_out)
70 output_prompt_template = string.Template(prompt_out)
71
71
72 input_prompt_template = string.Template(prompt_in1)
72 input_prompt_template = string.Template(prompt_in1)
73
73
74 # Print debug info on what is happening to the console.
74 # Print debug info on what is happening to the console.
75 debug = False
75 debug = False
76
76
77 # The title of the terminal, as captured through the ANSI escape
77 # The title of the terminal, as captured through the ANSI escape
78 # sequences.
78 # sequences.
79 def _set_title(self, title):
79 def _set_title(self, title):
80 return self.Parent.SetTitle(title)
80 return self.Parent.SetTitle(title)
81
81
82 def _get_title(self):
82 def _get_title(self):
83 return self.Parent.GetTitle()
83 return self.Parent.GetTitle()
84
84
85 title = property(_get_title, _set_title)
85 title = property(_get_title, _set_title)
86
86
87
87
88 # The buffer being edited.
88 # The buffer being edited.
89 # We are duplicating the definition here because of multiple
89 # We are duplicating the definition here because of multiple
90 # inheritence
90 # inheritence
91 def _set_input_buffer(self, string):
91 def _set_input_buffer(self, string):
92 ConsoleWidget._set_input_buffer(self, string)
92 ConsoleWidget._set_input_buffer(self, string)
93 self._colorize_input_buffer()
93 self._colorize_input_buffer()
94
94
95 def _get_input_buffer(self):
95 def _get_input_buffer(self):
96 """ Returns the text in current edit buffer.
96 """ Returns the text in current edit buffer.
97 """
97 """
98 return ConsoleWidget._get_input_buffer(self)
98 return ConsoleWidget._get_input_buffer(self)
99
99
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
100 input_buffer = property(_get_input_buffer, _set_input_buffer)
101
101
102
102
103 #--------------------------------------------------------------------------
103 #--------------------------------------------------------------------------
104 # Private Attributes
104 # Private Attributes
105 #--------------------------------------------------------------------------
105 #--------------------------------------------------------------------------
106
106
107 # A flag governing the behavior of the input. Can be:
107 # A flag governing the behavior of the input. Can be:
108 #
108 #
109 # 'readline' for readline-like behavior with a prompt
109 # 'readline' for readline-like behavior with a prompt
110 # and an edit buffer.
110 # and an edit buffer.
111 # 'raw_input' similar to readline, but triggered by a raw-input
111 # 'raw_input' similar to readline, but triggered by a raw-input
112 # call. Can be used by subclasses to act differently.
112 # call. Can be used by subclasses to act differently.
113 # 'subprocess' for sending the raw input directly to a
113 # 'subprocess' for sending the raw input directly to a
114 # subprocess.
114 # subprocess.
115 # 'buffering' for buffering of the input, that will be used
115 # 'buffering' for buffering of the input, that will be used
116 # when the input state switches back to another state.
116 # when the input state switches back to another state.
117 _input_state = 'readline'
117 _input_state = 'readline'
118
118
119 # Attribute to store reference to the pipes of a subprocess, if we
119 # Attribute to store reference to the pipes of a subprocess, if we
120 # are running any.
120 # are running any.
121 _running_process = False
121 _running_process = False
122
122
123 # A queue for writing fast streams to the screen without flooding the
123 # A queue for writing fast streams to the screen without flooding the
124 # event loop
124 # event loop
125 _out_buffer = []
125 _out_buffer = []
126
126
127 # A lock to lock the _out_buffer to make sure we don't empty it
127 # A lock to lock the _out_buffer to make sure we don't empty it
128 # while it is being swapped
128 # while it is being swapped
129 _out_buffer_lock = Lock()
129 _out_buffer_lock = Lock()
130
130
131 # The different line markers used to higlight the prompts.
131 # The different line markers used to higlight the prompts.
132 _markers = dict()
132 _markers = dict()
133
133
134 #--------------------------------------------------------------------------
134 #--------------------------------------------------------------------------
135 # Public API
135 # Public API
136 #--------------------------------------------------------------------------
136 #--------------------------------------------------------------------------
137
137
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
138 def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139 size=wx.DefaultSize,
139 size=wx.DefaultSize,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
140 style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
141 *args, **kwds):
141 *args, **kwds):
142 """ Create Shell instance.
142 """ Create Shell instance.
143 """
143 """
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
144 ConsoleWidget.__init__(self, parent, id, pos, size, style)
145 PrefilterFrontEnd.__init__(self, **kwds)
145 PrefilterFrontEnd.__init__(self, **kwds)
146
146
147 # Marker for complete buffer.
147 # Marker for complete buffer.
148 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
148 self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
149 background=_COMPLETE_BUFFER_BG)
149 background=_COMPLETE_BUFFER_BG)
150 # Marker for current input buffer.
150 # Marker for current input buffer.
151 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
151 self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
152 background=_INPUT_BUFFER_BG)
152 background=_INPUT_BUFFER_BG)
153 # Marker for tracebacks.
153 # Marker for tracebacks.
154 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
154 self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
155 background=_ERROR_BG)
155 background=_ERROR_BG)
156
156
157 # A time for flushing the write buffer
157 # A time for flushing the write buffer
158 BUFFER_FLUSH_TIMER_ID = 100
158 BUFFER_FLUSH_TIMER_ID = 100
159 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
159 self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
160 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
160 wx.EVT_TIMER(self, BUFFER_FLUSH_TIMER_ID, self._buffer_flush)
161
161
162 if 'debug' in kwds:
162 if 'debug' in kwds:
163 self.debug = kwds['debug']
163 self.debug = kwds['debug']
164 kwds.pop('debug')
164 kwds.pop('debug')
165
165
166 # Inject self in namespace, for debug
166 # Inject self in namespace, for debug
167 if self.debug:
167 if self.debug:
168 self.shell.user_ns['self'] = self
168 self.shell.user_ns['self'] = self
169
169
170
170
171 def raw_input(self, prompt):
171 def raw_input(self, prompt):
172 """ A replacement from python's raw_input.
172 """ A replacement from python's raw_input.
173 """
173 """
174 self.new_prompt(prompt)
174 self.new_prompt(prompt)
175 self._input_state = 'raw_input'
175 self._input_state = 'raw_input'
176 if hasattr(self, '_cursor'):
176 if hasattr(self, '_cursor'):
177 del self._cursor
177 del self._cursor
178 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
178 self.SetCursor(wx.StockCursor(wx.CURSOR_CROSS))
179 self.waiting = True
179 self.waiting = True
180 self.__old_on_enter = self._on_enter
180 self.__old_on_enter = self._on_enter
181 def my_on_enter():
181 def my_on_enter():
182 self.waiting = False
182 self.waiting = False
183 self._on_enter = my_on_enter
183 self._on_enter = my_on_enter
184 # XXX: Busy waiting, ugly.
184 # XXX: Busy waiting, ugly.
185 while self.waiting:
185 while self.waiting:
186 wx.Yield()
186 wx.Yield()
187 sleep(0.1)
187 sleep(0.1)
188 self._on_enter = self.__old_on_enter
188 self._on_enter = self.__old_on_enter
189 self._input_state = 'buffering'
189 self._input_state = 'buffering'
190 self._cursor = wx.BusyCursor()
190 self._cursor = wx.BusyCursor()
191 return self.input_buffer.rstrip('\n')
191 return self.input_buffer.rstrip('\n')
192
192
193
193
194 def system_call(self, command_string):
194 def system_call(self, command_string):
195 self._input_state = 'subprocess'
195 self._input_state = 'subprocess'
196 self._running_process = PipedProcess(command_string,
196 self._running_process = PipedProcess(command_string,
197 out_callback=self.buffered_write,
197 out_callback=self.buffered_write,
198 end_callback = self._end_system_call)
198 end_callback = self._end_system_call)
199 self._running_process.start()
199 self._running_process.start()
200 # XXX: another one of these polling loops to have a blocking
200 # XXX: another one of these polling loops to have a blocking
201 # call
201 # call
202 wx.Yield()
202 wx.Yield()
203 while self._running_process:
203 while self._running_process:
204 wx.Yield()
204 wx.Yield()
205 sleep(0.1)
205 sleep(0.1)
206 # Be sure to flush the buffer.
206 # Be sure to flush the buffer.
207 self._buffer_flush(event=None)
207 self._buffer_flush(event=None)
208
208
209
209
210 def do_calltip(self):
210 def do_calltip(self):
211 """ Analyse current and displays useful calltip for it.
211 """ Analyse current and displays useful calltip for it.
212 """
212 """
213 if self.debug:
213 if self.debug:
214 print >>sys.__stdout__, "do_calltip"
214 print >>sys.__stdout__, "do_calltip"
215 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
215 separators = re.compile('[\s\{\}\[\]\(\)\= ,:]')
216 symbol = self.input_buffer
216 symbol = self.input_buffer
217 symbol_string = separators.split(symbol)[-1]
217 symbol_string = separators.split(symbol)[-1]
218 base_symbol_string = symbol_string.split('.')[0]
218 base_symbol_string = symbol_string.split('.')[0]
219 if base_symbol_string in self.shell.user_ns:
219 if base_symbol_string in self.shell.user_ns:
220 symbol = self.shell.user_ns[base_symbol_string]
220 symbol = self.shell.user_ns[base_symbol_string]
221 elif base_symbol_string in self.shell.user_global_ns:
221 elif base_symbol_string in self.shell.user_global_ns:
222 symbol = self.shell.user_global_ns[base_symbol_string]
222 symbol = self.shell.user_global_ns[base_symbol_string]
223 elif base_symbol_string in __builtin__.__dict__:
223 elif base_symbol_string in __builtin__.__dict__:
224 symbol = __builtin__.__dict__[base_symbol_string]
224 symbol = __builtin__.__dict__[base_symbol_string]
225 else:
225 else:
226 return False
226 return False
227 try:
227 try:
228 for name in symbol_string.split('.')[1:] + ['__doc__']:
228 for name in symbol_string.split('.')[1:] + ['__doc__']:
229 symbol = getattr(symbol, name)
229 symbol = getattr(symbol, name)
230 self.AutoCompCancel()
230 self.AutoCompCancel()
231 wx.Yield()
231 wx.Yield()
232 self.CallTipShow(self.GetCurrentPos(), symbol)
232 self.CallTipShow(self.GetCurrentPos(), symbol)
233 except:
233 except:
234 # The retrieve symbol couldn't be converted to a string
234 # The retrieve symbol couldn't be converted to a string
235 pass
235 pass
236
236
237
237
238 def _popup_completion(self, create=False):
238 def _popup_completion(self, create=False):
239 """ Updates the popup completion menu if it exists. If create is
239 """ Updates the popup completion menu if it exists. If create is
240 true, open the menu.
240 true, open the menu.
241 """
241 """
242 if self.debug:
242 if self.debug:
243 print >>sys.__stdout__, "_popup_completion"
243 print >>sys.__stdout__, "_popup_completion"
244 line = self.input_buffer
244 line = self.input_buffer
245 if (self.AutoCompActive() and line and not line[-1] == '.') \
245 if (self.AutoCompActive() and line and not line[-1] == '.') \
246 or create==True:
246 or create==True:
247 suggestion, completions = self.complete(line)
247 suggestion, completions = self.complete(line)
248 offset=0
248 offset=0
249 if completions:
249 if completions:
250 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
250 complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
251 residual = complete_sep.split(line)[-1]
251 residual = complete_sep.split(line)[-1]
252 offset = len(residual)
252 offset = len(residual)
253 self.pop_completion(completions, offset=offset)
253 self.pop_completion(completions, offset=offset)
254 if self.debug:
254 if self.debug:
255 print >>sys.__stdout__, completions
255 print >>sys.__stdout__, completions
256
256
257
257
258 def buffered_write(self, text):
258 def buffered_write(self, text):
259 """ A write method for streams, that caches the stream in order
259 """ A write method for streams, that caches the stream in order
260 to avoid flooding the event loop.
260 to avoid flooding the event loop.
261
261
262 This can be called outside of the main loop, in separate
262 This can be called outside of the main loop, in separate
263 threads.
263 threads.
264 """
264 """
265 self._out_buffer_lock.acquire()
265 self._out_buffer_lock.acquire()
266 self._out_buffer.append(text)
266 self._out_buffer.append(text)
267 self._out_buffer_lock.release()
267 self._out_buffer_lock.release()
268 if not self._buffer_flush_timer.IsRunning():
268 if not self._buffer_flush_timer.IsRunning():
269 wx.CallAfter(self._buffer_flush_timer.Start,
269 wx.CallAfter(self._buffer_flush_timer.Start,
270 milliseconds=100, oneShot=True)
270 milliseconds=100, oneShot=True)
271
271
272
272
273 #--------------------------------------------------------------------------
273 #--------------------------------------------------------------------------
274 # LineFrontEnd interface
274 # LineFrontEnd interface
275 #--------------------------------------------------------------------------
275 #--------------------------------------------------------------------------
276
276
277 def execute(self, python_string, raw_string=None):
277 def execute(self, python_string, raw_string=None):
278 self._input_state = 'buffering'
278 self._input_state = 'buffering'
279 self.CallTipCancel()
279 self.CallTipCancel()
280 self._cursor = wx.BusyCursor()
280 self._cursor = wx.BusyCursor()
281 if raw_string is None:
281 if raw_string is None:
282 raw_string = python_string
282 raw_string = python_string
283 end_line = self.current_prompt_line \
283 end_line = self.current_prompt_line \
284 + max(1, len(raw_string.split('\n'))-1)
284 + max(1, len(raw_string.split('\n'))-1)
285 for i in range(self.current_prompt_line, end_line):
285 for i in range(self.current_prompt_line, end_line):
286 if i in self._markers:
286 if i in self._markers:
287 self.MarkerDeleteHandle(self._markers[i])
287 self.MarkerDeleteHandle(self._markers[i])
288 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
288 self._markers[i] = self.MarkerAdd(i, _COMPLETE_BUFFER_MARKER)
289 # Use a callafter to update the display robustly under windows
289 # Use a callafter to update the display robustly under windows
290 def callback():
290 def callback():
291 self.GotoPos(self.GetLength())
291 self.GotoPos(self.GetLength())
292 PrefilterFrontEnd.execute(self, python_string,
292 PrefilterFrontEnd.execute(self, python_string,
293 raw_string=raw_string)
293 raw_string=raw_string)
294 wx.CallAfter(callback)
294 wx.CallAfter(callback)
295
295
296 def save_output_hooks(self):
296 def save_output_hooks(self):
297 self.__old_raw_input = __builtin__.raw_input
297 self.__old_raw_input = __builtin__.raw_input
298 PrefilterFrontEnd.save_output_hooks(self)
298 PrefilterFrontEnd.save_output_hooks(self)
299
299
300 def capture_output(self):
300 def capture_output(self):
301 __builtin__.raw_input = self.raw_input
301 __builtin__.raw_input = self.raw_input
302 self.SetLexer(stc.STC_LEX_NULL)
302 self.SetLexer(stc.STC_LEX_NULL)
303 PrefilterFrontEnd.capture_output(self)
303 PrefilterFrontEnd.capture_output(self)
304
304
305
305
306 def release_output(self):
306 def release_output(self):
307 __builtin__.raw_input = self.__old_raw_input
307 __builtin__.raw_input = self.__old_raw_input
308 PrefilterFrontEnd.release_output(self)
308 PrefilterFrontEnd.release_output(self)
309 self.SetLexer(stc.STC_LEX_PYTHON)
309 self.SetLexer(stc.STC_LEX_PYTHON)
310
310
311
311
312 def after_execute(self):
312 def after_execute(self):
313 PrefilterFrontEnd.after_execute(self)
313 PrefilterFrontEnd.after_execute(self)
314 # Clear the wait cursor
314 # Clear the wait cursor
315 if hasattr(self, '_cursor'):
315 if hasattr(self, '_cursor'):
316 del self._cursor
316 del self._cursor
317 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
317 self.SetCursor(wx.StockCursor(wx.CURSOR_CHAR))
318
318
319
319
320 def show_traceback(self):
320 def show_traceback(self):
321 start_line = self.GetCurrentLine()
321 start_line = self.GetCurrentLine()
322 PrefilterFrontEnd.show_traceback(self)
322 PrefilterFrontEnd.show_traceback(self)
323 self.ProcessEvent(wx.PaintEvent())
323 self.ProcessEvent(wx.PaintEvent())
324 #wx.Yield()
324 #wx.Yield()
325 for i in range(start_line, self.GetCurrentLine()):
325 for i in range(start_line, self.GetCurrentLine()):
326 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
326 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
327
327
328
328
329 #--------------------------------------------------------------------------
329 #--------------------------------------------------------------------------
330 # FrontEndBase interface
330 # FrontEndBase interface
331 #--------------------------------------------------------------------------
331 #--------------------------------------------------------------------------
332
332
333 def render_error(self, e):
333 def render_error(self, e):
334 start_line = self.GetCurrentLine()
334 start_line = self.GetCurrentLine()
335 self.write('\n' + e + '\n')
335 self.write('\n' + e + '\n')
336 for i in range(start_line, self.GetCurrentLine()):
336 for i in range(start_line, self.GetCurrentLine()):
337 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
337 self._markers[i] = self.MarkerAdd(i, _ERROR_MARKER)
338
338
339
339
340 #--------------------------------------------------------------------------
340 #--------------------------------------------------------------------------
341 # ConsoleWidget interface
341 # ConsoleWidget interface
342 #--------------------------------------------------------------------------
342 #--------------------------------------------------------------------------
343
343
344 def new_prompt(self, prompt):
344 def new_prompt(self, prompt):
345 """ Display a new prompt, and start a new input buffer.
345 """ Display a new prompt, and start a new input buffer.
346 """
346 """
347 self._input_state = 'readline'
347 self._input_state = 'readline'
348 ConsoleWidget.new_prompt(self, prompt)
348 ConsoleWidget.new_prompt(self, prompt)
349 i = self.current_prompt_line
349 i = self.current_prompt_line
350 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
350 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
351
351
352
352
353 def write(self, *args, **kwargs):
353 def write(self, *args, **kwargs):
354 # Avoid multiple inheritence, be explicit about which
354 # Avoid multiple inheritence, be explicit about which
355 # parent method class gets called
355 # parent method class gets called
356 ConsoleWidget.write(self, *args, **kwargs)
356 ConsoleWidget.write(self, *args, **kwargs)
357
357
358
358
359 def _on_key_down(self, event, skip=True):
359 def _on_key_down(self, event, skip=True):
360 """ Capture the character events, let the parent
360 """ Capture the character events, let the parent
361 widget handle them, and put our logic afterward.
361 widget handle them, and put our logic afterward.
362 """
362 """
363 # FIXME: This method needs to be broken down in smaller ones.
363 # FIXME: This method needs to be broken down in smaller ones.
364 current_line_number = self.GetCurrentLine()
364 current_line_number = self.GetCurrentLine()
365 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
365 if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
366 # Capture Control-C
366 # Capture Control-C
367 if self._input_state == 'subprocess':
367 if self._input_state == 'subprocess':
368 if self.debug:
368 if self.debug:
369 print >>sys.__stderr__, 'Killing running process'
369 print >>sys.__stderr__, 'Killing running process'
370 if hasattr(self._running_process, 'process'):
370 if hasattr(self._running_process, 'process'):
371 self._running_process.process.kill()
371 self._running_process.process.kill()
372 elif self._input_state == 'buffering':
372 elif self._input_state == 'buffering':
373 if self.debug:
373 if self.debug:
374 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
374 print >>sys.__stderr__, 'Raising KeyboardInterrupt'
375 raise KeyboardInterrupt
375 raise KeyboardInterrupt
376 # XXX: We need to make really sure we
376 # XXX: We need to make really sure we
377 # get back to a prompt.
377 # get back to a prompt.
378 elif self._input_state == 'subprocess' and (
378 elif self._input_state == 'subprocess' and (
379 ( event.KeyCode<256 and
379 ( event.KeyCode<256 and
380 not event.ControlDown() )
380 not event.ControlDown() )
381 or
381 or
382 ( event.KeyCode in (ord('d'), ord('D')) and
382 ( event.KeyCode in (ord('d'), ord('D')) and
383 event.ControlDown())):
383 event.ControlDown())):
384 # We are running a process, we redirect keys.
384 # We are running a process, we redirect keys.
385 ConsoleWidget._on_key_down(self, event, skip=skip)
385 ConsoleWidget._on_key_down(self, event, skip=skip)
386 char = chr(event.KeyCode)
386 char = chr(event.KeyCode)
387 # Deal with some inconsistency in wx keycodes:
387 # Deal with some inconsistency in wx keycodes:
388 if char == '\r':
388 if char == '\r':
389 char = '\n'
389 char = '\n'
390 elif not event.ShiftDown():
390 elif not event.ShiftDown():
391 char = char.lower()
391 char = char.lower()
392 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
392 if event.ControlDown() and event.KeyCode in (ord('d'), ord('D')):
393 char = '\04'
393 char = '\04'
394 self._running_process.process.stdin.write(char)
394 self._running_process.process.stdin.write(char)
395 self._running_process.process.stdin.flush()
395 self._running_process.process.stdin.flush()
396 elif event.KeyCode in (ord('('), 57):
396 elif event.KeyCode in (ord('('), 57):
397 # Calltips
397 # Calltips
398 event.Skip()
398 event.Skip()
399 self.do_calltip()
399 self.do_calltip()
400 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
400 elif self.AutoCompActive() and not event.KeyCode == ord('\t'):
401 event.Skip()
401 event.Skip()
402 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
402 if event.KeyCode in (wx.WXK_BACK, wx.WXK_DELETE):
403 wx.CallAfter(self._popup_completion, create=True)
403 wx.CallAfter(self._popup_completion, create=True)
404 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
404 elif not event.KeyCode in (wx.WXK_UP, wx.WXK_DOWN, wx.WXK_LEFT,
405 wx.WXK_RIGHT, wx.WXK_ESCAPE):
405 wx.WXK_RIGHT, wx.WXK_ESCAPE):
406 wx.CallAfter(self._popup_completion)
406 wx.CallAfter(self._popup_completion)
407 else:
407 else:
408 # Up history
408 # Up history
409 if event.KeyCode == wx.WXK_UP and (
409 if event.KeyCode == wx.WXK_UP and (
410 ( current_line_number == self.current_prompt_line and
410 ( current_line_number == self.current_prompt_line and
411 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
411 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
412 or event.ControlDown() ):
412 or event.ControlDown() ):
413 new_buffer = self.get_history_previous(
413 new_buffer = self.get_history_previous(
414 self.input_buffer)
414 self.input_buffer)
415 if new_buffer is not None:
415 if new_buffer is not None:
416 self.input_buffer = new_buffer
416 self.input_buffer = new_buffer
417 if self.GetCurrentLine() > self.current_prompt_line:
417 if self.GetCurrentLine() > self.current_prompt_line:
418 # Go to first line, for seemless history up.
418 # Go to first line, for seemless history up.
419 self.GotoPos(self.current_prompt_pos)
419 self.GotoPos(self.current_prompt_pos)
420 # Down history
420 # Down history
421 elif event.KeyCode == wx.WXK_DOWN and (
421 elif event.KeyCode == wx.WXK_DOWN and (
422 ( current_line_number == self.LineCount -1 and
422 ( current_line_number == self.LineCount -1 and
423 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
423 event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
424 or event.ControlDown() ):
424 or event.ControlDown() ):
425 new_buffer = self.get_history_next()
425 new_buffer = self.get_history_next()
426 if new_buffer is not None:
426 if new_buffer is not None:
427 self.input_buffer = new_buffer
427 self.input_buffer = new_buffer
428 # Tab-completion
428 # Tab-completion
429 elif event.KeyCode == ord('\t'):
429 elif event.KeyCode == ord('\t'):
430 last_line = self.input_buffer.split('\n')[-1]
430 current_line, current_line_number = self.CurLine
431 if not re.match(r'^\s*$', last_line):
431 if not re.match(r'^\s*$', current_line):
432 self.complete_current_input()
432 self.complete_current_input()
433 if self.AutoCompActive():
433 if self.AutoCompActive():
434 wx.CallAfter(self._popup_completion, create=True)
434 wx.CallAfter(self._popup_completion, create=True)
435 else:
435 else:
436 event.Skip()
436 event.Skip()
437 else:
437 else:
438 ConsoleWidget._on_key_down(self, event, skip=skip)
438 ConsoleWidget._on_key_down(self, event, skip=skip)
439
439
440
440
441 def _on_key_up(self, event, skip=True):
441 def _on_key_up(self, event, skip=True):
442 """ Called when any key is released.
442 """ Called when any key is released.
443 """
443 """
444 if event.KeyCode in (59, ord('.')):
444 if event.KeyCode in (59, ord('.')):
445 # Intercepting '.'
445 # Intercepting '.'
446 event.Skip()
446 event.Skip()
447 wx.CallAfter(self._popup_completion, create=True)
447 wx.CallAfter(self._popup_completion, create=True)
448 else:
448 else:
449 ConsoleWidget._on_key_up(self, event, skip=skip)
449 ConsoleWidget._on_key_up(self, event, skip=skip)
450
450
451
451
452 def _on_enter(self):
452 def _on_enter(self):
453 """ Called on return key down, in readline input_state.
453 """ Called on return key down, in readline input_state.
454 """
454 """
455 if self.debug:
455 if self.debug:
456 print >>sys.__stdout__, repr(self.input_buffer)
456 print >>sys.__stdout__, repr(self.input_buffer)
457 PrefilterFrontEnd._on_enter(self)
457 PrefilterFrontEnd._on_enter(self)
458
458
459
459
460 #--------------------------------------------------------------------------
460 #--------------------------------------------------------------------------
461 # EditWindow API
461 # EditWindow API
462 #--------------------------------------------------------------------------
462 #--------------------------------------------------------------------------
463
463
464 def OnUpdateUI(self, event):
464 def OnUpdateUI(self, event):
465 """ Override the OnUpdateUI of the EditWindow class, to prevent
465 """ Override the OnUpdateUI of the EditWindow class, to prevent
466 syntax highlighting both for faster redraw, and for more
466 syntax highlighting both for faster redraw, and for more
467 consistent look and feel.
467 consistent look and feel.
468 """
468 """
469 if not self._input_state == 'readline':
469 if not self._input_state == 'readline':
470 ConsoleWidget.OnUpdateUI(self, event)
470 ConsoleWidget.OnUpdateUI(self, event)
471
471
472 #--------------------------------------------------------------------------
472 #--------------------------------------------------------------------------
473 # Private API
473 # Private API
474 #--------------------------------------------------------------------------
474 #--------------------------------------------------------------------------
475
475
476 def _end_system_call(self):
476 def _end_system_call(self):
477 """ Called at the end of a system call.
477 """ Called at the end of a system call.
478 """
478 """
479 self._input_state = 'buffering'
479 self._input_state = 'buffering'
480 self._running_process = False
480 self._running_process = False
481
481
482
482
483 def _buffer_flush(self, event):
483 def _buffer_flush(self, event):
484 """ Called by the timer to flush the write buffer.
484 """ Called by the timer to flush the write buffer.
485
485
486 This is always called in the mainloop, by the wx timer.
486 This is always called in the mainloop, by the wx timer.
487 """
487 """
488 self._out_buffer_lock.acquire()
488 self._out_buffer_lock.acquire()
489 _out_buffer = self._out_buffer
489 _out_buffer = self._out_buffer
490 self._out_buffer = []
490 self._out_buffer = []
491 self._out_buffer_lock.release()
491 self._out_buffer_lock.release()
492 self.write(''.join(_out_buffer), refresh=False)
492 self.write(''.join(_out_buffer), refresh=False)
493
493
494
494
495 def _colorize_input_buffer(self):
495 def _colorize_input_buffer(self):
496 """ Keep the input buffer lines at a bright color.
496 """ Keep the input buffer lines at a bright color.
497 """
497 """
498 if not self._input_state in ('readline', 'raw_input'):
498 if not self._input_state in ('readline', 'raw_input'):
499 return
499 return
500 end_line = self.GetCurrentLine()
500 end_line = self.GetCurrentLine()
501 if not sys.platform == 'win32':
501 if not sys.platform == 'win32':
502 end_line += 1
502 end_line += 1
503 for i in range(self.current_prompt_line, end_line):
503 for i in range(self.current_prompt_line, end_line):
504 if i in self._markers:
504 if i in self._markers:
505 self.MarkerDeleteHandle(self._markers[i])
505 self.MarkerDeleteHandle(self._markers[i])
506 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
506 self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
507
507
508
508
509 if __name__ == '__main__':
509 if __name__ == '__main__':
510 class MainWindow(wx.Frame):
510 class MainWindow(wx.Frame):
511 def __init__(self, parent, id, title):
511 def __init__(self, parent, id, title):
512 wx.Frame.__init__(self, parent, id, title, size=(300,250))
512 wx.Frame.__init__(self, parent, id, title, size=(300,250))
513 self._sizer = wx.BoxSizer(wx.VERTICAL)
513 self._sizer = wx.BoxSizer(wx.VERTICAL)
514 self.shell = WxController(self)
514 self.shell = WxController(self)
515 self._sizer.Add(self.shell, 1, wx.EXPAND)
515 self._sizer.Add(self.shell, 1, wx.EXPAND)
516 self.SetSizer(self._sizer)
516 self.SetSizer(self._sizer)
517 self.SetAutoLayout(1)
517 self.SetAutoLayout(1)
518 self.Show(True)
518 self.Show(True)
519
519
520 app = wx.PySimpleApp()
520 app = wx.PySimpleApp()
521 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
521 frame = MainWindow(None, wx.ID_ANY, 'Ipython')
522 frame.shell.SetFocus()
522 frame.shell.SetFocus()
523 frame.SetSize((680, 460))
523 frame.SetSize((680, 460))
524 self = frame.shell
524 self = frame.shell
525
525
526 app.MainLoop()
526 app.MainLoop()
527
527
General Comments 0
You need to be logged in to leave comments. Login now