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