Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
85bfacf
Initial code for new InteractiveShell subclass using prompt_toolkit
takluyver Dec 22, 2015
a9b0c6c
Refine multiline behaviour
takluyver Dec 23, 2015
f524604
Store history so prompt number increases
takluyver Dec 23, 2015
239389a
Hook up command history and populate it from IPython's history DB
takluyver Jan 8, 2016
e870f74
Do sensible things on Ctrl-C and Ctrl-D
takluyver Jan 8, 2016
db2ff6f
Add completion support
takluyver Jan 8, 2016
ba5734e
Improve order of completions
takluyver Jan 8, 2016
fbb4e63
Nicer default colours
takluyver Jan 8, 2016
342b175
Add blank line before input prompt
takluyver Jan 11, 2016
e778cba
Turn out prompt & traceback colours on when using prompt_toolkit
takluyver Jan 12, 2016
5584533
Move printing blank line to before each input prompt
takluyver Jan 12, 2016
4a9dcfd
Don't try to complete on an empty line
takluyver Jan 12, 2016
535e240
Only use our Enter handling when the default buffer is active
takluyver Jan 12, 2016
904d56e
Enable history search with up arrow
takluyver Jan 12, 2016
3d5a073
Improve default colours for light & dark terminal backgrounds
takluyver Jan 12, 2016
e9ee3ec
Add config option for vi mode
takluyver Jan 14, 2016
cde57a3
Config options for syntax higlighting style
takluyver Jan 14, 2016
0cb9e90
Python 2.7 support & highlighting in prompt_toolkit interface
takluyver Jan 14, 2016
995b794
Add continuation prompts
takluyver Jan 16, 2016
c0fffd1
Write & borrow some inputhooks for prompt_toolkit
takluyver Jan 16, 2016
a9ca5b1
More tweaks to highlighting colours
takluyver Jan 18, 2016
48013ca
Add prompt_toolkit input hooks for wx
takluyver Jan 18, 2016
1f47f59
Add inputhook for pyglet
takluyver Jan 18, 2016
24fa5b7
Add GLUT input hook
takluyver Jan 18, 2016
f34eb22
Fix some deprecation warnings in GTK3 example
takluyver Jan 18, 2016
ae59e6e
Initialise threads for pygtk input hook
takluyver Jan 18, 2016
7da2783
Implement pre-filling prompt from set_next_input()
takluyver Jan 18, 2016
0a16e9d
Integrate colorama for coloured output on Windows
takluyver Jan 19, 2016
7300933
Fix %edit - editor attribute was missing
takluyver Jan 21, 2016
0fe9565
Fix set_next_input on Python 2
takluyver Jan 27, 2016
b40a2dd
Make behaviour more natural with blank lines at the end of input
takluyver Jan 27, 2016
90e30a8
Add a mainloop() method to mimic existing shell API
takluyver Feb 3, 2016
e938416
Config option to enable mouse support
takluyver Feb 3, 2016
ac0799c
Disable continuation prompts to work with stable release of prompt_to…
takluyver Feb 22, 2016
67d7c40
Switch over to use prompt_toolkit in IPython
takluyver Feb 22, 2016
7505eae
Fix 'interactive' tests using pipes to a subprocess
takluyver Feb 22, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 26 additions & 30 deletions IPython/core/completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,41 +170,38 @@ def compress_user(path, tilde_expand, tilde_val):



def penalize_magics_key(word):
"""key for sorting that penalizes magic commands in the ordering
def completions_sorting_key(word):
"""key for sorting completions

Normal words are left alone.

Magic commands have the initial % moved to the end, e.g.
%matplotlib is transformed as follows:

%matplotlib -> matplotlib%

[The choice of the final % is arbitrary.]

Since "matplotlib" < "matplotlib%" as strings,
"timeit" will appear before the magic "%timeit" in the ordering

For consistency, move "%%" to the end, so cell magics appear *after*
line magics with the same name.

A check is performed that there are no other "%" in the string;
if there are, then the string is not a magic command and is left unchanged.
This does several things:

