ipython_view.py 22.6 KB
Newer Older
1
#!/usr/bin/python
Philipp Hörist's avatar
Philipp Hörist committed
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

# Copyright (C) 2008-2014 Yann Leboulanger <asterix AT lagaule.org>
#
# This file is part of Gajim.
#
# Gajim is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation; version 3 only.
#
# Gajim is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
#
# Copyright (c) 2007, IBM Corporation
# All rights reserved.
roidelapluie's avatar
roidelapluie committed
21

Daniel Brötzmann's avatar
Daniel Brötzmann committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:

# * Redistributions of source code must retain the above copyright notice,
#   this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright notice,
#   this list of conditions and the following disclaimer in the documentation
#   and/or other materials provided with the distribution.
# * Neither the name of the IBM Corporation nor the names of its contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
roidelapluie's avatar
roidelapluie committed
45

46 47
"""
Provides IPython console widget
48 49 50 51 52 53

@author: Eitan Isaacson
@organization: IBM Corporation
@copyright: Copyright (c) 2007 IBM Corporation
@license: BSD

54 55
All rights reserved. This program and the accompanying materials are made
available under the terms of the BSD which accompanies this distribution, and
56
is available at U{http://www.opensource.org/licenses/bsd-license.php}
57
"""
58

59 60 61 62
import re
import sys
import os
from io import StringIO
63 64
from functools import reduce

65
from gi.repository import Gtk
66
from gi.repository import Gdk
67
from gi.repository import GObject
Yann Leboulanger's avatar
Yann Leboulanger committed
68
from gi.repository import GLib
69
from gi.repository import Pango
70

71
from pygments.token import Token
Philipp Hörist's avatar
Philipp Hörist committed
72 73

import IPython
74 75
from IPython.core.displayhook import DisplayHook
from IPython.core.display_trap import DisplayTrap
Philipp Hörist's avatar
Philipp Hörist committed
76

77 78 79 80 81 82 83 84 85 86 87


class MyPromptDisplayHook(DisplayHook):
    def __init__(self, shell, view):
        DisplayHook.__init__(self, shell=shell)
        self.view = view

    def write_output_prompt(self):
        tokens = self.shell.prompts.out_prompt_tokens()
        self.view.write('\n')
        self.view.write(tokens)
Yann Leboulanger's avatar
Yann Leboulanger committed
88

Daniel Brötzmann's avatar
Daniel Brötzmann committed
89

90
class IterableIPShell:
91
    """
92
    Create an IPython instance. Does not start a blocking event loop,
Daniel Brötzmann's avatar
Daniel Brötzmann committed
93
    instead allow single iterations. This allows embedding in GTK
94 95 96 97 98 99 100 101 102 103
    without blockage

    @ivar IP: IPython instance.
    @type IP: IPython.iplib.InteractiveShell
    @ivar iter_more: Indicates if the line executed was a complete command,
    or we should wait for more.
    @type iter_more: integer
    @ivar history_level: The place in history where we currently are
    when pressing up/down.
    @type history_level: integer
Emmanuel Gil Peyrot's avatar
Emmanuel Gil Peyrot committed
104
    @ivar complete_sep: Separation delimiters for completion function.
105 106
    @type complete_sep: _sre.SRE_Pattern
    """
107 108
    def __init__(self, argv=None, user_ns=None, user_global_ns=None, cin=None,
                 cout=None, cerr=None, input_func=None):
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
        """
        @param argv: Command line options for IPython
        @type argv: list
        @param user_ns: User namespace.
        @type user_ns: dictionary
        @param user_global_ns: User global namespace.
        @type user_global_ns: dictionary.
        @param cin: Console standard input.
        @type cin: IO stream
        @param cout: Console standard output.
        @type cout: IO stream
        @param cerr: Console standard error.
        @type cerr: IO stream
        @param input_func: Replacement for builtin raw_input()
        @type input_func: function
        """
125 126 127
        if argv is None:
            argv = []

Yann Leboulanger's avatar
Yann Leboulanger committed
128
        io = IPython.utils.io
129
        if input_func:
130 131
            IPython.terminal.interactiveshell.raw_input_original = input_func

132

Emmanuel Gil Peyrot's avatar
Emmanuel Gil Peyrot committed
133
        # This is to get rid of the blockage that occurs during
134
        # IPython.Shell.InteractiveShell.user_setup()
Yann Leboulanger's avatar
Yann Leboulanger committed
135
        io.raw_input = lambda x: None
