25
25
# Major library imports
28
from time import sleep
30
29
from threading import Lock
36
34
# Ipython-specific imports.
37
from IPython.frontend._process import PipedProcess
38
from console_widget import ConsoleWidget
35
from IPython.frontend.process import PipedProcess
36
from console_widget import ConsoleWidget, _COMPLETE_BUFFER_MARKER, \
37
_ERROR_MARKER, _INPUT_MARKER
39
38
from IPython.frontend.prefilterfrontend import PrefilterFrontEnd
41
40
#-------------------------------------------------------------------------------
43
#-------------------------------------------------------------------------------
45
_COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46
_INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47
_ERROR_BG = '#FFF1F1' # Nice red
49
_COMPLETE_BUFFER_MARKER = 31
54
'\n\x01\x1b[0;34m\x02In [\x01\x1b[1;34m\x02$number\x01\x1b[0;34m\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'
59
#-------------------------------------------------------------------------------
60
41
# Classes to implement the Wx frontend
61
42
#-------------------------------------------------------------------------------
62
43
class WxController(ConsoleWidget, PrefilterFrontEnd):
66
47
This class inherits from ConsoleWidget, that provides a console-like
67
48
widget to provide a text-rendering widget suitable for a terminal.
70
output_prompt_template = string.Template(prompt_out)
72
input_prompt_template = string.Template(prompt_in1)
74
51
# Print debug info on what is happening to the console.
138
115
def __init__(self, parent, id=wx.ID_ANY, pos=wx.DefaultPosition,
139
116
size=wx.DefaultSize,
140
117
style=wx.CLIP_CHILDREN|wx.WANTS_CHARS,
142
120
""" Create Shell instance.
124
styledef : dict, optional
125
styledef is the dictionary of options used to define the
128
if styledef is not None:
129
self.style = styledef
144
130
ConsoleWidget.__init__(self, parent, id, pos, size, style)
145
131
PrefilterFrontEnd.__init__(self, **kwds)
147
133
# Stick in our own raw_input:
148
134
self.ipython0.raw_input = self.raw_input
150
# Marker for complete buffer.
151
self.MarkerDefine(_COMPLETE_BUFFER_MARKER, stc.STC_MARK_BACKGROUND,
152
background=_COMPLETE_BUFFER_BG)
153
# Marker for current input buffer.
154
self.MarkerDefine(_INPUT_MARKER, stc.STC_MARK_BACKGROUND,
155
background=_INPUT_BUFFER_BG)
156
# Marker for tracebacks.
157
self.MarkerDefine(_ERROR_MARKER, stc.STC_MARK_BACKGROUND,
158
background=_ERROR_BG)
160
136
# A time for flushing the write buffer
161
137
BUFFER_FLUSH_TIMER_ID = 100
162
138
self._buffer_flush_timer = wx.Timer(self, BUFFER_FLUSH_TIMER_ID)
171
147
self.shell.user_ns['self'] = self
172
148
# Inject our own raw_input in namespace
173
149
self.shell.user_ns['raw_input'] = self.raw_input
176
151
def raw_input(self, prompt=''):
177
152
""" A replacement from python's raw_input.
251
226
if (self.AutoCompActive() and line and not line[-1] == '.') \
253
228
suggestion, completions = self.complete(line)
256
complete_sep = re.compile('[\s\{\}\[\]\(\)\= ,:]')
257
residual = complete_sep.split(line)[-1]
258
offset = len(residual)
230
offset = len(self._get_completion_text(line))
259
231
self.pop_completion(completions, offset=offset)
261
233
print >>sys.__stdout__, completions
276
248
milliseconds=100, oneShot=True)
251
def clear_screen(self):
252
""" Empty completely the widget.
255
self.new_prompt(self.input_prompt_template.substitute(
256
number=(self.last_result['number'] + 1)))
279
259
#--------------------------------------------------------------------------
280
260
# LineFrontEnd interface
281
261
#--------------------------------------------------------------------------
299
279
raw_string=raw_string)
300
280
wx.CallAfter(callback)
283
def execute_command(self, command, hidden=False):
284
""" Execute a command, not only in the model, but also in the
287
# XXX: This method needs to be integrated in the base fronted
290
return self.shell.execute(command)
292
# XXX: we are not storing the input buffer previous to the
293
# execution, as this forces us to run the execution
294
# input_buffer a yield, which is not good.
295
##current_buffer = self.shell.control.input_buffer
296
command = command.rstrip()
297
if len(command.split('\n')) > 1:
298
# The input command is several lines long, we need to
299
# force the execution to happen
301
cleaned_command = self.prefilter_input(command)
302
self.input_buffer = command
303
# Do not use wx.Yield() (aka GUI.process_events()) to avoid
305
self.ProcessEvent(wx.PaintEvent())
307
if not self.is_complete(cleaned_command + '\n'):
308
self._colorize_input_buffer()
309
self.render_error('Incomplete or invalid input')
310
self.new_prompt(self.input_prompt_template.substitute(
311
number=(self.last_result['number'] + 1)))
302
317
def save_output_hooks(self):
303
318
self.__old_raw_input = __builtin__.raw_input
304
319
PrefilterFrontEnd.save_output_hooks(self)
356
371
self._markers[i] = self.MarkerAdd(i, _INPUT_MARKER)
374
def continuation_prompt(self, *args, **kwargs):
375
# Avoid multiple inheritence, be explicit about which
376
# parent method class gets called
377
return ConsoleWidget.continuation_prompt(self, *args, **kwargs)
359
380
def write(self, *args, **kwargs):
360
381
# Avoid multiple inheritence, be explicit about which
361
382
# parent method class gets called
362
ConsoleWidget.write(self, *args, **kwargs)
383
return ConsoleWidget.write(self, *args, **kwargs)
365
386
def _on_key_down(self, event, skip=True):
367
388
widget handle them, and put our logic afterward.
369
390
# FIXME: This method needs to be broken down in smaller ones.
370
current_line_number = self.GetCurrentLine()
391
current_line_num = self.GetCurrentLine()
371
392
if event.KeyCode in (ord('c'), ord('C')) and event.ControlDown():
372
393
# Capture Control-C
373
394
if self._input_state == 'subprocess':
415
436
if event.KeyCode == wx.WXK_UP and (
416
( current_line_number == self.current_prompt_line and
437
( current_line_num == self.current_prompt_line and
417
438
event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
418
439
or event.ControlDown() ):
419
440
new_buffer = self.get_history_previous(
425
446
self.GotoPos(self.current_prompt_pos)
427
448
elif event.KeyCode == wx.WXK_DOWN and (
428
( current_line_number == self.LineCount -1 and
449
( current_line_num == self.LineCount -1 and
429
450
event.Modifiers in (wx.MOD_NONE, wx.MOD_WIN) )
430
451
or event.ControlDown() ):
431
452
new_buffer = self.get_history_next()
433
454
self.input_buffer = new_buffer
435
456
elif event.KeyCode == ord('\t'):
436
current_line, current_line_number = self.CurLine
457
current_line, current_line_num = self.CurLine
437
458
if not re.match(r'^\s*$', current_line):
438
459
self.complete_current_input()
439
460
if self.AutoCompActive():
440
461
wx.CallAfter(self._popup_completion, create=True)
464
elif event.KeyCode == wx.WXK_BACK:
465
# If characters where erased, check if we have to
467
# XXX: What about DEL?
468
# FIXME: This logics should be in ConsoleWidget, as it is
469
# independant of IPython
470
current_line, _ = self.CurLine
471
current_pos = self.GetCurrentPos()
472
current_line_num = self.LineFromPosition(current_pos)
473
current_col = self.GetColumn(current_pos)
474
len_prompt = len(self.continuation_prompt())
475
if ( current_line.startswith(self.continuation_prompt())
476
and current_col == len_prompt):
478
for line_num, line in enumerate(
479
self.input_buffer.split('\n')):
480
if (line_num + self.current_prompt_line ==
482
new_lines.append(line[len_prompt:])
484
new_lines.append('\n'+line)
485
# The first character is '\n', due to the above
487
self.input_buffer = ''.join(new_lines)[1:]
488
self.GotoPos(current_pos - 1 - len_prompt)
490
ConsoleWidget._on_key_down(self, event, skip=skip)
444
492
ConsoleWidget._on_key_down(self, event, skip=skip)
447
496
def _on_key_up(self, event, skip=True):
453
502
wx.CallAfter(self._popup_completion, create=True)
455
504
ConsoleWidget._on_key_up(self, event, skip=skip)
505
# Make sure the continuation_prompts are always followed by a
508
if self._input_state == 'readline':
509
position = self.GetCurrentPos()
510
continuation_prompt = self.continuation_prompt()[:-1]
511
for line in self.input_buffer.split('\n'):
512
if not line == continuation_prompt:
513
new_lines.append(line)
514
self.input_buffer = '\n'.join(new_lines)
515
self.GotoPos(position)
458
518
def _on_enter(self):
459
519
""" Called on return key down, in readline input_state.
521
last_line_num = self.LineFromPosition(self.GetLength())
522
current_line_num = self.LineFromPosition(self.GetCurrentPos())
523
new_line_pos = (last_line_num - current_line_num)
462
525
print >>sys.__stdout__, repr(self.input_buffer)
463
PrefilterFrontEnd._on_enter(self)
526
self.write('\n', refresh=False)
527
# Under windows scintilla seems to be doing funny
528
# stuff to the line returns here, but the getter for
529
# input_buffer filters this out.
530
if sys.platform == 'win32':
531
self.input_buffer = self.input_buffer
532
old_prompt_num = self.current_prompt_pos
533
has_executed = PrefilterFrontEnd._on_enter(self,
534
new_line_pos=new_line_pos)
535
if old_prompt_num == self.current_prompt_pos:
536
# No execution has happened
537
self.GotoPos(self.GetLineEndPosition(current_line_num + 1))
466
541
#--------------------------------------------------------------------------