- Lowercase all completions, so they are sorted alphabetically with
upper and lower case words mingled
- Demote any completions starting with underscores to the end
- Insert any %magic and %%cellmagic completions in the alphabetical order
by their name
"""
# Case insensitive sort
word = word.lower()

# Move any % signs from start to end of the key
# provided there are no others elsewhere in the string
prio1, prio2 = 0, 0

if word[:2] == "%%":
if not "%" in word[2:]:
return word[2:] + "%%"
if word.startswith('__'):
prio1 = 2
elif word.startswith('_'):
prio1 = 1

if word[:1] == "%":
if word.startswith('%%'):
# If there's another % in there, this is something else, so leave it alone
if not "%" in word[2:]:
word = word[2:]
prio2 = 2
elif word.startswith('%'):
if not "%" in word[1:]:
return word[1:] + "%"

return word
word = word[1:]
prio2 = 1

return prio1, word, prio2


@undoc
Expand Down Expand Up @@ -1206,8 +1203,7 @@ def complete(self, text=None, line_buffer=None, cursor_pos=None):
# simply collapse the dict into a list for readline, but we'd have
# richer completion semantics in other evironments.

# use penalize_magics_key to put magics after variables with same name
self.matches = sorted(set(self.matches), key=penalize_magics_key)
self.matches = sorted(set(self.matches), key=completions_sorting_key)

#io.rprint('COMP TEXT, MATCHES: %r, %r' % (text, self.matches)) # dbg
return text, self.matches
Expand Down
6 changes: 4 additions & 2 deletions IPython/core/tests/test_shellapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ def test_py_script_file_attribute_interactively(self):
src = "True\n"
self.mktmp(src)

out = 'In [1]: False\n\nIn [2]:'
err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
tt.ipexec_validate(self.fname, 'False', err, options=['-i'],
tt.ipexec_validate(self.fname, out, err, options=['-i'],
commands=['"__file__" in globals()', 'exit()'])

@dec.skip_win32
Expand All @@ -63,6 +64,7 @@ def test_py_script_file_compiler_directive(self):
src = "from __future__ import division\n"
self.mktmp(src)

out = 'In [1]: float\n\nIn [2]:'
err = SQLITE_NOT_AVAILABLE_ERROR if sqlite_err_maybe else None
tt.ipexec_validate(self.fname, 'float', err, options=['-i'],
tt.ipexec_validate(self.fname, out, err, options=['-i'],
commands=['type(1/2)', 'exit()'])
2 changes: 1 addition & 1 deletion IPython/terminal/ipapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
InteractiveShellApp, shell_flags, shell_aliases
)
from IPython.extensions.storemagic import StoreMagics
from IPython.terminal.interactiveshell import TerminalInteractiveShell
from .ptshell import PTInteractiveShell as TerminalInteractiveShell
from IPython.utils import warn
from IPython.paths import get_ipython_dir
from traitlets import (
Expand Down
16 changes: 16 additions & 0 deletions IPython/terminal/pt_inputhooks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import importlib
import os

aliases = {
'qt4': 'qt',
}

def get_inputhook_func(gui):
if gui in aliases:
return get_inputhook_func(aliases[gui])

if gui == 'qt5':
os.environ['QT_API'] = 'pyqt5'

mod = importlib.import_module('IPython.terminal.pt_inputhooks.'+gui)
return mod.inputhook
141 changes: 141 additions & 0 deletions IPython/terminal/pt_inputhooks/glut.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"""GLUT Input hook for interactive use with prompt_toolkit
"""
from __future__ import print_function


# GLUT is quite an old library and it is difficult to ensure proper
# integration within IPython since original GLUT does not allow to handle
# events one by one. Instead, it requires for the mainloop to be entered
# and never returned (there is not even a function to exit he
# mainloop). Fortunately, there are alternatives such as freeglut
# (available for linux and windows) and the OSX implementation gives
# access to a glutCheckLoop() function that blocks itself until a new
# event is received. This means we have to setup the idle callback to
# ensure we got at least one event that will unblock the function.
#
# Furthermore, it is not possible to install these handlers without a window
# being first created. We choose to make this window invisible. This means that
# display mode options are set at this level and user won't be able to change
# them later without modifying the code. This should probably be made available
# via IPython options system.

import sys
import time
import signal
import OpenGL.GLUT as glut
import OpenGL.platform as platform
from timeit import default_timer as clock

# Frame per second : 60
# Should probably be an IPython option
glut_fps = 60

# Display mode : double buffeed + rgba + depth
# Should probably be an IPython option
glut_display_mode = (glut.GLUT_DOUBLE |
glut.GLUT_RGBA |
glut.GLUT_DEPTH)

glutMainLoopEvent = None
if sys.platform == 'darwin':
try:
glutCheckLoop = platform.createBaseFunction(
'glutCheckLoop', dll=platform.GLUT, resultType=None,
argTypes=[],
doc='glutCheckLoop( ) -> None',
argNames=(),
)
except AttributeError:
raise RuntimeError(
'''Your glut implementation does not allow interactive sessions'''
'''Consider installing freeglut.''')
glutMainLoopEvent = glutCheckLoop
elif glut.HAVE_FREEGLUT:
glutMainLoopEvent = glut.glutMainLoopEvent
else:
raise RuntimeError(
'''Your glut implementation does not allow interactive sessions. '''
'''Consider installing freeglut.''')


def glut_display():
# Dummy display function
pass

def glut_idle():
# Dummy idle function
pass

def glut_close():
# Close function only hides the current window
glut.glutHideWindow()
glutMainLoopEvent()

def glut_int_handler(signum, frame):
# Catch sigint and print the defaultipyt message
signal.signal(signal.SIGINT, signal.default_int_handler)
print('\nKeyboardInterrupt')
# Need to reprint the prompt at this stage

# Initialisation code
glut.glutInit( sys.argv )
glut.glutInitDisplayMode( glut_display_mode )
# This is specific to freeglut
if bool(glut.glutSetOption):
glut.glutSetOption( glut.GLUT_ACTION_ON_WINDOW_CLOSE,
glut.GLUT_ACTION_GLUTMAINLOOP_RETURNS )
glut.glutCreateWindow( b'ipython' )
glut.glutReshapeWindow( 1, 1 )
glut.glutHideWindow( )
glut.glutWMCloseFunc( glut_close )
glut.glutDisplayFunc( glut_display )
glut.glutIdleFunc( glut_idle )


def inputhook(context):
"""Run the pyglet event loop by processing pending events only.