136 137 138

        os.environ['TERM'] = 'dumb'
        excepthook = sys.excepthook
139

140
        from traitlets.config.loader import Config
Yann Leboulanger's avatar
Yann Leboulanger committed
141

142 143 144
        cfg = Config()
        cfg.InteractiveShell.colors = "Linux"

145 146
        # InteractiveShell's __init__ gets a reference of stdout and stderr
        # so we save the standard here to revert it after init
Yann Leboulanger's avatar
Yann Leboulanger committed
147
        old_stdout, old_stderr = sys.stdout, sys.stderr
148
        sys.stdout, sys.stderr = cout, cerr
Yann Leboulanger's avatar
Yann Leboulanger committed
149 150

        # InteractiveShell inherits from SingletonConfigurable so use instance()
151 152
        self.IP = IPython.terminal.embed.InteractiveShellEmbed.instance(
            config=cfg, user_ns=user_ns, user_module=user_global_ns)
Yann Leboulanger's avatar
Yann Leboulanger committed
153

154
        # Set back stdout and stderr to what it was before
Yann Leboulanger's avatar
Yann Leboulanger committed
155 156
        sys.stdout, sys.stderr = old_stdout, old_stderr

157 158
        self.IP.system = lambda cmd: self.shell(self.IP.var_expand(cmd),
                                                header='IPython system call: ',
159 160 161
                                                local_ns=user_ns)

        self.IP.raw_input = input_func
162 163 164
        sys.excepthook = excepthook
        self.iter_more = 0
        self.history_level = 0
165
        self.complete_sep = re.compile(r'[\s\{\}\[\]\(\)]')
Daniel Brötzmann's avatar
Daniel Brötzmann committed
166 167 168
        self.updateNamespace({'exit': lambda: None})
        self.updateNamespace({'quit': lambda: None})

169 170 171
        # Workaround for updating namespace with sys.modules
        #
        self.__update_namespace()
Daniel Brötzmann's avatar
Daniel Brötzmann committed
172 173
        self.lines = []

174 175 176 177
    def __update_namespace(self):
        '''
        Update self.IP namespace for autocompletion with sys.modules
        '''
178
        for k, v in sys.modules.items():
Daniel Brötzmann's avatar
Daniel Brötzmann committed
179 180
            if '.' not in k:
                self.IP.user_ns.update({k: v})
181 182 183 184 185 186 187

    def execute(self):
        """
        Execute the current line provided by the shell object
        """
        self.history_level = 0
        orig_stdout = sys.stdout
Yann Leboulanger's avatar
Yann Leboulanger committed
188
        sys.stdout = IPython.utils.io.stdout
189 190

        orig_stdin = sys.stdin
191
        sys.stdin = IPython.utils.io.stdin
192 193 194 195 196 197
        self.prompt = self.generatePrompt(self.iter_more)

        self.IP.hooks.pre_prompt_hook()
        if self.iter_more:
            try:
                self.prompt = self.generatePrompt(True)
198
            except Exception:
199
                self.IP.showtraceback()
200
            if self.IP.autoindent:
201 202 203
                self.IP.rl_do_indent = True

        try:
André's avatar
André committed
204
            self.IP.raw_input(self.prompt)
205 206
        except KeyboardInterrupt:
            self.IP.write('\nKeyboardInterrupt\n')
207
            self.IP.input_splitter.reset()
208
        except Exception:
209 210
            self.IP.showtraceback()
        else:
André's avatar
André committed
211 212 213
            self.lines.append(self.IP.raw_input(self.prompt))
            self.iter_more = self.IP.check_complete(
                '\n'.join(self.lines))[0] == 'incomplete'
Daniel Brötzmann's avatar
Daniel Brötzmann committed
214

215 216
            self.prompt = self.generatePrompt(self.iter_more)
            if not self.iter_more:
André's avatar
André committed
217 218
                source_raw = '\n'.join(self.lines)
                self.lines = []
219

220
                self.IP.run_cell(source_raw, store_history=True)
Yann Leboulanger's avatar
Yann Leboulanger committed
221
                self.IP.rl_do_indent = False
222 223 224
            else:
                # TODO: Auto-indent
                #
Yann Leboulanger's avatar
Yann Leboulanger committed
225
                self.IP.rl_do_indent = True
226

227
        sys.stdout = orig_stdout
228
        sys.stdin = orig_stdin
Yann Leboulanger's avatar
Yann Leboulanger committed
229

