##// END OF EJS Templates
Inspect continuation prompt signature and pass only viable arguments....
Matthias Bussonnier -
Show More
@@ -1,998 +1,1015 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import asyncio
3 import asyncio
4 import os
4 import os
5 import sys
5 import sys
6 import inspect
6 from warnings import warn
7 from warnings import warn
7 from typing import Union as UnionType, Optional
8 from typing import Union as UnionType, Optional
8
9
9 from IPython.core.async_helpers import get_asyncio_loop
10 from IPython.core.async_helpers import get_asyncio_loop
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.utils.py3compat import input
12 from IPython.utils.py3compat import input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
13 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
13 from IPython.utils.process import abbrev_cwd
14 from IPython.utils.process import abbrev_cwd
14 from traitlets import (
15 from traitlets import (
15 Bool,
16 Bool,
16 Unicode,
17 Unicode,
17 Dict,
18 Dict,
18 Integer,
19 Integer,
19 List,
20 List,
20 observe,
21 observe,
21 Instance,
22 Instance,
22 Type,
23 Type,
23 default,
24 default,
24 Enum,
25 Enum,
25 Union,
26 Union,
26 Any,
27 Any,
27 validate,
28 validate,
28 Float,
29 Float,
29 )
30 )
30
31
31 from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
32 from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
32 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
33 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
33 from prompt_toolkit.filters import HasFocus, Condition, IsDone
34 from prompt_toolkit.filters import HasFocus, Condition, IsDone
34 from prompt_toolkit.formatted_text import PygmentsTokens
35 from prompt_toolkit.formatted_text import PygmentsTokens
35 from prompt_toolkit.history import History
36 from prompt_toolkit.history import History
36 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
37 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
37 from prompt_toolkit.output import ColorDepth
38 from prompt_toolkit.output import ColorDepth
38 from prompt_toolkit.patch_stdout import patch_stdout
39 from prompt_toolkit.patch_stdout import patch_stdout
39 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
40 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
40 from prompt_toolkit.styles import DynamicStyle, merge_styles
41 from prompt_toolkit.styles import DynamicStyle, merge_styles
41 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
42 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
42 from prompt_toolkit import __version__ as ptk_version
43 from prompt_toolkit import __version__ as ptk_version
43
44
44 from pygments.styles import get_style_by_name
45 from pygments.styles import get_style_by_name
45 from pygments.style import Style
46 from pygments.style import Style
46 from pygments.token import Token
47 from pygments.token import Token
47
48
48 from .debugger import TerminalPdb, Pdb
49 from .debugger import TerminalPdb, Pdb
49 from .magics import TerminalMagics
50 from .magics import TerminalMagics
50 from .pt_inputhooks import get_inputhook_name_and_func
51 from .pt_inputhooks import get_inputhook_name_and_func
51 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
52 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
52 from .ptutils import IPythonPTCompleter, IPythonPTLexer
53 from .ptutils import IPythonPTCompleter, IPythonPTLexer
53 from .shortcuts import (
54 from .shortcuts import (
54 KEY_BINDINGS,
55 KEY_BINDINGS,
55 create_ipython_shortcuts,
56 create_ipython_shortcuts,
56 create_identifier,
57 create_identifier,
57 RuntimeBinding,
58 RuntimeBinding,
58 add_binding,
59 add_binding,
59 )
60 )
60 from .shortcuts.filters import KEYBINDING_FILTERS, filter_from_string
61 from .shortcuts.filters import KEYBINDING_FILTERS, filter_from_string
61 from .shortcuts.auto_suggest import (
62 from .shortcuts.auto_suggest import (
62 NavigableAutoSuggestFromHistory,
63 NavigableAutoSuggestFromHistory,
63 AppendAutoSuggestionInAnyLine,
64 AppendAutoSuggestionInAnyLine,
64 )
65 )
65
66
66 PTK3 = ptk_version.startswith('3.')
67 PTK3 = ptk_version.startswith('3.')
67
68
68
69
69 class _NoStyle(Style): pass
70 class _NoStyle(Style):
70
71 pass
71
72
72
73
73 _style_overrides_light_bg = {
74 _style_overrides_light_bg = {
74 Token.Prompt: '#ansibrightblue',
75 Token.Prompt: '#ansibrightblue',
75 Token.PromptNum: '#ansiblue bold',
76 Token.PromptNum: '#ansiblue bold',
76 Token.OutPrompt: '#ansibrightred',
77 Token.OutPrompt: '#ansibrightred',
77 Token.OutPromptNum: '#ansired bold',
78 Token.OutPromptNum: '#ansired bold',
78 }
79 }
79
80
80 _style_overrides_linux = {
81 _style_overrides_linux = {
81 Token.Prompt: '#ansibrightgreen',
82 Token.Prompt: '#ansibrightgreen',
82 Token.PromptNum: '#ansigreen bold',
83 Token.PromptNum: '#ansigreen bold',
83 Token.OutPrompt: '#ansibrightred',
84 Token.OutPrompt: '#ansibrightred',
84 Token.OutPromptNum: '#ansired bold',
85 Token.OutPromptNum: '#ansired bold',
85 }
86 }
86
87
88
89 def _backward_compat_continuation_prompt_tokens(method, width: int, *, lineno: int):
90 """
91 Sagemath use custom prompt and we broke them in 8.19.
92 """
93 sig = inspect.signature(method)
94 if "lineno" in inspect.signature(method).parameters or any(
95 [p.kind == p.VAR_KEYWORD for p in sig.parameters.values()]
96 ):
97 return method(width, lineno=lineno)
98 else:
99 return method(width)
100
101
87 def get_default_editor():
102 def get_default_editor():
88 try:
103 try:
89 return os.environ['EDITOR']
104 return os.environ['EDITOR']
90 except KeyError:
105 except KeyError:
91 pass
106 pass
92 except UnicodeError:
107 except UnicodeError:
93 warn("$EDITOR environment variable is not pure ASCII. Using platform "
108 warn("$EDITOR environment variable is not pure ASCII. Using platform "
94 "default editor.")
109 "default editor.")
95
110
96 if os.name == 'posix':
111 if os.name == 'posix':
97 return 'vi' # the only one guaranteed to be there!
112 return 'vi' # the only one guaranteed to be there!
98 else:
113 else:
99 return 'notepad' # same in Windows!
114 return 'notepad' # same in Windows!
100
115
101 # conservatively check for tty
116 # conservatively check for tty
102 # overridden streams can result in things like:
117 # overridden streams can result in things like:
103 # - sys.stdin = None
118 # - sys.stdin = None
104 # - no isatty method
119 # - no isatty method
105 for _name in ('stdin', 'stdout', 'stderr'):
120 for _name in ('stdin', 'stdout', 'stderr'):
106 _stream = getattr(sys, _name)
121 _stream = getattr(sys, _name)
107 try:
122 try:
108 if not _stream or not hasattr(_stream, "isatty") or not _stream.isatty():
123 if not _stream or not hasattr(_stream, "isatty") or not _stream.isatty():
109 _is_tty = False
124 _is_tty = False
110 break
125 break
111 except ValueError:
126 except ValueError:
112 # stream is closed
127 # stream is closed
113 _is_tty = False
128 _is_tty = False
114 break
129 break
115 else:
130 else:
116 _is_tty = True
131 _is_tty = True
117
132
118
133
119 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
134 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
120
135
121 def black_reformat_handler(text_before_cursor):
136 def black_reformat_handler(text_before_cursor):
122 """
137 """
123 We do not need to protect against error,
138 We do not need to protect against error,
124 this is taken care at a higher level where any reformat error is ignored.
139 this is taken care at a higher level where any reformat error is ignored.
125 Indeed we may call reformatting on incomplete code.
140 Indeed we may call reformatting on incomplete code.
126 """
141 """
127 import black
142 import black
128
143
129 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
144 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
130 if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
145 if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
131 formatted_text = formatted_text[:-1]
146 formatted_text = formatted_text[:-1]
132 return formatted_text
147 return formatted_text
133
148
134
149
135 def yapf_reformat_handler(text_before_cursor):
150 def yapf_reformat_handler(text_before_cursor):
136 from yapf.yapflib import file_resources
151 from yapf.yapflib import file_resources
137 from yapf.yapflib import yapf_api
152 from yapf.yapflib import yapf_api
138
153
139 style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
154 style_config = file_resources.GetDefaultStyleForDir(os.getcwd())
140 formatted_text, was_formatted = yapf_api.FormatCode(
155 formatted_text, was_formatted = yapf_api.FormatCode(
141 text_before_cursor, style_config=style_config
156 text_before_cursor, style_config=style_config
142 )
157 )
143 if was_formatted:
158 if was_formatted:
144 if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
159 if not text_before_cursor.endswith("\n") and formatted_text.endswith("\n"):
145 formatted_text = formatted_text[:-1]
160 formatted_text = formatted_text[:-1]
146 return formatted_text
161 return formatted_text
147 else:
162 else:
148 return text_before_cursor
163 return text_before_cursor
149
164
150
165
151 class PtkHistoryAdapter(History):
166 class PtkHistoryAdapter(History):
152 """
167 """
153 Prompt toolkit has it's own way of handling history, Where it assumes it can
168 Prompt toolkit has it's own way of handling history, Where it assumes it can
154 Push/pull from history.
169 Push/pull from history.
155
170
156 """
171 """
157
172
158 def __init__(self, shell):
173 def __init__(self, shell):
159 super().__init__()
174 super().__init__()
160 self.shell = shell
175 self.shell = shell
161 self._refresh()
176 self._refresh()
162
177
163 def append_string(self, string):
178 def append_string(self, string):
164 # we rely on sql for that.
179 # we rely on sql for that.
165 self._loaded = False
180 self._loaded = False
166 self._refresh()
181 self._refresh()
167
182
168 def _refresh(self):
183 def _refresh(self):
169 if not self._loaded:
184 if not self._loaded:
170 self._loaded_strings = list(self.load_history_strings())
185 self._loaded_strings = list(self.load_history_strings())
171
186
172 def load_history_strings(self):
187 def load_history_strings(self):
173 last_cell = ""
188 last_cell = ""
174 res = []
189 res = []
175 for __, ___, cell in self.shell.history_manager.get_tail(
190 for __, ___, cell in self.shell.history_manager.get_tail(
176 self.shell.history_load_length, include_latest=True
191 self.shell.history_load_length, include_latest=True
177 ):
192 ):
178 # Ignore blank lines and consecutive duplicates
193 # Ignore blank lines and consecutive duplicates
179 cell = cell.rstrip()
194 cell = cell.rstrip()
180 if cell and (cell != last_cell):
195 if cell and (cell != last_cell):
181 res.append(cell)
196 res.append(cell)
182 last_cell = cell
197 last_cell = cell
183 yield from res[::-1]
198 yield from res[::-1]
184
199
185 def store_string(self, string: str) -> None:
200 def store_string(self, string: str) -> None:
186 pass
201 pass
187
202
188 class TerminalInteractiveShell(InteractiveShell):
203 class TerminalInteractiveShell(InteractiveShell):
189 mime_renderers = Dict().tag(config=True)
204 mime_renderers = Dict().tag(config=True)
190
205
191 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
206 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
192 'to reserve for the tab completion menu, '
207 'to reserve for the tab completion menu, '
193 'search history, ...etc, the height of '
208 'search history, ...etc, the height of '
194 'these menus will at most this value. '
209 'these menus will at most this value. '
195 'Increase it is you prefer long and skinny '
210 'Increase it is you prefer long and skinny '
196 'menus, decrease for short and wide.'
211 'menus, decrease for short and wide.'
197 ).tag(config=True)
212 ).tag(config=True)
198
213
199 pt_app: UnionType[PromptSession, None] = None
214 pt_app: UnionType[PromptSession, None] = None
200 auto_suggest: UnionType[
215 auto_suggest: UnionType[
201 AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None
216 AutoSuggestFromHistory, NavigableAutoSuggestFromHistory, None
202 ] = None
217 ] = None
203 debugger_history = None
218 debugger_history = None
204
219
205 debugger_history_file = Unicode(
220 debugger_history_file = Unicode(
206 "~/.pdbhistory", help="File in which to store and read history"
221 "~/.pdbhistory", help="File in which to store and read history"
207 ).tag(config=True)
222 ).tag(config=True)
208
223
209 simple_prompt = Bool(_use_simple_prompt,
224 simple_prompt = Bool(_use_simple_prompt,
210 help="""Use `raw_input` for the REPL, without completion and prompt colors.
225 help="""Use `raw_input` for the REPL, without completion and prompt colors.
211
226
212 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
227 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
213 IPython own testing machinery, and emacs inferior-shell integration through elpy.
228 IPython own testing machinery, and emacs inferior-shell integration through elpy.
214
229
215 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
230 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
216 environment variable is set, or the current terminal is not a tty."""
231 environment variable is set, or the current terminal is not a tty."""
217 ).tag(config=True)
232 ).tag(config=True)
218
233
219 @property
234 @property
220 def debugger_cls(self):
235 def debugger_cls(self):
221 return Pdb if self.simple_prompt else TerminalPdb
236 return Pdb if self.simple_prompt else TerminalPdb
222
237
223 confirm_exit = Bool(True,
238 confirm_exit = Bool(True,
224 help="""
239 help="""
225 Set to confirm when you try to exit IPython with an EOF (Control-D
240 Set to confirm when you try to exit IPython with an EOF (Control-D
226 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
241 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
227 you can force a direct exit without any confirmation.""",
242 you can force a direct exit without any confirmation.""",
228 ).tag(config=True)
243 ).tag(config=True)
229
244
230 editing_mode = Unicode('emacs',
245 editing_mode = Unicode('emacs',
231 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
246 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
232 ).tag(config=True)
247 ).tag(config=True)
233
248
234 emacs_bindings_in_vi_insert_mode = Bool(
249 emacs_bindings_in_vi_insert_mode = Bool(
235 True,
250 True,
236 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
251 help="Add shortcuts from 'emacs' insert mode to 'vi' insert mode.",
237 ).tag(config=True)
252 ).tag(config=True)
238
253
239 modal_cursor = Bool(
254 modal_cursor = Bool(
240 True,
255 True,
241 help="""
256 help="""
242 Cursor shape changes depending on vi mode: beam in vi insert mode,
257 Cursor shape changes depending on vi mode: beam in vi insert mode,
243 block in nav mode, underscore in replace mode.""",
258 block in nav mode, underscore in replace mode.""",
244 ).tag(config=True)
259 ).tag(config=True)
245
260
246 ttimeoutlen = Float(
261 ttimeoutlen = Float(
247 0.01,
262 0.01,
248 help="""The time in milliseconds that is waited for a key code
263 help="""The time in milliseconds that is waited for a key code
249 to complete.""",
264 to complete.""",
250 ).tag(config=True)
265 ).tag(config=True)
251
266
252 timeoutlen = Float(
267 timeoutlen = Float(
253 0.5,
268 0.5,
254 help="""The time in milliseconds that is waited for a mapped key
269 help="""The time in milliseconds that is waited for a mapped key
255 sequence to complete.""",
270 sequence to complete.""",
256 ).tag(config=True)
271 ).tag(config=True)
257
272
258 autoformatter = Unicode(
273 autoformatter = Unicode(
259 None,
274 None,
260 help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`",
275 help="Autoformatter to reformat Terminal code. Can be `'black'`, `'yapf'` or `None`",
261 allow_none=True
276 allow_none=True
262 ).tag(config=True)
277 ).tag(config=True)
263
278
264 auto_match = Bool(
279 auto_match = Bool(
265 False,
280 False,
266 help="""
281 help="""
267 Automatically add/delete closing bracket or quote when opening bracket or quote is entered/deleted.
282 Automatically add/delete closing bracket or quote when opening bracket or quote is entered/deleted.
268 Brackets: (), [], {}
283 Brackets: (), [], {}
269 Quotes: '', \"\"
284 Quotes: '', \"\"
270 """,
285 """,
271 ).tag(config=True)
286 ).tag(config=True)
272
287
273 mouse_support = Bool(False,
288 mouse_support = Bool(False,
274 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
289 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
275 ).tag(config=True)
290 ).tag(config=True)
276
291
277 # We don't load the list of styles for the help string, because loading
292 # We don't load the list of styles for the help string, because loading
278 # Pygments plugins takes time and can cause unexpected errors.
293 # Pygments plugins takes time and can cause unexpected errors.
279 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
294 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
280 help="""The name or class of a Pygments style to use for syntax
295 help="""The name or class of a Pygments style to use for syntax
281 highlighting. To see available styles, run `pygmentize -L styles`."""
296 highlighting. To see available styles, run `pygmentize -L styles`."""
282 ).tag(config=True)
297 ).tag(config=True)
283
298
284 @validate('editing_mode')
299 @validate('editing_mode')
285 def _validate_editing_mode(self, proposal):
300 def _validate_editing_mode(self, proposal):
286 if proposal['value'].lower() == 'vim':
301 if proposal['value'].lower() == 'vim':
287 proposal['value']= 'vi'
302 proposal['value']= 'vi'
288 elif proposal['value'].lower() == 'default':
303 elif proposal['value'].lower() == 'default':
289 proposal['value']= 'emacs'
304 proposal['value']= 'emacs'
290
305
291 if hasattr(EditingMode, proposal['value'].upper()):
306 if hasattr(EditingMode, proposal['value'].upper()):
292 return proposal['value'].lower()
307 return proposal['value'].lower()
293
308
294 return self.editing_mode
309 return self.editing_mode
295
310
296
311
297 @observe('editing_mode')
312 @observe('editing_mode')
298 def _editing_mode(self, change):
313 def _editing_mode(self, change):
299 if self.pt_app:
314 if self.pt_app:
300 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
315 self.pt_app.editing_mode = getattr(EditingMode, change.new.upper())
301
316
302 def _set_formatter(self, formatter):
317 def _set_formatter(self, formatter):
303 if formatter is None:
318 if formatter is None:
304 self.reformat_handler = lambda x:x
319 self.reformat_handler = lambda x:x
305 elif formatter == 'black':
320 elif formatter == 'black':
306 self.reformat_handler = black_reformat_handler
321 self.reformat_handler = black_reformat_handler
307 elif formatter == "yapf":
322 elif formatter == "yapf":
308 self.reformat_handler = yapf_reformat_handler
323 self.reformat_handler = yapf_reformat_handler
309 else:
324 else:
310 raise ValueError
325 raise ValueError
311
326
312 @observe("autoformatter")
327 @observe("autoformatter")
313 def _autoformatter_changed(self, change):
328 def _autoformatter_changed(self, change):
314 formatter = change.new
329 formatter = change.new
315 self._set_formatter(formatter)
330 self._set_formatter(formatter)
316
331
317 @observe('highlighting_style')
332 @observe('highlighting_style')
318 @observe('colors')
333 @observe('colors')
319 def _highlighting_style_changed(self, change):
334 def _highlighting_style_changed(self, change):
320 self.refresh_style()
335 self.refresh_style()
321
336
322 def refresh_style(self):
337 def refresh_style(self):
323 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
338 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
324
339
325
340
326 highlighting_style_overrides = Dict(
341 highlighting_style_overrides = Dict(
327 help="Override highlighting format for specific tokens"
342 help="Override highlighting format for specific tokens"
328 ).tag(config=True)
343 ).tag(config=True)
329
344
330 true_color = Bool(False,
345 true_color = Bool(False,
331 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
346 help="""Use 24bit colors instead of 256 colors in prompt highlighting.
332 If your terminal supports true color, the following command should
347 If your terminal supports true color, the following command should
333 print ``TRUECOLOR`` in orange::
348 print ``TRUECOLOR`` in orange::
334
349
335 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
350 printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"
336 """,
351 """,
337 ).tag(config=True)
352 ).tag(config=True)
338
353
339 editor = Unicode(get_default_editor(),
354 editor = Unicode(get_default_editor(),
340 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
355 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
341 ).tag(config=True)
356 ).tag(config=True)
342
357
343 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
358 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
344
359
345 prompts = Instance(Prompts)
360 prompts = Instance(Prompts)
346
361
347 @default('prompts')
362 @default('prompts')
348 def _prompts_default(self):
363 def _prompts_default(self):
349 return self.prompts_class(self)
364 return self.prompts_class(self)
350
365
351 # @observe('prompts')
366 # @observe('prompts')
352 # def _(self, change):
367 # def _(self, change):
353 # self._update_layout()
368 # self._update_layout()
354
369
355 @default('displayhook_class')
370 @default('displayhook_class')
356 def _displayhook_class_default(self):
371 def _displayhook_class_default(self):
357 return RichPromptDisplayHook
372 return RichPromptDisplayHook
358
373
359 term_title = Bool(True,
374 term_title = Bool(True,
360 help="Automatically set the terminal title"
375 help="Automatically set the terminal title"
361 ).tag(config=True)
376 ).tag(config=True)
362
377
363 term_title_format = Unicode("IPython: {cwd}",
378 term_title_format = Unicode("IPython: {cwd}",
364 help="Customize the terminal title format. This is a python format string. " +
379 help="Customize the terminal title format. This is a python format string. " +
365 "Available substitutions are: {cwd}."
380 "Available substitutions are: {cwd}."
366 ).tag(config=True)
381 ).tag(config=True)
367
382
368 display_completions = Enum(('column', 'multicolumn','readlinelike'),
383 display_completions = Enum(('column', 'multicolumn','readlinelike'),
369 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
384 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
370 "'readlinelike'. These options are for `prompt_toolkit`, see "
385 "'readlinelike'. These options are for `prompt_toolkit`, see "
371 "`prompt_toolkit` documentation for more information."
386 "`prompt_toolkit` documentation for more information."
372 ),
387 ),
373 default_value='multicolumn').tag(config=True)
388 default_value='multicolumn').tag(config=True)
374
389
375 highlight_matching_brackets = Bool(True,
390 highlight_matching_brackets = Bool(True,
376 help="Highlight matching brackets.",
391 help="Highlight matching brackets.",
377 ).tag(config=True)
392 ).tag(config=True)
378
393
379 extra_open_editor_shortcuts = Bool(False,
394 extra_open_editor_shortcuts = Bool(False,
380 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
395 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
381 "This is in addition to the F2 binding, which is always enabled."
396 "This is in addition to the F2 binding, which is always enabled."
382 ).tag(config=True)
397 ).tag(config=True)
383
398
384 handle_return = Any(None,
399 handle_return = Any(None,
385 help="Provide an alternative handler to be called when the user presses "
400 help="Provide an alternative handler to be called when the user presses "
386 "Return. This is an advanced option intended for debugging, which "
401 "Return. This is an advanced option intended for debugging, which "
387 "may be changed or removed in later releases."
402 "may be changed or removed in later releases."
388 ).tag(config=True)
403 ).tag(config=True)
389
404
390 enable_history_search = Bool(True,
405 enable_history_search = Bool(True,
391 help="Allows to enable/disable the prompt toolkit history search"
406 help="Allows to enable/disable the prompt toolkit history search"
392 ).tag(config=True)
407 ).tag(config=True)
393
408
394 autosuggestions_provider = Unicode(
409 autosuggestions_provider = Unicode(
395 "NavigableAutoSuggestFromHistory",
410 "NavigableAutoSuggestFromHistory",
396 help="Specifies from which source automatic suggestions are provided. "
411 help="Specifies from which source automatic suggestions are provided. "
397 "Can be set to ``'NavigableAutoSuggestFromHistory'`` (:kbd:`up` and "
412 "Can be set to ``'NavigableAutoSuggestFromHistory'`` (:kbd:`up` and "
398 ":kbd:`down` swap suggestions), ``'AutoSuggestFromHistory'``, "
413 ":kbd:`down` swap suggestions), ``'AutoSuggestFromHistory'``, "
399 " or ``None`` to disable automatic suggestions. "
414 " or ``None`` to disable automatic suggestions. "
400 "Default is `'NavigableAutoSuggestFromHistory`'.",
415 "Default is `'NavigableAutoSuggestFromHistory`'.",
401 allow_none=True,
416 allow_none=True,
402 ).tag(config=True)
417 ).tag(config=True)
403
418
404 def _set_autosuggestions(self, provider):
419 def _set_autosuggestions(self, provider):
405 # disconnect old handler
420 # disconnect old handler
406 if self.auto_suggest and isinstance(
421 if self.auto_suggest and isinstance(
407 self.auto_suggest, NavigableAutoSuggestFromHistory
422 self.auto_suggest, NavigableAutoSuggestFromHistory
408 ):
423 ):
409 self.auto_suggest.disconnect()
424 self.auto_suggest.disconnect()
410 if provider is None:
425 if provider is None:
411 self.auto_suggest = None
426 self.auto_suggest = None
412 elif provider == "AutoSuggestFromHistory":
427 elif provider == "AutoSuggestFromHistory":
413 self.auto_suggest = AutoSuggestFromHistory()
428 self.auto_suggest = AutoSuggestFromHistory()
414 elif provider == "NavigableAutoSuggestFromHistory":
429 elif provider == "NavigableAutoSuggestFromHistory":
415 self.auto_suggest = NavigableAutoSuggestFromHistory()
430 self.auto_suggest = NavigableAutoSuggestFromHistory()
416 else:
431 else:
417 raise ValueError("No valid provider.")
432 raise ValueError("No valid provider.")
418 if self.pt_app:
433 if self.pt_app:
419 self.pt_app.auto_suggest = self.auto_suggest
434 self.pt_app.auto_suggest = self.auto_suggest
420
435
421 @observe("autosuggestions_provider")
436 @observe("autosuggestions_provider")
422 def _autosuggestions_provider_changed(self, change):
437 def _autosuggestions_provider_changed(self, change):
423 provider = change.new
438 provider = change.new
424 self._set_autosuggestions(provider)
439 self._set_autosuggestions(provider)
425
440
426 shortcuts = List(
441 shortcuts = List(
427 trait=Dict(
442 trait=Dict(
428 key_trait=Enum(
443 key_trait=Enum(
429 [
444 [
430 "command",
445 "command",
431 "match_keys",
446 "match_keys",
432 "match_filter",
447 "match_filter",
433 "new_keys",
448 "new_keys",
434 "new_filter",
449 "new_filter",
435 "create",
450 "create",
436 ]
451 ]
437 ),
452 ),
438 per_key_traits={
453 per_key_traits={
439 "command": Unicode(),
454 "command": Unicode(),
440 "match_keys": List(Unicode()),
455 "match_keys": List(Unicode()),
441 "match_filter": Unicode(),
456 "match_filter": Unicode(),
442 "new_keys": List(Unicode()),
457 "new_keys": List(Unicode()),
443 "new_filter": Unicode(),
458 "new_filter": Unicode(),
444 "create": Bool(False),
459 "create": Bool(False),
445 },
460 },
446 ),
461 ),
447 help="""Add, disable or modifying shortcuts.
462 help="""Add, disable or modifying shortcuts.
448
463
449 Each entry on the list should be a dictionary with ``command`` key
464 Each entry on the list should be a dictionary with ``command`` key
450 identifying the target function executed by the shortcut and at least
465 identifying the target function executed by the shortcut and at least
451 one of the following:
466 one of the following:
452
467
453 - ``match_keys``: list of keys used to match an existing shortcut,
468 - ``match_keys``: list of keys used to match an existing shortcut,
454 - ``match_filter``: shortcut filter used to match an existing shortcut,
469 - ``match_filter``: shortcut filter used to match an existing shortcut,
455 - ``new_keys``: list of keys to set,
470 - ``new_keys``: list of keys to set,
456 - ``new_filter``: a new shortcut filter to set
471 - ``new_filter``: a new shortcut filter to set
457
472
458 The filters have to be composed of pre-defined verbs and joined by one
473 The filters have to be composed of pre-defined verbs and joined by one
459 of the following conjunctions: ``&`` (and), ``|`` (or), ``~`` (not).
474 of the following conjunctions: ``&`` (and), ``|`` (or), ``~`` (not).
460 The pre-defined verbs are:
475 The pre-defined verbs are:
461
476
462 {}
477 {}
463
478
464
479
465 To disable a shortcut set ``new_keys`` to an empty list.
480 To disable a shortcut set ``new_keys`` to an empty list.
466 To add a shortcut add key ``create`` with value ``True``.
481 To add a shortcut add key ``create`` with value ``True``.
467
482
468 When modifying/disabling shortcuts, ``match_keys``/``match_filter`` can
483 When modifying/disabling shortcuts, ``match_keys``/``match_filter`` can
469 be omitted if the provided specification uniquely identifies a shortcut
484 be omitted if the provided specification uniquely identifies a shortcut
470 to be modified/disabled. When modifying a shortcut ``new_filter`` or
485 to be modified/disabled. When modifying a shortcut ``new_filter`` or
471 ``new_keys`` can be omitted which will result in reuse of the existing
486 ``new_keys`` can be omitted which will result in reuse of the existing
472 filter/keys.
487 filter/keys.
473
488
474 Only shortcuts defined in IPython (and not default prompt-toolkit
489 Only shortcuts defined in IPython (and not default prompt-toolkit
475 shortcuts) can be modified or disabled. The full list of shortcuts,
490 shortcuts) can be modified or disabled. The full list of shortcuts,
476 command identifiers and filters is available under
491 command identifiers and filters is available under
477 :ref:`terminal-shortcuts-list`.
492 :ref:`terminal-shortcuts-list`.
478 """.format(
493 """.format(
479 "\n ".join([f"- `{k}`" for k in KEYBINDING_FILTERS])
494 "\n ".join([f"- `{k}`" for k in KEYBINDING_FILTERS])
480 ),
495 ),
481 ).tag(config=True)
496 ).tag(config=True)
482
497
483 @observe("shortcuts")
498 @observe("shortcuts")
484 def _shortcuts_changed(self, change):
499 def _shortcuts_changed(self, change):
485 if self.pt_app:
500 if self.pt_app:
486 self.pt_app.key_bindings = self._merge_shortcuts(user_shortcuts=change.new)
501 self.pt_app.key_bindings = self._merge_shortcuts(user_shortcuts=change.new)
487
502
488 def _merge_shortcuts(self, user_shortcuts):
503 def _merge_shortcuts(self, user_shortcuts):
489 # rebuild the bindings list from scratch
504 # rebuild the bindings list from scratch
490 key_bindings = create_ipython_shortcuts(self)
505 key_bindings = create_ipython_shortcuts(self)
491
506
492 # for now we only allow adding shortcuts for commands which are already
507 # for now we only allow adding shortcuts for commands which are already
493 # registered; this is a security precaution.
508 # registered; this is a security precaution.
494 known_commands = {
509 known_commands = {
495 create_identifier(binding.command): binding.command
510 create_identifier(binding.command): binding.command
496 for binding in KEY_BINDINGS
511 for binding in KEY_BINDINGS
497 }
512 }
498 shortcuts_to_skip = []
513 shortcuts_to_skip = []
499 shortcuts_to_add = []
514 shortcuts_to_add = []
500
515
501 for shortcut in user_shortcuts:
516 for shortcut in user_shortcuts:
502 command_id = shortcut["command"]
517 command_id = shortcut["command"]
503 if command_id not in known_commands:
518 if command_id not in known_commands:
504 allowed_commands = "\n - ".join(known_commands)
519 allowed_commands = "\n - ".join(known_commands)
505 raise ValueError(
520 raise ValueError(
506 f"{command_id} is not a known shortcut command."
521 f"{command_id} is not a known shortcut command."
507 f" Allowed commands are: \n - {allowed_commands}"
522 f" Allowed commands are: \n - {allowed_commands}"
508 )
523 )
509 old_keys = shortcut.get("match_keys", None)
524 old_keys = shortcut.get("match_keys", None)
510 old_filter = (
525 old_filter = (
511 filter_from_string(shortcut["match_filter"])
526 filter_from_string(shortcut["match_filter"])
512 if "match_filter" in shortcut
527 if "match_filter" in shortcut
513 else None
528 else None
514 )
529 )
515 matching = [
530 matching = [
516 binding
531 binding
517 for binding in KEY_BINDINGS
532 for binding in KEY_BINDINGS
518 if (
533 if (
519 (old_filter is None or binding.filter == old_filter)
534 (old_filter is None or binding.filter == old_filter)
520 and (old_keys is None or [k for k in binding.keys] == old_keys)
535 and (old_keys is None or [k for k in binding.keys] == old_keys)
521 and create_identifier(binding.command) == command_id
536 and create_identifier(binding.command) == command_id
522 )
537 )
523 ]
538 ]
524
539
525 new_keys = shortcut.get("new_keys", None)
540 new_keys = shortcut.get("new_keys", None)
526 new_filter = shortcut.get("new_filter", None)
541 new_filter = shortcut.get("new_filter", None)
527
542
528 command = known_commands[command_id]
543 command = known_commands[command_id]
529
544
530 creating_new = shortcut.get("create", False)
545 creating_new = shortcut.get("create", False)
531 modifying_existing = not creating_new and (
546 modifying_existing = not creating_new and (
532 new_keys is not None or new_filter
547 new_keys is not None or new_filter
533 )
548 )
534
549
535 if creating_new and new_keys == []:
550 if creating_new and new_keys == []:
536 raise ValueError("Cannot add a shortcut without keys")
551 raise ValueError("Cannot add a shortcut without keys")
537
552
538 if modifying_existing:
553 if modifying_existing:
539 specification = {
554 specification = {
540 key: shortcut[key]
555 key: shortcut[key]
541 for key in ["command", "filter"]
556 for key in ["command", "filter"]
542 if key in shortcut
557 if key in shortcut
543 }
558 }
544 if len(matching) == 0:
559 if len(matching) == 0:
545 raise ValueError(
560 raise ValueError(
546 f"No shortcuts matching {specification} found in {KEY_BINDINGS}"
561 f"No shortcuts matching {specification} found in {KEY_BINDINGS}"
547 )
562 )
548 elif len(matching) > 1:
563 elif len(matching) > 1:
549 raise ValueError(
564 raise ValueError(
550 f"Multiple shortcuts matching {specification} found,"
565 f"Multiple shortcuts matching {specification} found,"
551 f" please add keys/filter to select one of: {matching}"
566 f" please add keys/filter to select one of: {matching}"
552 )
567 )
553
568
554 matched = matching[0]
569 matched = matching[0]
555 old_filter = matched.filter
570 old_filter = matched.filter
556 old_keys = list(matched.keys)
571 old_keys = list(matched.keys)
557 shortcuts_to_skip.append(
572 shortcuts_to_skip.append(
558 RuntimeBinding(
573 RuntimeBinding(
559 command,
574 command,
560 keys=old_keys,
575 keys=old_keys,
561 filter=old_filter,
576 filter=old_filter,
562 )
577 )
563 )
578 )
564
579
565 if new_keys != []:
580 if new_keys != []:
566 shortcuts_to_add.append(
581 shortcuts_to_add.append(
567 RuntimeBinding(
582 RuntimeBinding(
568 command,
583 command,
569 keys=new_keys or old_keys,
584 keys=new_keys or old_keys,
570 filter=filter_from_string(new_filter)
585 filter=filter_from_string(new_filter)
571 if new_filter is not None
586 if new_filter is not None
572 else (
587 else (
573 old_filter
588 old_filter
574 if old_filter is not None
589 if old_filter is not None
575 else filter_from_string("always")
590 else filter_from_string("always")
576 ),
591 ),
577 )
592 )
578 )
593 )
579
594
580 # rebuild the bindings list from scratch
595 # rebuild the bindings list from scratch
581 key_bindings = create_ipython_shortcuts(self, skip=shortcuts_to_skip)
596 key_bindings = create_ipython_shortcuts(self, skip=shortcuts_to_skip)
582 for binding in shortcuts_to_add:
597 for binding in shortcuts_to_add:
583 add_binding(key_bindings, binding)
598 add_binding(key_bindings, binding)
584
599
585 return key_bindings
600 return key_bindings
586
601
587 prompt_includes_vi_mode = Bool(True,
602 prompt_includes_vi_mode = Bool(True,
588 help="Display the current vi mode (when using vi editing mode)."
603 help="Display the current vi mode (when using vi editing mode)."
589 ).tag(config=True)
604 ).tag(config=True)
590
605
591 prompt_line_number_format = Unicode(
606 prompt_line_number_format = Unicode(
592 "",
607 "",
593 help="The format for line numbering, will be passed `line` (int, 1 based)"
608 help="The format for line numbering, will be passed `line` (int, 1 based)"
594 " the current line number and `rel_line` the relative line number."
609 " the current line number and `rel_line` the relative line number."
595 " for example to display both you can use the following template string :"
610 " for example to display both you can use the following template string :"
596 " c.TerminalInteractiveShell.prompt_line_number_format='{line: 4d}/{rel_line:+03d} | '"
611 " c.TerminalInteractiveShell.prompt_line_number_format='{line: 4d}/{rel_line:+03d} | '"
597 " This will display the current line number, with leading space and a width of at least 4"
612 " This will display the current line number, with leading space and a width of at least 4"
598 " character, as well as the relative line number 0 padded and always with a + or - sign."
613 " character, as well as the relative line number 0 padded and always with a + or - sign."
599 " Note that when using Emacs mode the prompt of the first line may not update.",
614 " Note that when using Emacs mode the prompt of the first line may not update.",
600 ).tag(config=True)
615 ).tag(config=True)
601
616
602 @observe('term_title')
617 @observe('term_title')
603 def init_term_title(self, change=None):
618 def init_term_title(self, change=None):
604 # Enable or disable the terminal title.
619 # Enable or disable the terminal title.
605 if self.term_title and _is_tty:
620 if self.term_title and _is_tty:
606 toggle_set_term_title(True)
621 toggle_set_term_title(True)
607 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
622 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
608 else:
623 else:
609 toggle_set_term_title(False)
624 toggle_set_term_title(False)
610
625
611 def restore_term_title(self):
626 def restore_term_title(self):
612 if self.term_title and _is_tty:
627 if self.term_title and _is_tty:
613 restore_term_title()
628 restore_term_title()
614
629
615 def init_display_formatter(self):
630 def init_display_formatter(self):
616 super(TerminalInteractiveShell, self).init_display_formatter()
631 super(TerminalInteractiveShell, self).init_display_formatter()
617 # terminal only supports plain text
632 # terminal only supports plain text
618 self.display_formatter.active_types = ["text/plain"]
633 self.display_formatter.active_types = ["text/plain"]
619
634
620 def init_prompt_toolkit_cli(self):
635 def init_prompt_toolkit_cli(self):
621 if self.simple_prompt:
636 if self.simple_prompt:
622 # Fall back to plain non-interactive output for tests.
637 # Fall back to plain non-interactive output for tests.
623 # This is very limited.
638 # This is very limited.
624 def prompt():
639 def prompt():
625 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
640 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
626 lines = [input(prompt_text)]
641 lines = [input(prompt_text)]
627 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
642 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
628 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
643 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
629 lines.append( input(prompt_continuation) )
644 lines.append( input(prompt_continuation) )
630 return '\n'.join(lines)
645 return '\n'.join(lines)
631 self.prompt_for_code = prompt
646 self.prompt_for_code = prompt
632 return
647 return
633
648
634 # Set up keyboard shortcuts
649 # Set up keyboard shortcuts
635 key_bindings = self._merge_shortcuts(user_shortcuts=self.shortcuts)
650 key_bindings = self._merge_shortcuts(user_shortcuts=self.shortcuts)
636
651
637 # Pre-populate history from IPython's history database
652 # Pre-populate history from IPython's history database
638 history = PtkHistoryAdapter(self)
653 history = PtkHistoryAdapter(self)
639
654
640 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
655 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
641 self.style = DynamicStyle(lambda: self._style)
656 self.style = DynamicStyle(lambda: self._style)
642
657
643 editing_mode = getattr(EditingMode, self.editing_mode.upper())
658 editing_mode = getattr(EditingMode, self.editing_mode.upper())
644
659
645 self._use_asyncio_inputhook = False
660 self._use_asyncio_inputhook = False
646 self.pt_app = PromptSession(
661 self.pt_app = PromptSession(
647 auto_suggest=self.auto_suggest,
662 auto_suggest=self.auto_suggest,
648 editing_mode=editing_mode,
663 editing_mode=editing_mode,
649 key_bindings=key_bindings,
664 key_bindings=key_bindings,
650 history=history,
665 history=history,
651 completer=IPythonPTCompleter(shell=self),
666 completer=IPythonPTCompleter(shell=self),
652 enable_history_search=self.enable_history_search,
667 enable_history_search=self.enable_history_search,
653 style=self.style,
668 style=self.style,
654 include_default_pygments_style=False,
669 include_default_pygments_style=False,
655 mouse_support=self.mouse_support,
670 mouse_support=self.mouse_support,
656 enable_open_in_editor=self.extra_open_editor_shortcuts,
671 enable_open_in_editor=self.extra_open_editor_shortcuts,
657 color_depth=self.color_depth,
672 color_depth=self.color_depth,
658 tempfile_suffix=".py",
673 tempfile_suffix=".py",
659 **self._extra_prompt_options(),
674 **self._extra_prompt_options(),
660 )
675 )
661 if isinstance(self.auto_suggest, NavigableAutoSuggestFromHistory):
676 if isinstance(self.auto_suggest, NavigableAutoSuggestFromHistory):
662 self.auto_suggest.connect(self.pt_app)
677 self.auto_suggest.connect(self.pt_app)
663
678
664 def _make_style_from_name_or_cls(self, name_or_cls):
679 def _make_style_from_name_or_cls(self, name_or_cls):
665 """
680 """
666 Small wrapper that make an IPython compatible style from a style name
681 Small wrapper that make an IPython compatible style from a style name
667
682
668 We need that to add style for prompt ... etc.
683 We need that to add style for prompt ... etc.
669 """
684 """
670 style_overrides = {}
685 style_overrides = {}
671 if name_or_cls == 'legacy':
686 if name_or_cls == 'legacy':
672 legacy = self.colors.lower()
687 legacy = self.colors.lower()
673 if legacy == 'linux':
688 if legacy == 'linux':
674 style_cls = get_style_by_name('monokai')
689 style_cls = get_style_by_name('monokai')
675 style_overrides = _style_overrides_linux
690 style_overrides = _style_overrides_linux
676 elif legacy == 'lightbg':
691 elif legacy == 'lightbg':
677 style_overrides = _style_overrides_light_bg
692 style_overrides = _style_overrides_light_bg
678 style_cls = get_style_by_name('pastie')
693 style_cls = get_style_by_name('pastie')
679 elif legacy == 'neutral':
694 elif legacy == 'neutral':
680 # The default theme needs to be visible on both a dark background
695 # The default theme needs to be visible on both a dark background
681 # and a light background, because we can't tell what the terminal
696 # and a light background, because we can't tell what the terminal
682 # looks like. These tweaks to the default theme help with that.
697 # looks like. These tweaks to the default theme help with that.
683 style_cls = get_style_by_name('default')
698 style_cls = get_style_by_name('default')
684 style_overrides.update({
699 style_overrides.update({
685 Token.Number: '#ansigreen',
700 Token.Number: '#ansigreen',
686 Token.Operator: 'noinherit',
701 Token.Operator: 'noinherit',
687 Token.String: '#ansiyellow',
702 Token.String: '#ansiyellow',
688 Token.Name.Function: '#ansiblue',
703 Token.Name.Function: '#ansiblue',
689 Token.Name.Class: 'bold #ansiblue',
704 Token.Name.Class: 'bold #ansiblue',
690 Token.Name.Namespace: 'bold #ansiblue',
705 Token.Name.Namespace: 'bold #ansiblue',
691 Token.Name.Variable.Magic: '#ansiblue',
706 Token.Name.Variable.Magic: '#ansiblue',
692 Token.Prompt: '#ansigreen',
707 Token.Prompt: '#ansigreen',
693 Token.PromptNum: '#ansibrightgreen bold',
708 Token.PromptNum: '#ansibrightgreen bold',
694 Token.OutPrompt: '#ansired',
709 Token.OutPrompt: '#ansired',
695 Token.OutPromptNum: '#ansibrightred bold',
710 Token.OutPromptNum: '#ansibrightred bold',
696 })
711 })
697
712
698 # Hack: Due to limited color support on the Windows console
713 # Hack: Due to limited color support on the Windows console
699 # the prompt colors will be wrong without this
714 # the prompt colors will be wrong without this
700 if os.name == 'nt':
715 if os.name == 'nt':
701 style_overrides.update({
716 style_overrides.update({
702 Token.Prompt: '#ansidarkgreen',
717 Token.Prompt: '#ansidarkgreen',
703 Token.PromptNum: '#ansigreen bold',
718 Token.PromptNum: '#ansigreen bold',
704 Token.OutPrompt: '#ansidarkred',
719 Token.OutPrompt: '#ansidarkred',
705 Token.OutPromptNum: '#ansired bold',
720 Token.OutPromptNum: '#ansired bold',
706 })
721 })
707 elif legacy =='nocolor':
722 elif legacy =='nocolor':
708 style_cls=_NoStyle
723 style_cls=_NoStyle
709 style_overrides = {}
724 style_overrides = {}
710 else :
725 else :
711 raise ValueError('Got unknown colors: ', legacy)
726 raise ValueError('Got unknown colors: ', legacy)
712 else :
727 else :
713 if isinstance(name_or_cls, str):
728 if isinstance(name_or_cls, str):
714 style_cls = get_style_by_name(name_or_cls)
729 style_cls = get_style_by_name(name_or_cls)
715 else:
730 else:
716 style_cls = name_or_cls
731 style_cls = name_or_cls
717 style_overrides = {
732 style_overrides = {
718 Token.Prompt: '#ansigreen',
733 Token.Prompt: '#ansigreen',
719 Token.PromptNum: '#ansibrightgreen bold',
734 Token.PromptNum: '#ansibrightgreen bold',
720 Token.OutPrompt: '#ansired',
735 Token.OutPrompt: '#ansired',
721 Token.OutPromptNum: '#ansibrightred bold',
736 Token.OutPromptNum: '#ansibrightred bold',
722 }
737 }
723 style_overrides.update(self.highlighting_style_overrides)
738 style_overrides.update(self.highlighting_style_overrides)
724 style = merge_styles([
739 style = merge_styles([
725 style_from_pygments_cls(style_cls),
740 style_from_pygments_cls(style_cls),
726 style_from_pygments_dict(style_overrides),
741 style_from_pygments_dict(style_overrides),
727 ])
742 ])
728
743
729 return style
744 return style
730
745
731 @property
746 @property
732 def pt_complete_style(self):
747 def pt_complete_style(self):
733 return {
748 return {
734 'multicolumn': CompleteStyle.MULTI_COLUMN,
749 'multicolumn': CompleteStyle.MULTI_COLUMN,
735 'column': CompleteStyle.COLUMN,
750 'column': CompleteStyle.COLUMN,
736 'readlinelike': CompleteStyle.READLINE_LIKE,
751 'readlinelike': CompleteStyle.READLINE_LIKE,
737 }[self.display_completions]
752 }[self.display_completions]
738
753
739 @property
754 @property
740 def color_depth(self):
755 def color_depth(self):
741 return (ColorDepth.TRUE_COLOR if self.true_color else None)
756 return (ColorDepth.TRUE_COLOR if self.true_color else None)
742
757
743 def _extra_prompt_options(self):
758 def _extra_prompt_options(self):
744 """
759 """
745 Return the current layout option for the current Terminal InteractiveShell
760 Return the current layout option for the current Terminal InteractiveShell
746 """
761 """
747 def get_message():
762 def get_message():
748 return PygmentsTokens(self.prompts.in_prompt_tokens())
763 return PygmentsTokens(self.prompts.in_prompt_tokens())
749
764
750 if self.editing_mode == "emacs" and self.prompt_line_number_format == "":
765 if self.editing_mode == "emacs" and self.prompt_line_number_format == "":
751 # with emacs mode the prompt is (usually) static, so we call only
766 # with emacs mode the prompt is (usually) static, so we call only
752 # the function once. With VI mode it can toggle between [ins] and
767 # the function once. With VI mode it can toggle between [ins] and
753 # [nor] so we can't precompute.
768 # [nor] so we can't precompute.
754 # here I'm going to favor the default keybinding which almost
769 # here I'm going to favor the default keybinding which almost
755 # everybody uses to decrease CPU usage.
770 # everybody uses to decrease CPU usage.
756 # if we have issues with users with custom Prompts we can see how to
771 # if we have issues with users with custom Prompts we can see how to
757 # work around this.
772 # work around this.
758 get_message = get_message()
773 get_message = get_message()
759
774
760 options = {
775 options = {
761 "complete_in_thread": False,
776 "complete_in_thread": False,
762 "lexer": IPythonPTLexer(),
777 "lexer": IPythonPTLexer(),
763 "reserve_space_for_menu": self.space_for_menu,
778 "reserve_space_for_menu": self.space_for_menu,
764 "message": get_message,
779 "message": get_message,
765 "prompt_continuation": (
780 "prompt_continuation": (
766 lambda width, lineno, is_soft_wrap: PygmentsTokens(
781 lambda width, lineno, is_soft_wrap: PygmentsTokens(
767 self.prompts.continuation_prompt_tokens(width, lineno=lineno)
782 _backward_compat_continuation_prompt_tokens(
783 self.prompts.continuation_prompt_tokens, width, lineno=lineno
784 )
768 )
785 )
769 ),
786 ),
770 "multiline": True,
787 "multiline": True,
771 "complete_style": self.pt_complete_style,
788 "complete_style": self.pt_complete_style,
772 "input_processors": [
789 "input_processors": [
773 # Highlight matching brackets, but only when this setting is
790 # Highlight matching brackets, but only when this setting is
774 # enabled, and only when the DEFAULT_BUFFER has the focus.
791 # enabled, and only when the DEFAULT_BUFFER has the focus.
775 ConditionalProcessor(
792 ConditionalProcessor(
776 processor=HighlightMatchingBracketProcessor(chars="[](){}"),
793 processor=HighlightMatchingBracketProcessor(chars="[](){}"),
777 filter=HasFocus(DEFAULT_BUFFER)
794 filter=HasFocus(DEFAULT_BUFFER)
778 & ~IsDone()
795 & ~IsDone()
779 & Condition(lambda: self.highlight_matching_brackets),
796 & Condition(lambda: self.highlight_matching_brackets),
780 ),
797 ),
781 # Show auto-suggestion in lines other than the last line.
798 # Show auto-suggestion in lines other than the last line.
782 ConditionalProcessor(
799 ConditionalProcessor(
783 processor=AppendAutoSuggestionInAnyLine(),
800 processor=AppendAutoSuggestionInAnyLine(),
784 filter=HasFocus(DEFAULT_BUFFER)
801 filter=HasFocus(DEFAULT_BUFFER)
785 & ~IsDone()
802 & ~IsDone()
786 & Condition(
803 & Condition(
787 lambda: isinstance(
804 lambda: isinstance(
788 self.auto_suggest, NavigableAutoSuggestFromHistory
805 self.auto_suggest, NavigableAutoSuggestFromHistory
789 )
806 )
790 ),
807 ),
791 ),
808 ),
792 ],
809 ],
793 }
810 }
794 if not PTK3:
811 if not PTK3:
795 options['inputhook'] = self.inputhook
812 options['inputhook'] = self.inputhook
796
813
797 return options
814 return options
798
815
799 def prompt_for_code(self):
816 def prompt_for_code(self):
800 if self.rl_next_input:
817 if self.rl_next_input:
801 default = self.rl_next_input
818 default = self.rl_next_input
802 self.rl_next_input = None
819 self.rl_next_input = None
803 else:
820 else:
804 default = ''
821 default = ''
805
822
806 # In order to make sure that asyncio code written in the
823 # In order to make sure that asyncio code written in the
807 # interactive shell doesn't interfere with the prompt, we run the
824 # interactive shell doesn't interfere with the prompt, we run the
808 # prompt in a different event loop.
825 # prompt in a different event loop.
809 # If we don't do this, people could spawn coroutine with a
826 # If we don't do this, people could spawn coroutine with a
810 # while/true inside which will freeze the prompt.
827 # while/true inside which will freeze the prompt.
811
828
812 with patch_stdout(raw=True):
829 with patch_stdout(raw=True):
813 if self._use_asyncio_inputhook:
830 if self._use_asyncio_inputhook:
814 # When we integrate the asyncio event loop, run the UI in the
831 # When we integrate the asyncio event loop, run the UI in the
815 # same event loop as the rest of the code. don't use an actual
832 # same event loop as the rest of the code. don't use an actual
816 # input hook. (Asyncio is not made for nesting event loops.)
833 # input hook. (Asyncio is not made for nesting event loops.)
817 asyncio_loop = get_asyncio_loop()
834 asyncio_loop = get_asyncio_loop()
818 text = asyncio_loop.run_until_complete(
835 text = asyncio_loop.run_until_complete(
819 self.pt_app.prompt_async(
836 self.pt_app.prompt_async(
820 default=default, **self._extra_prompt_options()
837 default=default, **self._extra_prompt_options()
821 )
838 )
822 )
839 )
823 else:
840 else:
824 text = self.pt_app.prompt(
841 text = self.pt_app.prompt(
825 default=default,
842 default=default,
826 inputhook=self._inputhook,
843 inputhook=self._inputhook,
827 **self._extra_prompt_options(),
844 **self._extra_prompt_options(),
828 )
845 )
829
846
830 return text
847 return text
831
848
832 def enable_win_unicode_console(self):
849 def enable_win_unicode_console(self):
833 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
850 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
834 # console by default, so WUC shouldn't be needed.
851 # console by default, so WUC shouldn't be needed.
835 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
852 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
836 DeprecationWarning,
853 DeprecationWarning,
837 stacklevel=2)
854 stacklevel=2)
838
855
839 def init_io(self):
856 def init_io(self):
840 if sys.platform not in {'win32', 'cli'}:
857 if sys.platform not in {'win32', 'cli'}:
841 return
858 return
842
859
843 import colorama
860 import colorama
844 colorama.init()
861 colorama.init()
845
862
846 def init_magics(self):
863 def init_magics(self):
847 super(TerminalInteractiveShell, self).init_magics()
864 super(TerminalInteractiveShell, self).init_magics()
848 self.register_magics(TerminalMagics)
865 self.register_magics(TerminalMagics)
849
866
850 def init_alias(self):
867 def init_alias(self):
851 # The parent class defines aliases that can be safely used with any
868 # The parent class defines aliases that can be safely used with any
852 # frontend.
869 # frontend.
853 super(TerminalInteractiveShell, self).init_alias()
870 super(TerminalInteractiveShell, self).init_alias()
854
871
855 # Now define aliases that only make sense on the terminal, because they
872 # Now define aliases that only make sense on the terminal, because they
856 # need direct access to the console in a way that we can't emulate in
873 # need direct access to the console in a way that we can't emulate in
857 # GUI or web frontend
874 # GUI or web frontend
858 if os.name == 'posix':
875 if os.name == 'posix':
859 for cmd in ('clear', 'more', 'less', 'man'):
876 for cmd in ('clear', 'more', 'less', 'man'):
860 self.alias_manager.soft_define_alias(cmd, cmd)
877 self.alias_manager.soft_define_alias(cmd, cmd)
861
878
862
879
863 def __init__(self, *args, **kwargs) -> None:
880 def __init__(self, *args, **kwargs) -> None:
864 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
881 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
865 self._set_autosuggestions(self.autosuggestions_provider)
882 self._set_autosuggestions(self.autosuggestions_provider)
866 self.init_prompt_toolkit_cli()
883 self.init_prompt_toolkit_cli()
867 self.init_term_title()
884 self.init_term_title()
868 self.keep_running = True
885 self.keep_running = True
869 self._set_formatter(self.autoformatter)
886 self._set_formatter(self.autoformatter)
870
887
871
888
872 def ask_exit(self):
889 def ask_exit(self):
873 self.keep_running = False
890 self.keep_running = False
874
891
875 rl_next_input = None
892 rl_next_input = None
876
893
877 def interact(self):
894 def interact(self):
878 self.keep_running = True
895 self.keep_running = True
879 while self.keep_running:
896 while self.keep_running:
880 print(self.separate_in, end='')
897 print(self.separate_in, end='')
881
898
882 try:
899 try:
883 code = self.prompt_for_code()
900 code = self.prompt_for_code()
884 except EOFError:
901 except EOFError:
885 if (not self.confirm_exit) \
902 if (not self.confirm_exit) \
886 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
903 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
887 self.ask_exit()
904 self.ask_exit()
888
905
889 else:
906 else:
890 if code:
907 if code:
891 self.run_cell(code, store_history=True)
908 self.run_cell(code, store_history=True)
892
909
893 def mainloop(self):
910 def mainloop(self):
894 # An extra layer of protection in case someone mashing Ctrl-C breaks
911 # An extra layer of protection in case someone mashing Ctrl-C breaks
895 # out of our internal code.
912 # out of our internal code.
896 while True:
913 while True:
897 try:
914 try:
898 self.interact()
915 self.interact()
899 break
916 break
900 except KeyboardInterrupt as e:
917 except KeyboardInterrupt as e:
901 print("\n%s escaped interact()\n" % type(e).__name__)
918 print("\n%s escaped interact()\n" % type(e).__name__)
902 finally:
919 finally:
903 # An interrupt during the eventloop will mess up the
920 # An interrupt during the eventloop will mess up the
904 # internal state of the prompt_toolkit library.
921 # internal state of the prompt_toolkit library.
905 # Stopping the eventloop fixes this, see
922 # Stopping the eventloop fixes this, see
906 # https://github.com/ipython/ipython/pull/9867
923 # https://github.com/ipython/ipython/pull/9867
907 if hasattr(self, '_eventloop'):
924 if hasattr(self, '_eventloop'):
908 self._eventloop.stop()
925 self._eventloop.stop()
909
926
910 self.restore_term_title()
927 self.restore_term_title()
911
928
912 # try to call some at-exit operation optimistically as some things can't
929 # try to call some at-exit operation optimistically as some things can't
913 # be done during interpreter shutdown. this is technically inaccurate as
930 # be done during interpreter shutdown. this is technically inaccurate as
914 # this make mainlool not re-callable, but that should be a rare if not
931 # this make mainlool not re-callable, but that should be a rare if not
915 # in existent use case.
932 # in existent use case.
916
933
917 self._atexit_once()
934 self._atexit_once()
918
935
919
936
920 _inputhook = None
937 _inputhook = None
921 def inputhook(self, context):
938 def inputhook(self, context):
922 if self._inputhook is not None:
939 if self._inputhook is not None:
923 self._inputhook(context)
940 self._inputhook(context)
924
941
925 active_eventloop: Optional[str] = None
942 active_eventloop: Optional[str] = None
926
943
927 def enable_gui(self, gui: Optional[str] = None) -> None:
944 def enable_gui(self, gui: Optional[str] = None) -> None:
928 if self.simple_prompt is True and gui is not None:
945 if self.simple_prompt is True and gui is not None:
929 print(
946 print(
930 f'Cannot install event loop hook for "{gui}" when running with `--simple-prompt`.'
947 f'Cannot install event loop hook for "{gui}" when running with `--simple-prompt`.'
931 )
948 )
932 print(
949 print(
933 "NOTE: Tk is supported natively; use Tk apps and Tk backends with `--simple-prompt`."
950 "NOTE: Tk is supported natively; use Tk apps and Tk backends with `--simple-prompt`."
934 )
951 )
935 return
952 return
936
953
937 if self._inputhook is None and gui is None:
954 if self._inputhook is None and gui is None:
938 print("No event loop hook running.")
955 print("No event loop hook running.")
939 return
956 return
940
957
941 if self._inputhook is not None and gui is not None:
958 if self._inputhook is not None and gui is not None:
942 newev, newinhook = get_inputhook_name_and_func(gui)
959 newev, newinhook = get_inputhook_name_and_func(gui)
943 if self._inputhook == newinhook:
960 if self._inputhook == newinhook:
944 # same inputhook, do nothing
961 # same inputhook, do nothing
945 self.log.info(
962 self.log.info(
946 f"Shell is already running the {self.active_eventloop} eventloop. Doing nothing"
963 f"Shell is already running the {self.active_eventloop} eventloop. Doing nothing"
947 )
964 )
948 return
965 return
949 self.log.warning(
966 self.log.warning(
950 f"Shell is already running a different gui event loop for {self.active_eventloop}. "
967 f"Shell is already running a different gui event loop for {self.active_eventloop}. "
951 "Call with no arguments to disable the current loop."
968 "Call with no arguments to disable the current loop."
952 )
969 )
953 return
970 return
954 if self._inputhook is not None and gui is None:
971 if self._inputhook is not None and gui is None:
955 self.active_eventloop = self._inputhook = None
972 self.active_eventloop = self._inputhook = None
956
973
957 if gui and (gui not in {"inline", "webagg"}):
974 if gui and (gui not in {"inline", "webagg"}):
958 # This hook runs with each cycle of the `prompt_toolkit`'s event loop.
975 # This hook runs with each cycle of the `prompt_toolkit`'s event loop.
959 self.active_eventloop, self._inputhook = get_inputhook_name_and_func(gui)
976 self.active_eventloop, self._inputhook = get_inputhook_name_and_func(gui)
960 else:
977 else:
961 self.active_eventloop = self._inputhook = None
978 self.active_eventloop = self._inputhook = None
962
979
963 self._use_asyncio_inputhook = gui == "asyncio"
980 self._use_asyncio_inputhook = gui == "asyncio"
964
981
965 # Run !system commands directly, not through pipes, so terminal programs
982 # Run !system commands directly, not through pipes, so terminal programs
966 # work correctly.
983 # work correctly.
967 system = InteractiveShell.system_raw
984 system = InteractiveShell.system_raw
968
985
969 def auto_rewrite_input(self, cmd):
986 def auto_rewrite_input(self, cmd):
970 """Overridden from the parent class to use fancy rewriting prompt"""
987 """Overridden from the parent class to use fancy rewriting prompt"""
971 if not self.show_rewritten_input:
988 if not self.show_rewritten_input:
972 return
989 return
973
990
974 tokens = self.prompts.rewrite_prompt_tokens()
991 tokens = self.prompts.rewrite_prompt_tokens()
975 if self.pt_app:
992 if self.pt_app:
976 print_formatted_text(PygmentsTokens(tokens), end='',
993 print_formatted_text(PygmentsTokens(tokens), end='',
977 style=self.pt_app.app.style)
994 style=self.pt_app.app.style)
978 print(cmd)
995 print(cmd)
979 else:
996 else:
980 prompt = ''.join(s for t, s in tokens)
997 prompt = ''.join(s for t, s in tokens)
981 print(prompt, cmd, sep='')
998 print(prompt, cmd, sep='')
982
999
983 _prompts_before = None
1000 _prompts_before = None
984 def switch_doctest_mode(self, mode):
1001 def switch_doctest_mode(self, mode):
985 """Switch prompts to classic for %doctest_mode"""
1002 """Switch prompts to classic for %doctest_mode"""
986 if mode:
1003 if mode:
987 self._prompts_before = self.prompts
1004 self._prompts_before = self.prompts
988 self.prompts = ClassicPrompts(self)
1005 self.prompts = ClassicPrompts(self)
989 elif self._prompts_before:
1006 elif self._prompts_before:
990 self.prompts = self._prompts_before
1007 self.prompts = self._prompts_before
991 self._prompts_before = None
1008 self._prompts_before = None
992 # self._update_layout()
1009 # self._update_layout()
993
1010
994
1011
995 InteractiveShellABC.register(TerminalInteractiveShell)
1012 InteractiveShellABC.register(TerminalInteractiveShell)
996
1013
997 if __name__ == '__main__':
1014 if __name__ == '__main__':
998 TerminalInteractiveShell.instance().interact()
1015 TerminalInteractiveShell.instance().interact()
@@ -1,323 +1,323 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, 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
72 See ``IPython/example/utils/cwd_prompt.py`` for an example of how to write
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
104 ``TerminalInteractiveShell.highlighting_style`` determines prompt colours and
105 syntax highlighting. It takes the name (as a string) or class (as a subclass of
105 syntax highlighting. It takes the name (as a string) or class (as a subclass of
106 ``pygments.style.Style``) of a Pygments style, or the special value ``'legacy'``
106 ``pygments.style.Style``) of a Pygments style, or the special value ``'legacy'``
107 to pick a style in accordance with ``InteractiveShell.colors``.
107 to pick a style in accordance with ``InteractiveShell.colors``.
108
108
109 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::
110
110
111 from pygments.styles import get_all_styles
111 from pygments.styles import get_all_styles
112 list(get_all_styles())
112 list(get_all_styles())
113
113
114 Additionally, ``TerminalInteractiveShell.highlighting_style_overrides`` can override
114 Additionally, ``TerminalInteractiveShell.highlighting_style_overrides`` can override
115 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
116 token types to strings defining the style. See `Pygments' documentation
116 token types to strings defining the style. See `Pygments' documentation
117 <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
118 to define styles.
118 to define styles.
119
119
120 Colors in the pager
120 Colors in the pager
121 -------------------
121 -------------------
122
122
123 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.
124 To configure your default pager to allow these:
124 To configure your default pager to allow these:
125
125
126 1. Set the environment PAGER variable to ``less``.
126 1. Set the environment PAGER variable to ``less``.
127 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
128 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
129 properly interpret control sequences, which is how color
129 properly interpret control sequences, which is how color
130 information is given to your terminal.
130 information is given to your terminal.
131
131
132 .. _editors:
132 .. _editors:
133
133
134 Editor configuration
134 Editor configuration
135 ====================
135 ====================
136
136
137 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:
138
138
139 * Editors (such as `(X)Emacs`_, vim_ and TextMate_) can
139 * Editors (such as `(X)Emacs`_, vim_ and TextMate_) can
140 send code to IPython for execution.
140 send code to IPython for execution.
141
141
142 * 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
143 a code block.
143 a code block.
144
144
145 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
146 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
147 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
148 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
149 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
150 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
151 inside IPython.
151 inside IPython.
152
152
153 You can also control the editor by setting :attr:`TerminalInteractiveShell.editor`
153 You can also control the editor by setting :attr:`TerminalInteractiveShell.editor`
154 in :file:`ipython_config.py`.
154 in :file:`ipython_config.py`.
155
155
156 Vim
156 Vim
157 ---
157 ---
158
158
159 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
160 powerful IPython integration for vim.
160 powerful IPython integration for vim.
161
161
162 .. _emacs:
162 .. _emacs:
163
163
164 (X)Emacs
164 (X)Emacs
165 --------
165 --------
166
166
167 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
168 ``%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
169 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
170 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
171 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
172 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
173 used in your :file:`.emacs` file to enable the server:
173 used in your :file:`.emacs` file to enable the server:
174
174
175 .. code-block:: common-lisp
175 .. code-block:: common-lisp
176
176
177 (defvar server-buffer-clients)
177 (defvar server-buffer-clients)
178 (when (and (fboundp 'server-start) (string-equal (getenv "TERM") 'xterm))
178 (when (and (fboundp 'server-start) (string-equal (getenv "TERM") 'xterm))
179 (server-start)
179 (server-start)
180 (defun fp-kill-server-with-buffer-routine ()
180 (defun fp-kill-server-with-buffer-routine ()
181 (and server-buffer-clients (server-done)))
181 (and server-buffer-clients (server-done)))
182 (add-hook 'kill-buffer-hook 'fp-kill-server-with-buffer-routine))
182 (add-hook 'kill-buffer-hook 'fp-kill-server-with-buffer-routine))
183
183
184 Thanks to the work of Alexander Schmolck and Prabhu Ramachandran,
184 Thanks to the work of Alexander Schmolck and Prabhu Ramachandran,
185 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.
186
186
187 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:
188
188
189 .. code-block:: common-lisp
189 .. code-block:: common-lisp
190
190
191 (require 'python)
191 (require 'python)
192 (setq python-shell-interpreter "ipython")
192 (setq python-shell-interpreter "ipython")
193
193
194 .. _`(X)Emacs`: http://www.gnu.org/software/emacs/
194 .. _`(X)Emacs`: http://www.gnu.org/software/emacs/
195 .. _TextMate: http://macromates.com/
195 .. _TextMate: http://macromates.com/
196 .. _vim: http://www.vim.org/
196 .. _vim: http://www.vim.org/
197
197
198 .. _custom_keyboard_shortcuts:
198 .. _custom_keyboard_shortcuts:
199
199
200 Keyboard Shortcuts
200 Keyboard Shortcuts
201 ==================
201 ==================
202
202
203 .. versionadded:: 8.11
203 .. versionadded:: 8.11
204
204
205 You can modify, disable or modify keyboard shortcuts for IPython Terminal using
205 You can modify, disable or modify keyboard shortcuts for IPython Terminal using
206 :std:configtrait:`TerminalInteractiveShell.shortcuts` traitlet.
206 :std:configtrait:`TerminalInteractiveShell.shortcuts` traitlet.
207
207
208 The list of shortcuts is available in the Configuring IPython :ref:`terminal-shortcuts-list` section.
208 The list of shortcuts is available in the Configuring IPython :ref:`terminal-shortcuts-list` section.
209
209
210 Advanced configuration
210 Advanced configuration
211 ----------------------
211 ----------------------
212
212
213 .. versionchanged:: 5.0
213 .. versionchanged:: 5.0
214
214
215 Creating custom commands requires adding custom code to a
215 Creating custom commands requires adding custom code to a
216 :ref:`startup file <startup_files>`::
216 :ref:`startup file <startup_files>`::
217
217
218 from IPython import get_ipython
218 from IPython import get_ipython
219 from prompt_toolkit.enums import DEFAULT_BUFFER
219 from prompt_toolkit.enums import DEFAULT_BUFFER
220 from prompt_toolkit.keys import Keys
220 from prompt_toolkit.keys import Keys
221 from prompt_toolkit.filters import HasFocus, HasSelection, ViInsertMode, EmacsInsertMode
221 from prompt_toolkit.filters import HasFocus, HasSelection, ViInsertMode, EmacsInsertMode
222
222
223 ip = get_ipython()
223 ip = get_ipython()
224 insert_mode = ViInsertMode() | EmacsInsertMode()
224 insert_mode = ViInsertMode() | EmacsInsertMode()
225
225
226 def insert_unexpected(event):
226 def insert_unexpected(event):
227 buf = event.current_buffer
227 buf = event.current_buffer
228 buf.insert_text('The Spanish Inquisition')
228 buf.insert_text('The Spanish Inquisition')
229 # Register the shortcut if IPython is using prompt_toolkit
229 # Register the shortcut if IPython is using prompt_toolkit
230 if getattr(ip, 'pt_app', None):
230 if getattr(ip, 'pt_app', None):
231 registry = ip.pt_app.key_bindings
231 registry = ip.pt_app.key_bindings
232 registry.add_binding(Keys.ControlN,
232 registry.add_binding(Keys.ControlN,
233 filter=(HasFocus(DEFAULT_BUFFER)
233 filter=(HasFocus(DEFAULT_BUFFER)
234 & ~HasSelection()
234 & ~HasSelection()
235 & insert_mode))(insert_unexpected)
235 & insert_mode))(insert_unexpected)
236
236
237
237
238 Here is a second example that bind the key sequence ``j``, ``k`` to switch to
238 Here is a second example that bind the key sequence ``j``, ``k`` to switch to
239 VI input mode to ``Normal`` when in insert mode::
239 VI input mode to ``Normal`` when in insert mode::
240
240
241 from IPython import get_ipython
241 from IPython import get_ipython
242 from prompt_toolkit.enums import DEFAULT_BUFFER
242 from prompt_toolkit.enums import DEFAULT_BUFFER
243 from prompt_toolkit.filters import HasFocus, ViInsertMode
243 from prompt_toolkit.filters import HasFocus, ViInsertMode
244 from prompt_toolkit.key_binding.vi_state import InputMode
244 from prompt_toolkit.key_binding.vi_state import InputMode
245
245
246 ip = get_ipython()
246 ip = get_ipython()
247
247
248 def switch_to_navigation_mode(event):
248 def switch_to_navigation_mode(event):
249 vi_state = event.cli.vi_state
249 vi_state = event.cli.vi_state
250 vi_state.input_mode = InputMode.NAVIGATION
250 vi_state.input_mode = InputMode.NAVIGATION
251
251
252 if getattr(ip, 'pt_app', None):
252 if getattr(ip, 'pt_app', None):
253 registry = ip.pt_app.key_bindings
253 registry = ip.pt_app.key_bindings
254 registry.add_binding(u'j',u'k',
254 registry.add_binding(u'j',u'k',
255 filter=(HasFocus(DEFAULT_BUFFER)
255 filter=(HasFocus(DEFAULT_BUFFER)
256 & ViInsertMode()))(switch_to_navigation_mode)
256 & ViInsertMode()))(switch_to_navigation_mode)
257
257
258 For more information on filters and what you can do with the ``event`` object,
258 For more information on filters and what you can do with the ``event`` object,
259 `see the prompt_toolkit docs
259 `see the prompt_toolkit docs
260 <https://python-prompt-toolkit.readthedocs.io/en/latest/pages/asking_for_input.html#adding-custom-key-bindings>`__.
260 <https://python-prompt-toolkit.readthedocs.io/en/latest/pages/asking_for_input.html#adding-custom-key-bindings>`__.
261
261
262
262
263 Enter to execute
263 Enter to execute
264 ----------------
264 ----------------
265
265
266 In the Terminal IPython shell – which by default uses the ``prompt_toolkit``
266 In the Terminal IPython shell – which by default uses the ``prompt_toolkit``
267 interface, the semantic meaning of pressing the :kbd:`Enter` key can be
267 interface, the semantic meaning of pressing the :kbd:`Enter` key can be
268 ambiguous. In some case :kbd:`Enter` should execute code, and in others it
268 ambiguous. In some case :kbd:`Enter` should execute code, and in others it
269 should add a new line. IPython uses heuristics to decide whether to execute or
269 should add a new line. IPython uses heuristics to decide whether to execute or
270 insert a new line at cursor position. For example, if we detect that the current
270 insert a new line at cursor position. For example, if we detect that the current
271 code is not valid Python, then the user is likely editing code and the right
271 code is not valid Python, then the user is likely editing code and the right
272 behavior is to likely to insert a new line. If the current code is a simple
272 behavior is to likely to insert a new line. If the current code is a simple
273 statement like `ord('*')`, then the right behavior is likely to execute. Though
273 statement like `ord('*')`, then the right behavior is likely to execute. Though
274 the exact desired semantics often varies from users to users.
274 the exact desired semantics often varies from users to users.
275
275
276 As the exact behavior of :kbd:`Enter` is ambiguous, it has been special cased
276 As the exact behavior of :kbd:`Enter` is ambiguous, it has been special cased
277 to allow users to completely configure the behavior they like. Hence you can
277 to allow users to completely configure the behavior they like. Hence you can
278 have enter always execute code. If you prefer fancier behavior, you need to get
278 have enter always execute code. If you prefer fancier behavior, you need to get
279 your hands dirty and read the ``prompt_toolkit`` and IPython documentation
279 your hands dirty and read the ``prompt_toolkit`` and IPython documentation
280 though. See :ghpull:`10500`, set the
280 though. See :ghpull:`10500`, set the
281 ``c.TerminalInteractiveShell.handle_return`` option and get inspiration from the
281 ``c.TerminalInteractiveShell.handle_return`` option and get inspiration from the
282 following example that only auto-executes the input if it begins with a bang or
282 following example that only auto-executes the input if it begins with a bang or
283 a modulo character (``!`` or ``%``). To use the following code, add it to your
283 a modulo character (``!`` or ``%``). To use the following code, add it to your
284 IPython configuration::
284 IPython configuration::
285
285
286 def custom_return(shell):
286 def custom_return(shell):
287
287
288 """This function is required by the API. It takes a reference to
288 """This function is required by the API. It takes a reference to
289 the shell, which is the same thing `get_ipython()` evaluates to.
289 the shell, which is the same thing `get_ipython()` evaluates to.
290 This function must return a function that handles each keypress
290 This function must return a function that handles each keypress
291 event. That function, named `handle` here, references `shell`
291 event. That function, named `handle` here, references `shell`
292 by closure."""
292 by closure."""
293
293
294 def handle(event):
294 def handle(event):
295
295
296 """This function is called each time `Enter` is pressed,
296 """This function is called each time `Enter` is pressed,
297 and takes a reference to a Prompt Toolkit event object.
297 and takes a reference to a Prompt Toolkit event object.
298 If the current input starts with a bang or modulo, then
298 If the current input starts with a bang or modulo, then
299 the input is executed, otherwise a newline is entered,
299 the input is executed, otherwise a newline is entered,
300 followed by any spaces needed to auto-indent."""
300 followed by any spaces needed to auto-indent."""
301
301
302 # set up a few handy references to nested items...
302 # set up a few handy references to nested items...
303
303
304 buffer = event.current_buffer
304 buffer = event.current_buffer
305 document = buffer.document
305 document = buffer.document
306 text = document.text
306 text = document.text
307
307
308 if text.startswith('!') or text.startswith('%'): # execute the input...
308 if text.startswith('!') or text.startswith('%'): # execute the input...
309
309
310 buffer.accept_action.validate_and_handle(event.cli, buffer)
310 buffer.accept_action.validate_and_handle(event.cli, buffer)
311
311
312 else: # insert a newline with auto-indentation...
312 else: # insert a newline with auto-indentation...
313
313
314 if document.line_count > 1: text = text[:document.cursor_position]
314 if document.line_count > 1: text = text[:document.cursor_position]
315 indent = shell.check_complete(text)[1]
315 indent = shell.check_complete(text)[1]
316 buffer.insert_text('\n' + indent)
316 buffer.insert_text('\n' + indent)
317
317
318 # if you just wanted a plain newline without any indentation, you
318 # if you just wanted a plain newline without any indentation, you
319 # could use `buffer.insert_text('\n')` instead of the lines above
319 # could use `buffer.insert_text('\n')` instead of the lines above
320
320
321 return handle
321 return handle
322
322
323 c.TerminalInteractiveShell.handle_return = custom_return
323 c.TerminalInteractiveShell.handle_return = custom_return
General Comments 0
You need to be logged in to leave comments. Login now