##// END OF EJS Templates
Allow configuring a function to handle return in the terminal...
Thomas Kluyver -
Show More
@@ -1,516 +1,525
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import os
3 import os
4 import sys
4 import sys
5 import warnings
5 import warnings
6 from warnings import warn
6 from warnings import warn
7
7
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils import io
9 from IPython.utils import io
10 from IPython.utils.py3compat import cast_unicode_py2, input
10 from IPython.utils.py3compat import cast_unicode_py2, input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.process import abbrev_cwd
12 from IPython.utils.process import abbrev_cwd
13 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union
13 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any,
16 )
14
17
15 from prompt_toolkit.document import Document
18 from prompt_toolkit.document import Document
16 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
18 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
22 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
20 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.interface import CommandLineInterface
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
24
27
25 from pygments.styles import get_style_by_name, get_all_styles
28 from pygments.styles import get_style_by_name, get_all_styles
26 from pygments.style import Style
29 from pygments.style import Style
27 from pygments.token import Token
30 from pygments.token import Token
28
31
29 from .debugger import TerminalPdb, Pdb
32 from .debugger import TerminalPdb, Pdb
30 from .magics import TerminalMagics
33 from .magics import TerminalMagics
31 from .pt_inputhooks import get_inputhook_name_and_func
34 from .pt_inputhooks import get_inputhook_name_and_func
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
34 from .shortcuts import register_ipython_shortcuts
37 from .shortcuts import register_ipython_shortcuts
35
38
36 DISPLAY_BANNER_DEPRECATED = object()
39 DISPLAY_BANNER_DEPRECATED = object()
37
40
38
41
39 class _NoStyle(Style): pass
42 class _NoStyle(Style): pass
40
43
41
44
42
45
43 _style_overrides_light_bg = {
46 _style_overrides_light_bg = {
44 Token.Prompt: '#0000ff',
47 Token.Prompt: '#0000ff',
45 Token.PromptNum: '#0000ee bold',
48 Token.PromptNum: '#0000ee bold',
46 Token.OutPrompt: '#cc0000',
49 Token.OutPrompt: '#cc0000',
47 Token.OutPromptNum: '#bb0000 bold',
50 Token.OutPromptNum: '#bb0000 bold',
48 }
51 }
49
52
50 _style_overrides_linux = {
53 _style_overrides_linux = {
51 Token.Prompt: '#00cc00',
54 Token.Prompt: '#00cc00',
52 Token.PromptNum: '#00bb00 bold',
55 Token.PromptNum: '#00bb00 bold',
53 Token.OutPrompt: '#cc0000',
56 Token.OutPrompt: '#cc0000',
54 Token.OutPromptNum: '#bb0000 bold',
57 Token.OutPromptNum: '#bb0000 bold',
55 }
58 }
56
59
57
60
58
61
59 def get_default_editor():
62 def get_default_editor():
60 try:
63 try:
61 return os.environ['EDITOR']
64 return os.environ['EDITOR']
62 except KeyError:
65 except KeyError:
63 pass
66 pass
64 except UnicodeError:
67 except UnicodeError:
65 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 warn("$EDITOR environment variable is not pure ASCII. Using platform "
66 "default editor.")
69 "default editor.")
67
70
68 if os.name == 'posix':
71 if os.name == 'posix':
69 return 'vi' # the only one guaranteed to be there!
72 return 'vi' # the only one guaranteed to be there!
70 else:
73 else:
71 return 'notepad' # same in Windows!
74 return 'notepad' # same in Windows!
72
75
73 # conservatively check for tty
76 # conservatively check for tty
74 # overridden streams can result in things like:
77 # overridden streams can result in things like:
75 # - sys.stdin = None
78 # - sys.stdin = None
76 # - no isatty method
79 # - no isatty method
77 for _name in ('stdin', 'stdout', 'stderr'):
80 for _name in ('stdin', 'stdout', 'stderr'):
78 _stream = getattr(sys, _name)
81 _stream = getattr(sys, _name)
79 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
82 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
80 _is_tty = False
83 _is_tty = False
81 break
84 break
82 else:
85 else:
83 _is_tty = True
86 _is_tty = True
84
87
85
88
86 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
89 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
87
90
88 class TerminalInteractiveShell(InteractiveShell):
91 class TerminalInteractiveShell(InteractiveShell):
89 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
92 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
90 'to reserve for the completion menu'
93 'to reserve for the completion menu'
91 ).tag(config=True)
94 ).tag(config=True)
92
95
93 def _space_for_menu_changed(self, old, new):
96 def _space_for_menu_changed(self, old, new):
94 self._update_layout()
97 self._update_layout()
95
98
96 pt_cli = None
99 pt_cli = None
97 debugger_history = None
100 debugger_history = None
98 _pt_app = None
101 _pt_app = None
99
102
100 simple_prompt = Bool(_use_simple_prompt,
103 simple_prompt = Bool(_use_simple_prompt,
101 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
104 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
102
105
103 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
104 IPython own testing machinery, and emacs inferior-shell integration through elpy.
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
105
108
106 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
107 environment variable is set, or the current terminal is not a tty."""
110 environment variable is set, or the current terminal is not a tty."""
108 ).tag(config=True)
111 ).tag(config=True)
109
112
110 @property
113 @property
111 def debugger_cls(self):
114 def debugger_cls(self):
112 return Pdb if self.simple_prompt else TerminalPdb
115 return Pdb if self.simple_prompt else TerminalPdb
113
116
114 confirm_exit = Bool(True,
117 confirm_exit = Bool(True,
115 help="""
118 help="""
116 Set to confirm when you try to exit IPython with an EOF (Control-D
119 Set to confirm when you try to exit IPython with an EOF (Control-D
117 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
120 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
118 you can force a direct exit without any confirmation.""",
121 you can force a direct exit without any confirmation.""",
119 ).tag(config=True)
122 ).tag(config=True)
120
123
121 editing_mode = Unicode('emacs',
124 editing_mode = Unicode('emacs',
122 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
125 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
123 ).tag(config=True)
126 ).tag(config=True)
124
127
125 mouse_support = Bool(False,
128 mouse_support = Bool(False,
126 help="Enable mouse support in the prompt"
129 help="Enable mouse support in the prompt"
127 ).tag(config=True)
130 ).tag(config=True)
128
131
129 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
132 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
130 help="""The name or class of a Pygments style to use for syntax
133 help="""The name or class of a Pygments style to use for syntax
131 highlighting: \n %s""" % ', '.join(get_all_styles())
134 highlighting: \n %s""" % ', '.join(get_all_styles())
132 ).tag(config=True)
135 ).tag(config=True)
133
136
134
137
135 @observe('highlighting_style')
138 @observe('highlighting_style')
136 @observe('colors')
139 @observe('colors')
137 def _highlighting_style_changed(self, change):
140 def _highlighting_style_changed(self, change):
138 self.refresh_style()
141 self.refresh_style()
139
142
140 def refresh_style(self):
143 def refresh_style(self):
141 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
144 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
142
145
143
146
144 highlighting_style_overrides = Dict(
147 highlighting_style_overrides = Dict(
145 help="Override highlighting format for specific tokens"
148 help="Override highlighting format for specific tokens"
146 ).tag(config=True)
149 ).tag(config=True)
147
150
148 true_color = Bool(False,
151 true_color = Bool(False,
149 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
152 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
150 "If your terminal supports true color, the following command "
153 "If your terminal supports true color, the following command "
151 "should print 'TRUECOLOR' in orange: "
154 "should print 'TRUECOLOR' in orange: "
152 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
155 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
153 ).tag(config=True)
156 ).tag(config=True)
154
157
155 editor = Unicode(get_default_editor(),
158 editor = Unicode(get_default_editor(),
156 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
159 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
157 ).tag(config=True)
160 ).tag(config=True)
158
161
159 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
162 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
160
163
161 prompts = Instance(Prompts)
164 prompts = Instance(Prompts)
162
165
163 @default('prompts')
166 @default('prompts')
164 def _prompts_default(self):
167 def _prompts_default(self):
165 return self.prompts_class(self)
168 return self.prompts_class(self)
166
169
167 @observe('prompts')
170 @observe('prompts')
168 def _(self, change):
171 def _(self, change):
169 self._update_layout()
172 self._update_layout()
170
173
171 @default('displayhook_class')
174 @default('displayhook_class')
172 def _displayhook_class_default(self):
175 def _displayhook_class_default(self):
173 return RichPromptDisplayHook
176 return RichPromptDisplayHook
174
177
175 term_title = Bool(True,
178 term_title = Bool(True,
176 help="Automatically set the terminal title"
179 help="Automatically set the terminal title"
177 ).tag(config=True)
180 ).tag(config=True)
178
181
179 display_completions = Enum(('column', 'multicolumn','readlinelike'),
182 display_completions = Enum(('column', 'multicolumn','readlinelike'),
180 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
183 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
181 "'readlinelike'. These options are for `prompt_toolkit`, see "
184 "'readlinelike'. These options are for `prompt_toolkit`, see "
182 "`prompt_toolkit` documentation for more information."
185 "`prompt_toolkit` documentation for more information."
183 ),
186 ),
184 default_value='multicolumn').tag(config=True)
187 default_value='multicolumn').tag(config=True)
185
188
186 highlight_matching_brackets = Bool(True,
189 highlight_matching_brackets = Bool(True,
187 help="Highlight matching brackets.",
190 help="Highlight matching brackets.",
188 ).tag(config=True)
191 ).tag(config=True)
189
192
190 extra_open_editor_shortcuts = Bool(False,
193 extra_open_editor_shortcuts = Bool(False,
191 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
194 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
192 "This is in addition to the F2 binding, which is always enabled."
195 "This is in addition to the F2 binding, which is always enabled."
193 ).tag(config=True)
196 ).tag(config=True)
194
197
198 handle_return = Any(None,
199 help="Provide an alternative handler to be called when the user presses "
200 "Return. This is an advanced option intended for debugging, which "
201 "may be changed or removed in later releases."
202 ).tag(config=True)
203
195 @observe('term_title')
204 @observe('term_title')
196 def init_term_title(self, change=None):
205 def init_term_title(self, change=None):
197 # Enable or disable the terminal title.
206 # Enable or disable the terminal title.
198 if self.term_title:
207 if self.term_title:
199 toggle_set_term_title(True)
208 toggle_set_term_title(True)
200 set_term_title('IPython: ' + abbrev_cwd())
209 set_term_title('IPython: ' + abbrev_cwd())
201 else:
210 else:
202 toggle_set_term_title(False)
211 toggle_set_term_title(False)
203
212
204 def init_display_formatter(self):
213 def init_display_formatter(self):
205 super(TerminalInteractiveShell, self).init_display_formatter()
214 super(TerminalInteractiveShell, self).init_display_formatter()
206 # terminal only supports plain text
215 # terminal only supports plain text
207 self.display_formatter.active_types = ['text/plain']
216 self.display_formatter.active_types = ['text/plain']
208 # disable `_ipython_display_`
217 # disable `_ipython_display_`
209 self.display_formatter.ipython_display_formatter.enabled = False
218 self.display_formatter.ipython_display_formatter.enabled = False
210
219
211 def init_prompt_toolkit_cli(self):
220 def init_prompt_toolkit_cli(self):
212 if self.simple_prompt:
221 if self.simple_prompt:
213 # Fall back to plain non-interactive output for tests.
222 # Fall back to plain non-interactive output for tests.
214 # This is very limited, and only accepts a single line.
223 # This is very limited, and only accepts a single line.
215 def prompt():
224 def prompt():
216 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
225 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
217 self.prompt_for_code = prompt
226 self.prompt_for_code = prompt
218 return
227 return
219
228
220 # Set up keyboard shortcuts
229 # Set up keyboard shortcuts
221 kbmanager = KeyBindingManager.for_prompt(
230 kbmanager = KeyBindingManager.for_prompt(
222 enable_open_in_editor=self.extra_open_editor_shortcuts,
231 enable_open_in_editor=self.extra_open_editor_shortcuts,
223 )
232 )
224 register_ipython_shortcuts(kbmanager.registry, self)
233 register_ipython_shortcuts(kbmanager.registry, self)
225
234
226 # Pre-populate history from IPython's history database
235 # Pre-populate history from IPython's history database
227 history = InMemoryHistory()
236 history = InMemoryHistory()
228 last_cell = u""
237 last_cell = u""
229 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
238 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
230 include_latest=True):
239 include_latest=True):
231 # Ignore blank lines and consecutive duplicates
240 # Ignore blank lines and consecutive duplicates
232 cell = cell.rstrip()
241 cell = cell.rstrip()
233 if cell and (cell != last_cell):
242 if cell and (cell != last_cell):
234 history.append(cell)
243 history.append(cell)
235 last_cell = cell
244 last_cell = cell
236
245
237 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
246 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
238 self.style = DynamicStyle(lambda: self._style)
247 self.style = DynamicStyle(lambda: self._style)
239
248
240 editing_mode = getattr(EditingMode, self.editing_mode.upper())
249 editing_mode = getattr(EditingMode, self.editing_mode.upper())
241
250
242 def patch_stdout(**kwargs):
251 def patch_stdout(**kwargs):
243 return self.pt_cli.patch_stdout_context(**kwargs)
252 return self.pt_cli.patch_stdout_context(**kwargs)
244
253
245 self._pt_app = create_prompt_application(
254 self._pt_app = create_prompt_application(
246 editing_mode=editing_mode,
255 editing_mode=editing_mode,
247 key_bindings_registry=kbmanager.registry,
256 key_bindings_registry=kbmanager.registry,
248 history=history,
257 history=history,
249 completer=IPythonPTCompleter(shell=self,
258 completer=IPythonPTCompleter(shell=self,
250 patch_stdout=patch_stdout),
259 patch_stdout=patch_stdout),
251 enable_history_search=True,
260 enable_history_search=True,
252 style=self.style,
261 style=self.style,
253 mouse_support=self.mouse_support,
262 mouse_support=self.mouse_support,
254 **self._layout_options()
263 **self._layout_options()
255 )
264 )
256 self._eventloop = create_eventloop(self.inputhook)
265 self._eventloop = create_eventloop(self.inputhook)
257 self.pt_cli = CommandLineInterface(
266 self.pt_cli = CommandLineInterface(
258 self._pt_app, eventloop=self._eventloop,
267 self._pt_app, eventloop=self._eventloop,
259 output=create_output(true_color=self.true_color))
268 output=create_output(true_color=self.true_color))
260
269
261 def _make_style_from_name_or_cls(self, name_or_cls):
270 def _make_style_from_name_or_cls(self, name_or_cls):
262 """
271 """
263 Small wrapper that make an IPython compatible style from a style name
272 Small wrapper that make an IPython compatible style from a style name
264
273
265 We need that to add style for prompt ... etc.
274 We need that to add style for prompt ... etc.
266 """
275 """
267 style_overrides = {}
276 style_overrides = {}
268 if name_or_cls == 'legacy':
277 if name_or_cls == 'legacy':
269 legacy = self.colors.lower()
278 legacy = self.colors.lower()
270 if legacy == 'linux':
279 if legacy == 'linux':
271 style_cls = get_style_by_name('monokai')
280 style_cls = get_style_by_name('monokai')
272 style_overrides = _style_overrides_linux
281 style_overrides = _style_overrides_linux
273 elif legacy == 'lightbg':
282 elif legacy == 'lightbg':
274 style_overrides = _style_overrides_light_bg
283 style_overrides = _style_overrides_light_bg
275 style_cls = get_style_by_name('pastie')
284 style_cls = get_style_by_name('pastie')
276 elif legacy == 'neutral':
285 elif legacy == 'neutral':
277 # The default theme needs to be visible on both a dark background
286 # The default theme needs to be visible on both a dark background
278 # and a light background, because we can't tell what the terminal
287 # and a light background, because we can't tell what the terminal
279 # looks like. These tweaks to the default theme help with that.
288 # looks like. These tweaks to the default theme help with that.
280 style_cls = get_style_by_name('default')
289 style_cls = get_style_by_name('default')
281 style_overrides.update({
290 style_overrides.update({
282 Token.Number: '#007700',
291 Token.Number: '#007700',
283 Token.Operator: 'noinherit',
292 Token.Operator: 'noinherit',
284 Token.String: '#BB6622',
293 Token.String: '#BB6622',
285 Token.Name.Function: '#2080D0',
294 Token.Name.Function: '#2080D0',
286 Token.Name.Class: 'bold #2080D0',
295 Token.Name.Class: 'bold #2080D0',
287 Token.Name.Namespace: 'bold #2080D0',
296 Token.Name.Namespace: 'bold #2080D0',
288 Token.Prompt: '#009900',
297 Token.Prompt: '#009900',
289 Token.PromptNum: '#00ff00 bold',
298 Token.PromptNum: '#00ff00 bold',
290 Token.OutPrompt: '#990000',
299 Token.OutPrompt: '#990000',
291 Token.OutPromptNum: '#ff0000 bold',
300 Token.OutPromptNum: '#ff0000 bold',
292 })
301 })
293
302
294 # Hack: Due to limited color support on the Windows console
303 # Hack: Due to limited color support on the Windows console
295 # the prompt colors will be wrong without this
304 # the prompt colors will be wrong without this
296 if os.name == 'nt':
305 if os.name == 'nt':
297 style_overrides.update({
306 style_overrides.update({
298 Token.Prompt: '#ansidarkgreen',
307 Token.Prompt: '#ansidarkgreen',
299 Token.PromptNum: '#ansigreen bold',
308 Token.PromptNum: '#ansigreen bold',
300 Token.OutPrompt: '#ansidarkred',
309 Token.OutPrompt: '#ansidarkred',
301 Token.OutPromptNum: '#ansired bold',
310 Token.OutPromptNum: '#ansired bold',
302 })
311 })
303 elif legacy =='nocolor':
312 elif legacy =='nocolor':
304 style_cls=_NoStyle
313 style_cls=_NoStyle
305 style_overrides = {}
314 style_overrides = {}
306 else :
315 else :
307 raise ValueError('Got unknown colors: ', legacy)
316 raise ValueError('Got unknown colors: ', legacy)
308 else :
317 else :
309 if isinstance(name_or_cls, str):
318 if isinstance(name_or_cls, str):
310 style_cls = get_style_by_name(name_or_cls)
319 style_cls = get_style_by_name(name_or_cls)
311 else:
320 else:
312 style_cls = name_or_cls
321 style_cls = name_or_cls
313 style_overrides = {
322 style_overrides = {
314 Token.Prompt: '#009900',
323 Token.Prompt: '#009900',
315 Token.PromptNum: '#00ff00 bold',
324 Token.PromptNum: '#00ff00 bold',
316 Token.OutPrompt: '#990000',
325 Token.OutPrompt: '#990000',
317 Token.OutPromptNum: '#ff0000 bold',
326 Token.OutPromptNum: '#ff0000 bold',
318 }
327 }
319 style_overrides.update(self.highlighting_style_overrides)
328 style_overrides.update(self.highlighting_style_overrides)
320 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
329 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
321 style_dict=style_overrides)
330 style_dict=style_overrides)
322
331
323 return style
332 return style
324
333
325 def _layout_options(self):
334 def _layout_options(self):
326 """
335 """
327 Return the current layout option for the current Terminal InteractiveShell
336 Return the current layout option for the current Terminal InteractiveShell
328 """
337 """
329 return {
338 return {
330 'lexer':IPythonPTLexer(),
339 'lexer':IPythonPTLexer(),
331 'reserve_space_for_menu':self.space_for_menu,
340 'reserve_space_for_menu':self.space_for_menu,
332 'get_prompt_tokens':self.prompts.in_prompt_tokens,
341 'get_prompt_tokens':self.prompts.in_prompt_tokens,
333 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
342 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
334 'multiline':True,
343 'multiline':True,
335 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
344 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
336
345
337 # Highlight matching brackets, but only when this setting is
346 # Highlight matching brackets, but only when this setting is
338 # enabled, and only when the DEFAULT_BUFFER has the focus.
347 # enabled, and only when the DEFAULT_BUFFER has the focus.
339 'extra_input_processors': [ConditionalProcessor(
348 'extra_input_processors': [ConditionalProcessor(
340 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
349 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
341 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
350 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
342 Condition(lambda cli: self.highlight_matching_brackets))],
351 Condition(lambda cli: self.highlight_matching_brackets))],
343 }
352 }
344
353
345 def _update_layout(self):
354 def _update_layout(self):
346 """
355 """
347 Ask for a re computation of the application layout, if for example ,
356 Ask for a re computation of the application layout, if for example ,
348 some configuration options have changed.
357 some configuration options have changed.
349 """
358 """
350 if self._pt_app:
359 if self._pt_app:
351 self._pt_app.layout = create_prompt_layout(**self._layout_options())
360 self._pt_app.layout = create_prompt_layout(**self._layout_options())
352
361
353 def prompt_for_code(self):
362 def prompt_for_code(self):
354 document = self.pt_cli.run(
363 document = self.pt_cli.run(
355 pre_run=self.pre_prompt, reset_current_buffer=True)
364 pre_run=self.pre_prompt, reset_current_buffer=True)
356 return document.text
365 return document.text
357
366
358 def enable_win_unicode_console(self):
367 def enable_win_unicode_console(self):
359 if sys.version_info >= (3, 6):
368 if sys.version_info >= (3, 6):
360 # Since PEP 528, Python uses the unicode APIs for the Windows
369 # Since PEP 528, Python uses the unicode APIs for the Windows
361 # console by default, so WUC shouldn't be needed.
370 # console by default, so WUC shouldn't be needed.
362 return
371 return
363
372
364 import win_unicode_console
373 import win_unicode_console
365 win_unicode_console.enable()
374 win_unicode_console.enable()
366
375
367 def init_io(self):
376 def init_io(self):
368 if sys.platform not in {'win32', 'cli'}:
377 if sys.platform not in {'win32', 'cli'}:
369 return
378 return
370
379
371 self.enable_win_unicode_console()
380 self.enable_win_unicode_console()
372
381
373 import colorama
382 import colorama
374 colorama.init()
383 colorama.init()
375
384
376 # For some reason we make these wrappers around stdout/stderr.
385 # For some reason we make these wrappers around stdout/stderr.
377 # For now, we need to reset them so all output gets coloured.
386 # For now, we need to reset them so all output gets coloured.
378 # https://github.com/ipython/ipython/issues/8669
387 # https://github.com/ipython/ipython/issues/8669
379 # io.std* are deprecated, but don't show our own deprecation warnings
388 # io.std* are deprecated, but don't show our own deprecation warnings
380 # during initialization of the deprecated API.
389 # during initialization of the deprecated API.
381 with warnings.catch_warnings():
390 with warnings.catch_warnings():
382 warnings.simplefilter('ignore', DeprecationWarning)
391 warnings.simplefilter('ignore', DeprecationWarning)
383 io.stdout = io.IOStream(sys.stdout)
392 io.stdout = io.IOStream(sys.stdout)
384 io.stderr = io.IOStream(sys.stderr)
393 io.stderr = io.IOStream(sys.stderr)
385
394
386 def init_magics(self):
395 def init_magics(self):
387 super(TerminalInteractiveShell, self).init_magics()
396 super(TerminalInteractiveShell, self).init_magics()
388 self.register_magics(TerminalMagics)
397 self.register_magics(TerminalMagics)
389
398
390 def init_alias(self):
399 def init_alias(self):
391 # The parent class defines aliases that can be safely used with any
400 # The parent class defines aliases that can be safely used with any
392 # frontend.
401 # frontend.
393 super(TerminalInteractiveShell, self).init_alias()
402 super(TerminalInteractiveShell, self).init_alias()
394
403
395 # Now define aliases that only make sense on the terminal, because they
404 # Now define aliases that only make sense on the terminal, because they
396 # need direct access to the console in a way that we can't emulate in
405 # need direct access to the console in a way that we can't emulate in
397 # GUI or web frontend
406 # GUI or web frontend
398 if os.name == 'posix':
407 if os.name == 'posix':
399 for cmd in ['clear', 'more', 'less', 'man']:
408 for cmd in ['clear', 'more', 'less', 'man']:
400 self.alias_manager.soft_define_alias(cmd, cmd)
409 self.alias_manager.soft_define_alias(cmd, cmd)
401
410
402
411
403 def __init__(self, *args, **kwargs):
412 def __init__(self, *args, **kwargs):
404 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
413 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
405 self.init_prompt_toolkit_cli()
414 self.init_prompt_toolkit_cli()
406 self.init_term_title()
415 self.init_term_title()
407 self.keep_running = True
416 self.keep_running = True
408
417
409 self.debugger_history = InMemoryHistory()
418 self.debugger_history = InMemoryHistory()
410
419
411 def ask_exit(self):
420 def ask_exit(self):
412 self.keep_running = False
421 self.keep_running = False
413
422
414 rl_next_input = None
423 rl_next_input = None
415
424
416 def pre_prompt(self):
425 def pre_prompt(self):
417 if self.rl_next_input:
426 if self.rl_next_input:
418 # We can't set the buffer here, because it will be reset just after
427 # We can't set the buffer here, because it will be reset just after
419 # this. Adding a callable to pre_run_callables does what we need
428 # this. Adding a callable to pre_run_callables does what we need
420 # after the buffer is reset.
429 # after the buffer is reset.
421 s = cast_unicode_py2(self.rl_next_input)
430 s = cast_unicode_py2(self.rl_next_input)
422 def set_doc():
431 def set_doc():
423 self.pt_cli.application.buffer.document = Document(s)
432 self.pt_cli.application.buffer.document = Document(s)
424 if hasattr(self.pt_cli, 'pre_run_callables'):
433 if hasattr(self.pt_cli, 'pre_run_callables'):
425 self.pt_cli.pre_run_callables.append(set_doc)
434 self.pt_cli.pre_run_callables.append(set_doc)
426 else:
435 else:
427 # Older version of prompt_toolkit; it's OK to set the document
436 # Older version of prompt_toolkit; it's OK to set the document
428 # directly here.
437 # directly here.
429 set_doc()
438 set_doc()
430 self.rl_next_input = None
439 self.rl_next_input = None
431
440
432 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
441 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
433
442
434 if display_banner is not DISPLAY_BANNER_DEPRECATED:
443 if display_banner is not DISPLAY_BANNER_DEPRECATED:
435 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
444 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
436
445
437 self.keep_running = True
446 self.keep_running = True
438 while self.keep_running:
447 while self.keep_running:
439 print(self.separate_in, end='')
448 print(self.separate_in, end='')
440
449
441 try:
450 try:
442 code = self.prompt_for_code()
451 code = self.prompt_for_code()
443 except EOFError:
452 except EOFError:
444 if (not self.confirm_exit) \
453 if (not self.confirm_exit) \
445 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
454 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
446 self.ask_exit()
455 self.ask_exit()
447
456
448 else:
457 else:
449 if code:
458 if code:
450 self.run_cell(code, store_history=True)
459 self.run_cell(code, store_history=True)
451
460
452 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
461 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
453 # An extra layer of protection in case someone mashing Ctrl-C breaks
462 # An extra layer of protection in case someone mashing Ctrl-C breaks
454 # out of our internal code.
463 # out of our internal code.
455 if display_banner is not DISPLAY_BANNER_DEPRECATED:
464 if display_banner is not DISPLAY_BANNER_DEPRECATED:
456 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
465 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
457 while True:
466 while True:
458 try:
467 try:
459 self.interact()
468 self.interact()
460 break
469 break
461 except KeyboardInterrupt as e:
470 except KeyboardInterrupt as e:
462 print("\n%s escaped interact()\n" % type(e).__name__)
471 print("\n%s escaped interact()\n" % type(e).__name__)
463 finally:
472 finally:
464 # An interrupt during the eventloop will mess up the
473 # An interrupt during the eventloop will mess up the
465 # internal state of the prompt_toolkit library.
474 # internal state of the prompt_toolkit library.
466 # Stopping the eventloop fixes this, see
475 # Stopping the eventloop fixes this, see
467 # https://github.com/ipython/ipython/pull/9867
476 # https://github.com/ipython/ipython/pull/9867
468 if hasattr(self, '_eventloop'):
477 if hasattr(self, '_eventloop'):
469 self._eventloop.stop()
478 self._eventloop.stop()
470
479
471 _inputhook = None
480 _inputhook = None
472 def inputhook(self, context):
481 def inputhook(self, context):
473 if self._inputhook is not None:
482 if self._inputhook is not None:
474 self._inputhook(context)
483 self._inputhook(context)
475
484
476 active_eventloop = None
485 active_eventloop = None
477 def enable_gui(self, gui=None):
486 def enable_gui(self, gui=None):
478 if gui:
487 if gui:
479 self.active_eventloop, self._inputhook =\
488 self.active_eventloop, self._inputhook =\
480 get_inputhook_name_and_func(gui)
489 get_inputhook_name_and_func(gui)
481 else:
490 else:
482 self.active_eventloop = self._inputhook = None
491 self.active_eventloop = self._inputhook = None
483
492
484 # Run !system commands directly, not through pipes, so terminal programs
493 # Run !system commands directly, not through pipes, so terminal programs
485 # work correctly.
494 # work correctly.
486 system = InteractiveShell.system_raw
495 system = InteractiveShell.system_raw
487
496
488 def auto_rewrite_input(self, cmd):
497 def auto_rewrite_input(self, cmd):
489 """Overridden from the parent class to use fancy rewriting prompt"""
498 """Overridden from the parent class to use fancy rewriting prompt"""
490 if not self.show_rewritten_input:
499 if not self.show_rewritten_input:
491 return
500 return
492
501
493 tokens = self.prompts.rewrite_prompt_tokens()
502 tokens = self.prompts.rewrite_prompt_tokens()
494 if self.pt_cli:
503 if self.pt_cli:
495 self.pt_cli.print_tokens(tokens)
504 self.pt_cli.print_tokens(tokens)
496 print(cmd)
505 print(cmd)
497 else:
506 else:
498 prompt = ''.join(s for t, s in tokens)
507 prompt = ''.join(s for t, s in tokens)
499 print(prompt, cmd, sep='')
508 print(prompt, cmd, sep='')
500
509
501 _prompts_before = None
510 _prompts_before = None
502 def switch_doctest_mode(self, mode):
511 def switch_doctest_mode(self, mode):
503 """Switch prompts to classic for %doctest_mode"""
512 """Switch prompts to classic for %doctest_mode"""
504 if mode:
513 if mode:
505 self._prompts_before = self.prompts
514 self._prompts_before = self.prompts
506 self.prompts = ClassicPrompts(self)
515 self.prompts = ClassicPrompts(self)
507 elif self._prompts_before:
516 elif self._prompts_before:
508 self.prompts = self._prompts_before
517 self.prompts = self._prompts_before
509 self._prompts_before = None
518 self._prompts_before = None
510 self._update_layout()
519 self._update_layout()
511
520
512
521
513 InteractiveShellABC.register(TerminalInteractiveShell)
522 InteractiveShellABC.register(TerminalInteractiveShell)
514
523
515 if __name__ == '__main__':
524 if __name__ == '__main__':
516 TerminalInteractiveShell.instance().interact()
525 TerminalInteractiveShell.instance().interact()
@@ -1,246 +1,251
1 """
1 """
2 Module to define and register Terminal IPython shortcuts with
2 Module to define and register Terminal IPython shortcuts with
3 :mod:`prompt_toolkit`
3 :mod:`prompt_toolkit`
4 """
4 """
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import warnings
9 import warnings
10 import signal
10 import signal
11 import sys
11 import sys
12 from typing import Callable
12 from typing import Callable
13
13
14
14
15 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
15 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
16 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
16 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
17 ViInsertMode, EmacsInsertMode, HasCompletions)
17 ViInsertMode, EmacsInsertMode, HasCompletions)
18 from prompt_toolkit.filters.cli import ViMode, ViNavigationMode
18 from prompt_toolkit.filters.cli import ViMode, ViNavigationMode
19 from prompt_toolkit.keys import Keys
19 from prompt_toolkit.keys import Keys
20 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
20 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
21
21
22 from IPython.utils.decorators import undoc
22 from IPython.utils.decorators import undoc
23
23
24 @undoc
24 @undoc
25 @Condition
25 @Condition
26 def cursor_in_leading_ws(cli):
26 def cursor_in_leading_ws(cli):
27 before = cli.application.buffer.document.current_line_before_cursor
27 before = cli.application.buffer.document.current_line_before_cursor
28 return (not before) or before.isspace()
28 return (not before) or before.isspace()
29
29
30 def register_ipython_shortcuts(registry, shell):
30 def register_ipython_shortcuts(registry, shell):
31 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
31 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
32 insert_mode = ViInsertMode() | EmacsInsertMode()
32 insert_mode = ViInsertMode() | EmacsInsertMode()
33
33
34 if getattr(shell, 'handle_return', None):
35 return_handler = shell.handle_return(shell)
36 else:
37 return_handler = newline_or_execute_outer(shell)
38
34 # Ctrl+J == Enter, seemingly
39 # Ctrl+J == Enter, seemingly
35 registry.add_binding(Keys.ControlJ,
40 registry.add_binding(Keys.ControlJ,
36 filter=(HasFocus(DEFAULT_BUFFER)
41 filter=(HasFocus(DEFAULT_BUFFER)
37 & ~HasSelection()
42 & ~HasSelection()
38 & insert_mode
43 & insert_mode
39 ))(newline_or_execute_outer(shell))
44 ))(return_handler)
40
45
41 registry.add_binding(Keys.ControlBackslash)(force_exit)
46 registry.add_binding(Keys.ControlBackslash)(force_exit)
42
47
43 registry.add_binding(Keys.ControlP,
48 registry.add_binding(Keys.ControlP,
44 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
49 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
45 ))(previous_history_or_previous_completion)
50 ))(previous_history_or_previous_completion)
46
51
47 registry.add_binding(Keys.ControlN,
52 registry.add_binding(Keys.ControlN,
48 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
53 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
49 ))(next_history_or_next_completion)
54 ))(next_history_or_next_completion)
50
55
51 registry.add_binding(Keys.ControlG,
56 registry.add_binding(Keys.ControlG,
52 filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions()
57 filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions()
53 ))(dismiss_completion)
58 ))(dismiss_completion)
54
59
55 registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)
60 registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)
56 )(reset_buffer)
61 )(reset_buffer)
57
62
58 registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)
63 registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)
59 )(reset_search_buffer)
64 )(reset_search_buffer)
60
65
61 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
66 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
62 registry.add_binding(Keys.ControlZ, filter=supports_suspend
67 registry.add_binding(Keys.ControlZ, filter=supports_suspend
63 )(suspend_to_bg)
68 )(suspend_to_bg)
64
69
65 # Ctrl+I == Tab
70 # Ctrl+I == Tab
66 registry.add_binding(Keys.ControlI,
71 registry.add_binding(Keys.ControlI,
67 filter=(HasFocus(DEFAULT_BUFFER)
72 filter=(HasFocus(DEFAULT_BUFFER)
68 & ~HasSelection()
73 & ~HasSelection()
69 & insert_mode
74 & insert_mode
70 & cursor_in_leading_ws
75 & cursor_in_leading_ws
71 ))(indent_buffer)
76 ))(indent_buffer)
72
77
73 registry.add_binding(Keys.ControlO,
78 registry.add_binding(Keys.ControlO,
74 filter=(HasFocus(DEFAULT_BUFFER)
79 filter=(HasFocus(DEFAULT_BUFFER)
75 & EmacsInsertMode()))(newline_autoindent_outer(shell.input_splitter))
80 & EmacsInsertMode()))(newline_autoindent_outer(shell.input_splitter))
76
81
77 registry.add_binding(Keys.F2,
82 registry.add_binding(Keys.F2,
78 filter=HasFocus(DEFAULT_BUFFER)
83 filter=HasFocus(DEFAULT_BUFFER)
79 )(open_input_in_editor)
84 )(open_input_in_editor)
80
85
81 if shell.display_completions == 'readlinelike':
86 if shell.display_completions == 'readlinelike':
82 registry.add_binding(Keys.ControlI,
87 registry.add_binding(Keys.ControlI,
83 filter=(HasFocus(DEFAULT_BUFFER)
88 filter=(HasFocus(DEFAULT_BUFFER)
84 & ~HasSelection()
89 & ~HasSelection()
85 & insert_mode
90 & insert_mode
86 & ~cursor_in_leading_ws
91 & ~cursor_in_leading_ws
87 ))(display_completions_like_readline)
92 ))(display_completions_like_readline)
88
93
89 if sys.platform == 'win32':
94 if sys.platform == 'win32':
90 registry.add_binding(Keys.ControlV,
95 registry.add_binding(Keys.ControlV,
91 filter=(
96 filter=(
92 HasFocus(
97 HasFocus(
93 DEFAULT_BUFFER) & ~ViMode()
98 DEFAULT_BUFFER) & ~ViMode()
94 ))(win_paste)
99 ))(win_paste)
95
100
96
101
97 def newline_or_execute_outer(shell):
102 def newline_or_execute_outer(shell):
98 def newline_or_execute(event):
103 def newline_or_execute(event):
99 """When the user presses return, insert a newline or execute the code."""
104 """When the user presses return, insert a newline or execute the code."""
100 b = event.current_buffer
105 b = event.current_buffer
101 d = b.document
106 d = b.document
102
107
103 if b.complete_state:
108 if b.complete_state:
104 cc = b.complete_state.current_completion
109 cc = b.complete_state.current_completion
105 if cc:
110 if cc:
106 b.apply_completion(cc)
111 b.apply_completion(cc)
107 else:
112 else:
108 b.cancel_completion()
113 b.cancel_completion()
109 return
114 return
110
115
111 before_text = d.text[:d.cursor_position]
116 before_text = d.text[:d.cursor_position]
112 status, indent = shell.input_splitter.check_complete(before_text + '\n')
117 status, indent = shell.input_splitter.check_complete(before_text + '\n')
113
118
114 if not (d.on_last_line or
119 if not (d.on_last_line or
115 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
120 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
116 ):
121 ):
117 b.insert_text('\n' + (' ' * (indent or 0)))
122 b.insert_text('\n' + (' ' * (indent or 0)))
118 return
123 return
119
124
120 if (status != 'incomplete') and b.accept_action.is_returnable:
125 if (status != 'incomplete') and b.accept_action.is_returnable:
121 b.accept_action.validate_and_handle(event.cli, b)
126 b.accept_action.validate_and_handle(event.cli, b)
122 else:
127 else:
123 b.insert_text('\n' + (' ' * (indent or 0)))
128 b.insert_text('\n' + (' ' * (indent or 0)))
124 return newline_or_execute
129 return newline_or_execute
125
130
126
131
127 def previous_history_or_previous_completion(event):
132 def previous_history_or_previous_completion(event):
128 """
133 """
129 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
134 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
130
135
131 If completer is open this still select previous completion.
136 If completer is open this still select previous completion.
132 """
137 """
133 event.current_buffer.auto_up()
138 event.current_buffer.auto_up()
134
139
135
140
136 def next_history_or_next_completion(event):
141 def next_history_or_next_completion(event):
137 """
142 """
138 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
143 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
139
144
140 If completer is open this still select next completion.
145 If completer is open this still select next completion.
141 """
146 """
142 event.current_buffer.auto_down()
147 event.current_buffer.auto_down()
143
148
144
149
145 def dismiss_completion(event):
150 def dismiss_completion(event):
146 b = event.current_buffer
151 b = event.current_buffer
147 if b.complete_state:
152 if b.complete_state:
148 b.cancel_completion()
153 b.cancel_completion()
149
154
150
155
151 def reset_buffer(event):
156 def reset_buffer(event):
152 b = event.current_buffer
157 b = event.current_buffer
153 if b.complete_state:
158 if b.complete_state:
154 b.cancel_completion()
159 b.cancel_completion()
155 else:
160 else:
156 b.reset()
161 b.reset()
157
162
158
163
159 def reset_search_buffer(event):
164 def reset_search_buffer(event):
160 if event.current_buffer.document.text:
165 if event.current_buffer.document.text:
161 event.current_buffer.reset()
166 event.current_buffer.reset()
162 else:
167 else:
163 event.cli.push_focus(DEFAULT_BUFFER)
168 event.cli.push_focus(DEFAULT_BUFFER)
164
169
165 def suspend_to_bg(event):
170 def suspend_to_bg(event):
166 event.cli.suspend_to_background()
171 event.cli.suspend_to_background()
167
172
168 def force_exit(event):
173 def force_exit(event):
169 """
174 """
170 Force exit (with a non-zero return value)
175 Force exit (with a non-zero return value)
171 """
176 """
172 sys.exit("Quit")
177 sys.exit("Quit")
173
178
174 def indent_buffer(event):
179 def indent_buffer(event):
175 event.current_buffer.insert_text(' ' * 4)
180 event.current_buffer.insert_text(' ' * 4)
176
181
177 @undoc
182 @undoc
178 def newline_with_copy_margin(event):
183 def newline_with_copy_margin(event):
179 """
184 """
180 DEPRECATED since IPython 6.0
185 DEPRECATED since IPython 6.0
181
186
182 See :any:`newline_autoindent_outer` for a replacement.
187 See :any:`newline_autoindent_outer` for a replacement.
183
188
184 Preserve margin and cursor position when using
189 Preserve margin and cursor position when using
185 Control-O to insert a newline in EMACS mode
190 Control-O to insert a newline in EMACS mode
186 """
191 """
187 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
192 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
188 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
193 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
189 DeprecationWarning, stacklevel=2)
194 DeprecationWarning, stacklevel=2)
190
195
191 b = event.current_buffer
196 b = event.current_buffer
192 cursor_start_pos = b.document.cursor_position_col
197 cursor_start_pos = b.document.cursor_position_col
193 b.newline(copy_margin=True)
198 b.newline(copy_margin=True)
194 b.cursor_up(count=1)
199 b.cursor_up(count=1)
195 cursor_end_pos = b.document.cursor_position_col
200 cursor_end_pos = b.document.cursor_position_col
196 if cursor_start_pos != cursor_end_pos:
201 if cursor_start_pos != cursor_end_pos:
197 pos_diff = cursor_start_pos - cursor_end_pos
202 pos_diff = cursor_start_pos - cursor_end_pos
198 b.cursor_right(count=pos_diff)
203 b.cursor_right(count=pos_diff)
199
204
200 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
205 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
201 """
206 """
202 Return a function suitable for inserting a indented newline after the cursor.
207 Return a function suitable for inserting a indented newline after the cursor.
203
208
204 Fancier version of deprecated ``newline_with_copy_margin`` which should
209 Fancier version of deprecated ``newline_with_copy_margin`` which should
205 compute the correct indentation of the inserted line. That is to say, indent
210 compute the correct indentation of the inserted line. That is to say, indent
206 by 4 extra space after a function definition, class definition, context
211 by 4 extra space after a function definition, class definition, context
207 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
212 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
208 """
213 """
209
214
210 def newline_autoindent(event):
215 def newline_autoindent(event):
211 """insert a newline after the cursor indented appropriately."""
216 """insert a newline after the cursor indented appropriately."""
212 b = event.current_buffer
217 b = event.current_buffer
213 d = b.document
218 d = b.document
214
219
215 if b.complete_state:
220 if b.complete_state:
216 b.cancel_completion()
221 b.cancel_completion()
217 text = d.text[:d.cursor_position] + '\n'
222 text = d.text[:d.cursor_position] + '\n'
218 _, indent = inputsplitter.check_complete(text)
223 _, indent = inputsplitter.check_complete(text)
219 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
224 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
220
225
221 return newline_autoindent
226 return newline_autoindent
222
227
223
228
224 def open_input_in_editor(event):
229 def open_input_in_editor(event):
225 event.cli.current_buffer.tempfile_suffix = ".py"
230 event.cli.current_buffer.tempfile_suffix = ".py"
226 event.cli.current_buffer.open_in_editor(event.cli)
231 event.cli.current_buffer.open_in_editor(event.cli)
227
232
228
233
229 if sys.platform == 'win32':
234 if sys.platform == 'win32':
230 from IPython.core.error import TryNext
235 from IPython.core.error import TryNext
231 from IPython.lib.clipboard import (ClipboardEmpty,
236 from IPython.lib.clipboard import (ClipboardEmpty,
232 win32_clipboard_get,
237 win32_clipboard_get,
233 tkinter_clipboard_get)
238 tkinter_clipboard_get)
234
239
235 @undoc
240 @undoc
236 def win_paste(event):
241 def win_paste(event):
237 try:
242 try:
238 text = win32_clipboard_get()
243 text = win32_clipboard_get()
239 except TryNext:
244 except TryNext:
240 try:
245 try:
241 text = tkinter_clipboard_get()
246 text = tkinter_clipboard_get()
242 except (TryNext, ClipboardEmpty):
247 except (TryNext, ClipboardEmpty):
243 return
248 return
244 except ClipboardEmpty:
249 except ClipboardEmpty:
245 return
250 return
246 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
251 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
General Comments 0
You need to be logged in to leave comments. Login now