230 231 232 233 234
    def generatePrompt(self, is_continuation):
        '''
        Generate prompt depending on is_continuation value

        @param is_continuation
235
        @type is_continuation: boolean
236 237 238 239 240

        @return: The prompt string representation
        @rtype: string

        '''
241 242
        # TODO: Update to IPython 5.x and later
        prompt = "In [%d]: " % self.IP.execution_count
243 244

        return prompt
245 246 247 248

    def historyBack(self):
        """
        Provide one history command back
249

250 251 252 253 254
        @return: The command string.
        @rtype: string
        """
        self.history_level -= 1
        return self._getHistory()
255

256 257 258
    def historyForward(self):
        """
        Provide one history command forward
259

260 261 262 263 264
        @return: The command string.
        @rtype: string
        """
        self.history_level += 1
        return self._getHistory()
265

266 267 268
    def _getHistory(self):
        """
        Get the command string of the current history level
269

270 271 272 273 274 275 276 277 278 279 280 281 282
        @return: Historic command string.
        @rtype: string
        """
        try:
            rv = self.IP.user_ns['In'][self.history_level].strip('\n')
        except IndexError:
            self.history_level = 0
            rv = ''
        return rv

    def updateNamespace(self, ns_dict):
        """
        Add the current dictionary to the shell namespace
283

284 285 286 287
        @param ns_dict: A dictionary of symbol-values.
        @type ns_dict: dictionary
        """
        self.IP.user_ns.update(ns_dict)
288

289
    def complete(self, line):
290
        """
Alexander Krotov's avatar
Alexander Krotov committed
291
        Returns an auto completed line and/or possibilities for completion
292

293 294
        @param line: Given line so far.
        @type line: string
295

296 297 298 299 300
        @return: Line completed as for as possible,
        and possible further completions.
        @rtype: tuple
        """
        split_line = self.complete_sep.split(line)
301 302 303 304
        if split_line[-1]:
            possibilities = self.IP.complete(split_line[-1])
        else:
            completed = line
305
            possibilities = ['', []]
306
        if possibilities:
307 308 309 310 311 312 313 314 315 316 317 318 319
            def _commonPrefix(str1, str2):
                '''
                Reduction function. returns common prefix of two given strings.

                @param str1: First string.
                @type str1: string
                @param str2: Second string
                @type str2: string

                @return: Common prefix to both strings.
                @rtype: string
                '''
                for i in range(len(str1)):
320
                    if not str2.startswith(str1[:i + 1]):
321 322 323 324
                        return str1[:i]
                return str1

            if possibilities[1]:
Daniel Brötzmann's avatar
Daniel Brötzmann committed
325 326 327
                common_prefix = \
                    reduce(_commonPrefix, possibilities[1]) or line[-1]
                completed = line[:-len(split_line[-1])] + common_prefix
328 329
            else:
                completed = line
330 331
        else:
            completed = line
332
        return completed, possibilities[1]
333 334


335
class ConsoleView(Gtk.TextView):
336
    """
337
    Specialized text view for console-like workflow
338

339 340
    @cvar ANSI_COLORS: Mapping of terminal colors to X11 names.
    @type ANSI_COLORS: dictionary
341

342
    @ivar text_buffer: Widget's text buffer.
343
    @type text_buffer: Gtk.TextBuffer
344 345 346
    @ivar color_pat: Regex of terminal color pattern
    @type color_pat: _sre.SRE_Pattern
    @ivar mark: Scroll mark for automatic scrolling on input.
347
    @type mark: Gtk.TextMark
348
    @ivar line_start: Start of command line mark.
349
    @type line_start: Gtk.TextMark
350
    """
351

352 353 354 355 356 357 358 359
    ANSI_COLORS = {'0;30': 'Black', '0;31': 'Red',
                   '0;32': 'Green', '0;33': 'Brown',
                   '0;34': 'Blue', '0;35': 'Purple',
                   '0;36': 'Cyan', '0;37': 'LightGray',
                   '1;30': 'DarkGray', '1;31': 'DarkRed',
                   '1;32': 'SeaGreen', '1;33': 'Yellow',
                   '1;34': 'LightBlue', '1;35': 'MediumPurple',
                   '1;36': 'LightCyan', '1;37': 'White'}
360

361 362 363 364
    def __init__(self):
        """
        Initialize console view
        """
365
        GObject.GObject.__init__(self)
366
        self.override_font(Pango.FontDescription('Mono'))
367 368
        self.set_cursor_visible(True)
        self.text_buffer = self.get_buffer()
