##// END OF EJS Templates
Re enable multi line in PT-TerminalInteractiveShell...
Re enable multi line in PT-TerminalInteractiveShell Made it to false by mistake (mine) in #9423 Note that it "just" removed continuation prompt as "multiline" is just a hint for prompt_toolkit.

File last commit:

r22283:c69d93ae
r22283:c69d93ae
Show More
ptshell.py
451 lines | 16.6 KiB | text/x-python | PythonLexer
Thomas Kluyver
Python 2.7 support & highlighting in prompt_toolkit interface
r21930 """IPython terminal interface using prompt_toolkit in place of readline"""
from __future__ import print_function
Thomas Kluyver
Restore terminal magics and aliases...
r22128 import os
Thomas Kluyver
Integrate colorama for coloured output on Windows
r21950 import sys
Jonathan Slenders
ControlZ should suspend IPython to the background.
r22194 import signal
Jonathan Slenders
Bugfix: when doing \dot completion. Compose unicode character from completion.
r22227 import unicodedata
Thomas Kluyver
Restore option to auto-edit files on syntax error
r22204 from warnings import warn
Jonathan Slenders
Bugfix: when doing \dot completion. Compose unicode character from completion.
r22227 from wcwidth import wcwidth
Thomas Kluyver
Integrate colorama for coloured output on Windows
r21950
Thomas Kluyver
Restore option to auto-edit files on syntax error
r22204 from IPython.core.error import TryNext
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 from IPython.core.interactiveshell import InteractiveShell
Thomas Kluyver
Fix 'interactive' tests using pipes to a subprocess
r22107 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
Min RK
support term_title in pt shell...
r22124 from IPython.utils.terminal import toggle_set_term_title, set_term_title
from IPython.utils.process import abbrev_cwd
Matthias Bussonnier
Make "reserve_space_for_menu" configurable....
r22276 from traitlets import Bool, CBool, Unicode, Dict, Integer
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911
Thomas Kluyver
Add completion support
r21916 from prompt_toolkit.completion import Completer, Completion
Gil Forsyth
Have Ctrl-C in search mode clear query, if present...
r22241 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
Thomas Kluyver
Tab key inserts four spaces at the start of the line...
r22136 from prompt_toolkit.filters import HasFocus, HasSelection, Condition
Thomas Kluyver
Hook up command history and populate it from IPython's history DB
r21914 from prompt_toolkit.history import InMemoryHistory
Matthias Bussonnier
Make style and layout dynamic....
r22277 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
Thomas Kluyver
Refine multiline behaviour
r21912 from prompt_toolkit.interface import CommandLineInterface
from prompt_toolkit.key_binding.manager import KeyBindingManager
Thomas Kluyver
Add config option for vi mode
r21928 from prompt_toolkit.key_binding.vi_state import InputMode
from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
Thomas Kluyver
Refine multiline behaviour
r21912 from prompt_toolkit.keys import Keys
Jonathan Slenders
Created IPythonPTLexer, which is a wrapper around BashLexer and PythonLexer. (When the input starts with an exclamation mark, use the BashLexer.)
r22197 from prompt_toolkit.layout.lexers import Lexer
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 from prompt_toolkit.layout.lexers import PygmentsLexer
Matthias Bussonnier
Make style and layout dynamic....
r22277 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911
Matthias Bussonnier
Make style and layout dynamic....
r22277 from pygments.styles import get_style_by_name, get_all_styles
Jonathan Slenders
Created IPythonPTLexer, which is a wrapper around BashLexer and PythonLexer. (When the input starts with an exclamation mark, use the BashLexer.)
r22197 from pygments.lexers import Python3Lexer, BashLexer, PythonLexer
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 from pygments.token import Token
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934 from .pt_inputhooks import get_inputhook_func
Thomas Kluyver
Restore terminal magics and aliases...
r22128 from .interactiveshell import get_default_editor, TerminalMagics
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911
Thomas Kluyver
Add completion support
r21916 class IPythonPTCompleter(Completer):
"""Adaptor to provide IPython completions to prompt_toolkit"""
def __init__(self, ipy_completer):
self.ipy_completer = ipy_completer
def get_completions(self, document, complete_event):
Thomas Kluyver
Don't try to complete on an empty line
r21922 if not document.current_line.strip():
return
Thomas Kluyver
Add completion support
r21916 used, matches = self.ipy_completer.complete(
line_buffer=document.current_line,
cursor_pos=document.cursor_position_col
)
start_pos = -len(used)
for m in matches:
Jonathan Slenders
Bugfix: when doing \dot completion. Compose unicode character from completion.
r22227 m = unicodedata.normalize('NFC', m)
# When the first character of the completion has a zero length,
# then it's probably a decomposed unicode character. E.g. caused by
# the "\dot" completion. Try to compose again with the previous
# character.
if wcwidth(m[0]) == 0:
if document.cursor_position + start_pos > 0:
char_before = document.text[document.cursor_position + start_pos - 1]
m = unicodedata.normalize('NFC', char_before + m)
# Yield the modified completion instead, if this worked.
if wcwidth(m[0:1]) == 1:
yield Completion(m, start_position=start_pos - 1)
continue
Thomas Kluyver
Add completion support
r21916 yield Completion(m, start_position=start_pos)
Jonathan Slenders
Created IPythonPTLexer, which is a wrapper around BashLexer and PythonLexer. (When the input starts with an exclamation mark, use the BashLexer.)
r22197
class IPythonPTLexer(Lexer):
"""
Wrapper around PythonLexer and BashLexer.
"""
def __init__(self):
self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer)
self.shell_lexer = PygmentsLexer(BashLexer)
def lex_document(self, cli, document):
if document.text.startswith('!'):
return self.shell_lexer.lex_document(cli, document)
else:
return self.python_lexer.lex_document(cli, document)
Thomas Kluyver
Rename PTInteractiveShell to TerminalInteractiveShell...
r22112 class TerminalInteractiveShell(InteractiveShell):
Thomas Kluyver
Turn out prompt & traceback colours on when using prompt_toolkit
r21920 colors_force = True
Matthias Bussonnier
'fix help string'
r22280 space_for_menu = Integer(6, config=True, help='Number of line at the bottom of the screen '
'to reserve for the completion menu')
Matthias Bussonnier
Make "reserve_space_for_menu" configurable....
r22276
Matthias Bussonnier
Make style and layout dynamic....
r22277 def _space_for_menu_changed(self, old, new):
Matthias Bussonnier
Rename relayout to _update_layout
r22279 self._update_layout()
Matthias Bussonnier
Make style and layout dynamic....
r22277
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 pt_cli = None
Thomas Kluyver
Restore option to auto-edit files on syntax error
r22204 autoedit_syntax = CBool(False, config=True,
help="auto editing of files with syntax errors.")
Thomas Kluyver
Add and use confirm_exit flag in prompt_toolkit shell...
r22186 confirm_exit = CBool(True, config=True,
help="""
Set to confirm when you try to exit IPython with an EOF (Control-D
in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
you can force a direct exit without any confirmation.""",
)
Thomas Kluyver
Add config option for vi mode
r21928 vi_mode = Bool(False, config=True,
help="Use vi style keybindings at the prompt",
)
Thomas Kluyver
Config option to enable mouse support
r22055 mouse_support = Bool(False, config=True,
help="Enable mouse support in the prompt"
)
Matthias Bussonnier
Make style and layout dynamic....
r22277 highlighting_style = Unicode('default', config=True,
help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
Thomas Kluyver
Config options for syntax higlighting style
r21929 )
Matthias Bussonnier
Make style and layout dynamic....
r22277 def _highlighting_style_changed(self, old, new):
self._style = self._make_style_from_name(self.highlighting_style)
Thomas Kluyver
Config options for syntax higlighting style
r21929 highlighting_style_overrides = Dict(config=True,
help="Override highlighting format for specific tokens"
)
Thomas Kluyver
Fix %edit - editor attribute was missing
r21969 editor = Unicode(get_default_editor(), config=True,
help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
)
Min RK
support term_title in pt shell...
r22124
term_title = Bool(True, config=True,
help="Automatically set the terminal title"
)
def _term_title_changed(self, name, new_value):
self.init_term_title()
def init_term_title(self):
# Enable or disable the terminal title.
if self.term_title:
toggle_set_term_title(True)
set_term_title('IPython: ' + abbrev_cwd())
else:
toggle_set_term_title(False)
Thomas Kluyver
Fix %edit - editor attribute was missing
r21969
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 def get_prompt_tokens(self, cli):
return [
(Token.Prompt, 'In ['),
Thomas Kluyver
Nicer default colours
r21918 (Token.PromptNum, str(self.execution_count)),
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 (Token.Prompt, ']: '),
]
Thomas Kluyver
Add continuation prompts
r21933 def get_continuation_tokens(self, cli, width):
return [
Matthias Bussonnier
'Restore `...:` as continuation prompt '
r22122 (Token.Prompt, (' ' * (width - 5)) + '...: '),
Thomas Kluyver
Add continuation prompts
r21933 ]
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 def init_prompt_toolkit_cli(self):
Thomas Kluyver
Use simplified prompt in test_embed...
r22132 if ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or not sys.stdin.isatty():
# Fall back to plain non-interactive output for tests.
# This is very limited, and only accepts a single line.
Thomas Kluyver
Fix 'interactive' tests using pipes to a subprocess
r22107 def prompt():
return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
self.prompt_for_code = prompt
return
Thomas Kluyver
Add config option for vi mode
r21928 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
Thomas Kluyver
Only use our Enter handling when the default buffer is active...
r21923 # Ctrl+J == Enter, seemingly
@kbmanager.registry.add_binding(Keys.ControlJ,
Thomas Kluyver
Add config option for vi mode
r21928 filter=(HasFocus(DEFAULT_BUFFER)
& ~HasSelection()
& insert_mode
))
Thomas Kluyver
Refine multiline behaviour
r21912 def _(event):
b = event.current_buffer
Thomas Kluyver
Make behaviour more natural with blank lines at the end of input
r22012 d = b.document
if not (d.on_last_line or d.cursor_position_row >= d.line_count
- d.empty_line_count_at_the_end()):
Thomas Kluyver
Refine multiline behaviour
r21912 b.newline()
return
Thomas Kluyver
Make behaviour more natural with blank lines at the end of input
r22012 status, indent = self.input_splitter.check_complete(d.text)
Thomas Kluyver
Refine multiline behaviour
r21912
if (status != 'incomplete') and b.accept_action.is_returnable:
b.accept_action.validate_and_handle(event.cli, b)
else:
Thomas Kluyver
Hook up command history and populate it from IPython's history DB
r21914 b.insert_text('\n' + (' ' * (indent or 0)))
Thomas Kluyver
Only handle Ctrl-C when in main input buffer...
r22162 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
Thomas Kluyver
Do sensible things on Ctrl-C and Ctrl-D
r21915 def _(event):
event.current_buffer.reset()
Gil Forsyth
Have Ctrl-C in search mode clear query, if present...
r22241 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
def _(event):
if event.current_buffer.document.text:
event.current_buffer.reset()
else:
event.cli.push_focus(DEFAULT_BUFFER)
Jonathan Slenders
ControlZ should suspend IPython to the background.
r22194 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
@kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
def _(event):
event.cli.suspend_to_background()
Thomas Kluyver
Tab key inserts four spaces at the start of the line...
r22136 @Condition
def cursor_in_leading_ws(cli):
before = cli.application.buffer.document.current_line_before_cursor
return (not before) or before.isspace()
# Ctrl+I == Tab
@kbmanager.registry.add_binding(Keys.ControlI,
filter=(HasFocus(DEFAULT_BUFFER)
& ~HasSelection()
& insert_mode
& cursor_in_leading_ws
))
def _(event):
event.current_buffer.insert_text(' ' * 4)
Thomas Kluyver
Hook up command history and populate it from IPython's history DB
r21914 # Pre-populate history from IPython's history database
history = InMemoryHistory()
last_cell = u""
for _, _, cell in self.history_manager.get_tail(self.history_load_length,
include_latest=True):
# Ignore blank lines and consecutive duplicates
cell = cell.rstrip()
if cell and (cell != last_cell):
history.append(cell)
Thomas Kluyver
Refine multiline behaviour
r21912
Matthias Bussonnier
Make style and layout dynamic....
r22277 self._style = self._make_style_from_name(self.highlighting_style)
style = DynamicStyle(lambda: self._style)
self._app = create_prompt_application(
key_bindings_registry=kbmanager.registry,
history=history,
completer=IPythonPTCompleter(self.Completer),
enable_history_search=True,
style=style,
mouse_support=self.mouse_support,
**self._layout_options()
)
self.pt_cli = CommandLineInterface(self._app,
eventloop=create_eventloop(self.inputhook))
def _make_style_from_name(self, name):
"""
Small wrapper that make an IPython compatible style from a style name
We need that to add style for prompt ... etc.
"""
style_cls = get_style_by_name(name)
Thomas Kluyver
Config options for syntax higlighting style
r21929 style_overrides = {
Matthias Bussonnier
Pick default prompt colors form already existing Tokens if possible....
r22278 Token.Prompt: style_cls.styles.get( Token.Keyword, '#009900'),
Token.PromptNum: style_cls.styles.get( Token.Literal.Number, '#00ff00 bold')
Thomas Kluyver
Config options for syntax higlighting style
r21929 }
Matthias Bussonnier
Make style and layout dynamic....
r22277 if name is 'default':
Thomas Kluyver
Config options for syntax higlighting style
r21929 style_cls = get_style_by_name('default')
Thomas Kluyver
More tweaks to highlighting colours
r21939 # The default theme needs to be visible on both a dark background
# and a light background, because we can't tell what the terminal
# looks like. These tweaks to the default theme help with that.
Thomas Kluyver
Config options for syntax higlighting style
r21929 style_overrides.update({
Token.Number: '#007700',
Token.Operator: 'noinherit',
Token.String: '#BB6622',
Thomas Kluyver
More tweaks to highlighting colours
r21939 Token.Name.Function: '#2080D0',
Token.Name.Class: 'bold #2080D0',
Token.Name.Namespace: 'bold #2080D0',
Thomas Kluyver
Config options for syntax higlighting style
r21929 })
style_overrides.update(self.highlighting_style_overrides)
style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
style_dict=style_overrides)
Thomas Kluyver
Nicer default colours
r21918
Matthias Bussonnier
Make style and layout dynamic....
r22277 return style
def _layout_options(self):
"""
Return the current layout option for the current Terminal InteractiveShell
"""
return {
'lexer':IPythonPTLexer(),
'reserve_space_for_menu':self.space_for_menu,
'get_prompt_tokens':self.get_prompt_tokens,
'get_continuation_tokens':self.get_continuation_tokens,
Matthias Bussonnier
Re enable multi line in PT-TerminalInteractiveShell...
r22283 'multiline':True,
Matthias Bussonnier
Make style and layout dynamic....
r22277 }
Matthias Bussonnier
Rename relayout to _update_layout
r22279 def _update_layout(self):
Matthias Bussonnier
Make style and layout dynamic....
r22277 """
Ask for a re computation of the application layout, if for example ,
some configuration options have changed.
"""
self._app.layout = create_prompt_layout(**self._layout_options())
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911
Thomas Kluyver
Fix 'interactive' tests using pipes to a subprocess
r22107 def prompt_for_code(self):
document = self.pt_cli.run(pre_run=self.pre_prompt)
return document.text
Thomas Kluyver
Integrate colorama for coloured output on Windows
r21950 def init_io(self):
if sys.platform not in {'win32', 'cli'}:
return
import colorama
colorama.init()
# For some reason we make these wrappers around stdout/stderr.
# For now, we need to reset them so all output gets coloured.
# https://github.com/ipython/ipython/issues/8669
from IPython.utils import io
io.stdout = io.IOStream(sys.stdout)
io.stderr = io.IOStream(sys.stderr)
Thomas Kluyver
Restore terminal magics and aliases...
r22128 def init_magics(self):
super(TerminalInteractiveShell, self).init_magics()
self.register_magics(TerminalMagics)
def init_alias(self):
# The parent class defines aliases that can be safely used with any
# frontend.
super(TerminalInteractiveShell, self).init_alias()
# Now define aliases that only make sense on the terminal, because they
# need direct access to the console in a way that we can't emulate in
# GUI or web frontend
if os.name == 'posix':
for cmd in ['clear', 'more', 'less', 'man']:
self.alias_manager.soft_define_alias(cmd, cmd)
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 def __init__(self, *args, **kwargs):
Thomas Kluyver
Rename PTInteractiveShell to TerminalInteractiveShell...
r22112 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 self.init_prompt_toolkit_cli()
Min RK
support term_title in pt shell...
r22124 self.init_term_title()
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 self.keep_running = True
def ask_exit(self):
self.keep_running = False
Thomas Kluyver
Implement pre-filling prompt from set_next_input()
r21948 rl_next_input = None
def pre_prompt(self):
if self.rl_next_input:
Thomas Kluyver
Fix set_next_input on Python 2
r22011 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
Thomas Kluyver
Implement pre-filling prompt from set_next_input()
r21948 self.rl_next_input = None
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 def interact(self):
while self.keep_running:
Thomas Kluyver
Move printing blank line to before each input prompt
r21921 print(self.separate_in, end='')
Thomas Kluyver
Implement pre-filling prompt from set_next_input()
r21948
Thomas Kluyver
Do sensible things on Ctrl-C and Ctrl-D
r21915 try:
Thomas Kluyver
Fix 'interactive' tests using pipes to a subprocess
r22107 code = self.prompt_for_code()
Thomas Kluyver
Do sensible things on Ctrl-C and Ctrl-D
r21915 except EOFError:
Thomas Kluyver
Add and use confirm_exit flag in prompt_toolkit shell...
r22186 if (not self.confirm_exit) \
or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
Thomas Kluyver
Do sensible things on Ctrl-C and Ctrl-D
r21915 self.ask_exit()
else:
Thomas Kluyver
Fix 'interactive' tests using pipes to a subprocess
r22107 if code:
self.run_cell(code, store_history=True)
Thomas Kluyver
Restore option to auto-edit files on syntax error
r22204 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
self.edit_syntax_error()
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911
Thomas Kluyver
Add a mainloop() method to mimic existing shell API
r22054 def mainloop(self):
# An extra layer of protection in case someone mashing Ctrl-C breaks
# out of our internal code.
while True:
try:
self.interact()
break
except KeyboardInterrupt:
print("\nKeyboardInterrupt escaped interact()\n")
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934 _inputhook = None
def inputhook(self, context):
if self._inputhook is not None:
self._inputhook(context)
def enable_gui(self, gui=None):
if gui:
self._inputhook = get_inputhook_func(gui)
else:
self._inputhook = None
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911
Thomas Kluyver
Restore option to auto-edit files on syntax error
r22204 # Methods to support auto-editing of SyntaxErrors:
def edit_syntax_error(self):
"""The bottom half of the syntax error handler called in the main loop.
Loop until syntax error is fixed or user cancels.
"""
while self.SyntaxTB.last_syntax_error:
# copy and clear last_syntax_error
err = self.SyntaxTB.clear_err_state()
if not self._should_recompile(err):
return
try:
# may set last_syntax_error again if a SyntaxError is raised
self.safe_execfile(err.filename, self.user_ns)
except:
self.showtraceback()
else:
try:
Thomas Kluyver
Simplify code with a context manager
r22205 with open(err.filename) as f:
Thomas Kluyver
Restore option to auto-edit files on syntax error
r22204 # This should be inside a display_trap block and I
# think it is.
sys.displayhook(f.read())
except:
self.showtraceback()
def _should_recompile(self, e):
"""Utility routine for edit_syntax_error"""
if e.filename in ('<ipython console>', '<input>', '<string>',
'<console>', '<BackgroundJob compilation>',
None):
return False
try:
if (self.autoedit_syntax and
not self.ask_yes_no(
'Return to editor to correct syntax error? '
'[Y/n] ', 'y')):
return False
except EOFError:
return False
def int0(x):
try:
return int(x)
except TypeError:
return 0
# always pass integer line and offset values to editor hook
try:
self.hooks.fix_error_editor(e.filename,
int0(e.lineno), int0(e.offset),
e.msg)
except TryNext:
warn('Could not open editor')
return False
return True
Thomas Kluyver
Use system_raw in ptshell...
r22239 # Run !system commands directly, not through pipes, so terminal programs
# work correctly.
system = InteractiveShell.system_raw
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 if __name__ == '__main__':
Thomas Kluyver
Rename PTInteractiveShell to TerminalInteractiveShell...
r22112 TerminalInteractiveShell.instance().interact()