~pivanov/ipython/devel

« back to all changes in this revision

Viewing changes to IPython/frontend/wx/wx_frontend.py

  • Committer: Brian Granger
  • Date: 2009-04-25 22:59:20 UTC
  • mfrom: (1166.2.37 bugfixes0411409)
  • Revision ID: ellisonbg@gmail.com-20090425225920-27mdz9foesgltbiy
Merging bugfixes0411409 branch.

This branch has a massive number of bug fixes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
25
25
# Major library imports
26
26
import re
27
27
import __builtin__
28
 
from time import sleep
29
28
import sys
30
29
from threading import Lock
31
 
import string
32
30
 
33
31
import wx
34
32
from wx import stc
35
33
 
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
40
39
 
41
40
#-------------------------------------------------------------------------------
42
 
# Constants 
43
 
#-------------------------------------------------------------------------------
44
 
 
45
 
_COMPLETE_BUFFER_BG = '#FAFAF1' # Nice green
46
 
_INPUT_BUFFER_BG = '#FDFFD3' # Nice yellow
47
 
_ERROR_BG = '#FFF1F1' # Nice red
48
 
 
49
 
_COMPLETE_BUFFER_MARKER = 31
50
 
_ERROR_MARKER = 30
51
 
_INPUT_MARKER = 29
52
 
 
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'
55
 
 
56
 
prompt_out = \
57
 
    '\x01\x1b[0;31m\x02Out[\x01\x1b[1;31m\x02$number\x01\x1b[0;31m\x02]: \x01\x1b[0m\x02'
58
 
 
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.
68
49
    """
69
 
 
70
 
    output_prompt_template = string.Template(prompt_out)
71
 
 
72
 
    input_prompt_template = string.Template(prompt_in1)
73
 
 
 
50
    
74
51
    # Print debug info on what is happening to the console.
75
52
    debug = False
76
53
 
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,
 
118
                 styledef=None,
141
119
                 *args, **kwds):
142
120
        """ Create Shell instance.
 
121
 
 
122
            Parameters
 
123
            -----------
 
124
            styledef : dict, optional
 
125
                styledef is the dictionary of options used to define the
 
126
                style.
143
127
        """
 
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)
146
132
        
147
133
        # Stick in our own raw_input:
148
134
        self.ipython0.raw_input = self.raw_input
149
135
 
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)
159
 
 
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
174
 
 
175
 
 
 
150
        
176
151
    def raw_input(self, prompt=''):
177
152
        """ A replacement from python's raw_input.
178
153
        """
251
226
        if (self.AutoCompActive() and line and not line[-1] == '.') \
252
227
                    or create==True:
253
228
            suggestion, completions = self.complete(line)
254
 
            offset=0
255
229
            if completions:
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)
260
232
                if self.debug:
261
233
                    print >>sys.__stdout__, completions 
276
248
                                        milliseconds=100, oneShot=True)
277
249
 
278
250
 
 
251
    def clear_screen(self):
 
252
        """ Empty completely the widget.
 
253
        """
 
254
        self.ClearAll()
 
255
        self.new_prompt(self.input_prompt_template.substitute(
 
256
                                number=(self.last_result['number'] + 1)))
 
257
 
 
258
 
279
259
    #--------------------------------------------------------------------------
280
260
    # LineFrontEnd interface 
281
261
    #--------------------------------------------------------------------------
299
279
                                            raw_string=raw_string)
300
280
        wx.CallAfter(callback)
301
281
 
 
282
 
 
283
    def execute_command(self, command, hidden=False):
 
284
        """ Execute a command, not only in the model, but also in the
 
285
            view.
 
286
        """
 
287
        # XXX: This method needs to be integrated in the base fronted
 
288
        # interface
 
289
        if hidden:
 
290
            return self.shell.execute(command)
 
291
        else:
 
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
 
300
                command += '\n'
 
301
            cleaned_command = self.prefilter_input(command)
 
302
            self.input_buffer = command
 
303
            # Do not use wx.Yield() (aka GUI.process_events()) to avoid
 
304
            # recursive yields.
 
305
            self.ProcessEvent(wx.PaintEvent())
 
306
            self.write('\n')
 
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)))
 
312
                return False
 
313
            self._on_enter()
 
314
            return True
 
315
 
 
316
 
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)
357
372
 
358
373
 
 
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)
 
378
 
 
379
 
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)
363
384
 
364
385
 
365
386
    def _on_key_down(self, event, skip=True):
367
388
            widget handle them, and put our logic afterward.
368
389
        """
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':
413
434
        else:
414
435
            # Up history
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)
426
447
            # Down history
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
434
455
            # Tab-completion
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)
441
462
                else:
442
463
                    event.Skip()
 
464
            elif event.KeyCode == wx.WXK_BACK:
 
465
                # If characters where erased, check if we have to
 
466
                # remove a line.
 
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):
 
477
                    new_lines = []
 
478
                    for line_num, line in enumerate(
 
479
                                    self.input_buffer.split('\n')):
 
480
                        if (line_num + self.current_prompt_line ==
 
481
                                            current_line_num):
 
482
                            new_lines.append(line[len_prompt:])
 
483
                        else:
 
484
                            new_lines.append('\n'+line)
 
485
                    # The first character is '\n', due to the above
 
486
                    # code:
 
487
                    self.input_buffer = ''.join(new_lines)[1:]
 
488
                    self.GotoPos(current_pos - 1 - len_prompt)
 
489
                else:
 
490
                    ConsoleWidget._on_key_down(self, event, skip=skip)
443
491
            else:
444
492
                ConsoleWidget._on_key_down(self, event, skip=skip)
 
493
                        
445
494
 
446
495
 
447
496
    def _on_key_up(self, event, skip=True):
453
502
            wx.CallAfter(self._popup_completion, create=True)
454
503
        else:
455
504
            ConsoleWidget._on_key_up(self, event, skip=skip)
 
505
        # Make sure the continuation_prompts are always followed by a 
 
506
        # whitespace
 
507
        new_lines = []
 
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)
456
516
 
457
517
 
458
518
    def _on_enter(self):
459
519
        """ Called on return key down, in readline input_state.
460
520
        """
 
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)
461
524
        if self.debug:
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))
 
538
        return has_executed
464
539
 
465
540
 
466
541
    #--------------------------------------------------------------------------