Daniel Brötzmann's avatar
Daniel Brötzmann committed
369 370 371 372
        self.mark = self.text_buffer.create_mark(
            'scroll_mark',
            self.text_buffer.get_end_iter(),
            False)
373 374 375 376 377 378
        for code in self.ANSI_COLORS:
            self.text_buffer.create_tag(code,
                                        foreground=self.ANSI_COLORS[code],
                                        weight=700)
        self.text_buffer.create_tag('0')
        self.text_buffer.create_tag('notouch', editable=False)
379
        self.color_pat = re.compile(r'\x01?\x1b\[(.*?)m\x02?')
380 381 382 383 384 385 386 387

        self.style_dict = {
            Token.Prompt: '0;32',
            Token.PromptNum: '1;32',
            Token.OutPrompt: '0;31',
            Token.OutPromptNum: '1;31',
        }

388 389
        self.line_start = \
            self.text_buffer.create_mark('line_start',
Daniel Brötzmann's avatar
Daniel Brötzmann committed
390 391
                                         self.text_buffer.get_end_iter(),
                                         True)
392 393 394
        self.connect('key-press-event', self.onKeyPress)

    def write(self, text, editable=False):
395
        if isinstance(text, str):
Yann Leboulanger's avatar
Yann Leboulanger committed
396
            GLib.idle_add(self._write, text, editable)
397
        else:
Yann Leboulanger's avatar
Yann Leboulanger committed
398 399 400 401 402 403 404 405 406 407 408
            GLib.idle_add(self._write5, text, editable)

    def _write5(self, text, editable=False):
        """
        Write given text to buffer

        @param text: Text to append.
        @type text: list of (token: string)
        @param editable: If true, added text is editable.
        @type editable: boolean
        """
Daniel Brötzmann's avatar
Daniel Brötzmann committed
409 410
        start_mark = self.text_buffer.create_mark(
            None, self.text_buffer.get_end_iter(), True)
Yann Leboulanger's avatar
Yann Leboulanger committed
411 412 413

        for token, segment in text:
            tag = self.style_dict[token]
Daniel Brötzmann's avatar
Daniel Brötzmann committed
414 415
            self.text_buffer.insert_with_tags_by_name(
                self.text_buffer.get_end_iter(), segment, tag)
Yann Leboulanger's avatar
Yann Leboulanger committed
416
        if not editable:
Daniel Brötzmann's avatar
Daniel Brötzmann committed
417 418 419 420
            self.text_buffer.apply_tag_by_name(
                'notouch',
                self.text_buffer.get_iter_at_mark(start_mark),
                self.text_buffer.get_end_iter())
Yann Leboulanger's avatar
Yann Leboulanger committed
421 422
        self.text_buffer.delete_mark(start_mark)
        self.scroll_mark_onscreen(self.mark)
423 424 425 426

    def _write(self, text, editable=False):
        """
        Write given text to buffer
427

428 429 430 431 432
        @param text: Text to append.
        @type text: string
        @param editable: If true, added text is editable.
        @type editable: boolean
        """
433
        if isinstance(text, list):
Yann Leboulanger's avatar
Yann Leboulanger committed
434 435
            self._write5(text, editable)
            return
436 437
        segments = self.color_pat.split(text)
        segment = segments.pop(0)
Daniel Brötzmann's avatar
Daniel Brötzmann committed
438 439
        start_mark = self.text_buffer.create_mark(
            None, self.text_buffer.get_end_iter(), True)
440 441 442 443 444 445
        self.text_buffer.insert(self.text_buffer.get_end_iter(), segment)

        if segments:
            ansi_tags = self.color_pat.findall(text)
            for tag in ansi_tags:
                i = segments.index(tag)
Daniel Brötzmann's avatar
Daniel Brötzmann committed
446
                self.text_buffer.insert_with_tags_by_name(
447
                    self.text_buffer.get_end_iter(), segments[i + 1], str(tag))
448 449
                segments.pop(i)
        if not editable:
Daniel Brötzmann's avatar
Daniel Brötzmann committed
450 451 452 453 454
            self.text_buffer.apply_tag_by_name(
                'notouch',
                self.text_buffer.get_iter_at_mark(start_mark),
                self.text_buffer.get_end_iter())

455 456 457 458
        self.text_buffer.delete_mark(start_mark)
        self.scroll_mark_onscreen(self.mark)

    def showPrompt(self, prompt):
