##// END OF EJS Templates
Remove deprecated `newline_with_copy_margin`
Yann Pellegrini -
Show More
@@ -1,626 +1,599 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 os
9 import os
10 import signal
10 import signal
11 import sys
11 import sys
12 import warnings
12 import warnings
13 from dataclasses import dataclass
13 from dataclasses import dataclass
14 from typing import Callable, Any, Optional, List
14 from typing import Callable, Any, Optional, List
15
15
16 from prompt_toolkit.application.current import get_app
16 from prompt_toolkit.application.current import get_app
17 from prompt_toolkit.key_binding import KeyBindings
17 from prompt_toolkit.key_binding import KeyBindings
18 from prompt_toolkit.key_binding.key_processor import KeyPressEvent
18 from prompt_toolkit.key_binding.key_processor import KeyPressEvent
19 from prompt_toolkit.key_binding.bindings import named_commands as nc
19 from prompt_toolkit.key_binding.bindings import named_commands as nc
20 from prompt_toolkit.key_binding.bindings.completion import (
20 from prompt_toolkit.key_binding.bindings.completion import (
21 display_completions_like_readline,
21 display_completions_like_readline,
22 )
22 )
23 from prompt_toolkit.key_binding.vi_state import InputMode, ViState
23 from prompt_toolkit.key_binding.vi_state import InputMode, ViState
24 from prompt_toolkit.filters import Condition
24 from prompt_toolkit.filters import Condition
25
25
26 from IPython.core.getipython import get_ipython
26 from IPython.core.getipython import get_ipython
27 from IPython.terminal.shortcuts import auto_match as match
27 from IPython.terminal.shortcuts import auto_match as match
28 from IPython.terminal.shortcuts import auto_suggest
28 from IPython.terminal.shortcuts import auto_suggest
29 from IPython.terminal.shortcuts.filters import filter_from_string
29 from IPython.terminal.shortcuts.filters import filter_from_string
30 from IPython.utils.decorators import undoc
30 from IPython.utils.decorators import undoc
31
31
32 __all__ = ["create_ipython_shortcuts"]
32 __all__ = ["create_ipython_shortcuts"]
33
33
34
34
35 @dataclass
35 @dataclass
36 class BaseBinding:
36 class BaseBinding:
37 command: Callable[[KeyPressEvent], Any]
37 command: Callable[[KeyPressEvent], Any]
38 keys: List[str]
38 keys: List[str]
39
39
40
40
41 @dataclass
41 @dataclass
42 class RuntimeBinding(BaseBinding):
42 class RuntimeBinding(BaseBinding):
43 filter: Condition
43 filter: Condition
44
44
45
45
46 @dataclass
46 @dataclass
47 class Binding(BaseBinding):
47 class Binding(BaseBinding):
48 # while filter could be created by referencing variables directly (rather
48 # while filter could be created by referencing variables directly (rather
49 # than created from strings), by using strings we ensure that users will
49 # than created from strings), by using strings we ensure that users will
50 # be able to create filters in configuration (e.g. JSON) files too, which
50 # be able to create filters in configuration (e.g. JSON) files too, which
51 # also benefits the documentation by enforcing human-readable filter names.
51 # also benefits the documentation by enforcing human-readable filter names.
52 condition: Optional[str] = None
52 condition: Optional[str] = None
53
53
54 def __post_init__(self):
54 def __post_init__(self):
55 if self.condition:
55 if self.condition:
56 self.filter = filter_from_string(self.condition)
56 self.filter = filter_from_string(self.condition)
57 else:
57 else:
58 self.filter = None
58 self.filter = None
59
59
60
60
61 def create_identifier(handler: Callable):
61 def create_identifier(handler: Callable):
62 parts = handler.__module__.split(".")
62 parts = handler.__module__.split(".")
63 name = handler.__name__
63 name = handler.__name__
64 package = parts[0]
64 package = parts[0]
65 if len(parts) > 1:
65 if len(parts) > 1:
66 final_module = parts[-1]
66 final_module = parts[-1]
67 return f"{package}:{final_module}.{name}"
67 return f"{package}:{final_module}.{name}"
68 else:
68 else:
69 return f"{package}:{name}"
69 return f"{package}:{name}"
70
70
71
71
72 AUTO_MATCH_BINDINGS = [
72 AUTO_MATCH_BINDINGS = [
73 *[
73 *[
74 Binding(
74 Binding(
75 cmd, [key], "focused_insert & auto_match & followed_by_closing_paren_or_end"
75 cmd, [key], "focused_insert & auto_match & followed_by_closing_paren_or_end"
76 )
76 )
77 for key, cmd in match.auto_match_parens.items()
77 for key, cmd in match.auto_match_parens.items()
78 ],
78 ],
79 *[
79 *[
80 # raw string
80 # raw string
81 Binding(cmd, [key], "focused_insert & auto_match & preceded_by_raw_str_prefix")
81 Binding(cmd, [key], "focused_insert & auto_match & preceded_by_raw_str_prefix")
82 for key, cmd in match.auto_match_parens_raw_string.items()
82 for key, cmd in match.auto_match_parens_raw_string.items()
83 ],
83 ],
84 Binding(
84 Binding(
85 match.double_quote,
85 match.double_quote,
86 ['"'],
86 ['"'],
87 "focused_insert"
87 "focused_insert"
88 " & auto_match"
88 " & auto_match"
89 " & not_inside_unclosed_string"
89 " & not_inside_unclosed_string"
90 " & preceded_by_paired_double_quotes"
90 " & preceded_by_paired_double_quotes"
91 " & followed_by_closing_paren_or_end",
91 " & followed_by_closing_paren_or_end",
92 ),
92 ),
93 Binding(
93 Binding(
94 match.single_quote,
94 match.single_quote,
95 ["'"],
95 ["'"],
96 "focused_insert"
96 "focused_insert"
97 " & auto_match"
97 " & auto_match"
98 " & not_inside_unclosed_string"
98 " & not_inside_unclosed_string"
99 " & preceded_by_paired_single_quotes"
99 " & preceded_by_paired_single_quotes"
100 " & followed_by_closing_paren_or_end",
100 " & followed_by_closing_paren_or_end",
101 ),
101 ),
102 Binding(
102 Binding(
103 match.docstring_double_quotes,
103 match.docstring_double_quotes,
104 ['"'],
104 ['"'],
105 "focused_insert"
105 "focused_insert"
106 " & auto_match"
106 " & auto_match"
107 " & not_inside_unclosed_string"
107 " & not_inside_unclosed_string"
108 " & preceded_by_two_double_quotes",
108 " & preceded_by_two_double_quotes",
109 ),
109 ),
110 Binding(
110 Binding(
111 match.docstring_single_quotes,
111 match.docstring_single_quotes,
112 ["'"],
112 ["'"],
113 "focused_insert"
113 "focused_insert"
114 " & auto_match"
114 " & auto_match"
115 " & not_inside_unclosed_string"
115 " & not_inside_unclosed_string"
116 " & preceded_by_two_single_quotes",
116 " & preceded_by_two_single_quotes",
117 ),
117 ),
118 Binding(
118 Binding(
119 match.skip_over,
119 match.skip_over,
120 [")"],
120 [")"],
121 "focused_insert & auto_match & followed_by_closing_round_paren",
121 "focused_insert & auto_match & followed_by_closing_round_paren",
122 ),
122 ),
123 Binding(
123 Binding(
124 match.skip_over,
124 match.skip_over,
125 ["]"],
125 ["]"],
126 "focused_insert & auto_match & followed_by_closing_bracket",
126 "focused_insert & auto_match & followed_by_closing_bracket",
127 ),
127 ),
128 Binding(
128 Binding(
129 match.skip_over,
129 match.skip_over,
130 ["}"],
130 ["}"],
131 "focused_insert & auto_match & followed_by_closing_brace",
131 "focused_insert & auto_match & followed_by_closing_brace",
132 ),
132 ),
133 Binding(
133 Binding(
134 match.skip_over, ['"'], "focused_insert & auto_match & followed_by_double_quote"
134 match.skip_over, ['"'], "focused_insert & auto_match & followed_by_double_quote"
135 ),
135 ),
136 Binding(
136 Binding(
137 match.skip_over, ["'"], "focused_insert & auto_match & followed_by_single_quote"
137 match.skip_over, ["'"], "focused_insert & auto_match & followed_by_single_quote"
138 ),
138 ),
139 Binding(
139 Binding(
140 match.delete_pair,
140 match.delete_pair,
141 ["backspace"],
141 ["backspace"],
142 "focused_insert"
142 "focused_insert"
143 " & preceded_by_opening_round_paren"
143 " & preceded_by_opening_round_paren"
144 " & auto_match"
144 " & auto_match"
145 " & followed_by_closing_round_paren",
145 " & followed_by_closing_round_paren",
146 ),
146 ),
147 Binding(
147 Binding(
148 match.delete_pair,
148 match.delete_pair,
149 ["backspace"],
149 ["backspace"],
150 "focused_insert"
150 "focused_insert"
151 " & preceded_by_opening_bracket"
151 " & preceded_by_opening_bracket"
152 " & auto_match"
152 " & auto_match"
153 " & followed_by_closing_bracket",
153 " & followed_by_closing_bracket",
154 ),
154 ),
155 Binding(
155 Binding(
156 match.delete_pair,
156 match.delete_pair,
157 ["backspace"],
157 ["backspace"],
158 "focused_insert"
158 "focused_insert"
159 " & preceded_by_opening_brace"
159 " & preceded_by_opening_brace"
160 " & auto_match"
160 " & auto_match"
161 " & followed_by_closing_brace",
161 " & followed_by_closing_brace",
162 ),
162 ),
163 Binding(
163 Binding(
164 match.delete_pair,
164 match.delete_pair,
165 ["backspace"],
165 ["backspace"],
166 "focused_insert"
166 "focused_insert"
167 " & preceded_by_double_quote"
167 " & preceded_by_double_quote"
168 " & auto_match"
168 " & auto_match"
169 " & followed_by_double_quote",
169 " & followed_by_double_quote",
170 ),
170 ),
171 Binding(
171 Binding(
172 match.delete_pair,
172 match.delete_pair,
173 ["backspace"],
173 ["backspace"],
174 "focused_insert"
174 "focused_insert"
175 " & preceded_by_single_quote"
175 " & preceded_by_single_quote"
176 " & auto_match"
176 " & auto_match"
177 " & followed_by_single_quote",
177 " & followed_by_single_quote",
178 ),
178 ),
179 ]
179 ]
180
180
181 AUTO_SUGGEST_BINDINGS = [
181 AUTO_SUGGEST_BINDINGS = [
182 Binding(
182 Binding(
183 auto_suggest.accept_in_vi_insert_mode,
183 auto_suggest.accept_in_vi_insert_mode,
184 ["end"],
184 ["end"],
185 "default_buffer_focused & (ebivim | ~vi_insert_mode)",
185 "default_buffer_focused & (ebivim | ~vi_insert_mode)",
186 ),
186 ),
187 Binding(
187 Binding(
188 auto_suggest.accept_in_vi_insert_mode,
188 auto_suggest.accept_in_vi_insert_mode,
189 ["c-e"],
189 ["c-e"],
190 "vi_insert_mode & default_buffer_focused & ebivim",
190 "vi_insert_mode & default_buffer_focused & ebivim",
191 ),
191 ),
192 Binding(auto_suggest.accept, ["c-f"], "vi_insert_mode & default_buffer_focused"),
192 Binding(auto_suggest.accept, ["c-f"], "vi_insert_mode & default_buffer_focused"),
193 Binding(
193 Binding(
194 auto_suggest.accept_word,
194 auto_suggest.accept_word,
195 ["escape", "f"],
195 ["escape", "f"],
196 "vi_insert_mode & default_buffer_focused & ebivim",
196 "vi_insert_mode & default_buffer_focused & ebivim",
197 ),
197 ),
198 Binding(
198 Binding(
199 auto_suggest.accept_token,
199 auto_suggest.accept_token,
200 ["c-right"],
200 ["c-right"],
201 "has_suggestion & default_buffer_focused",
201 "has_suggestion & default_buffer_focused",
202 ),
202 ),
203 Binding(
203 Binding(
204 auto_suggest.discard,
204 auto_suggest.discard,
205 ["escape"],
205 ["escape"],
206 "has_suggestion & default_buffer_focused & emacs_insert_mode",
206 "has_suggestion & default_buffer_focused & emacs_insert_mode",
207 ),
207 ),
208 Binding(
208 Binding(
209 auto_suggest.swap_autosuggestion_up,
209 auto_suggest.swap_autosuggestion_up,
210 ["up"],
210 ["up"],
211 "navigable_suggestions"
211 "navigable_suggestions"
212 " & ~has_line_above"
212 " & ~has_line_above"
213 " & has_suggestion"
213 " & has_suggestion"
214 " & default_buffer_focused",
214 " & default_buffer_focused",
215 ),
215 ),
216 Binding(
216 Binding(
217 auto_suggest.swap_autosuggestion_down,
217 auto_suggest.swap_autosuggestion_down,
218 ["down"],
218 ["down"],
219 "navigable_suggestions"
219 "navigable_suggestions"
220 " & ~has_line_below"
220 " & ~has_line_below"
221 " & has_suggestion"
221 " & has_suggestion"
222 " & default_buffer_focused",
222 " & default_buffer_focused",
223 ),
223 ),
224 Binding(
224 Binding(
225 auto_suggest.up_and_update_hint,
225 auto_suggest.up_and_update_hint,
226 ["up"],
226 ["up"],
227 "has_line_above & navigable_suggestions & default_buffer_focused",
227 "has_line_above & navigable_suggestions & default_buffer_focused",
228 ),
228 ),
229 Binding(
229 Binding(
230 auto_suggest.down_and_update_hint,
230 auto_suggest.down_and_update_hint,
231 ["down"],
231 ["down"],
232 "has_line_below & navigable_suggestions & default_buffer_focused",
232 "has_line_below & navigable_suggestions & default_buffer_focused",
233 ),
233 ),
234 Binding(
234 Binding(
235 auto_suggest.accept_character,
235 auto_suggest.accept_character,
236 ["escape", "right"],
236 ["escape", "right"],
237 "has_suggestion & default_buffer_focused",
237 "has_suggestion & default_buffer_focused",
238 ),
238 ),
239 Binding(
239 Binding(
240 auto_suggest.accept_and_move_cursor_left,
240 auto_suggest.accept_and_move_cursor_left,
241 ["c-left"],
241 ["c-left"],
242 "has_suggestion & default_buffer_focused",
242 "has_suggestion & default_buffer_focused",
243 ),
243 ),
244 Binding(
244 Binding(
245 auto_suggest.accept_and_keep_cursor,
245 auto_suggest.accept_and_keep_cursor,
246 ["c-down"],
246 ["c-down"],
247 "has_suggestion & default_buffer_focused",
247 "has_suggestion & default_buffer_focused",
248 ),
248 ),
249 Binding(
249 Binding(
250 auto_suggest.backspace_and_resume_hint,
250 auto_suggest.backspace_and_resume_hint,
251 ["backspace"],
251 ["backspace"],
252 "has_suggestion & default_buffer_focused",
252 "has_suggestion & default_buffer_focused",
253 ),
253 ),
254 ]
254 ]
255
255
256
256
257 SIMPLE_CONTROL_BINDINGS = [
257 SIMPLE_CONTROL_BINDINGS = [
258 Binding(cmd, [key], "vi_insert_mode & default_buffer_focused & ebivim")
258 Binding(cmd, [key], "vi_insert_mode & default_buffer_focused & ebivim")
259 for key, cmd in {
259 for key, cmd in {
260 "c-a": nc.beginning_of_line,
260 "c-a": nc.beginning_of_line,
261 "c-b": nc.backward_char,
261 "c-b": nc.backward_char,
262 "c-k": nc.kill_line,
262 "c-k": nc.kill_line,
263 "c-w": nc.backward_kill_word,
263 "c-w": nc.backward_kill_word,
264 "c-y": nc.yank,
264 "c-y": nc.yank,
265 "c-_": nc.undo,
265 "c-_": nc.undo,
266 }.items()
266 }.items()
267 ]
267 ]
268
268
269
269
270 ALT_AND_COMOBO_CONTROL_BINDINGS = [
270 ALT_AND_COMOBO_CONTROL_BINDINGS = [
271 Binding(cmd, list(keys), "vi_insert_mode & default_buffer_focused & ebivim")
271 Binding(cmd, list(keys), "vi_insert_mode & default_buffer_focused & ebivim")
272 for keys, cmd in {
272 for keys, cmd in {
273 # Control Combos
273 # Control Combos
274 ("c-x", "c-e"): nc.edit_and_execute,
274 ("c-x", "c-e"): nc.edit_and_execute,
275 ("c-x", "e"): nc.edit_and_execute,
275 ("c-x", "e"): nc.edit_and_execute,
276 # Alt
276 # Alt
277 ("escape", "b"): nc.backward_word,
277 ("escape", "b"): nc.backward_word,
278 ("escape", "c"): nc.capitalize_word,
278 ("escape", "c"): nc.capitalize_word,
279 ("escape", "d"): nc.kill_word,
279 ("escape", "d"): nc.kill_word,
280 ("escape", "h"): nc.backward_kill_word,
280 ("escape", "h"): nc.backward_kill_word,
281 ("escape", "l"): nc.downcase_word,
281 ("escape", "l"): nc.downcase_word,
282 ("escape", "u"): nc.uppercase_word,
282 ("escape", "u"): nc.uppercase_word,
283 ("escape", "y"): nc.yank_pop,
283 ("escape", "y"): nc.yank_pop,
284 ("escape", "."): nc.yank_last_arg,
284 ("escape", "."): nc.yank_last_arg,
285 }.items()
285 }.items()
286 ]
286 ]
287
287
288
288
289 def add_binding(bindings: KeyBindings, binding: Binding):
289 def add_binding(bindings: KeyBindings, binding: Binding):
290 bindings.add(
290 bindings.add(
291 *binding.keys,
291 *binding.keys,
292 **({"filter": binding.filter} if binding.filter is not None else {}),
292 **({"filter": binding.filter} if binding.filter is not None else {}),
293 )(binding.command)
293 )(binding.command)
294
294
295
295
296 def create_ipython_shortcuts(shell, skip=None) -> KeyBindings:
296 def create_ipython_shortcuts(shell, skip=None) -> KeyBindings:
297 """Set up the prompt_toolkit keyboard shortcuts for IPython.
297 """Set up the prompt_toolkit keyboard shortcuts for IPython.
298
298
299 Parameters
299 Parameters
300 ----------
300 ----------
301 shell: InteractiveShell
301 shell: InteractiveShell
302 The current IPython shell Instance
302 The current IPython shell Instance
303 skip: List[Binding]
303 skip: List[Binding]
304 Bindings to skip.
304 Bindings to skip.
305
305
306 Returns
306 Returns
307 -------
307 -------
308 KeyBindings
308 KeyBindings
309 the keybinding instance for prompt toolkit.
309 the keybinding instance for prompt toolkit.
310
310
311 """
311 """
312 kb = KeyBindings()
312 kb = KeyBindings()
313 skip = skip or []
313 skip = skip or []
314 for binding in KEY_BINDINGS:
314 for binding in KEY_BINDINGS:
315 skip_this_one = False
315 skip_this_one = False
316 for to_skip in skip:
316 for to_skip in skip:
317 if (
317 if (
318 to_skip.command == binding.command
318 to_skip.command == binding.command
319 and to_skip.filter == binding.filter
319 and to_skip.filter == binding.filter
320 and to_skip.keys == binding.keys
320 and to_skip.keys == binding.keys
321 ):
321 ):
322 skip_this_one = True
322 skip_this_one = True
323 break
323 break
324 if skip_this_one:
324 if skip_this_one:
325 continue
325 continue
326 add_binding(kb, binding)
326 add_binding(kb, binding)
327
327
328 def get_input_mode(self):
328 def get_input_mode(self):
329 app = get_app()
329 app = get_app()
330 app.ttimeoutlen = shell.ttimeoutlen
330 app.ttimeoutlen = shell.ttimeoutlen
331 app.timeoutlen = shell.timeoutlen
331 app.timeoutlen = shell.timeoutlen
332
332
333 return self._input_mode
333 return self._input_mode
334
334
335 def set_input_mode(self, mode):
335 def set_input_mode(self, mode):
336 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
336 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
337 cursor = "\x1b[{} q".format(shape)
337 cursor = "\x1b[{} q".format(shape)
338
338
339 sys.stdout.write(cursor)
339 sys.stdout.write(cursor)
340 sys.stdout.flush()
340 sys.stdout.flush()
341
341
342 self._input_mode = mode
342 self._input_mode = mode
343
343
344 if shell.editing_mode == "vi" and shell.modal_cursor:
344 if shell.editing_mode == "vi" and shell.modal_cursor:
345 ViState._input_mode = InputMode.INSERT # type: ignore
345 ViState._input_mode = InputMode.INSERT # type: ignore
346 ViState.input_mode = property(get_input_mode, set_input_mode) # type: ignore
346 ViState.input_mode = property(get_input_mode, set_input_mode) # type: ignore
347
347
348 return kb
348 return kb
349
349
350
350
351 def reformat_and_execute(event):
351 def reformat_and_execute(event):
352 """Reformat code and execute it"""
352 """Reformat code and execute it"""
353 shell = get_ipython()
353 shell = get_ipython()
354 reformat_text_before_cursor(
354 reformat_text_before_cursor(
355 event.current_buffer, event.current_buffer.document, shell
355 event.current_buffer, event.current_buffer.document, shell
356 )
356 )
357 event.current_buffer.validate_and_handle()
357 event.current_buffer.validate_and_handle()
358
358
359
359
360 def reformat_text_before_cursor(buffer, document, shell):
360 def reformat_text_before_cursor(buffer, document, shell):
361 text = buffer.delete_before_cursor(len(document.text[: document.cursor_position]))
361 text = buffer.delete_before_cursor(len(document.text[: document.cursor_position]))
362 try:
362 try:
363 formatted_text = shell.reformat_handler(text)
363 formatted_text = shell.reformat_handler(text)
364 buffer.insert_text(formatted_text)
364 buffer.insert_text(formatted_text)
365 except Exception as e:
365 except Exception as e:
366 buffer.insert_text(text)
366 buffer.insert_text(text)
367
367
368
368
369 def handle_return_or_newline_or_execute(event):
369 def handle_return_or_newline_or_execute(event):
370 shell = get_ipython()
370 shell = get_ipython()
371 if getattr(shell, "handle_return", None):
371 if getattr(shell, "handle_return", None):
372 return shell.handle_return(shell)(event)
372 return shell.handle_return(shell)(event)
373 else:
373 else:
374 return newline_or_execute_outer(shell)(event)
374 return newline_or_execute_outer(shell)(event)
375
375
376
376
377 def newline_or_execute_outer(shell):
377 def newline_or_execute_outer(shell):
378 def newline_or_execute(event):
378 def newline_or_execute(event):
379 """When the user presses return, insert a newline or execute the code."""
379 """When the user presses return, insert a newline or execute the code."""
380 b = event.current_buffer
380 b = event.current_buffer
381 d = b.document
381 d = b.document
382
382
383 if b.complete_state:
383 if b.complete_state:
384 cc = b.complete_state.current_completion
384 cc = b.complete_state.current_completion
385 if cc:
385 if cc:
386 b.apply_completion(cc)
386 b.apply_completion(cc)
387 else:
387 else:
388 b.cancel_completion()
388 b.cancel_completion()
389 return
389 return
390
390
391 # If there's only one line, treat it as if the cursor is at the end.
391 # If there's only one line, treat it as if the cursor is at the end.
392 # See https://github.com/ipython/ipython/issues/10425
392 # See https://github.com/ipython/ipython/issues/10425
393 if d.line_count == 1:
393 if d.line_count == 1:
394 check_text = d.text
394 check_text = d.text
395 else:
395 else:
396 check_text = d.text[: d.cursor_position]
396 check_text = d.text[: d.cursor_position]
397 status, indent = shell.check_complete(check_text)
397 status, indent = shell.check_complete(check_text)
398
398
399 # if all we have after the cursor is whitespace: reformat current text
399 # if all we have after the cursor is whitespace: reformat current text
400 # before cursor
400 # before cursor
401 after_cursor = d.text[d.cursor_position :]
401 after_cursor = d.text[d.cursor_position :]
402 reformatted = False
402 reformatted = False
403 if not after_cursor.strip():
403 if not after_cursor.strip():
404 reformat_text_before_cursor(b, d, shell)
404 reformat_text_before_cursor(b, d, shell)
405 reformatted = True
405 reformatted = True
406 if not (
406 if not (
407 d.on_last_line
407 d.on_last_line
408 or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
408 or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
409 ):
409 ):
410 if shell.autoindent:
410 if shell.autoindent:
411 b.insert_text("\n" + indent)
411 b.insert_text("\n" + indent)
412 else:
412 else:
413 b.insert_text("\n")
413 b.insert_text("\n")
414 return
414 return
415
415
416 if (status != "incomplete") and b.accept_handler:
416 if (status != "incomplete") and b.accept_handler:
417 if not reformatted:
417 if not reformatted:
418 reformat_text_before_cursor(b, d, shell)
418 reformat_text_before_cursor(b, d, shell)
419 b.validate_and_handle()
419 b.validate_and_handle()
420 else:
420 else:
421 if shell.autoindent:
421 if shell.autoindent:
422 b.insert_text("\n" + indent)
422 b.insert_text("\n" + indent)
423 else:
423 else:
424 b.insert_text("\n")
424 b.insert_text("\n")
425
425
426 return newline_or_execute
426 return newline_or_execute
427
427
428
428
429 def previous_history_or_previous_completion(event):
429 def previous_history_or_previous_completion(event):
430 """
430 """
431 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
431 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
432
432
433 If completer is open this still select previous completion.
433 If completer is open this still select previous completion.
434 """
434 """
435 event.current_buffer.auto_up()
435 event.current_buffer.auto_up()
436
436
437
437
438 def next_history_or_next_completion(event):
438 def next_history_or_next_completion(event):
439 """
439 """
440 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
440 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
441
441
442 If completer is open this still select next completion.
442 If completer is open this still select next completion.
443 """
443 """
444 event.current_buffer.auto_down()
444 event.current_buffer.auto_down()
445
445
446
446
447 def dismiss_completion(event):
447 def dismiss_completion(event):
448 """Dismiss completion"""
448 """Dismiss completion"""
449 b = event.current_buffer
449 b = event.current_buffer
450 if b.complete_state:
450 if b.complete_state:
451 b.cancel_completion()
451 b.cancel_completion()
452
452
453
453
454 def reset_buffer(event):
454 def reset_buffer(event):
455 """Reset buffer"""
455 """Reset buffer"""
456 b = event.current_buffer
456 b = event.current_buffer
457 if b.complete_state:
457 if b.complete_state:
458 b.cancel_completion()
458 b.cancel_completion()
459 else:
459 else:
460 b.reset()
460 b.reset()
461
461
462
462
463 def reset_search_buffer(event):
463 def reset_search_buffer(event):
464 """Reset search buffer"""
464 """Reset search buffer"""
465 if event.current_buffer.document.text:
465 if event.current_buffer.document.text:
466 event.current_buffer.reset()
466 event.current_buffer.reset()
467 else:
467 else:
468 event.app.layout.focus(DEFAULT_BUFFER)
468 event.app.layout.focus(DEFAULT_BUFFER)
469
469
470
470
471 def suspend_to_bg(event):
471 def suspend_to_bg(event):
472 """Suspend to background"""
472 """Suspend to background"""
473 event.app.suspend_to_background()
473 event.app.suspend_to_background()
474
474
475
475
476 def quit(event):
476 def quit(event):
477 """
477 """
478 Quit application with ``SIGQUIT`` if supported or ``sys.exit`` otherwise.
478 Quit application with ``SIGQUIT`` if supported or ``sys.exit`` otherwise.
479
479
480 On platforms that support SIGQUIT, send SIGQUIT to the current process.
480 On platforms that support SIGQUIT, send SIGQUIT to the current process.
481 On other platforms, just exit the process with a message.
481 On other platforms, just exit the process with a message.
482 """
482 """
483 sigquit = getattr(signal, "SIGQUIT", None)
483 sigquit = getattr(signal, "SIGQUIT", None)
484 if sigquit is not None:
484 if sigquit is not None:
485 os.kill(0, signal.SIGQUIT)
485 os.kill(0, signal.SIGQUIT)
486 else:
486 else:
487 sys.exit("Quit")
487 sys.exit("Quit")
488
488
489
489
490 def indent_buffer(event):
490 def indent_buffer(event):
491 """Indent buffer"""
491 """Indent buffer"""
492 event.current_buffer.insert_text(" " * 4)
492 event.current_buffer.insert_text(" " * 4)
493
493
494
494
495 @undoc
496 def newline_with_copy_margin(event):
497 """
498 DEPRECATED since IPython 6.0
499
500 See :any:`newline_autoindent_outer` for a replacement.
501
502 Preserve margin and cursor position when using
503 Control-O to insert a newline in EMACS mode
504 """
505 warnings.warn(
506 "`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
507 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
508 DeprecationWarning,
509 stacklevel=2,
510 )
511
512 b = event.current_buffer
513 cursor_start_pos = b.document.cursor_position_col
514 b.newline(copy_margin=True)
515 b.cursor_up(count=1)
516 cursor_end_pos = b.document.cursor_position_col
517 if cursor_start_pos != cursor_end_pos:
518 pos_diff = cursor_start_pos - cursor_end_pos
519 b.cursor_right(count=pos_diff)
520
521
522 def newline_autoindent(event):
495 def newline_autoindent(event):
523 """Insert a newline after the cursor indented appropriately.
496 """Insert a newline after the cursor indented appropriately.
524
497
525 Fancier version of deprecated ``newline_with_copy_margin`` which should
498 Fancier version of former ``newline_with_copy_margin`` which should
526 compute the correct indentation of the inserted line. That is to say, indent
499 compute the correct indentation of the inserted line. That is to say, indent
527 by 4 extra space after a function definition, class definition, context
500 by 4 extra space after a function definition, class definition, context
528 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
501 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
529 """
502 """
530 shell = get_ipython()
503 shell = get_ipython()
531 inputsplitter = shell.input_transformer_manager
504 inputsplitter = shell.input_transformer_manager
532 b = event.current_buffer
505 b = event.current_buffer
533 d = b.document
506 d = b.document
534
507
535 if b.complete_state:
508 if b.complete_state:
536 b.cancel_completion()
509 b.cancel_completion()
537 text = d.text[: d.cursor_position] + "\n"
510 text = d.text[: d.cursor_position] + "\n"
538 _, indent = inputsplitter.check_complete(text)
511 _, indent = inputsplitter.check_complete(text)
539 b.insert_text("\n" + (" " * (indent or 0)), move_cursor=False)
512 b.insert_text("\n" + (" " * (indent or 0)), move_cursor=False)
540
513
541
514
542 def open_input_in_editor(event):
515 def open_input_in_editor(event):
543 """Open code from input in external editor"""
516 """Open code from input in external editor"""
544 event.app.current_buffer.open_in_editor()
517 event.app.current_buffer.open_in_editor()
545
518
546
519
547 if sys.platform == "win32":
520 if sys.platform == "win32":
548 from IPython.core.error import TryNext
521 from IPython.core.error import TryNext
549 from IPython.lib.clipboard import (
522 from IPython.lib.clipboard import (
550 ClipboardEmpty,
523 ClipboardEmpty,
551 tkinter_clipboard_get,
524 tkinter_clipboard_get,
552 win32_clipboard_get,
525 win32_clipboard_get,
553 )
526 )
554
527
555 @undoc
528 @undoc
556 def win_paste(event):
529 def win_paste(event):
557 try:
530 try:
558 text = win32_clipboard_get()
531 text = win32_clipboard_get()
559 except TryNext:
532 except TryNext:
560 try:
533 try:
561 text = tkinter_clipboard_get()
534 text = tkinter_clipboard_get()
562 except (TryNext, ClipboardEmpty):
535 except (TryNext, ClipboardEmpty):
563 return
536 return
564 except ClipboardEmpty:
537 except ClipboardEmpty:
565 return
538 return
566 event.current_buffer.insert_text(text.replace("\t", " " * 4))
539 event.current_buffer.insert_text(text.replace("\t", " " * 4))
567
540
568 else:
541 else:
569
542
570 @undoc
543 @undoc
571 def win_paste(event):
544 def win_paste(event):
572 """Stub used on other platforms"""
545 """Stub used on other platforms"""
573 pass
546 pass
574
547
575
548
576 KEY_BINDINGS = [
549 KEY_BINDINGS = [
577 Binding(
550 Binding(
578 handle_return_or_newline_or_execute,
551 handle_return_or_newline_or_execute,
579 ["enter"],
552 ["enter"],
580 "default_buffer_focused & ~has_selection & insert_mode",
553 "default_buffer_focused & ~has_selection & insert_mode",
581 ),
554 ),
582 Binding(
555 Binding(
583 reformat_and_execute,
556 reformat_and_execute,
584 ["escape", "enter"],
557 ["escape", "enter"],
585 "default_buffer_focused & ~has_selection & insert_mode & ebivim",
558 "default_buffer_focused & ~has_selection & insert_mode & ebivim",
586 ),
559 ),
587 Binding(quit, ["c-\\"]),
560 Binding(quit, ["c-\\"]),
588 Binding(
561 Binding(
589 previous_history_or_previous_completion,
562 previous_history_or_previous_completion,
590 ["c-p"],
563 ["c-p"],
591 "vi_insert_mode & default_buffer_focused",
564 "vi_insert_mode & default_buffer_focused",
592 ),
565 ),
593 Binding(
566 Binding(
594 next_history_or_next_completion,
567 next_history_or_next_completion,
595 ["c-n"],
568 ["c-n"],
596 "vi_insert_mode & default_buffer_focused",
569 "vi_insert_mode & default_buffer_focused",
597 ),
570 ),
598 Binding(dismiss_completion, ["c-g"], "default_buffer_focused & has_completions"),
571 Binding(dismiss_completion, ["c-g"], "default_buffer_focused & has_completions"),
599 Binding(reset_buffer, ["c-c"], "default_buffer_focused"),
572 Binding(reset_buffer, ["c-c"], "default_buffer_focused"),
600 Binding(reset_search_buffer, ["c-c"], "search_buffer_focused"),
573 Binding(reset_search_buffer, ["c-c"], "search_buffer_focused"),
601 Binding(suspend_to_bg, ["c-z"], "supports_suspend"),
574 Binding(suspend_to_bg, ["c-z"], "supports_suspend"),
602 Binding(
575 Binding(
603 indent_buffer,
576 indent_buffer,
604 ["tab"], # Ctrl+I == Tab
577 ["tab"], # Ctrl+I == Tab
605 "default_buffer_focused"
578 "default_buffer_focused"
606 " & ~has_selection"
579 " & ~has_selection"
607 " & insert_mode"
580 " & insert_mode"
608 " & cursor_in_leading_ws",
581 " & cursor_in_leading_ws",
609 ),
582 ),
610 Binding(newline_autoindent, ["c-o"], "default_buffer_focused & emacs_insert_mode"),
583 Binding(newline_autoindent, ["c-o"], "default_buffer_focused & emacs_insert_mode"),
611 Binding(open_input_in_editor, ["f2"], "default_buffer_focused"),
584 Binding(open_input_in_editor, ["f2"], "default_buffer_focused"),
612 *AUTO_MATCH_BINDINGS,
585 *AUTO_MATCH_BINDINGS,
613 *AUTO_SUGGEST_BINDINGS,
586 *AUTO_SUGGEST_BINDINGS,
614 Binding(
587 Binding(
615 display_completions_like_readline,
588 display_completions_like_readline,
616 ["c-i"],
589 ["c-i"],
617 "readline_like_completions"
590 "readline_like_completions"
618 " & default_buffer_focused"
591 " & default_buffer_focused"
619 " & ~has_selection"
592 " & ~has_selection"
620 " & insert_mode"
593 " & insert_mode"
621 " & ~cursor_in_leading_ws",
594 " & ~cursor_in_leading_ws",
622 ),
595 ),
623 Binding(win_paste, ["c-v"], "default_buffer_focused & ~vi_mode & is_windows_os"),
596 Binding(win_paste, ["c-v"], "default_buffer_focused & ~vi_mode & is_windows_os"),
624 *SIMPLE_CONTROL_BINDINGS,
597 *SIMPLE_CONTROL_BINDINGS,
625 *ALT_AND_COMOBO_CONTROL_BINDINGS,
598 *ALT_AND_COMOBO_CONTROL_BINDINGS,
626 ]
599 ]
General Comments 0
You need to be logged in to leave comments. Login now