This keeps processing pending events until stdin is ready. After
processing all pending events, a call to time.sleep is inserted. This is
needed, otherwise, CPU usage is at 100%. This sleep time should be tuned
though for best performance.
"""
# We need to protect against a user pressing Control-C when IPython is
# idle and this is running. We trap KeyboardInterrupt and pass.

signal.signal(signal.SIGINT, glut_int_handler)

try:
t = clock()

# Make sure the default window is set after a window has been closed
if glut.glutGetWindow() == 0:
glut.glutSetWindow( 1 )
glutMainLoopEvent()
return 0

while not context.input_is_ready():
glutMainLoopEvent()
# We need to sleep at this point to keep the idle CPU load
# low. However, if sleep to long, GUI response is poor. As
# a compromise, we watch how often GUI events are being processed
# and switch between a short and long sleep time. Here are some
# stats useful in helping to tune this.
# time CPU load
# 0.001 13%
# 0.005 3%
# 0.01 1.5%
# 0.05 0.5%
used_time = clock() - t
if used_time > 10.0:
# print 'Sleep for 1 s' # dbg
time.sleep(1.0)
elif used_time > 0.1:
# Few GUI events coming in, so we can sleep longer
# print 'Sleep for 0.05 s' # dbg
time.sleep(0.05)
else:
# Many GUI events coming in, so sleep only very little
time.sleep(0.001)
except KeyboardInterrupt:
pass
59 changes: 59 additions & 0 deletions IPython/terminal/pt_inputhooks/gtk.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Code borrowed from python-prompt-toolkit examples
# https://github.com/jonathanslenders/python-prompt-toolkit/blob/77cdcfbc7f4b4c34a9d2f9a34d422d7152f16209/examples/inputhook.py

# Copyright (c) 2014, Jonathan Slenders
# All rights reserved.
#
# 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 {organization} 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 HOLDER 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.

"""
PyGTK input hook for prompt_toolkit.

Listens on the pipe prompt_toolkit sets up for a notification that it should
return control to the terminal event loop.
"""
from __future__ import absolute_import

import gtk, gobject

# Enable threading in GTK. (Otherwise, GTK will keep the GIL.)
gtk.gdk.threads_init()

def inputhook(context):
"""
When the eventloop of prompt-toolkit is idle, call this inputhook.

This will run the GTK main loop until the file descriptor
`context.fileno()` becomes ready.

:param context: An `InputHookContext` instance.
"""
def _main_quit(*a, **kw):
gtk.main_quit()
return False

gobject.io_add_watch(context.fileno(), gobject.IO_IN, _main_quit)
gtk.main()
12 changes: 12 additions & 0 deletions IPython/terminal/pt_inputhooks/gtk3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""prompt_toolkit input hook for GTK 3
"""

from gi.repository import Gtk, GLib

def _main_quit(*args, **kwargs):
Gtk.main_quit()
return False

def inputhook(context):
GLib.io_add_watch(context.fileno(), GLib.IO_IN, _main_quit)
Gtk.main()
Loading