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