##// END OF EJS Templates
Merge pull request #9360 from takluyver/i9359...
Merge pull request #9360 from takluyver/i9359 Add and use confirm_exit flag in prompt_toolkit shell

File last commit:

r22203:bad8a12c merge
r22203:bad8a12c merge
Show More
ptshell.py
321 lines | 11.7 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
Thomas Kluyver
Integrate colorama for coloured output on Windows
r21950
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
Thomas Kluyver
Add and use confirm_exit flag in prompt_toolkit shell...
r22186 from traitlets import Bool, CBool, Unicode, Dict
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
Thomas Kluyver
Only use our Enter handling when the default buffer is active...
r21923 from prompt_toolkit.enums import DEFAULT_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
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
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
Thomas Kluyver
Nicer default colours
r21918 from prompt_toolkit.styles import PygmentsStyle
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911
Thomas Kluyver
Config options for syntax higlighting style
r21929 from pygments.styles import get_style_by_name
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
Min RK
support term_title in pt shell...
r22124
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:
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
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 pt_cli = None
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"
)
Thomas Kluyver
Config options for syntax higlighting style
r21929 highlighting_style = Unicode('', config=True,
help="The name of a Pygments style to use for syntax highlighting"
)
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()
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
Thomas Kluyver
Config options for syntax higlighting style
r21929 style_overrides = {
Thomas Kluyver
Nicer default colours
r21918 Token.Prompt: '#009900',
Token.PromptNum: '#00ff00 bold',
Thomas Kluyver
Config options for syntax higlighting style
r21929 }
if self.highlighting_style:
style_cls = get_style_by_name(self.highlighting_style)
else:
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
Thomas Kluyver
Refine multiline behaviour
r21912 app = create_prompt_application(multiline=True,
Jonathan Slenders
Created IPythonPTLexer, which is a wrapper around BashLexer and PythonLexer. (When the input starts with an exclamation mark, use the BashLexer.)
r22197 lexer=IPythonPTLexer(),
Thomas Kluyver
Refine multiline behaviour
r21912 get_prompt_tokens=self.get_prompt_tokens,
Thomas Kluyver
Re-enable continuation prompts...
r22120 get_continuation_tokens=self.get_continuation_tokens,
Thomas Kluyver
Refine multiline behaviour
r21912 key_bindings_registry=kbmanager.registry,
Thomas Kluyver
Hook up command history and populate it from IPython's history DB
r21914 history=history,
Thomas Kluyver
Add completion support
r21916 completer=IPythonPTCompleter(self.Completer),
Thomas Kluyver
Enable history search with up arrow
r21924 enable_history_search=True,
Thomas Kluyver
Nicer default colours
r21918 style=style,
Thomas Kluyver
Config option to enable mouse support
r22055 mouse_support=self.mouse_support,
Jonathan Slenders
Specify reserve_space_for_menu in create_prompt_application.
r22196 reserve_space_for_menu=6,
Thomas Kluyver
Initial code for new InteractiveShell subclass using prompt_toolkit
r21911 )
Thomas Kluyver
Refine multiline behaviour
r21912
Thomas Kluyver
Write & borrow some inputhooks for prompt_toolkit
r21934 self.pt_cli = CommandLineInterface(app,
eventloop=create_eventloop(self.inputhook))
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
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
if __name__ == '__main__':
Thomas Kluyver
Rename PTInteractiveShell to TerminalInteractiveShell...
r22112 TerminalInteractiveShell.instance().interact()