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