##// END OF EJS Templates
Merge pull request #9848 from memeplex/highclass...
Matthias Bussonnier -
r22825:c24d7bb1 merge
parent child Browse files
Show More
@@ -1,488 +1,493 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import warnings
6 import warnings
7 from warnings import warn
7 from warnings import warn
8
8
9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
10 from IPython.utils import io
10 from IPython.utils import io
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input, string_types
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union
15
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
16 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
17 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
18 from prompt_toolkit.history import InMemoryHistory
18 from prompt_toolkit.history import InMemoryHistory
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
20 from prompt_toolkit.interface import CommandLineInterface
20 from prompt_toolkit.interface import CommandLineInterface
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
23 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
24
24
25 from pygments.styles import get_style_by_name, get_all_styles
25 from pygments.styles import get_style_by_name, get_all_styles
26 from pygments.style import Style
26 from pygments.token import Token
27 from pygments.token import Token
27
28
28 from .debugger import TerminalPdb, Pdb
29 from .debugger import TerminalPdb, Pdb
29 from .magics import TerminalMagics
30 from .magics import TerminalMagics
30 from .pt_inputhooks import get_inputhook_func
31 from .pt_inputhooks import get_inputhook_func
31 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
32 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 from .shortcuts import register_ipython_shortcuts
34 from .shortcuts import register_ipython_shortcuts
34
35
35 DISPLAY_BANNER_DEPRECATED = object()
36 DISPLAY_BANNER_DEPRECATED = object()
36
37
37
38
38 from pygments.style import Style
39 from pygments.style import Style
39
40
40 class _NoStyle(Style): pass
41 class _NoStyle(Style): pass
41
42
42
43
43
44
44 _style_overrides_light_bg = {
45 _style_overrides_light_bg = {
45 Token.Prompt: '#0000ff',
46 Token.Prompt: '#0000ff',
46 Token.PromptNum: '#0000ee bold',
47 Token.PromptNum: '#0000ee bold',
47 Token.OutPrompt: '#cc0000',
48 Token.OutPrompt: '#cc0000',
48 Token.OutPromptNum: '#bb0000 bold',
49 Token.OutPromptNum: '#bb0000 bold',
49 }
50 }
50
51
51 _style_overrides_linux = {
52 _style_overrides_linux = {
52 Token.Prompt: '#00cc00',
53 Token.Prompt: '#00cc00',
53 Token.PromptNum: '#00bb00 bold',
54 Token.PromptNum: '#00bb00 bold',
54 Token.OutPrompt: '#cc0000',
55 Token.OutPrompt: '#cc0000',
55 Token.OutPromptNum: '#bb0000 bold',
56 Token.OutPromptNum: '#bb0000 bold',
56 }
57 }
57
58
58
59
59
60
60 def get_default_editor():
61 def get_default_editor():
61 try:
62 try:
62 ed = os.environ['EDITOR']
63 ed = os.environ['EDITOR']
63 if not PY3:
64 if not PY3:
64 ed = ed.decode()
65 ed = ed.decode()
65 return ed
66 return ed
66 except KeyError:
67 except KeyError:
67 pass
68 pass
68 except UnicodeError:
69 except UnicodeError:
69 warn("$EDITOR environment variable is not pure ASCII. Using platform "
70 warn("$EDITOR environment variable is not pure ASCII. Using platform "
70 "default editor.")
71 "default editor.")
71
72
72 if os.name == 'posix':
73 if os.name == 'posix':
73 return 'vi' # the only one guaranteed to be there!
74 return 'vi' # the only one guaranteed to be there!
74 else:
75 else:
75 return 'notepad' # same in Windows!
76 return 'notepad' # same in Windows!
76
77
77 # conservatively check for tty
78 # conservatively check for tty
78 # overridden streams can result in things like:
79 # overridden streams can result in things like:
79 # - sys.stdin = None
80 # - sys.stdin = None
80 # - no isatty method
81 # - no isatty method
81 for _name in ('stdin', 'stdout', 'stderr'):
82 for _name in ('stdin', 'stdout', 'stderr'):
82 _stream = getattr(sys, _name)
83 _stream = getattr(sys, _name)
83 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
84 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
84 _is_tty = False
85 _is_tty = False
85 break
86 break
86 else:
87 else:
87 _is_tty = True
88 _is_tty = True
88
89
89
90
90 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
91
92
92 class TerminalInteractiveShell(InteractiveShell):
93 class TerminalInteractiveShell(InteractiveShell):
93 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
94 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
94 'to reserve for the completion menu'
95 'to reserve for the completion menu'
95 ).tag(config=True)
96 ).tag(config=True)
96
97
97 def _space_for_menu_changed(self, old, new):
98 def _space_for_menu_changed(self, old, new):
98 self._update_layout()
99 self._update_layout()
99
100
100 pt_cli = None
101 pt_cli = None
101 debugger_history = None
102 debugger_history = None
102 _pt_app = None
103 _pt_app = None
103
104
104 simple_prompt = Bool(_use_simple_prompt,
105 simple_prompt = Bool(_use_simple_prompt,
105 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
106 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
106
107
107 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
108 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
108 IPython own testing machinery, and emacs inferior-shell integration through elpy.
109 IPython own testing machinery, and emacs inferior-shell integration through elpy.
109
110
110 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
111 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
111 environment variable is set, or the current terminal is not a tty.
112 environment variable is set, or the current terminal is not a tty.
112
113
113 """
114 """
114 ).tag(config=True)
115 ).tag(config=True)
115
116
116 @property
117 @property
117 def debugger_cls(self):
118 def debugger_cls(self):
118 return Pdb if self.simple_prompt else TerminalPdb
119 return Pdb if self.simple_prompt else TerminalPdb
119
120
120 confirm_exit = Bool(True,
121 confirm_exit = Bool(True,
121 help="""
122 help="""
122 Set to confirm when you try to exit IPython with an EOF (Control-D
123 Set to confirm when you try to exit IPython with an EOF (Control-D
123 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
124 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
124 you can force a direct exit without any confirmation.""",
125 you can force a direct exit without any confirmation.""",
125 ).tag(config=True)
126 ).tag(config=True)
126
127
127 editing_mode = Unicode('emacs',
128 editing_mode = Unicode('emacs',
128 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
129 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
129 ).tag(config=True)
130 ).tag(config=True)
130
131
131 mouse_support = Bool(False,
132 mouse_support = Bool(False,
132 help="Enable mouse support in the prompt"
133 help="Enable mouse support in the prompt"
133 ).tag(config=True)
134 ).tag(config=True)
134
135
135 highlighting_style = Unicode('legacy',
136 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
136 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
137 help="""The name or class of a Pygments style to use for syntax
138 highlighting: \n %s""" % ', '.join(get_all_styles())
137 ).tag(config=True)
139 ).tag(config=True)
138
140
139
141
140 @observe('highlighting_style')
142 @observe('highlighting_style')
141 @observe('colors')
143 @observe('colors')
142 def _highlighting_style_changed(self, change):
144 def _highlighting_style_changed(self, change):
143 self.refresh_style()
145 self.refresh_style()
144
146
145 def refresh_style(self):
147 def refresh_style(self):
146 self._style = self._make_style_from_name(self.highlighting_style)
148 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
147
149
148
150
149 highlighting_style_overrides = Dict(
151 highlighting_style_overrides = Dict(
150 help="Override highlighting format for specific tokens"
152 help="Override highlighting format for specific tokens"
151 ).tag(config=True)
153 ).tag(config=True)
152
154
153 true_color = Bool(False,
155 true_color = Bool(False,
154 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
156 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
155 "If your terminal supports true color, the following command "
157 "If your terminal supports true color, the following command "
156 "should print 'TRUECOLOR' in orange: "
158 "should print 'TRUECOLOR' in orange: "
157 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
159 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
158 ).tag(config=True)
160 ).tag(config=True)
159
161
160 editor = Unicode(get_default_editor(),
162 editor = Unicode(get_default_editor(),
161 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
163 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
162 ).tag(config=True)
164 ).tag(config=True)
163
165
164 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
166 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
165
167
166 prompts = Instance(Prompts)
168 prompts = Instance(Prompts)
167
169
168 @default('prompts')
170 @default('prompts')
169 def _prompts_default(self):
171 def _prompts_default(self):
170 return self.prompts_class(self)
172 return self.prompts_class(self)
171
173
172 @observe('prompts')
174 @observe('prompts')
173 def _(self, change):
175 def _(self, change):
174 self._update_layout()
176 self._update_layout()
175
177
176 @default('displayhook_class')
178 @default('displayhook_class')
177 def _displayhook_class_default(self):
179 def _displayhook_class_default(self):
178 return RichPromptDisplayHook
180 return RichPromptDisplayHook
179
181
180 term_title = Bool(True,
182 term_title = Bool(True,
181 help="Automatically set the terminal title"
183 help="Automatically set the terminal title"
182 ).tag(config=True)
184 ).tag(config=True)
183
185
184 display_completions = Enum(('column', 'multicolumn','readlinelike'),
186 display_completions = Enum(('column', 'multicolumn','readlinelike'),
185 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
187 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
186 "'readlinelike'. These options are for `prompt_toolkit`, see "
188 "'readlinelike'. These options are for `prompt_toolkit`, see "
187 "`prompt_toolkit` documentation for more information."
189 "`prompt_toolkit` documentation for more information."
188 ),
190 ),
189 default_value='multicolumn').tag(config=True)
191 default_value='multicolumn').tag(config=True)
190
192
191 highlight_matching_brackets = Bool(True,
193 highlight_matching_brackets = Bool(True,
192 help="Highlight matching brackets .",
194 help="Highlight matching brackets .",
193 ).tag(config=True)
195 ).tag(config=True)
194
196
195 @observe('term_title')
197 @observe('term_title')
196 def init_term_title(self, change=None):
198 def init_term_title(self, change=None):
197 # Enable or disable the terminal title.
199 # Enable or disable the terminal title.
198 if self.term_title:
200 if self.term_title:
199 toggle_set_term_title(True)
201 toggle_set_term_title(True)
200 set_term_title('IPython: ' + abbrev_cwd())
202 set_term_title('IPython: ' + abbrev_cwd())
201 else:
203 else:
202 toggle_set_term_title(False)
204 toggle_set_term_title(False)
203
205
204 def init_display_formatter(self):
206 def init_display_formatter(self):
205 super(TerminalInteractiveShell, self).init_display_formatter()
207 super(TerminalInteractiveShell, self).init_display_formatter()
206 # terminal only supports plain text
208 # terminal only supports plain text
207 self.display_formatter.active_types = ['text/plain']
209 self.display_formatter.active_types = ['text/plain']
208
210
209 def init_prompt_toolkit_cli(self):
211 def init_prompt_toolkit_cli(self):
210 if self.simple_prompt:
212 if self.simple_prompt:
211 # Fall back to plain non-interactive output for tests.
213 # Fall back to plain non-interactive output for tests.
212 # This is very limited, and only accepts a single line.
214 # This is very limited, and only accepts a single line.
213 def prompt():
215 def prompt():
214 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
216 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
215 self.prompt_for_code = prompt
217 self.prompt_for_code = prompt
216 return
218 return
217
219
218 # Set up keyboard shortcuts
220 # Set up keyboard shortcuts
219 kbmanager = KeyBindingManager.for_prompt()
221 kbmanager = KeyBindingManager.for_prompt()
220 register_ipython_shortcuts(kbmanager.registry, self)
222 register_ipython_shortcuts(kbmanager.registry, self)
221
223
222 # Pre-populate history from IPython's history database
224 # Pre-populate history from IPython's history database
223 history = InMemoryHistory()
225 history = InMemoryHistory()
224 last_cell = u""
226 last_cell = u""
225 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
227 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
226 include_latest=True):
228 include_latest=True):
227 # Ignore blank lines and consecutive duplicates
229 # Ignore blank lines and consecutive duplicates
228 cell = cell.rstrip()
230 cell = cell.rstrip()
229 if cell and (cell != last_cell):
231 if cell and (cell != last_cell):
230 history.append(cell)
232 history.append(cell)
231
233
232 self._style = self._make_style_from_name(self.highlighting_style)
234 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
233 style = DynamicStyle(lambda: self._style)
235 style = DynamicStyle(lambda: self._style)
234
236
235 editing_mode = getattr(EditingMode, self.editing_mode.upper())
237 editing_mode = getattr(EditingMode, self.editing_mode.upper())
236
238
237 self._pt_app = create_prompt_application(
239 self._pt_app = create_prompt_application(
238 editing_mode=editing_mode,
240 editing_mode=editing_mode,
239 key_bindings_registry=kbmanager.registry,
241 key_bindings_registry=kbmanager.registry,
240 history=history,
242 history=history,
241 completer=IPythonPTCompleter(shell=self),
243 completer=IPythonPTCompleter(shell=self),
242 enable_history_search=True,
244 enable_history_search=True,
243 style=style,
245 style=style,
244 mouse_support=self.mouse_support,
246 mouse_support=self.mouse_support,
245 **self._layout_options()
247 **self._layout_options()
246 )
248 )
247 self._eventloop = create_eventloop(self.inputhook)
249 self._eventloop = create_eventloop(self.inputhook)
248 self.pt_cli = CommandLineInterface(
250 self.pt_cli = CommandLineInterface(
249 self._pt_app, eventloop=self._eventloop,
251 self._pt_app, eventloop=self._eventloop,
250 output=create_output(true_color=self.true_color))
252 output=create_output(true_color=self.true_color))
251
253
252 def _make_style_from_name(self, name):
254 def _make_style_from_name_or_cls(self, name_or_cls):
253 """
255 """
254 Small wrapper that make an IPython compatible style from a style name
256 Small wrapper that make an IPython compatible style from a style name
255
257
256 We need that to add style for prompt ... etc.
258 We need that to add style for prompt ... etc.
257 """
259 """
258 style_overrides = {}
260 style_overrides = {}
259 if name == 'legacy':
261 if name_or_cls == 'legacy':
260 legacy = self.colors.lower()
262 legacy = self.colors.lower()
261 if legacy == 'linux':
263 if legacy == 'linux':
262 style_cls = get_style_by_name('monokai')
264 style_cls = get_style_by_name('monokai')
263 style_overrides = _style_overrides_linux
265 style_overrides = _style_overrides_linux
264 elif legacy == 'lightbg':
266 elif legacy == 'lightbg':
265 style_overrides = _style_overrides_light_bg
267 style_overrides = _style_overrides_light_bg
266 style_cls = get_style_by_name('pastie')
268 style_cls = get_style_by_name('pastie')
267 elif legacy == 'neutral':
269 elif legacy == 'neutral':
268 # The default theme needs to be visible on both a dark background
270 # The default theme needs to be visible on both a dark background
269 # and a light background, because we can't tell what the terminal
271 # and a light background, because we can't tell what the terminal
270 # looks like. These tweaks to the default theme help with that.
272 # looks like. These tweaks to the default theme help with that.
271 style_cls = get_style_by_name('default')
273 style_cls = get_style_by_name('default')
272 style_overrides.update({
274 style_overrides.update({
273 Token.Number: '#007700',
275 Token.Number: '#007700',
274 Token.Operator: 'noinherit',
276 Token.Operator: 'noinherit',
275 Token.String: '#BB6622',
277 Token.String: '#BB6622',
276 Token.Name.Function: '#2080D0',
278 Token.Name.Function: '#2080D0',
277 Token.Name.Class: 'bold #2080D0',
279 Token.Name.Class: 'bold #2080D0',
278 Token.Name.Namespace: 'bold #2080D0',
280 Token.Name.Namespace: 'bold #2080D0',
279 Token.Prompt: '#009900',
281 Token.Prompt: '#009900',
280 Token.PromptNum: '#00ff00 bold',
282 Token.PromptNum: '#00ff00 bold',
281 Token.OutPrompt: '#990000',
283 Token.OutPrompt: '#990000',
282 Token.OutPromptNum: '#ff0000 bold',
284 Token.OutPromptNum: '#ff0000 bold',
283 })
285 })
284 elif legacy =='nocolor':
286 elif legacy =='nocolor':
285 style_cls=_NoStyle
287 style_cls=_NoStyle
286 style_overrides = {}
288 style_overrides = {}
287 else :
289 else :
288 raise ValueError('Got unknown colors: ', legacy)
290 raise ValueError('Got unknown colors: ', legacy)
289 else :
291 else :
290 style_cls = get_style_by_name(name)
292 if isinstance(name_or_cls, string_types):
293 style_cls = get_style_by_name(name_or_cls)
294 else:
295 style_cls = name_or_cls
291 style_overrides = {
296 style_overrides = {
292 Token.Prompt: '#009900',
297 Token.Prompt: '#009900',
293 Token.PromptNum: '#00ff00 bold',
298 Token.PromptNum: '#00ff00 bold',
294 Token.OutPrompt: '#990000',
299 Token.OutPrompt: '#990000',
295 Token.OutPromptNum: '#ff0000 bold',
300 Token.OutPromptNum: '#ff0000 bold',
296 }
301 }
297 style_overrides.update(self.highlighting_style_overrides)
302 style_overrides.update(self.highlighting_style_overrides)
298 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
303 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
299 style_dict=style_overrides)
304 style_dict=style_overrides)
300
305
301 return style
306 return style
302
307
303 def _layout_options(self):
308 def _layout_options(self):
304 """
309 """
305 Return the current layout option for the current Terminal InteractiveShell
310 Return the current layout option for the current Terminal InteractiveShell
306 """
311 """
307 return {
312 return {
308 'lexer':IPythonPTLexer(),
313 'lexer':IPythonPTLexer(),
309 'reserve_space_for_menu':self.space_for_menu,
314 'reserve_space_for_menu':self.space_for_menu,
310 'get_prompt_tokens':self.prompts.in_prompt_tokens,
315 'get_prompt_tokens':self.prompts.in_prompt_tokens,
311 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
316 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
312 'multiline':True,
317 'multiline':True,
313 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
318 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
314
319
315 # Highlight matching brackets, but only when this setting is
320 # Highlight matching brackets, but only when this setting is
316 # enabled, and only when the DEFAULT_BUFFER has the focus.
321 # enabled, and only when the DEFAULT_BUFFER has the focus.
317 'extra_input_processors': [ConditionalProcessor(
322 'extra_input_processors': [ConditionalProcessor(
318 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
323 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
319 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
324 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
320 Condition(lambda cli: self.highlight_matching_brackets))],
325 Condition(lambda cli: self.highlight_matching_brackets))],
321 }
326 }
322
327
323 def _update_layout(self):
328 def _update_layout(self):
324 """
329 """
325 Ask for a re computation of the application layout, if for example ,
330 Ask for a re computation of the application layout, if for example ,
326 some configuration options have changed.
331 some configuration options have changed.
327 """
332 """
328 if self._pt_app:
333 if self._pt_app:
329 self._pt_app.layout = create_prompt_layout(**self._layout_options())
334 self._pt_app.layout = create_prompt_layout(**self._layout_options())
330
335
331 def prompt_for_code(self):
336 def prompt_for_code(self):
332 document = self.pt_cli.run(
337 document = self.pt_cli.run(
333 pre_run=self.pre_prompt, reset_current_buffer=True)
338 pre_run=self.pre_prompt, reset_current_buffer=True)
334 return document.text
339 return document.text
335
340
336 def enable_win_unicode_console(self):
341 def enable_win_unicode_console(self):
337 import win_unicode_console
342 import win_unicode_console
338
343
339 if PY3:
344 if PY3:
340 win_unicode_console.enable()
345 win_unicode_console.enable()
341 else:
346 else:
342 # https://github.com/ipython/ipython/issues/9768
347 # https://github.com/ipython/ipython/issues/9768
343 from win_unicode_console.streams import (TextStreamWrapper,
348 from win_unicode_console.streams import (TextStreamWrapper,
344 stdout_text_transcoded, stderr_text_transcoded)
349 stdout_text_transcoded, stderr_text_transcoded)
345
350
346 class LenientStrStreamWrapper(TextStreamWrapper):
351 class LenientStrStreamWrapper(TextStreamWrapper):
347 def write(self, s):
352 def write(self, s):
348 if isinstance(s, bytes):
353 if isinstance(s, bytes):
349 s = s.decode(self.encoding, 'replace')
354 s = s.decode(self.encoding, 'replace')
350
355
351 self.base.write(s)
356 self.base.write(s)
352
357
353 stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded)
358 stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded)
354 stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded)
359 stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded)
355
360
356 win_unicode_console.enable(stdout=stdout_text_str,
361 win_unicode_console.enable(stdout=stdout_text_str,
357 stderr=stderr_text_str)
362 stderr=stderr_text_str)
358
363
359 def init_io(self):
364 def init_io(self):
360 if sys.platform not in {'win32', 'cli'}:
365 if sys.platform not in {'win32', 'cli'}:
361 return
366 return
362
367
363 self.enable_win_unicode_console()
368 self.enable_win_unicode_console()
364
369
365 import colorama
370 import colorama
366 colorama.init()
371 colorama.init()
367
372
368 # For some reason we make these wrappers around stdout/stderr.
373 # For some reason we make these wrappers around stdout/stderr.
369 # For now, we need to reset them so all output gets coloured.
374 # For now, we need to reset them so all output gets coloured.
370 # https://github.com/ipython/ipython/issues/8669
375 # https://github.com/ipython/ipython/issues/8669
371 # io.std* are deprecated, but don't show our own deprecation warnings
376 # io.std* are deprecated, but don't show our own deprecation warnings
372 # during initialization of the deprecated API.
377 # during initialization of the deprecated API.
373 with warnings.catch_warnings():
378 with warnings.catch_warnings():
374 warnings.simplefilter('ignore', DeprecationWarning)
379 warnings.simplefilter('ignore', DeprecationWarning)
375 io.stdout = io.IOStream(sys.stdout)
380 io.stdout = io.IOStream(sys.stdout)
376 io.stderr = io.IOStream(sys.stderr)
381 io.stderr = io.IOStream(sys.stderr)
377
382
378 def init_magics(self):
383 def init_magics(self):
379 super(TerminalInteractiveShell, self).init_magics()
384 super(TerminalInteractiveShell, self).init_magics()
380 self.register_magics(TerminalMagics)
385 self.register_magics(TerminalMagics)
381
386
382 def init_alias(self):
387 def init_alias(self):
383 # The parent class defines aliases that can be safely used with any
388 # The parent class defines aliases that can be safely used with any
384 # frontend.
389 # frontend.
385 super(TerminalInteractiveShell, self).init_alias()
390 super(TerminalInteractiveShell, self).init_alias()
386
391
387 # Now define aliases that only make sense on the terminal, because they
392 # Now define aliases that only make sense on the terminal, because they
388 # need direct access to the console in a way that we can't emulate in
393 # need direct access to the console in a way that we can't emulate in
389 # GUI or web frontend
394 # GUI or web frontend
390 if os.name == 'posix':
395 if os.name == 'posix':
391 for cmd in ['clear', 'more', 'less', 'man']:
396 for cmd in ['clear', 'more', 'less', 'man']:
392 self.alias_manager.soft_define_alias(cmd, cmd)
397 self.alias_manager.soft_define_alias(cmd, cmd)
393
398
394
399
395 def __init__(self, *args, **kwargs):
400 def __init__(self, *args, **kwargs):
396 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
401 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
397 self.init_prompt_toolkit_cli()
402 self.init_prompt_toolkit_cli()
398 self.init_term_title()
403 self.init_term_title()
399 self.keep_running = True
404 self.keep_running = True
400
405
401 self.debugger_history = InMemoryHistory()
406 self.debugger_history = InMemoryHistory()
402
407
403 def ask_exit(self):
408 def ask_exit(self):
404 self.keep_running = False
409 self.keep_running = False
405
410
406 rl_next_input = None
411 rl_next_input = None
407
412
408 def pre_prompt(self):
413 def pre_prompt(self):
409 if self.rl_next_input:
414 if self.rl_next_input:
410 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
415 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
411 self.rl_next_input = None
416 self.rl_next_input = None
412
417
413 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
418 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
414
419
415 if display_banner is not DISPLAY_BANNER_DEPRECATED:
420 if display_banner is not DISPLAY_BANNER_DEPRECATED:
416 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
421 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
417
422
418 self.keep_running = True
423 self.keep_running = True
419 while self.keep_running:
424 while self.keep_running:
420 print(self.separate_in, end='')
425 print(self.separate_in, end='')
421
426
422 try:
427 try:
423 code = self.prompt_for_code()
428 code = self.prompt_for_code()
424 except EOFError:
429 except EOFError:
425 if (not self.confirm_exit) \
430 if (not self.confirm_exit) \
426 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
431 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
427 self.ask_exit()
432 self.ask_exit()
428
433
429 else:
434 else:
430 if code:
435 if code:
431 self.run_cell(code, store_history=True)
436 self.run_cell(code, store_history=True)
432
437
433 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
438 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
434 # An extra layer of protection in case someone mashing Ctrl-C breaks
439 # An extra layer of protection in case someone mashing Ctrl-C breaks
435 # out of our internal code.
440 # out of our internal code.
436 if display_banner is not DISPLAY_BANNER_DEPRECATED:
441 if display_banner is not DISPLAY_BANNER_DEPRECATED:
437 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
442 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
438 while True:
443 while True:
439 try:
444 try:
440 self.interact()
445 self.interact()
441 break
446 break
442 except KeyboardInterrupt:
447 except KeyboardInterrupt:
443 print("\nKeyboardInterrupt escaped interact()\n")
448 print("\nKeyboardInterrupt escaped interact()\n")
444
449
445 _inputhook = None
450 _inputhook = None
446 def inputhook(self, context):
451 def inputhook(self, context):
447 if self._inputhook is not None:
452 if self._inputhook is not None:
448 self._inputhook(context)
453 self._inputhook(context)
449
454
450 def enable_gui(self, gui=None):
455 def enable_gui(self, gui=None):
451 if gui:
456 if gui:
452 self._inputhook = get_inputhook_func(gui)
457 self._inputhook = get_inputhook_func(gui)
453 else:
458 else:
454 self._inputhook = None
459 self._inputhook = None
455
460
456 # Run !system commands directly, not through pipes, so terminal programs
461 # Run !system commands directly, not through pipes, so terminal programs
457 # work correctly.
462 # work correctly.
458 system = InteractiveShell.system_raw
463 system = InteractiveShell.system_raw
459
464
460 def auto_rewrite_input(self, cmd):
465 def auto_rewrite_input(self, cmd):
461 """Overridden from the parent class to use fancy rewriting prompt"""
466 """Overridden from the parent class to use fancy rewriting prompt"""
462 if not self.show_rewritten_input:
467 if not self.show_rewritten_input:
463 return
468 return
464
469
465 tokens = self.prompts.rewrite_prompt_tokens()
470 tokens = self.prompts.rewrite_prompt_tokens()
466 if self.pt_cli:
471 if self.pt_cli:
467 self.pt_cli.print_tokens(tokens)
472 self.pt_cli.print_tokens(tokens)
468 print(cmd)
473 print(cmd)
469 else:
474 else:
470 prompt = ''.join(s for t, s in tokens)
475 prompt = ''.join(s for t, s in tokens)
471 print(prompt, cmd, sep='')
476 print(prompt, cmd, sep='')
472
477
473 _prompts_before = None
478 _prompts_before = None
474 def switch_doctest_mode(self, mode):
479 def switch_doctest_mode(self, mode):
475 """Switch prompts to classic for %doctest_mode"""
480 """Switch prompts to classic for %doctest_mode"""
476 if mode:
481 if mode:
477 self._prompts_before = self.prompts
482 self._prompts_before = self.prompts
478 self.prompts = ClassicPrompts(self)
483 self.prompts = ClassicPrompts(self)
479 elif self._prompts_before:
484 elif self._prompts_before:
480 self.prompts = self._prompts_before
485 self.prompts = self._prompts_before
481 self._prompts_before = None
486 self._prompts_before = None
482 self._update_layout()
487 self._update_layout()
483
488
484
489
485 InteractiveShellABC.register(TerminalInteractiveShell)
490 InteractiveShellABC.register(TerminalInteractiveShell)
486
491
487 if __name__ == '__main__':
492 if __name__ == '__main__':
488 TerminalInteractiveShell.instance().interact()
493 TerminalInteractiveShell.instance().interact()
@@ -1,229 +1,230 b''
1 =======================
1 =======================
2 Specific config details
2 Specific config details
3 =======================
3 =======================
4
4
5 .. _custom_prompts:
5 .. _custom_prompts:
6
6
7 Custom Prompts
7 Custom Prompts
8 ==============
8 ==============
9
9
10 .. versionchanged:: 5.0
10 .. versionchanged:: 5.0
11
11
12 From IPython 5, prompts are produced as a list of Pygments tokens, which are
12 From IPython 5, prompts are produced as a list of Pygments tokens, which are
13 tuples of (token_type, text). You can customise prompts by writing a method
13 tuples of (token_type, text). You can customise prompts by writing a method
14 which generates a list of tokens.
14 which generates a list of tokens.
15
15
16 There are four kinds of prompt:
16 There are four kinds of prompt:
17
17
18 * The **in** prompt is shown before the first line of input
18 * The **in** prompt is shown before the first line of input
19 (default like ``In [1]:``).
19 (default like ``In [1]:``).
20 * The **continuation** prompt is shown before further lines of input
20 * The **continuation** prompt is shown before further lines of input
21 (default like ``...:``).
21 (default like ``...:``).
22 * The **rewrite** prompt is shown to highlight how special syntax has been
22 * The **rewrite** prompt is shown to highlight how special syntax has been
23 interpreted (default like ``----->``).
23 interpreted (default like ``----->``).
24 * The **out** prompt is shown before the result from evaluating the input
24 * The **out** prompt is shown before the result from evaluating the input
25 (default like ``Out[1]:``).
25 (default like ``Out[1]:``).
26
26
27 Custom prompts are supplied together as a class. If you want to customise only
27 Custom prompts are supplied together as a class. If you want to customise only
28 some of the prompts, inherit from :class:`IPython.terminal.prompts.Prompts`,
28 some of the prompts, inherit from :class:`IPython.terminal.prompts.Prompts`,
29 which defines the defaults. The required interface is like this:
29 which defines the defaults. The required interface is like this:
30
30
31 .. class:: MyPrompts(shell)
31 .. class:: MyPrompts(shell)
32
32
33 Prompt style definition. *shell* is a reference to the
33 Prompt style definition. *shell* is a reference to the
34 :class:`~.TerminalInteractiveShell` instance.
34 :class:`~.TerminalInteractiveShell` instance.
35
35
36 .. method:: in_prompt_tokens(cli=None)
36 .. method:: in_prompt_tokens(cli=None)
37 continuation_prompt_tokens(self, cli=None, width=None)
37 continuation_prompt_tokens(self, cli=None, width=None)
38 rewrite_prompt_tokens()
38 rewrite_prompt_tokens()
39 out_prompt_tokens()
39 out_prompt_tokens()
40
40
41 Return the respective prompts as lists of ``(token_type, text)`` tuples.
41 Return the respective prompts as lists of ``(token_type, text)`` tuples.
42
42
43 For continuation prompts, *width* is an integer representing the width of
43 For continuation prompts, *width* is an integer representing the width of
44 the prompt area in terminal columns.
44 the prompt area in terminal columns.
45
45
46 *cli*, where used, is the prompt_toolkit ``CommandLineInterface`` instance.
46 *cli*, where used, is the prompt_toolkit ``CommandLineInterface`` instance.
47 This is mainly for compatibility with the API prompt_toolkit expects.
47 This is mainly for compatibility with the API prompt_toolkit expects.
48
48
49 Here is an example Prompt class that will show the current working directory
49 Here is an example Prompt class that will show the current working directory
50 in the input prompt:
50 in the input prompt:
51
51
52 .. code-block:: python
52 .. code-block:: python
53
53
54 from IPython.terminal.prompts import Prompts, Token
54 from IPython.terminal.prompts import Prompts, Token
55 import os
55 import os
56
56
57 class MyPrompt(Prompts):
57 class MyPrompt(Prompts):
58 def in_prompt_tokens(self, cli=None):
58 def in_prompt_tokens(self, cli=None):
59 return [(Token, os.getcwd()),
59 return [(Token, os.getcwd()),
60 (Token.Prompt, ' >>>')]
60 (Token.Prompt, ' >>>')]
61
61
62 To set the new prompt, assign it to the ``prompts`` attribute of the IPython
62 To set the new prompt, assign it to the ``prompts`` attribute of the IPython
63 shell:
63 shell:
64
64
65 .. code-block:: python
65 .. code-block:: python
66
66
67 In [2]: ip = get_ipython()
67 In [2]: ip = get_ipython()
68 ...: ip.prompts = MyPrompt(ip)
68 ...: ip.prompts = MyPrompt(ip)
69
69
70 /home/bob >>> # it works
70 /home/bob >>> # it works
71
71
72 See ``IPython/example/utils/cwd_prompt.py`` for an example of how to write an
72 See ``IPython/example/utils/cwd_prompt.py`` for an example of how to write an
73 extensions to customise prompts.
73 extensions to customise prompts.
74
74
75 Inside IPython or in a startup script, you can use a custom prompts class
75 Inside IPython or in a startup script, you can use a custom prompts class
76 by setting ``get_ipython().prompts`` to an *instance* of the class.
76 by setting ``get_ipython().prompts`` to an *instance* of the class.
77 In configuration, ``TerminalInteractiveShell.prompts_class`` may be set to
77 In configuration, ``TerminalInteractiveShell.prompts_class`` may be set to
78 either the class object, or a string of its full importable name.
78 either the class object, or a string of its full importable name.
79
79
80 To include invisible terminal control sequences in a prompt, use
80 To include invisible terminal control sequences in a prompt, use
81 ``Token.ZeroWidthEscape`` as the token type. Tokens with this type are ignored
81 ``Token.ZeroWidthEscape`` as the token type. Tokens with this type are ignored
82 when calculating the width.
82 when calculating the width.
83
83
84 Colours in the prompt are determined by the token types and the highlighting
84 Colours in the prompt are determined by the token types and the highlighting
85 style; see below for more details. The tokens used in the default prompts are
85 style; see below for more details. The tokens used in the default prompts are
86 ``Prompt``, ``PromptNum``, ``OutPrompt`` and ``OutPromptNum``.
86 ``Prompt``, ``PromptNum``, ``OutPrompt`` and ``OutPromptNum``.
87
87
88 .. _termcolour:
88 .. _termcolour:
89
89
90 Terminal Colors
90 Terminal Colors
91 ===============
91 ===============
92
92
93 .. versionchanged:: 5.0
93 .. versionchanged:: 5.0
94
94
95 There are two main configuration options controlling colours.
95 There are two main configuration options controlling colours.
96
96
97 ``InteractiveShell.colors`` sets the colour of tracebacks and object info (the
97 ``InteractiveShell.colors`` sets the colour of tracebacks and object info (the
98 output from e.g. ``zip?``). It may also affect other things if the option below
98 output from e.g. ``zip?``). It may also affect other things if the option below
99 is set to ``'legacy'``. It has four case-insensitive values:
99 is set to ``'legacy'``. It has four case-insensitive values:
100 ``'nocolor', 'neutral', 'linux', 'lightbg'``. The default is *neutral*, which
100 ``'nocolor', 'neutral', 'linux', 'lightbg'``. The default is *neutral*, which
101 should be legible on either dark or light terminal backgrounds. *linux* is
101 should be legible on either dark or light terminal backgrounds. *linux* is
102 optimised for dark backgrounds and *lightbg* for light ones.
102 optimised for dark backgrounds and *lightbg* for light ones.
103
103
104 ``TerminalInteractiveShell.highlighting_style`` determines prompt colours and syntax
104 ``TerminalInteractiveShell.highlighting_style`` determines prompt colours and
105 highlighting. It takes the name of a Pygments style as a string, or the special
105 syntax highlighting. It takes the name (as a string) or class (as a subclass of
106 value ``'legacy'`` to pick a style in accordance with ``InteractiveShell.colors``.
106 ``pygments.style.Style``) of a Pygments style, or the special value ``'legacy'``
107 to pick a style in accordance with ``InteractiveShell.colors``.
107
108
108 You can see the Pygments styles available on your system by running::
109 You can see the Pygments styles available on your system by running::
109
110
110 import pygments
111 import pygments
111 list(pygments.styles.get_all_styles())
112 list(pygments.styles.get_all_styles())
112
113
113 Additionally, ``TerminalInteractiveShell.highlighting_style_overrides`` can override
114 Additionally, ``TerminalInteractiveShell.highlighting_style_overrides`` can override
114 specific styles in the highlighting. It should be a dictionary mapping Pygments
115 specific styles in the highlighting. It should be a dictionary mapping Pygments
115 token types to strings defining the style. See `Pygments' documentation
116 token types to strings defining the style. See `Pygments' documentation
116 <http://pygments.org/docs/styles/#creating-own-styles>`__ for the language used
117 <http://pygments.org/docs/styles/#creating-own-styles>`__ for the language used
117 to define styles.
118 to define styles.
118
119
119 Colors in the pager
120 Colors in the pager
120 -------------------
121 -------------------
121
122
122 On some systems, the default pager has problems with ANSI colour codes.
123 On some systems, the default pager has problems with ANSI colour codes.
123 To configure your default pager to allow these:
124 To configure your default pager to allow these:
124
125
125 1. Set the environment PAGER variable to ``less``.
126 1. Set the environment PAGER variable to ``less``.
126 2. Set the environment LESS variable to ``-r`` (plus any other options
127 2. Set the environment LESS variable to ``-r`` (plus any other options
127 you always want to pass to less by default). This tells less to
128 you always want to pass to less by default). This tells less to
128 properly interpret control sequences, which is how color
129 properly interpret control sequences, which is how color
129 information is given to your terminal.
130 information is given to your terminal.
130
131
131 .. _editors:
132 .. _editors:
132
133
133 Editor configuration
134 Editor configuration
134 ====================
135 ====================
135
136
136 IPython can integrate with text editors in a number of different ways:
137 IPython can integrate with text editors in a number of different ways:
137
138
138 * Editors (such as `(X)Emacs`_, vim_ and TextMate_) can
139 * Editors (such as `(X)Emacs`_, vim_ and TextMate_) can
139 send code to IPython for execution.
140 send code to IPython for execution.
140
141
141 * IPython's ``%edit`` magic command can open an editor of choice to edit
142 * IPython's ``%edit`` magic command can open an editor of choice to edit
142 a code block.
143 a code block.
143
144
144 The %edit command (and its alias %ed) will invoke the editor set in your
145 The %edit command (and its alias %ed) will invoke the editor set in your
145 environment as :envvar:`EDITOR`. If this variable is not set, it will default
146 environment as :envvar:`EDITOR`. If this variable is not set, it will default
146 to vi under Linux/Unix and to notepad under Windows. You may want to set this
147 to vi under Linux/Unix and to notepad under Windows. You may want to set this
147 variable properly and to a lightweight editor which doesn't take too long to
148 variable properly and to a lightweight editor which doesn't take too long to
148 start (that is, something other than a new instance of Emacs). This way you
149 start (that is, something other than a new instance of Emacs). This way you
149 can edit multi-line code quickly and with the power of a real editor right
150 can edit multi-line code quickly and with the power of a real editor right
150 inside IPython.
151 inside IPython.
151
152
152 You can also control the editor by setting :attr:`TerminalInteractiveShell.editor`
153 You can also control the editor by setting :attr:`TerminalInteractiveShell.editor`
153 in :file:`ipython_config.py`.
154 in :file:`ipython_config.py`.
154
155
155 Vim
156 Vim
156 ---
157 ---
157
158
158 Paul Ivanov's `vim-ipython <https://github.com/ivanov/vim-ipython>`_ provides
159 Paul Ivanov's `vim-ipython <https://github.com/ivanov/vim-ipython>`_ provides
159 powerful IPython integration for vim.
160 powerful IPython integration for vim.
160
161
161 .. _emacs:
162 .. _emacs:
162
163
163 (X)Emacs
164 (X)Emacs
164 --------
165 --------
165
166
166 If you are a dedicated Emacs user, and want to use Emacs when IPython's
167 If you are a dedicated Emacs user, and want to use Emacs when IPython's
167 ``%edit`` magic command is called you should set up the Emacs server so that
168 ``%edit`` magic command is called you should set up the Emacs server so that
168 new requests are handled by the original process. This means that almost no
169 new requests are handled by the original process. This means that almost no
169 time is spent in handling the request (assuming an Emacs process is already
170 time is spent in handling the request (assuming an Emacs process is already
170 running). For this to work, you need to set your EDITOR environment variable
171 running). For this to work, you need to set your EDITOR environment variable
171 to 'emacsclient'. The code below, supplied by Francois Pinard, can then be
172 to 'emacsclient'. The code below, supplied by Francois Pinard, can then be
172 used in your :file:`.emacs` file to enable the server:
173 used in your :file:`.emacs` file to enable the server:
173
174
174 .. code-block:: common-lisp
175 .. code-block:: common-lisp
175
176
176 (defvar server-buffer-clients)
177 (defvar server-buffer-clients)
177 (when (and (fboundp 'server-start) (string-equal (getenv "TERM") 'xterm))
178 (when (and (fboundp 'server-start) (string-equal (getenv "TERM") 'xterm))
178 (server-start)
179 (server-start)
179 (defun fp-kill-server-with-buffer-routine ()
180 (defun fp-kill-server-with-buffer-routine ()
180 (and server-buffer-clients (server-done)))
181 (and server-buffer-clients (server-done)))
181 (add-hook 'kill-buffer-hook 'fp-kill-server-with-buffer-routine))
182 (add-hook 'kill-buffer-hook 'fp-kill-server-with-buffer-routine))
182
183
183 Thanks to the work of Alexander Schmolck and Prabhu Ramachandran,
184 Thanks to the work of Alexander Schmolck and Prabhu Ramachandran,
184 currently (X)Emacs and IPython get along very well in other ways.
185 currently (X)Emacs and IPython get along very well in other ways.
185
186
186 With (X)EMacs >= 24, You can enable IPython in python-mode with:
187 With (X)EMacs >= 24, You can enable IPython in python-mode with:
187
188
188 .. code-block:: common-lisp
189 .. code-block:: common-lisp
189
190
190 (require 'python)
191 (require 'python)
191 (setq python-shell-interpreter "ipython")
192 (setq python-shell-interpreter "ipython")
192
193
193 .. _`(X)Emacs`: http://www.gnu.org/software/emacs/
194 .. _`(X)Emacs`: http://www.gnu.org/software/emacs/
194 .. _TextMate: http://macromates.com/
195 .. _TextMate: http://macromates.com/
195 .. _vim: http://www.vim.org/
196 .. _vim: http://www.vim.org/
196
197
197 .. _custom_keyboard_shortcuts
198 .. _custom_keyboard_shortcuts
198
199
199 Keyboard Shortcuts
200 Keyboard Shortcuts
200 ==================
201 ==================
201
202
202 .. versionchanged:: 5.0
203 .. versionchanged:: 5.0
203
204
204 You can customise keyboard shortcuts for terminal IPython. Put code like this in
205 You can customise keyboard shortcuts for terminal IPython. Put code like this in
205 a :ref:`startup file <startup_files>`::
206 a :ref:`startup file <startup_files>`::
206
207
207 from IPython import get_ipython
208 from IPython import get_ipython
208 from prompt_toolkit.enums import DEFAULT_BUFFER
209 from prompt_toolkit.enums import DEFAULT_BUFFER
209 from prompt_toolkit.keys import Keys
210 from prompt_toolkit.keys import Keys
210 from prompt_toolkit.filters import HasFocus, HasSelection, ViInsertMode, EmacsInsertMode
211 from prompt_toolkit.filters import HasFocus, HasSelection, ViInsertMode, EmacsInsertMode
211
212
212 ip = get_ipython()
213 ip = get_ipython()
213 insert_mode = ViInsertMode() | EmacsInsertMode()
214 insert_mode = ViInsertMode() | EmacsInsertMode()
214
215
215 def insert_unexpected(event):
216 def insert_unexpected(event):
216 buf = event.current_buffer
217 buf = event.current_buffer
217 buf.insert_text('The Spanish Inquisition')
218 buf.insert_text('The Spanish Inquisition')
218
219
219 # Register the shortcut if IPython is using prompt_toolkit
220 # Register the shortcut if IPython is using prompt_toolkit
220 if getattr(ip, 'pt_cli'):
221 if getattr(ip, 'pt_cli'):
221 registry = ip.pt_cli.application.key_bindings_registry
222 registry = ip.pt_cli.application.key_bindings_registry
222 registry.add_binding(Keys.ControlN,
223 registry.add_binding(Keys.ControlN,
223 filter=(HasFocus(DEFAULT_BUFFER)
224 filter=(HasFocus(DEFAULT_BUFFER)
224 & ~HasSelection()
225 & ~HasSelection()
225 & insert_mode))(insert_unexpected)
226 & insert_mode))(insert_unexpected)
226
227
227 For more information on filters and what you can do with the ``event`` object,
228 For more information on filters and what you can do with the ``event`` object,
228 `see the prompt_toolkit docs
229 `see the prompt_toolkit docs
229 <http://python-prompt-toolkit.readthedocs.io/en/latest/pages/building_prompts.html#adding-custom-key-bindings>`__.
230 <http://python-prompt-toolkit.readthedocs.io/en/latest/pages/building_prompts.html#adding-custom-key-bindings>`__.
General Comments 0
You need to be logged in to leave comments. Login now