Yann Leboulanger's avatar
Yann Leboulanger committed
459
        GLib.idle_add(self._showPrompt, prompt)
460 461 462 463

    def _showPrompt(self, prompt):
        """
        Print prompt at start of line
464

465 466 467 468 469 470
        @param prompt: Prompt to print.
        @type prompt: string
        """
        self._write(prompt)
        self.text_buffer.move_mark(self.line_start,
                                   self.text_buffer.get_end_iter())
471

472
    def changeLine(self, text):
Yann Leboulanger's avatar
Yann Leboulanger committed
473
        GLib.idle_add(self._changeLine, text)
474

475 476 477
    def _changeLine(self, text):
        """
        Replace currently entered command line with given text
478

479 480 481 482 483
        @param text: Text to use as replacement.
        @type text: string
        """
        iter_ = self.text_buffer.get_iter_at_mark(self.line_start)
        iter_.forward_to_line_end()
Daniel Brötzmann's avatar
Daniel Brötzmann committed
484 485
        self.text_buffer.delete(
            self.text_buffer.get_iter_at_mark(self.line_start), iter_)
486
        self._write(text, True)
Jonathan Schleifer's avatar
Jonathan Schleifer committed
487

488 489 490
    def getCurrentLine(self):
        """
        Get text in current command line
491

492 493 494 495
        @return: Text of current command line.
        @rtype: string
        """
        rv = self.text_buffer.get_slice(
496 497
            self.text_buffer.get_iter_at_mark(self.line_start),
            self.text_buffer.get_end_iter(), False)
498
        return rv
499

500
    def showReturned(self, text):
Yann Leboulanger's avatar
Yann Leboulanger committed
501
        GLib.idle_add(self._showReturned, text)
502 503 504 505 506 507 508 509 510 511 512

    def _showReturned(self, text):
        """
        Show returned text from last command and print new prompt

        @param text: Text to show.
        @type text: string
        """
        iter_ = self.text_buffer.get_iter_at_mark(self.line_start)
        iter_.forward_to_line_end()
        self.text_buffer.apply_tag_by_name(
513 514 515
            'notouch',
            self.text_buffer.get_iter_at_mark(self.line_start),
            iter_)
516
        self._write('\n' + text)
517 518 519
        if text:
            self._write('\n')
        self._showPrompt(self.prompt)
Daniel Brötzmann's avatar
Daniel Brötzmann committed
520 521
        self.text_buffer.move_mark(
            self.line_start, self.text_buffer.get_end_iter())
522 523
        self.text_buffer.place_cursor(self.text_buffer.get_end_iter())

Yann Leboulanger's avatar
Yann Leboulanger committed
524 525 526 527
        if self.IP.rl_do_indent:
            indentation = self.IP.input_splitter.indent_spaces * ' '
            self.text_buffer.insert_at_cursor(indentation)

528
    def onKeyPress(self, _widget, event):
529 530
        """
        Key press callback used for correcting behavior for console-like
Alexander Krotov's avatar
Alexander Krotov committed
531
        interfaces. For example 'home' should go to prompt, not to beginning of
532 533
        line

Alexander Krotov's avatar
Alexander Krotov committed
534
        @param widget: Widget that key press occurred in.
535
        @type widget: Gtk.Widget
536
        @param event: Event object
537
        @type event: Gdk.Event
538 539 540 541 542 543 544 545 546

        @return: Return True if event should not trickle.
        @rtype: boolean
        """
        insert_mark = self.text_buffer.get_insert()
        insert_iter = self.text_buffer.get_iter_at_mark(insert_mark)
        selection_mark = self.text_buffer.get_selection_bound()
        selection_iter = self.text_buffer.get_iter_at_mark(selection_mark)
        start_iter = self.text_buffer.get_iter_at_mark(self.line_start)
547 548
        if event.keyval == Gdk.KEY_Home:
            if event.get_state() == 0:
549 550
                self.text_buffer.place_cursor(start_iter)
                return True
551
            if event.get_state() == Gdk.ModifierType.SHIFT_MASK:
552 553
                self.text_buffer.move_mark(insert_mark, start_iter)
                return True
554 555

        if event.keyval == Gdk.KEY_Left:
556 557 558 559 560
            insert_iter.backward_cursor_position()
            if not insert_iter.editable(True):
                return True
        elif not event.string:
            pass
561 562
        elif (start_iter.compare(insert_iter) <= 0 and
                start_iter.compare(selection_iter) <= 0):
563
            pass
