##// END OF EJS Templates
Merge pull request #11734 from Carreau/autoformat-black...
Matthias Bussonnier -
r25249:17e9ca62 merge
parent child Browse files
Show More
@@ -0,0 +1,24 b''
1 Code autoformatting
2 ===================
3
4 The IPython terminal can now auto format your code just before entering a new
5 line or executing a command. To do so use the
6 ``--TerminalInteractiveShell.autoformatter`` option and set it to ``'black'``;
7 if black is installed IPython will use black to format your code when possible.
8
9 IPython cannot always properly format your code; in particular it will
10 auto formatting with *black* will only work if:
11
12 - Your code does not contains magics or special python syntax.
13
14 - There is no code after your cursor.
15
16 The Black API is also still in motion; so this may not work with all versions of
17 black.
18
19 It should be possible to register custom reformatter, though the API is till in
20 flux.
21
22
23
24
@@ -1,570 +1,593 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 input
10 from IPython.utils.py3compat import input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title, restore_term_title
12 from IPython.utils.process import abbrev_cwd
12 from IPython.utils.process import abbrev_cwd
13 from traitlets import (
13 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any, validate
15 Any, validate
16 )
16 )
17
17
18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.formatted_text import PygmentsTokens
20 from prompt_toolkit.formatted_text import PygmentsTokens
21 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.output import ColorDepth
23 from prompt_toolkit.output import ColorDepth
24 from prompt_toolkit.patch_stdout import patch_stdout
24 from prompt_toolkit.patch_stdout import patch_stdout
25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 from prompt_toolkit.styles import DynamicStyle, merge_styles
26 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
28
28
29 from pygments.styles import get_style_by_name
29 from pygments.styles import get_style_by_name
30 from pygments.style import Style
30 from pygments.style import Style
31 from pygments.token import Token
31 from pygments.token import Token
32
32
33 from .debugger import TerminalPdb, Pdb
33 from .debugger import TerminalPdb, Pdb
34 from .magics import TerminalMagics
34 from .magics import TerminalMagics
35 from .pt_inputhooks import get_inputhook_name_and_func
35 from .pt_inputhooks import get_inputhook_name_and_func
36 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
37 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37 from .ptutils import IPythonPTCompleter, IPythonPTLexer
38 from .shortcuts import create_ipython_shortcuts
38 from .shortcuts import create_ipython_shortcuts
39
39
40 DISPLAY_BANNER_DEPRECATED = object()
40 DISPLAY_BANNER_DEPRECATED = object()
41
41
42
42
43 class _NoStyle(Style): pass
43 class _NoStyle(Style): pass
44
44
45
45
46
46
47 _style_overrides_light_bg = {
47 _style_overrides_light_bg = {
48 Token.Prompt: '#0000ff',
48 Token.Prompt: '#0000ff',
49 Token.PromptNum: '#0000ee bold',
49 Token.PromptNum: '#0000ee bold',
50 Token.OutPrompt: '#cc0000',
50 Token.OutPrompt: '#cc0000',
51 Token.OutPromptNum: '#bb0000 bold',
51 Token.OutPromptNum: '#bb0000 bold',
52 }
52 }
53
53
54 _style_overrides_linux = {
54 _style_overrides_linux = {
55 Token.Prompt: '#00cc00',
55 Token.Prompt: '#00cc00',
56 Token.PromptNum: '#00bb00 bold',
56 Token.PromptNum: '#00bb00 bold',
57 Token.OutPrompt: '#cc0000',
57 Token.OutPrompt: '#cc0000',
58 Token.OutPromptNum: '#bb0000 bold',
58 Token.OutPromptNum: '#bb0000 bold',
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 def black_reformat_handler(text_before_cursor):
91 import black
92 formatted_text = black.format_str(text_before_cursor, mode=black.FileMode())
93 if not text_before_cursor.endswith('\n') and formatted_text.endswith('\n'):
94 formatted_text = formatted_text[:-1]
95 return formatted_text
96
97
90 class TerminalInteractiveShell(InteractiveShell):
98 class TerminalInteractiveShell(InteractiveShell):
91 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
99 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
92 'to reserve for the completion menu'
100 'to reserve for the completion menu'
93 ).tag(config=True)
101 ).tag(config=True)
94
102
95 pt_app = None
103 pt_app = None
96 debugger_history = None
104 debugger_history = None
97
105
98 simple_prompt = Bool(_use_simple_prompt,
106 simple_prompt = Bool(_use_simple_prompt,
99 help="""Use `raw_input` for the REPL, without completion and prompt colors.
107 help="""Use `raw_input` for the REPL, without completion and prompt colors.
100
108
101 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
109 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
102 IPython own testing machinery, and emacs inferior-shell integration through elpy.
110 IPython own testing machinery, and emacs inferior-shell integration through elpy.
103
111
104 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
112 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
105 environment variable is set, or the current terminal is not a tty."""
113 environment variable is set, or the current terminal is not a tty."""
106 ).tag(config=True)
114 ).tag(config=True)
107
115
108 @property
116 @property
109 def debugger_cls(self):
117 def debugger_cls(self):
110 return Pdb if self.simple_prompt else TerminalPdb
118 return Pdb if self.simple_prompt else TerminalPdb
111
119
112 confirm_exit = Bool(True,
120 confirm_exit = Bool(True,
113 help="""
121 help="""
114 Set to confirm when you try to exit IPython with an EOF (Control-D
122 Set to confirm when you try to exit IPython with an EOF (Control-D
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
123 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116 you can force a direct exit without any confirmation.""",
124 you can force a direct exit without any confirmation.""",
117 ).tag(config=True)
125 ).tag(config=True)
118
126
119 editing_mode = Unicode('emacs',
127 editing_mode = Unicode('emacs',
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
128 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
121 ).tag(config=True)
129 ).tag(config=True)
122
130
131 autoformatter = Unicode(None,
132 help="Autoformatter to reformat Terminal code. Can be `'black'` or `None`",
133 allow_none=True
134 ).tag(config=True)
135
123 mouse_support = Bool(False,
136 mouse_support = Bool(False,
124 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
137 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
125 ).tag(config=True)
138 ).tag(config=True)
126
139
127 # We don't load the list of styles for the help string, because loading
140 # We don't load the list of styles for the help string, because loading
128 # Pygments plugins takes time and can cause unexpected errors.
141 # Pygments plugins takes time and can cause unexpected errors.
129 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
142 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
130 help="""The name or class of a Pygments style to use for syntax
143 help="""The name or class of a Pygments style to use for syntax
131 highlighting. To see available styles, run `pygmentize -L styles`."""
144 highlighting. To see available styles, run `pygmentize -L styles`."""
132 ).tag(config=True)
145 ).tag(config=True)
133
146
134 @validate('editing_mode')
147 @validate('editing_mode')
135 def _validate_editing_mode(self, proposal):
148 def _validate_editing_mode(self, proposal):
136 if proposal['value'].lower() == 'vim':
149 if proposal['value'].lower() == 'vim':
137 proposal['value']= 'vi'
150 proposal['value']= 'vi'
138 elif proposal['value'].lower() == 'default':
151 elif proposal['value'].lower() == 'default':
139 proposal['value']= 'emacs'
152 proposal['value']= 'emacs'
140
153
141 if hasattr(EditingMode, proposal['value'].upper()):
154 if hasattr(EditingMode, proposal['value'].upper()):
142 return proposal['value'].lower()
155 return proposal['value'].lower()
143
156
144 return self.editing_mode
157 return self.editing_mode
145
158
146
159
147 @observe('editing_mode')
160 @observe('editing_mode')
148 def _editing_mode(self, change):
161 def _editing_mode(self, change):
149 u_mode = change.new.upper()
162 u_mode = change.new.upper()
150 if self.pt_app:
163 if self.pt_app:
151 self.pt_app.editing_mode = u_mode
164 self.pt_app.editing_mode = u_mode
152
165
166 @observe('autoformatter')
167 def _autoformatter_changed(self, change):
168 formatter = change.new
169 if formatter is None:
170 self.reformat_handler = lambda x:x
171 elif formatter == 'black':
172 self.reformat_handler = black_reformat_handler
173 else:
174 raise ValueError
175
153 @observe('highlighting_style')
176 @observe('highlighting_style')
154 @observe('colors')
177 @observe('colors')
155 def _highlighting_style_changed(self, change):
178 def _highlighting_style_changed(self, change):
156 self.refresh_style()
179 self.refresh_style()
157
180
158 def refresh_style(self):
181 def refresh_style(self):
159 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
182 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
160
183
161
184
162 highlighting_style_overrides = Dict(
185 highlighting_style_overrides = Dict(
163 help="Override highlighting format for specific tokens"
186 help="Override highlighting format for specific tokens"
164 ).tag(config=True)
187 ).tag(config=True)
165
188
166 true_color = Bool(False,
189 true_color = Bool(False,
167 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
190 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
168 "If your terminal supports true color, the following command "
191 "If your terminal supports true color, the following command "
169 "should print 'TRUECOLOR' in orange: "
192 "should print 'TRUECOLOR' in orange: "
170 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
193 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
171 ).tag(config=True)
194 ).tag(config=True)
172
195
173 editor = Unicode(get_default_editor(),
196 editor = Unicode(get_default_editor(),
174 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
197 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
175 ).tag(config=True)
198 ).tag(config=True)
176
199
177 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
200 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
178
201
179 prompts = Instance(Prompts)
202 prompts = Instance(Prompts)
180
203
181 @default('prompts')
204 @default('prompts')
182 def _prompts_default(self):
205 def _prompts_default(self):
183 return self.prompts_class(self)
206 return self.prompts_class(self)
184
207
185 # @observe('prompts')
208 # @observe('prompts')
186 # def _(self, change):
209 # def _(self, change):
187 # self._update_layout()
210 # self._update_layout()
188
211
189 @default('displayhook_class')
212 @default('displayhook_class')
190 def _displayhook_class_default(self):
213 def _displayhook_class_default(self):
191 return RichPromptDisplayHook
214 return RichPromptDisplayHook
192
215
193 term_title = Bool(True,
216 term_title = Bool(True,
194 help="Automatically set the terminal title"
217 help="Automatically set the terminal title"
195 ).tag(config=True)
218 ).tag(config=True)
196
219
197 term_title_format = Unicode("IPython: {cwd}",
220 term_title_format = Unicode("IPython: {cwd}",
198 help="Customize the terminal title format. This is a python format string. " +
221 help="Customize the terminal title format. This is a python format string. " +
199 "Available substitutions are: {cwd}."
222 "Available substitutions are: {cwd}."
200 ).tag(config=True)
223 ).tag(config=True)
201
224
202 display_completions = Enum(('column', 'multicolumn','readlinelike'),
225 display_completions = Enum(('column', 'multicolumn','readlinelike'),
203 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
226 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
204 "'readlinelike'. These options are for `prompt_toolkit`, see "
227 "'readlinelike'. These options are for `prompt_toolkit`, see "
205 "`prompt_toolkit` documentation for more information."
228 "`prompt_toolkit` documentation for more information."
206 ),
229 ),
207 default_value='multicolumn').tag(config=True)
230 default_value='multicolumn').tag(config=True)
208
231
209 highlight_matching_brackets = Bool(True,
232 highlight_matching_brackets = Bool(True,
210 help="Highlight matching brackets.",
233 help="Highlight matching brackets.",
211 ).tag(config=True)
234 ).tag(config=True)
212
235
213 extra_open_editor_shortcuts = Bool(False,
236 extra_open_editor_shortcuts = Bool(False,
214 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
237 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
215 "This is in addition to the F2 binding, which is always enabled."
238 "This is in addition to the F2 binding, which is always enabled."
216 ).tag(config=True)
239 ).tag(config=True)
217
240
218 handle_return = Any(None,
241 handle_return = Any(None,
219 help="Provide an alternative handler to be called when the user presses "
242 help="Provide an alternative handler to be called when the user presses "
220 "Return. This is an advanced option intended for debugging, which "
243 "Return. This is an advanced option intended for debugging, which "
221 "may be changed or removed in later releases."
244 "may be changed or removed in later releases."
222 ).tag(config=True)
245 ).tag(config=True)
223
246
224 enable_history_search = Bool(True,
247 enable_history_search = Bool(True,
225 help="Allows to enable/disable the prompt toolkit history search"
248 help="Allows to enable/disable the prompt toolkit history search"
226 ).tag(config=True)
249 ).tag(config=True)
227
250
228 prompt_includes_vi_mode = Bool(True,
251 prompt_includes_vi_mode = Bool(True,
229 help="Display the current vi mode (when using vi editing mode)."
252 help="Display the current vi mode (when using vi editing mode)."
230 ).tag(config=True)
253 ).tag(config=True)
231
254
232 @observe('term_title')
255 @observe('term_title')
233 def init_term_title(self, change=None):
256 def init_term_title(self, change=None):
234 # Enable or disable the terminal title.
257 # Enable or disable the terminal title.
235 if self.term_title:
258 if self.term_title:
236 toggle_set_term_title(True)
259 toggle_set_term_title(True)
237 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
260 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
238 else:
261 else:
239 toggle_set_term_title(False)
262 toggle_set_term_title(False)
240
263
241 def restore_term_title(self):
264 def restore_term_title(self):
242 if self.term_title:
265 if self.term_title:
243 restore_term_title()
266 restore_term_title()
244
267
245 def init_display_formatter(self):
268 def init_display_formatter(self):
246 super(TerminalInteractiveShell, self).init_display_formatter()
269 super(TerminalInteractiveShell, self).init_display_formatter()
247 # terminal only supports plain text
270 # terminal only supports plain text
248 self.display_formatter.active_types = ['text/plain']
271 self.display_formatter.active_types = ['text/plain']
249 # disable `_ipython_display_`
272 # disable `_ipython_display_`
250 self.display_formatter.ipython_display_formatter.enabled = False
273 self.display_formatter.ipython_display_formatter.enabled = False
251
274
252 def init_prompt_toolkit_cli(self):
275 def init_prompt_toolkit_cli(self):
253 if self.simple_prompt:
276 if self.simple_prompt:
254 # Fall back to plain non-interactive output for tests.
277 # Fall back to plain non-interactive output for tests.
255 # This is very limited.
278 # This is very limited.
256 def prompt():
279 def prompt():
257 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
280 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
258 lines = [input(prompt_text)]
281 lines = [input(prompt_text)]
259 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
282 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
260 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
283 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
261 lines.append( input(prompt_continuation) )
284 lines.append( input(prompt_continuation) )
262 return '\n'.join(lines)
285 return '\n'.join(lines)
263 self.prompt_for_code = prompt
286 self.prompt_for_code = prompt
264 return
287 return
265
288
266 # Set up keyboard shortcuts
289 # Set up keyboard shortcuts
267 key_bindings = create_ipython_shortcuts(self)
290 key_bindings = create_ipython_shortcuts(self)
268
291
269 # Pre-populate history from IPython's history database
292 # Pre-populate history from IPython's history database
270 history = InMemoryHistory()
293 history = InMemoryHistory()
271 last_cell = u""
294 last_cell = u""
272 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
295 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
273 include_latest=True):
296 include_latest=True):
274 # Ignore blank lines and consecutive duplicates
297 # Ignore blank lines and consecutive duplicates
275 cell = cell.rstrip()
298 cell = cell.rstrip()
276 if cell and (cell != last_cell):
299 if cell and (cell != last_cell):
277 history.append_string(cell)
300 history.append_string(cell)
278 last_cell = cell
301 last_cell = cell
279
302
280 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
303 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
281 self.style = DynamicStyle(lambda: self._style)
304 self.style = DynamicStyle(lambda: self._style)
282
305
283 editing_mode = getattr(EditingMode, self.editing_mode.upper())
306 editing_mode = getattr(EditingMode, self.editing_mode.upper())
284
307
285 self.pt_app = PromptSession(
308 self.pt_app = PromptSession(
286 editing_mode=editing_mode,
309 editing_mode=editing_mode,
287 key_bindings=key_bindings,
310 key_bindings=key_bindings,
288 history=history,
311 history=history,
289 completer=IPythonPTCompleter(shell=self),
312 completer=IPythonPTCompleter(shell=self),
290 enable_history_search = self.enable_history_search,
313 enable_history_search = self.enable_history_search,
291 style=self.style,
314 style=self.style,
292 include_default_pygments_style=False,
315 include_default_pygments_style=False,
293 mouse_support=self.mouse_support,
316 mouse_support=self.mouse_support,
294 enable_open_in_editor=self.extra_open_editor_shortcuts,
317 enable_open_in_editor=self.extra_open_editor_shortcuts,
295 color_depth=self.color_depth,
318 color_depth=self.color_depth,
296 **self._extra_prompt_options())
319 **self._extra_prompt_options())
297
320
298 def _make_style_from_name_or_cls(self, name_or_cls):
321 def _make_style_from_name_or_cls(self, name_or_cls):
299 """
322 """
300 Small wrapper that make an IPython compatible style from a style name
323 Small wrapper that make an IPython compatible style from a style name
301
324
302 We need that to add style for prompt ... etc.
325 We need that to add style for prompt ... etc.
303 """
326 """
304 style_overrides = {}
327 style_overrides = {}
305 if name_or_cls == 'legacy':
328 if name_or_cls == 'legacy':
306 legacy = self.colors.lower()
329 legacy = self.colors.lower()
307 if legacy == 'linux':
330 if legacy == 'linux':
308 style_cls = get_style_by_name('monokai')
331 style_cls = get_style_by_name('monokai')
309 style_overrides = _style_overrides_linux
332 style_overrides = _style_overrides_linux
310 elif legacy == 'lightbg':
333 elif legacy == 'lightbg':
311 style_overrides = _style_overrides_light_bg
334 style_overrides = _style_overrides_light_bg
312 style_cls = get_style_by_name('pastie')
335 style_cls = get_style_by_name('pastie')
313 elif legacy == 'neutral':
336 elif legacy == 'neutral':
314 # The default theme needs to be visible on both a dark background
337 # The default theme needs to be visible on both a dark background
315 # and a light background, because we can't tell what the terminal
338 # and a light background, because we can't tell what the terminal
316 # looks like. These tweaks to the default theme help with that.
339 # looks like. These tweaks to the default theme help with that.
317 style_cls = get_style_by_name('default')
340 style_cls = get_style_by_name('default')
318 style_overrides.update({
341 style_overrides.update({
319 Token.Number: '#007700',
342 Token.Number: '#007700',
320 Token.Operator: 'noinherit',
343 Token.Operator: 'noinherit',
321 Token.String: '#BB6622',
344 Token.String: '#BB6622',
322 Token.Name.Function: '#2080D0',
345 Token.Name.Function: '#2080D0',
323 Token.Name.Class: 'bold #2080D0',
346 Token.Name.Class: 'bold #2080D0',
324 Token.Name.Namespace: 'bold #2080D0',
347 Token.Name.Namespace: 'bold #2080D0',
325 Token.Prompt: '#009900',
348 Token.Prompt: '#009900',
326 Token.PromptNum: '#ansibrightgreen bold',
349 Token.PromptNum: '#ansibrightgreen bold',
327 Token.OutPrompt: '#990000',
350 Token.OutPrompt: '#990000',
328 Token.OutPromptNum: '#ansibrightred bold',
351 Token.OutPromptNum: '#ansibrightred bold',
329 })
352 })
330
353
331 # Hack: Due to limited color support on the Windows console
354 # Hack: Due to limited color support on the Windows console
332 # the prompt colors will be wrong without this
355 # the prompt colors will be wrong without this
333 if os.name == 'nt':
356 if os.name == 'nt':
334 style_overrides.update({
357 style_overrides.update({
335 Token.Prompt: '#ansidarkgreen',
358 Token.Prompt: '#ansidarkgreen',
336 Token.PromptNum: '#ansigreen bold',
359 Token.PromptNum: '#ansigreen bold',
337 Token.OutPrompt: '#ansidarkred',
360 Token.OutPrompt: '#ansidarkred',
338 Token.OutPromptNum: '#ansired bold',
361 Token.OutPromptNum: '#ansired bold',
339 })
362 })
340 elif legacy =='nocolor':
363 elif legacy =='nocolor':
341 style_cls=_NoStyle
364 style_cls=_NoStyle
342 style_overrides = {}
365 style_overrides = {}
343 else :
366 else :
344 raise ValueError('Got unknown colors: ', legacy)
367 raise ValueError('Got unknown colors: ', legacy)
345 else :
368 else :
346 if isinstance(name_or_cls, str):
369 if isinstance(name_or_cls, str):
347 style_cls = get_style_by_name(name_or_cls)
370 style_cls = get_style_by_name(name_or_cls)
348 else:
371 else:
349 style_cls = name_or_cls
372 style_cls = name_or_cls
350 style_overrides = {
373 style_overrides = {
351 Token.Prompt: '#009900',
374 Token.Prompt: '#009900',
352 Token.PromptNum: '#ansibrightgreen bold',
375 Token.PromptNum: '#ansibrightgreen bold',
353 Token.OutPrompt: '#990000',
376 Token.OutPrompt: '#990000',
354 Token.OutPromptNum: '#ansibrightred bold',
377 Token.OutPromptNum: '#ansibrightred bold',
355 }
378 }
356 style_overrides.update(self.highlighting_style_overrides)
379 style_overrides.update(self.highlighting_style_overrides)
357 style = merge_styles([
380 style = merge_styles([
358 style_from_pygments_cls(style_cls),
381 style_from_pygments_cls(style_cls),
359 style_from_pygments_dict(style_overrides),
382 style_from_pygments_dict(style_overrides),
360 ])
383 ])
361
384
362 return style
385 return style
363
386
364 @property
387 @property
365 def pt_complete_style(self):
388 def pt_complete_style(self):
366 return {
389 return {
367 'multicolumn': CompleteStyle.MULTI_COLUMN,
390 'multicolumn': CompleteStyle.MULTI_COLUMN,
368 'column': CompleteStyle.COLUMN,
391 'column': CompleteStyle.COLUMN,
369 'readlinelike': CompleteStyle.READLINE_LIKE,
392 'readlinelike': CompleteStyle.READLINE_LIKE,
370 }[self.display_completions]
393 }[self.display_completions]
371
394
372 @property
395 @property
373 def color_depth(self):
396 def color_depth(self):
374 return (ColorDepth.TRUE_COLOR if self.true_color else None)
397 return (ColorDepth.TRUE_COLOR if self.true_color else None)
375
398
376 def _extra_prompt_options(self):
399 def _extra_prompt_options(self):
377 """
400 """
378 Return the current layout option for the current Terminal InteractiveShell
401 Return the current layout option for the current Terminal InteractiveShell
379 """
402 """
380 def get_message():
403 def get_message():
381 return PygmentsTokens(self.prompts.in_prompt_tokens())
404 return PygmentsTokens(self.prompts.in_prompt_tokens())
382
405
383 if self.editing_mode == 'emacs':
406 if self.editing_mode == 'emacs':
384 # with emacs mode the prompt is (usually) static, so we call only
407 # with emacs mode the prompt is (usually) static, so we call only
385 # the function once. With VI mode it can toggle between [ins] and
408 # the function once. With VI mode it can toggle between [ins] and
386 # [nor] so we can't precompute.
409 # [nor] so we can't precompute.
387 # here I'm going to favor the default keybinding which almost
410 # here I'm going to favor the default keybinding which almost
388 # everybody uses to decrease CPU usage.
411 # everybody uses to decrease CPU usage.
389 # if we have issues with users with custom Prompts we can see how to
412 # if we have issues with users with custom Prompts we can see how to
390 # work around this.
413 # work around this.
391 get_message = get_message()
414 get_message = get_message()
392
415
393 return {
416 return {
394 'complete_in_thread': False,
417 'complete_in_thread': False,
395 'lexer':IPythonPTLexer(),
418 'lexer':IPythonPTLexer(),
396 'reserve_space_for_menu':self.space_for_menu,
419 'reserve_space_for_menu':self.space_for_menu,
397 'message': get_message,
420 'message': get_message,
398 'prompt_continuation': (
421 'prompt_continuation': (
399 lambda width, lineno, is_soft_wrap:
422 lambda width, lineno, is_soft_wrap:
400 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
423 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
401 'multiline': True,
424 'multiline': True,
402 'complete_style': self.pt_complete_style,
425 'complete_style': self.pt_complete_style,
403
426
404 # Highlight matching brackets, but only when this setting is
427 # Highlight matching brackets, but only when this setting is
405 # enabled, and only when the DEFAULT_BUFFER has the focus.
428 # enabled, and only when the DEFAULT_BUFFER has the focus.
406 'input_processors': [ConditionalProcessor(
429 'input_processors': [ConditionalProcessor(
407 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
430 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
408 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
431 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
409 Condition(lambda: self.highlight_matching_brackets))],
432 Condition(lambda: self.highlight_matching_brackets))],
410 'inputhook': self.inputhook,
433 'inputhook': self.inputhook,
411 }
434 }
412
435
413 def prompt_for_code(self):
436 def prompt_for_code(self):
414 if self.rl_next_input:
437 if self.rl_next_input:
415 default = self.rl_next_input
438 default = self.rl_next_input
416 self.rl_next_input = None
439 self.rl_next_input = None
417 else:
440 else:
418 default = ''
441 default = ''
419
442
420 with patch_stdout(raw=True):
443 with patch_stdout(raw=True):
421 text = self.pt_app.prompt(
444 text = self.pt_app.prompt(
422 default=default,
445 default=default,
423 # pre_run=self.pre_prompt,# reset_current_buffer=True,
446 # pre_run=self.pre_prompt,# reset_current_buffer=True,
424 **self._extra_prompt_options())
447 **self._extra_prompt_options())
425 return text
448 return text
426
449
427 def enable_win_unicode_console(self):
450 def enable_win_unicode_console(self):
428 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
451 # Since IPython 7.10 doesn't support python < 3.6 and PEP 528, Python uses the unicode APIs for the Windows
429 # console by default, so WUC shouldn't be needed.
452 # console by default, so WUC shouldn't be needed.
430 from warnings import warn
453 from warnings import warn
431 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
454 warn("`enable_win_unicode_console` is deprecated since IPython 7.10, does not do anything and will be removed in the future",
432 DeprecationWarning,
455 DeprecationWarning,
433 stacklevel=2)
456 stacklevel=2)
434
457
435 def init_io(self):
458 def init_io(self):
436 if sys.platform not in {'win32', 'cli'}:
459 if sys.platform not in {'win32', 'cli'}:
437 return
460 return
438
461
439 import colorama
462 import colorama
440 colorama.init()
463 colorama.init()
441
464
442 # For some reason we make these wrappers around stdout/stderr.
465 # For some reason we make these wrappers around stdout/stderr.
443 # For now, we need to reset them so all output gets coloured.
466 # For now, we need to reset them so all output gets coloured.
444 # https://github.com/ipython/ipython/issues/8669
467 # https://github.com/ipython/ipython/issues/8669
445 # io.std* are deprecated, but don't show our own deprecation warnings
468 # io.std* are deprecated, but don't show our own deprecation warnings
446 # during initialization of the deprecated API.
469 # during initialization of the deprecated API.
447 with warnings.catch_warnings():
470 with warnings.catch_warnings():
448 warnings.simplefilter('ignore', DeprecationWarning)
471 warnings.simplefilter('ignore', DeprecationWarning)
449 io.stdout = io.IOStream(sys.stdout)
472 io.stdout = io.IOStream(sys.stdout)
450 io.stderr = io.IOStream(sys.stderr)
473 io.stderr = io.IOStream(sys.stderr)
451
474
452 def init_magics(self):
475 def init_magics(self):
453 super(TerminalInteractiveShell, self).init_magics()
476 super(TerminalInteractiveShell, self).init_magics()
454 self.register_magics(TerminalMagics)
477 self.register_magics(TerminalMagics)
455
478
456 def init_alias(self):
479 def init_alias(self):
457 # The parent class defines aliases that can be safely used with any
480 # The parent class defines aliases that can be safely used with any
458 # frontend.
481 # frontend.
459 super(TerminalInteractiveShell, self).init_alias()
482 super(TerminalInteractiveShell, self).init_alias()
460
483
461 # Now define aliases that only make sense on the terminal, because they
484 # Now define aliases that only make sense on the terminal, because they
462 # need direct access to the console in a way that we can't emulate in
485 # need direct access to the console in a way that we can't emulate in
463 # GUI or web frontend
486 # GUI or web frontend
464 if os.name == 'posix':
487 if os.name == 'posix':
465 for cmd in ('clear', 'more', 'less', 'man'):
488 for cmd in ('clear', 'more', 'less', 'man'):
466 self.alias_manager.soft_define_alias(cmd, cmd)
489 self.alias_manager.soft_define_alias(cmd, cmd)
467
490
468
491
469 def __init__(self, *args, **kwargs):
492 def __init__(self, *args, **kwargs):
470 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
493 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
471 self.init_prompt_toolkit_cli()
494 self.init_prompt_toolkit_cli()
472 self.init_term_title()
495 self.init_term_title()
473 self.keep_running = True
496 self.keep_running = True
474
497
475 self.debugger_history = InMemoryHistory()
498 self.debugger_history = InMemoryHistory()
476
499
477 def ask_exit(self):
500 def ask_exit(self):
478 self.keep_running = False
501 self.keep_running = False
479
502
480 rl_next_input = None
503 rl_next_input = None
481
504
482 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
505 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
483
506
484 if display_banner is not DISPLAY_BANNER_DEPRECATED:
507 if display_banner is not DISPLAY_BANNER_DEPRECATED:
485 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
508 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
486
509
487 self.keep_running = True
510 self.keep_running = True
488 while self.keep_running:
511 while self.keep_running:
489 print(self.separate_in, end='')
512 print(self.separate_in, end='')
490
513
491 try:
514 try:
492 code = self.prompt_for_code()
515 code = self.prompt_for_code()
493 except EOFError:
516 except EOFError:
494 if (not self.confirm_exit) \
517 if (not self.confirm_exit) \
495 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
518 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
496 self.ask_exit()
519 self.ask_exit()
497
520
498 else:
521 else:
499 if code:
522 if code:
500 self.run_cell(code, store_history=True)
523 self.run_cell(code, store_history=True)
501
524
502 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
525 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
503 # An extra layer of protection in case someone mashing Ctrl-C breaks
526 # An extra layer of protection in case someone mashing Ctrl-C breaks
504 # out of our internal code.
527 # out of our internal code.
505 if display_banner is not DISPLAY_BANNER_DEPRECATED:
528 if display_banner is not DISPLAY_BANNER_DEPRECATED:
506 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
529 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
507 while True:
530 while True:
508 try:
531 try:
509 self.interact()
532 self.interact()
510 break
533 break
511 except KeyboardInterrupt as e:
534 except KeyboardInterrupt as e:
512 print("\n%s escaped interact()\n" % type(e).__name__)
535 print("\n%s escaped interact()\n" % type(e).__name__)
513 finally:
536 finally:
514 # An interrupt during the eventloop will mess up the
537 # An interrupt during the eventloop will mess up the
515 # internal state of the prompt_toolkit library.
538 # internal state of the prompt_toolkit library.
516 # Stopping the eventloop fixes this, see
539 # Stopping the eventloop fixes this, see
517 # https://github.com/ipython/ipython/pull/9867
540 # https://github.com/ipython/ipython/pull/9867
518 if hasattr(self, '_eventloop'):
541 if hasattr(self, '_eventloop'):
519 self._eventloop.stop()
542 self._eventloop.stop()
520
543
521 self.restore_term_title()
544 self.restore_term_title()
522
545
523
546
524 _inputhook = None
547 _inputhook = None
525 def inputhook(self, context):
548 def inputhook(self, context):
526 if self._inputhook is not None:
549 if self._inputhook is not None:
527 self._inputhook(context)
550 self._inputhook(context)
528
551
529 active_eventloop = None
552 active_eventloop = None
530 def enable_gui(self, gui=None):
553 def enable_gui(self, gui=None):
531 if gui:
554 if gui:
532 self.active_eventloop, self._inputhook =\
555 self.active_eventloop, self._inputhook =\
533 get_inputhook_name_and_func(gui)
556 get_inputhook_name_and_func(gui)
534 else:
557 else:
535 self.active_eventloop = self._inputhook = None
558 self.active_eventloop = self._inputhook = None
536
559
537 # Run !system commands directly, not through pipes, so terminal programs
560 # Run !system commands directly, not through pipes, so terminal programs
538 # work correctly.
561 # work correctly.
539 system = InteractiveShell.system_raw
562 system = InteractiveShell.system_raw
540
563
541 def auto_rewrite_input(self, cmd):
564 def auto_rewrite_input(self, cmd):
542 """Overridden from the parent class to use fancy rewriting prompt"""
565 """Overridden from the parent class to use fancy rewriting prompt"""
543 if not self.show_rewritten_input:
566 if not self.show_rewritten_input:
544 return
567 return
545
568
546 tokens = self.prompts.rewrite_prompt_tokens()
569 tokens = self.prompts.rewrite_prompt_tokens()
547 if self.pt_app:
570 if self.pt_app:
548 print_formatted_text(PygmentsTokens(tokens), end='',
571 print_formatted_text(PygmentsTokens(tokens), end='',
549 style=self.pt_app.app.style)
572 style=self.pt_app.app.style)
550 print(cmd)
573 print(cmd)
551 else:
574 else:
552 prompt = ''.join(s for t, s in tokens)
575 prompt = ''.join(s for t, s in tokens)
553 print(prompt, cmd, sep='')
576 print(prompt, cmd, sep='')
554
577
555 _prompts_before = None
578 _prompts_before = None
556 def switch_doctest_mode(self, mode):
579 def switch_doctest_mode(self, mode):
557 """Switch prompts to classic for %doctest_mode"""
580 """Switch prompts to classic for %doctest_mode"""
558 if mode:
581 if mode:
559 self._prompts_before = self.prompts
582 self._prompts_before = self.prompts
560 self.prompts = ClassicPrompts(self)
583 self.prompts = ClassicPrompts(self)
561 elif self._prompts_before:
584 elif self._prompts_before:
562 self.prompts = self._prompts_before
585 self.prompts = self._prompts_before
563 self._prompts_before = None
586 self._prompts_before = None
564 # self._update_layout()
587 # self._update_layout()
565
588
566
589
567 InteractiveShellABC.register(TerminalInteractiveShell)
590 InteractiveShellABC.register(TerminalInteractiveShell)
568
591
569 if __name__ == '__main__':
592 if __name__ == '__main__':
570 TerminalInteractiveShell.instance().interact()
593 TerminalInteractiveShell.instance().interact()
@@ -1,249 +1,274 b''
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.application.current import get_app
15 from prompt_toolkit.application.current import get_app
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
17 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
17 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
18 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
18 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
19 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
19 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
20 from prompt_toolkit.key_binding import KeyBindings
20 from prompt_toolkit.key_binding import KeyBindings
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():
26 def cursor_in_leading_ws():
27 before = get_app().current_buffer.document.current_line_before_cursor
27 before = get_app().current_buffer.document.current_line_before_cursor
28 return (not before) or before.isspace()
28 return (not before) or before.isspace()
29
29
30
30
31 def create_ipython_shortcuts(shell):
31 def create_ipython_shortcuts(shell):
32 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
32 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
33
33
34 kb = KeyBindings()
34 kb = KeyBindings()
35 insert_mode = vi_insert_mode | emacs_insert_mode
35 insert_mode = vi_insert_mode | emacs_insert_mode
36
36
37 if getattr(shell, 'handle_return', None):
37 if getattr(shell, 'handle_return', None):
38 return_handler = shell.handle_return(shell)
38 return_handler = shell.handle_return(shell)
39 else:
39 else:
40 return_handler = newline_or_execute_outer(shell)
40 return_handler = newline_or_execute_outer(shell)
41
41
42 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
42 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
43 & ~has_selection
43 & ~has_selection
44 & insert_mode
44 & insert_mode
45 ))(return_handler)
45 ))(return_handler)
46
46
47 def reformat_and_execute(event):
48 reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
49 event.current_buffer.validate_and_handle()
50
51 kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
52 & ~has_selection
53 & insert_mode
54 ))(reformat_and_execute)
55
47 kb.add('c-\\')(force_exit)
56 kb.add('c-\\')(force_exit)
48
57
49 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
58 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
50 )(previous_history_or_previous_completion)
59 )(previous_history_or_previous_completion)
51
60
52 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
61 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
53 )(next_history_or_next_completion)
62 )(next_history_or_next_completion)
54
63
55 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
64 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
56 )(dismiss_completion)
65 )(dismiss_completion)
57
66
58 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
67 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
59
68
60 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
69 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
61
70
62 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
71 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
63 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
72 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
64
73
65 # Ctrl+I == Tab
74 # Ctrl+I == Tab
66 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
75 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
67 & ~has_selection
76 & ~has_selection
68 & insert_mode
77 & insert_mode
69 & cursor_in_leading_ws
78 & cursor_in_leading_ws
70 ))(indent_buffer)
79 ))(indent_buffer)
71 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
80 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
72 )(newline_autoindent_outer(shell.input_transformer_manager))
81 )(newline_autoindent_outer(shell.input_transformer_manager))
73
82
74 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
83 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
75
84
76 if shell.display_completions == 'readlinelike':
85 if shell.display_completions == 'readlinelike':
77 kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER)
86 kb.add('c-i', filter=(has_focus(DEFAULT_BUFFER)
78 & ~has_selection
87 & ~has_selection
79 & insert_mode
88 & insert_mode
80 & ~cursor_in_leading_ws
89 & ~cursor_in_leading_ws
81 ))(display_completions_like_readline)
90 ))(display_completions_like_readline)
82
91
83 if sys.platform == 'win32':
92 if sys.platform == 'win32':
84 kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
93 kb.add('c-v', filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
85
94
86 return kb
95 return kb
87
96
88
97
98 def reformat_text_before_cursor(buffer, document, shell):
99 text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
100 try:
101 formatted_text = shell.reformat_handler(text)
102 buffer.insert_text(formatted_text)
103 except Exception as e:
104 buffer.insert_text(text)
105
106
89 def newline_or_execute_outer(shell):
107 def newline_or_execute_outer(shell):
108
90 def newline_or_execute(event):
109 def newline_or_execute(event):
91 """When the user presses return, insert a newline or execute the code."""
110 """When the user presses return, insert a newline or execute the code."""
92 b = event.current_buffer
111 b = event.current_buffer
93 d = b.document
112 d = b.document
94
113
95 if b.complete_state:
114 if b.complete_state:
96 cc = b.complete_state.current_completion
115 cc = b.complete_state.current_completion
97 if cc:
116 if cc:
98 b.apply_completion(cc)
117 b.apply_completion(cc)
99 else:
118 else:
100 b.cancel_completion()
119 b.cancel_completion()
101 return
120 return
102
121
103 # If there's only one line, treat it as if the cursor is at the end.
122 # If there's only one line, treat it as if the cursor is at the end.
104 # See https://github.com/ipython/ipython/issues/10425
123 # See https://github.com/ipython/ipython/issues/10425
105 if d.line_count == 1:
124 if d.line_count == 1:
106 check_text = d.text
125 check_text = d.text
107 else:
126 else:
108 check_text = d.text[:d.cursor_position]
127 check_text = d.text[:d.cursor_position]
109 status, indent = shell.check_complete(check_text)
128 status, indent = shell.check_complete(check_text)
110
129
130 # if all we have after the cursor is whitespace: reformat current text
131 # before cursor
132 after_cursor = d.text[d.cursor_position:]
133 if not after_cursor.strip():
134 reformat_text_before_cursor(b, d, shell)
111 if not (d.on_last_line or
135 if not (d.on_last_line or
112 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
136 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
113 ):
137 ):
114 if shell.autoindent:
138 if shell.autoindent:
115 b.insert_text('\n' + indent)
139 b.insert_text('\n' + indent)
116 else:
140 else:
117 b.insert_text('\n')
141 b.insert_text('\n')
118 return
142 return
119
143
120 if (status != 'incomplete') and b.accept_handler:
144 if (status != 'incomplete') and b.accept_handler:
145 reformat_text_before_cursor(b, d, shell)
121 b.validate_and_handle()
146 b.validate_and_handle()
122 else:
147 else:
123 if shell.autoindent:
148 if shell.autoindent:
124 b.insert_text('\n' + indent)
149 b.insert_text('\n' + indent)
125 else:
150 else:
126 b.insert_text('\n')
151 b.insert_text('\n')
127 return newline_or_execute
152 return newline_or_execute
128
153
129
154
130 def previous_history_or_previous_completion(event):
155 def previous_history_or_previous_completion(event):
131 """
156 """
132 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
157 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
133
158
134 If completer is open this still select previous completion.
159 If completer is open this still select previous completion.
135 """
160 """
136 event.current_buffer.auto_up()
161 event.current_buffer.auto_up()
137
162
138
163
139 def next_history_or_next_completion(event):
164 def next_history_or_next_completion(event):
140 """
165 """
141 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
166 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
142
167
143 If completer is open this still select next completion.
168 If completer is open this still select next completion.
144 """
169 """
145 event.current_buffer.auto_down()
170 event.current_buffer.auto_down()
146
171
147
172
148 def dismiss_completion(event):
173 def dismiss_completion(event):
149 b = event.current_buffer
174 b = event.current_buffer
150 if b.complete_state:
175 if b.complete_state:
151 b.cancel_completion()
176 b.cancel_completion()
152
177
153
178
154 def reset_buffer(event):
179 def reset_buffer(event):
155 b = event.current_buffer
180 b = event.current_buffer
156 if b.complete_state:
181 if b.complete_state:
157 b.cancel_completion()
182 b.cancel_completion()
158 else:
183 else:
159 b.reset()
184 b.reset()
160
185
161
186
162 def reset_search_buffer(event):
187 def reset_search_buffer(event):
163 if event.current_buffer.document.text:
188 if event.current_buffer.document.text:
164 event.current_buffer.reset()
189 event.current_buffer.reset()
165 else:
190 else:
166 event.app.layout.focus(DEFAULT_BUFFER)
191 event.app.layout.focus(DEFAULT_BUFFER)
167
192
168 def suspend_to_bg(event):
193 def suspend_to_bg(event):
169 event.app.suspend_to_background()
194 event.app.suspend_to_background()
170
195
171 def force_exit(event):
196 def force_exit(event):
172 """
197 """
173 Force exit (with a non-zero return value)
198 Force exit (with a non-zero return value)
174 """
199 """
175 sys.exit("Quit")
200 sys.exit("Quit")
176
201
177 def indent_buffer(event):
202 def indent_buffer(event):
178 event.current_buffer.insert_text(' ' * 4)
203 event.current_buffer.insert_text(' ' * 4)
179
204
180 @undoc
205 @undoc
181 def newline_with_copy_margin(event):
206 def newline_with_copy_margin(event):
182 """
207 """
183 DEPRECATED since IPython 6.0
208 DEPRECATED since IPython 6.0
184
209
185 See :any:`newline_autoindent_outer` for a replacement.
210 See :any:`newline_autoindent_outer` for a replacement.
186
211
187 Preserve margin and cursor position when using
212 Preserve margin and cursor position when using
188 Control-O to insert a newline in EMACS mode
213 Control-O to insert a newline in EMACS mode
189 """
214 """
190 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
215 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
191 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
216 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
192 DeprecationWarning, stacklevel=2)
217 DeprecationWarning, stacklevel=2)
193
218
194 b = event.current_buffer
219 b = event.current_buffer
195 cursor_start_pos = b.document.cursor_position_col
220 cursor_start_pos = b.document.cursor_position_col
196 b.newline(copy_margin=True)
221 b.newline(copy_margin=True)
197 b.cursor_up(count=1)
222 b.cursor_up(count=1)
198 cursor_end_pos = b.document.cursor_position_col
223 cursor_end_pos = b.document.cursor_position_col
199 if cursor_start_pos != cursor_end_pos:
224 if cursor_start_pos != cursor_end_pos:
200 pos_diff = cursor_start_pos - cursor_end_pos
225 pos_diff = cursor_start_pos - cursor_end_pos
201 b.cursor_right(count=pos_diff)
226 b.cursor_right(count=pos_diff)
202
227
203 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
228 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
204 """
229 """
205 Return a function suitable for inserting a indented newline after the cursor.
230 Return a function suitable for inserting a indented newline after the cursor.
206
231
207 Fancier version of deprecated ``newline_with_copy_margin`` which should
232 Fancier version of deprecated ``newline_with_copy_margin`` which should
208 compute the correct indentation of the inserted line. That is to say, indent
233 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
234 by 4 extra space after a function definition, class definition, context
210 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
235 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
211 """
236 """
212
237
213 def newline_autoindent(event):
238 def newline_autoindent(event):
214 """insert a newline after the cursor indented appropriately."""
239 """insert a newline after the cursor indented appropriately."""
215 b = event.current_buffer
240 b = event.current_buffer
216 d = b.document
241 d = b.document
217
242
218 if b.complete_state:
243 if b.complete_state:
219 b.cancel_completion()
244 b.cancel_completion()
220 text = d.text[:d.cursor_position] + '\n'
245 text = d.text[:d.cursor_position] + '\n'
221 _, indent = inputsplitter.check_complete(text)
246 _, indent = inputsplitter.check_complete(text)
222 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
247 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
223
248
224 return newline_autoindent
249 return newline_autoindent
225
250
226
251
227 def open_input_in_editor(event):
252 def open_input_in_editor(event):
228 event.app.current_buffer.tempfile_suffix = ".py"
253 event.app.current_buffer.tempfile_suffix = ".py"
229 event.app.current_buffer.open_in_editor()
254 event.app.current_buffer.open_in_editor()
230
255
231
256
232 if sys.platform == 'win32':
257 if sys.platform == 'win32':
233 from IPython.core.error import TryNext
258 from IPython.core.error import TryNext
234 from IPython.lib.clipboard import (ClipboardEmpty,
259 from IPython.lib.clipboard import (ClipboardEmpty,
235 win32_clipboard_get,
260 win32_clipboard_get,
236 tkinter_clipboard_get)
261 tkinter_clipboard_get)
237
262
238 @undoc
263 @undoc
239 def win_paste(event):
264 def win_paste(event):
240 try:
265 try:
241 text = win32_clipboard_get()
266 text = win32_clipboard_get()
242 except TryNext:
267 except TryNext:
243 try:
268 try:
244 text = tkinter_clipboard_get()
269 text = tkinter_clipboard_get()
245 except (TryNext, ClipboardEmpty):
270 except (TryNext, ClipboardEmpty):
246 return
271 return
247 except ClipboardEmpty:
272 except ClipboardEmpty:
248 return
273 return
249 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
274 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
General Comments 0
You need to be logged in to leave comments. Login now