564 565
        elif (start_iter.compare(insert_iter) > 0 and
                start_iter.compare(selection_iter) > 0):
566 567 568 569 570 571 572 573 574 575 576 577
            self.text_buffer.place_cursor(start_iter)
        elif insert_iter.compare(selection_iter) < 0:
            self.text_buffer.move_mark(insert_mark, start_iter)
        elif insert_iter.compare(selection_iter) > 0:
            self.text_buffer.move_mark(selection_mark, start_iter)

        return self.onKeyPressExtend(event)

    def onKeyPressExtend(self, event):
        """
        For some reason we can't extend onKeyPress directly (bug #500900)
        """
578

Daniel Brötzmann's avatar
Daniel Brötzmann committed
579

580
class IPythonView(ConsoleView, IterableIPShell):
581 582
    '''
    Sub-class of both modified IPython shell and L{ConsoleView} this makes
Daniel Brötzmann's avatar
Daniel Brötzmann committed
583
    a GTK IPython console.
584 585 586 587 588 589 590
    '''
    def __init__(self):
        """
        Initialize. Redirect I/O to console
        """
        ConsoleView.__init__(self)
        self.cout = StringIO()
591
        IterableIPShell.__init__(self, cout=self.cout, cerr=self.cout,
592
                                 input_func=self.raw_input)
593 594 595 596

        displayhook = MyPromptDisplayHook(shell=self.IP, view=self)
        self.IP.displayhook = displayhook
        self.IP.display_trap = DisplayTrap(hook=displayhook)
597

Yann Leboulanger's avatar
Yann Leboulanger committed
598
        self.interrupt = False
599
        self.execute()
600
        self.prompt = self.generatePrompt(False)
601 602
        self.cout.truncate(0)
        self.showPrompt(self.prompt)
603

Yann Leboulanger's avatar
Yann Leboulanger committed
604 605 606 607
    def prompt_for_code(self):
        # IPython 5.0 calls prompt_for_code instead of raw_input
        return self.raw_input(self)

608
    def raw_input(self, _prompt=''):
609
        """
Emmanuel Gil Peyrot's avatar
Emmanuel Gil Peyrot committed
610
        Custom raw_input() replacement. Gets current line from console buffer
611

Alexander Krotov's avatar
Alexander Krotov committed
612
        @param prompt: Prompt to print. Here for compatibility as replacement.
613
        @type prompt: string
614

615 616 617 618 619 620 621
        @return: The current command line text.
        @rtype: string
        """
        if self.interrupt:
            self.interrupt = False
            raise KeyboardInterrupt
        return self.getCurrentLine()
622

623 624 625 626
    def onKeyPressExtend(self, event):
        """
        Key press callback with plenty of shell goodness, like history,
        autocompletions, etc
627

Alexander Krotov's avatar
Alexander Krotov committed
628
        @param widget: Widget that key press occurred in.
629
        @type widget: Gtk.Widget
630
        @param event: Event object.
631
        @type event: Gdk.Event
632

633 634 635
        @return: True if event should not trickle.
        @rtype: boolean
        """
636 637
        if (event.get_state() & Gdk.ModifierType.CONTROL_MASK and
                event.keyval == 99):
638 639 640
            self.interrupt = True
            self._processLine()
            return True
641
        if event.keyval == Gdk.KEY_Return:
642 643
            self._processLine()
            return True
644
        if event.keyval == Gdk.KEY_Up:
645 646
            self.changeLine(self.historyBack())
            return True
647
        if event.keyval == Gdk.KEY_Down:
648 649
            self.changeLine(self.historyForward())
            return True
650
        if event.keyval == Gdk.KEY_Tab:
651 652 653 654 655 656 657
            if not self.getCurrentLine().strip():
                return False
            completed, possibilities = self.complete(self.getCurrentLine())
            if len(possibilities) > 1:
                slice_ = self.getCurrentLine()
                self.write('\n')
                for symbol in possibilities:
658
                    self.write(symbol + '\n')
659 660 661
                self.showPrompt(self.prompt)
            self.changeLine(completed or slice_)
            return True
662

663 664 665 666 667 668 669
    def _processLine(self):
        """
        Process current command line
        """
        self.history_pos = 0
        self.execute()
        rv = self.cout.getvalue()
670 671
        if rv:
            rv = rv.strip('\n')
672 673
        self.showReturned(rv)
        self.cout.truncate(0)
Yann Leboulanger's avatar
Yann Leboulanger committed
674
        self.cout.seek(0)