##// END OF EJS Templates
Change the default for character complete to Alt+Right
krassowski -
Show More
@@ -1,620 +1,620 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, ["c-e"], "focused_insert_vi & ebivim"
188 auto_suggest.accept_in_vi_insert_mode, ["c-e"], "focused_insert_vi & ebivim"
189 ),
189 ),
190 Binding(auto_suggest.accept, ["c-f"], "focused_insert_vi"),
190 Binding(auto_suggest.accept, ["c-f"], "focused_insert_vi"),
191 Binding(auto_suggest.accept_word, ["escape", "f"], "focused_insert_vi & ebivim"),
191 Binding(auto_suggest.accept_word, ["escape", "f"], "focused_insert_vi & ebivim"),
192 Binding(
192 Binding(
193 auto_suggest.accept_token,
193 auto_suggest.accept_token,
194 ["c-right"],
194 ["c-right"],
195 "has_suggestion & default_buffer_focused",
195 "has_suggestion & default_buffer_focused",
196 ),
196 ),
197 Binding(
197 Binding(
198 auto_suggest.discard,
198 auto_suggest.discard,
199 ["escape"],
199 ["escape"],
200 "has_suggestion & default_buffer_focused & emacs_insert_mode",
200 "has_suggestion & default_buffer_focused & emacs_insert_mode",
201 ),
201 ),
202 Binding(
202 Binding(
203 auto_suggest.swap_autosuggestion_up,
203 auto_suggest.swap_autosuggestion_up,
204 ["up"],
204 ["up"],
205 "navigable_suggestions"
205 "navigable_suggestions"
206 " & ~has_line_above"
206 " & ~has_line_above"
207 " & has_suggestion"
207 " & has_suggestion"
208 " & default_buffer_focused",
208 " & default_buffer_focused",
209 ),
209 ),
210 Binding(
210 Binding(
211 auto_suggest.swap_autosuggestion_down,
211 auto_suggest.swap_autosuggestion_down,
212 ["down"],
212 ["down"],
213 "navigable_suggestions"
213 "navigable_suggestions"
214 " & ~has_line_below"
214 " & ~has_line_below"
215 " & has_suggestion"
215 " & has_suggestion"
216 " & default_buffer_focused",
216 " & default_buffer_focused",
217 ),
217 ),
218 Binding(
218 Binding(
219 auto_suggest.up_and_update_hint,
219 auto_suggest.up_and_update_hint,
220 ["up"],
220 ["up"],
221 "has_line_above & navigable_suggestions & default_buffer_focused",
221 "has_line_above & navigable_suggestions & default_buffer_focused",
222 ),
222 ),
223 Binding(
223 Binding(
224 auto_suggest.down_and_update_hint,
224 auto_suggest.down_and_update_hint,
225 ["down"],
225 ["down"],
226 "has_line_below & navigable_suggestions & default_buffer_focused",
226 "has_line_below & navigable_suggestions & default_buffer_focused",
227 ),
227 ),
228 Binding(
228 Binding(
229 auto_suggest.accept_character,
229 auto_suggest.accept_character,
230 ["right"],
230 ["escape", "right"],
231 "has_suggestion & default_buffer_focused",
231 "has_suggestion & default_buffer_focused",
232 ),
232 ),
233 Binding(
233 Binding(
234 auto_suggest.accept_and_move_cursor_left,
234 auto_suggest.accept_and_move_cursor_left,
235 ["c-left"],
235 ["c-left"],
236 "has_suggestion & default_buffer_focused",
236 "has_suggestion & default_buffer_focused",
237 ),
237 ),
238 Binding(
238 Binding(
239 auto_suggest.accept_and_keep_cursor,
239 auto_suggest.accept_and_keep_cursor,
240 ["c-down"],
240 ["c-down"],
241 "has_suggestion & default_buffer_focused",
241 "has_suggestion & default_buffer_focused",
242 ),
242 ),
243 Binding(
243 Binding(
244 auto_suggest.backspace_and_resume_hint,
244 auto_suggest.backspace_and_resume_hint,
245 ["backspace"],
245 ["backspace"],
246 "has_suggestion & default_buffer_focused",
246 "has_suggestion & default_buffer_focused",
247 ),
247 ),
248 ]
248 ]
249
249
250
250
251 SIMPLE_CONTROL_BINDINGS = [
251 SIMPLE_CONTROL_BINDINGS = [
252 Binding(cmd, [key], "focused_insert_vi & ebivim")
252 Binding(cmd, [key], "focused_insert_vi & ebivim")
253 for key, cmd in {
253 for key, cmd in {
254 "c-a": nc.beginning_of_line,
254 "c-a": nc.beginning_of_line,
255 "c-b": nc.backward_char,
255 "c-b": nc.backward_char,
256 "c-k": nc.kill_line,
256 "c-k": nc.kill_line,
257 "c-w": nc.backward_kill_word,
257 "c-w": nc.backward_kill_word,
258 "c-y": nc.yank,
258 "c-y": nc.yank,
259 "c-_": nc.undo,
259 "c-_": nc.undo,
260 }.items()
260 }.items()
261 ]
261 ]
262
262
263
263
264 ALT_AND_COMOBO_CONTROL_BINDINGS = [
264 ALT_AND_COMOBO_CONTROL_BINDINGS = [
265 Binding(cmd, list(keys), "focused_insert_vi & ebivim")
265 Binding(cmd, list(keys), "focused_insert_vi & ebivim")
266 for keys, cmd in {
266 for keys, cmd in {
267 # Control Combos
267 # Control Combos
268 ("c-x", "c-e"): nc.edit_and_execute,
268 ("c-x", "c-e"): nc.edit_and_execute,
269 ("c-x", "e"): nc.edit_and_execute,
269 ("c-x", "e"): nc.edit_and_execute,
270 # Alt
270 # Alt
271 ("escape", "b"): nc.backward_word,
271 ("escape", "b"): nc.backward_word,
272 ("escape", "c"): nc.capitalize_word,
272 ("escape", "c"): nc.capitalize_word,
273 ("escape", "d"): nc.kill_word,
273 ("escape", "d"): nc.kill_word,
274 ("escape", "h"): nc.backward_kill_word,
274 ("escape", "h"): nc.backward_kill_word,
275 ("escape", "l"): nc.downcase_word,
275 ("escape", "l"): nc.downcase_word,
276 ("escape", "u"): nc.uppercase_word,
276 ("escape", "u"): nc.uppercase_word,
277 ("escape", "y"): nc.yank_pop,
277 ("escape", "y"): nc.yank_pop,
278 ("escape", "."): nc.yank_last_arg,
278 ("escape", "."): nc.yank_last_arg,
279 }.items()
279 }.items()
280 ]
280 ]
281
281
282
282
283 def add_binding(bindings: KeyBindings, binding: Binding):
283 def add_binding(bindings: KeyBindings, binding: Binding):
284 bindings.add(
284 bindings.add(
285 *binding.keys,
285 *binding.keys,
286 **({"filter": binding.filter} if binding.filter is not None else {}),
286 **({"filter": binding.filter} if binding.filter is not None else {}),
287 )(binding.command)
287 )(binding.command)
288
288
289
289
290 def create_ipython_shortcuts(shell, skip=None) -> KeyBindings:
290 def create_ipython_shortcuts(shell, skip=None) -> KeyBindings:
291 """Set up the prompt_toolkit keyboard shortcuts for IPython.
291 """Set up the prompt_toolkit keyboard shortcuts for IPython.
292
292
293 Parameters
293 Parameters
294 ----------
294 ----------
295 shell: InteractiveShell
295 shell: InteractiveShell
296 The current IPython shell Instance
296 The current IPython shell Instance
297 skip: List[Binding]
297 skip: List[Binding]
298 Bindings to skip.
298 Bindings to skip.
299
299
300 Returns
300 Returns
301 -------
301 -------
302 KeyBindings
302 KeyBindings
303 the keybinding instance for prompt toolkit.
303 the keybinding instance for prompt toolkit.
304
304
305 """
305 """
306 kb = KeyBindings()
306 kb = KeyBindings()
307 skip = skip or []
307 skip = skip or []
308 for binding in KEY_BINDINGS:
308 for binding in KEY_BINDINGS:
309 skip_this_one = False
309 skip_this_one = False
310 for to_skip in skip:
310 for to_skip in skip:
311 if (
311 if (
312 to_skip.command == binding.command
312 to_skip.command == binding.command
313 and to_skip.filter == binding.filter
313 and to_skip.filter == binding.filter
314 and to_skip.keys == binding.keys
314 and to_skip.keys == binding.keys
315 ):
315 ):
316 skip_this_one = True
316 skip_this_one = True
317 break
317 break
318 if skip_this_one:
318 if skip_this_one:
319 continue
319 continue
320 add_binding(kb, binding)
320 add_binding(kb, binding)
321
321
322 def get_input_mode(self):
322 def get_input_mode(self):
323 app = get_app()
323 app = get_app()
324 app.ttimeoutlen = shell.ttimeoutlen
324 app.ttimeoutlen = shell.ttimeoutlen
325 app.timeoutlen = shell.timeoutlen
325 app.timeoutlen = shell.timeoutlen
326
326
327 return self._input_mode
327 return self._input_mode
328
328
329 def set_input_mode(self, mode):
329 def set_input_mode(self, mode):
330 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
330 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
331 cursor = "\x1b[{} q".format(shape)
331 cursor = "\x1b[{} q".format(shape)
332
332
333 sys.stdout.write(cursor)
333 sys.stdout.write(cursor)
334 sys.stdout.flush()
334 sys.stdout.flush()
335
335
336 self._input_mode = mode
336 self._input_mode = mode
337
337
338 if shell.editing_mode == "vi" and shell.modal_cursor:
338 if shell.editing_mode == "vi" and shell.modal_cursor:
339 ViState._input_mode = InputMode.INSERT # type: ignore
339 ViState._input_mode = InputMode.INSERT # type: ignore
340 ViState.input_mode = property(get_input_mode, set_input_mode) # type: ignore
340 ViState.input_mode = property(get_input_mode, set_input_mode) # type: ignore
341
341
342 return kb
342 return kb
343
343
344
344
345 def reformat_and_execute(event):
345 def reformat_and_execute(event):
346 """Reformat code and execute it"""
346 """Reformat code and execute it"""
347 shell = get_ipython()
347 shell = get_ipython()
348 reformat_text_before_cursor(
348 reformat_text_before_cursor(
349 event.current_buffer, event.current_buffer.document, shell
349 event.current_buffer, event.current_buffer.document, shell
350 )
350 )
351 event.current_buffer.validate_and_handle()
351 event.current_buffer.validate_and_handle()
352
352
353
353
354 def reformat_text_before_cursor(buffer, document, shell):
354 def reformat_text_before_cursor(buffer, document, shell):
355 text = buffer.delete_before_cursor(len(document.text[: document.cursor_position]))
355 text = buffer.delete_before_cursor(len(document.text[: document.cursor_position]))
356 try:
356 try:
357 formatted_text = shell.reformat_handler(text)
357 formatted_text = shell.reformat_handler(text)
358 buffer.insert_text(formatted_text)
358 buffer.insert_text(formatted_text)
359 except Exception as e:
359 except Exception as e:
360 buffer.insert_text(text)
360 buffer.insert_text(text)
361
361
362
362
363 def handle_return_or_newline_or_execute(event):
363 def handle_return_or_newline_or_execute(event):
364 shell = get_ipython()
364 shell = get_ipython()
365 if getattr(shell, "handle_return", None):
365 if getattr(shell, "handle_return", None):
366 return shell.handle_return(shell)(event)
366 return shell.handle_return(shell)(event)
367 else:
367 else:
368 return newline_or_execute_outer(shell)(event)
368 return newline_or_execute_outer(shell)(event)
369
369
370
370
371 def newline_or_execute_outer(shell):
371 def newline_or_execute_outer(shell):
372 def newline_or_execute(event):
372 def newline_or_execute(event):
373 """When the user presses return, insert a newline or execute the code."""
373 """When the user presses return, insert a newline or execute the code."""
374 b = event.current_buffer
374 b = event.current_buffer
375 d = b.document
375 d = b.document
376
376
377 if b.complete_state:
377 if b.complete_state:
378 cc = b.complete_state.current_completion
378 cc = b.complete_state.current_completion
379 if cc:
379 if cc:
380 b.apply_completion(cc)
380 b.apply_completion(cc)
381 else:
381 else:
382 b.cancel_completion()
382 b.cancel_completion()
383 return
383 return
384
384
385 # If there's only one line, treat it as if the cursor is at the end.
385 # If there's only one line, treat it as if the cursor is at the end.
386 # See https://github.com/ipython/ipython/issues/10425
386 # See https://github.com/ipython/ipython/issues/10425
387 if d.line_count == 1:
387 if d.line_count == 1:
388 check_text = d.text
388 check_text = d.text
389 else:
389 else:
390 check_text = d.text[: d.cursor_position]
390 check_text = d.text[: d.cursor_position]
391 status, indent = shell.check_complete(check_text)
391 status, indent = shell.check_complete(check_text)
392
392
393 # if all we have after the cursor is whitespace: reformat current text
393 # if all we have after the cursor is whitespace: reformat current text
394 # before cursor
394 # before cursor
395 after_cursor = d.text[d.cursor_position :]
395 after_cursor = d.text[d.cursor_position :]
396 reformatted = False
396 reformatted = False
397 if not after_cursor.strip():
397 if not after_cursor.strip():
398 reformat_text_before_cursor(b, d, shell)
398 reformat_text_before_cursor(b, d, shell)
399 reformatted = True
399 reformatted = True
400 if not (
400 if not (
401 d.on_last_line
401 d.on_last_line
402 or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
402 or d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
403 ):
403 ):
404 if shell.autoindent:
404 if shell.autoindent:
405 b.insert_text("\n" + indent)
405 b.insert_text("\n" + indent)
406 else:
406 else:
407 b.insert_text("\n")
407 b.insert_text("\n")
408 return
408 return
409
409
410 if (status != "incomplete") and b.accept_handler:
410 if (status != "incomplete") and b.accept_handler:
411 if not reformatted:
411 if not reformatted:
412 reformat_text_before_cursor(b, d, shell)
412 reformat_text_before_cursor(b, d, shell)
413 b.validate_and_handle()
413 b.validate_and_handle()
414 else:
414 else:
415 if shell.autoindent:
415 if shell.autoindent:
416 b.insert_text("\n" + indent)
416 b.insert_text("\n" + indent)
417 else:
417 else:
418 b.insert_text("\n")
418 b.insert_text("\n")
419
419
420 return newline_or_execute
420 return newline_or_execute
421
421
422
422
423 def previous_history_or_previous_completion(event):
423 def previous_history_or_previous_completion(event):
424 """
424 """
425 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
425 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
426
426
427 If completer is open this still select previous completion.
427 If completer is open this still select previous completion.
428 """
428 """
429 event.current_buffer.auto_up()
429 event.current_buffer.auto_up()
430
430
431
431
432 def next_history_or_next_completion(event):
432 def next_history_or_next_completion(event):
433 """
433 """
434 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
434 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
435
435
436 If completer is open this still select next completion.
436 If completer is open this still select next completion.
437 """
437 """
438 event.current_buffer.auto_down()
438 event.current_buffer.auto_down()
439
439
440
440
441 def dismiss_completion(event):
441 def dismiss_completion(event):
442 """Dismiss completion"""
442 """Dismiss completion"""
443 b = event.current_buffer
443 b = event.current_buffer
444 if b.complete_state:
444 if b.complete_state:
445 b.cancel_completion()
445 b.cancel_completion()
446
446
447
447
448 def reset_buffer(event):
448 def reset_buffer(event):
449 """Reset buffer"""
449 """Reset buffer"""
450 b = event.current_buffer
450 b = event.current_buffer
451 if b.complete_state:
451 if b.complete_state:
452 b.cancel_completion()
452 b.cancel_completion()
453 else:
453 else:
454 b.reset()
454 b.reset()
455
455
456
456
457 def reset_search_buffer(event):
457 def reset_search_buffer(event):
458 """Reset search buffer"""
458 """Reset search buffer"""
459 if event.current_buffer.document.text:
459 if event.current_buffer.document.text:
460 event.current_buffer.reset()
460 event.current_buffer.reset()
461 else:
461 else:
462 event.app.layout.focus(DEFAULT_BUFFER)
462 event.app.layout.focus(DEFAULT_BUFFER)
463
463
464
464
465 def suspend_to_bg(event):
465 def suspend_to_bg(event):
466 """Suspend to background"""
466 """Suspend to background"""
467 event.app.suspend_to_background()
467 event.app.suspend_to_background()
468
468
469
469
470 def quit(event):
470 def quit(event):
471 """
471 """
472 Quit application with ``SIGQUIT`` if supported or ``sys.exit`` otherwise.
472 Quit application with ``SIGQUIT`` if supported or ``sys.exit`` otherwise.
473
473
474 On platforms that support SIGQUIT, send SIGQUIT to the current process.
474 On platforms that support SIGQUIT, send SIGQUIT to the current process.
475 On other platforms, just exit the process with a message.
475 On other platforms, just exit the process with a message.
476 """
476 """
477 sigquit = getattr(signal, "SIGQUIT", None)
477 sigquit = getattr(signal, "SIGQUIT", None)
478 if sigquit is not None:
478 if sigquit is not None:
479 os.kill(0, signal.SIGQUIT)
479 os.kill(0, signal.SIGQUIT)
480 else:
480 else:
481 sys.exit("Quit")
481 sys.exit("Quit")
482
482
483
483
484 def indent_buffer(event):
484 def indent_buffer(event):
485 """Indent buffer"""
485 """Indent buffer"""
486 event.current_buffer.insert_text(" " * 4)
486 event.current_buffer.insert_text(" " * 4)
487
487
488
488
489 @undoc
489 @undoc
490 def newline_with_copy_margin(event):
490 def newline_with_copy_margin(event):
491 """
491 """
492 DEPRECATED since IPython 6.0
492 DEPRECATED since IPython 6.0
493
493
494 See :any:`newline_autoindent_outer` for a replacement.
494 See :any:`newline_autoindent_outer` for a replacement.
495
495
496 Preserve margin and cursor position when using
496 Preserve margin and cursor position when using
497 Control-O to insert a newline in EMACS mode
497 Control-O to insert a newline in EMACS mode
498 """
498 """
499 warnings.warn(
499 warnings.warn(
500 "`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
500 "`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
501 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
501 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
502 DeprecationWarning,
502 DeprecationWarning,
503 stacklevel=2,
503 stacklevel=2,
504 )
504 )
505
505
506 b = event.current_buffer
506 b = event.current_buffer
507 cursor_start_pos = b.document.cursor_position_col
507 cursor_start_pos = b.document.cursor_position_col
508 b.newline(copy_margin=True)
508 b.newline(copy_margin=True)
509 b.cursor_up(count=1)
509 b.cursor_up(count=1)
510 cursor_end_pos = b.document.cursor_position_col
510 cursor_end_pos = b.document.cursor_position_col
511 if cursor_start_pos != cursor_end_pos:
511 if cursor_start_pos != cursor_end_pos:
512 pos_diff = cursor_start_pos - cursor_end_pos
512 pos_diff = cursor_start_pos - cursor_end_pos
513 b.cursor_right(count=pos_diff)
513 b.cursor_right(count=pos_diff)
514
514
515
515
516 def newline_autoindent(event):
516 def newline_autoindent(event):
517 """Insert a newline after the cursor indented appropriately.
517 """Insert a newline after the cursor indented appropriately.
518
518
519 Fancier version of deprecated ``newline_with_copy_margin`` which should
519 Fancier version of deprecated ``newline_with_copy_margin`` which should
520 compute the correct indentation of the inserted line. That is to say, indent
520 compute the correct indentation of the inserted line. That is to say, indent
521 by 4 extra space after a function definition, class definition, context
521 by 4 extra space after a function definition, class definition, context
522 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
522 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
523 """
523 """
524 shell = get_ipython()
524 shell = get_ipython()
525 inputsplitter = shell.input_transformer_manager
525 inputsplitter = shell.input_transformer_manager
526 b = event.current_buffer
526 b = event.current_buffer
527 d = b.document
527 d = b.document
528
528
529 if b.complete_state:
529 if b.complete_state:
530 b.cancel_completion()
530 b.cancel_completion()
531 text = d.text[: d.cursor_position] + "\n"
531 text = d.text[: d.cursor_position] + "\n"
532 _, indent = inputsplitter.check_complete(text)
532 _, indent = inputsplitter.check_complete(text)
533 b.insert_text("\n" + (" " * (indent or 0)), move_cursor=False)
533 b.insert_text("\n" + (" " * (indent or 0)), move_cursor=False)
534
534
535
535
536 def open_input_in_editor(event):
536 def open_input_in_editor(event):
537 """Open code from input in external editor"""
537 """Open code from input in external editor"""
538 event.app.current_buffer.open_in_editor()
538 event.app.current_buffer.open_in_editor()
539
539
540
540
541 if sys.platform == "win32":
541 if sys.platform == "win32":
542 from IPython.core.error import TryNext
542 from IPython.core.error import TryNext
543 from IPython.lib.clipboard import (
543 from IPython.lib.clipboard import (
544 ClipboardEmpty,
544 ClipboardEmpty,
545 tkinter_clipboard_get,
545 tkinter_clipboard_get,
546 win32_clipboard_get,
546 win32_clipboard_get,
547 )
547 )
548
548
549 @undoc
549 @undoc
550 def win_paste(event):
550 def win_paste(event):
551 try:
551 try:
552 text = win32_clipboard_get()
552 text = win32_clipboard_get()
553 except TryNext:
553 except TryNext:
554 try:
554 try:
555 text = tkinter_clipboard_get()
555 text = tkinter_clipboard_get()
556 except (TryNext, ClipboardEmpty):
556 except (TryNext, ClipboardEmpty):
557 return
557 return
558 except ClipboardEmpty:
558 except ClipboardEmpty:
559 return
559 return
560 event.current_buffer.insert_text(text.replace("\t", " " * 4))
560 event.current_buffer.insert_text(text.replace("\t", " " * 4))
561
561
562 else:
562 else:
563
563
564 @undoc
564 @undoc
565 def win_paste(event):
565 def win_paste(event):
566 """Stub used on other platforms"""
566 """Stub used on other platforms"""
567 pass
567 pass
568
568
569
569
570 KEY_BINDINGS = [
570 KEY_BINDINGS = [
571 Binding(
571 Binding(
572 handle_return_or_newline_or_execute,
572 handle_return_or_newline_or_execute,
573 ["enter"],
573 ["enter"],
574 "default_buffer_focused & ~has_selection & insert_mode",
574 "default_buffer_focused & ~has_selection & insert_mode",
575 ),
575 ),
576 Binding(
576 Binding(
577 reformat_and_execute,
577 reformat_and_execute,
578 ["escape", "enter"],
578 ["escape", "enter"],
579 "default_buffer_focused & ~has_selection & insert_mode & ebivim",
579 "default_buffer_focused & ~has_selection & insert_mode & ebivim",
580 ),
580 ),
581 Binding(quit, ["c-\\"]),
581 Binding(quit, ["c-\\"]),
582 Binding(
582 Binding(
583 previous_history_or_previous_completion,
583 previous_history_or_previous_completion,
584 ["c-p"],
584 ["c-p"],
585 "vi_insert_mode & default_buffer_focused",
585 "vi_insert_mode & default_buffer_focused",
586 ),
586 ),
587 Binding(
587 Binding(
588 next_history_or_next_completion,
588 next_history_or_next_completion,
589 ["c-n"],
589 ["c-n"],
590 "vi_insert_mode & default_buffer_focused",
590 "vi_insert_mode & default_buffer_focused",
591 ),
591 ),
592 Binding(dismiss_completion, ["c-g"], "default_buffer_focused & has_completions"),
592 Binding(dismiss_completion, ["c-g"], "default_buffer_focused & has_completions"),
593 Binding(reset_buffer, ["c-c"], "default_buffer_focused"),
593 Binding(reset_buffer, ["c-c"], "default_buffer_focused"),
594 Binding(reset_search_buffer, ["c-c"], "search_buffer_focused"),
594 Binding(reset_search_buffer, ["c-c"], "search_buffer_focused"),
595 Binding(suspend_to_bg, ["c-z"], "supports_suspend"),
595 Binding(suspend_to_bg, ["c-z"], "supports_suspend"),
596 Binding(
596 Binding(
597 indent_buffer,
597 indent_buffer,
598 ["tab"], # Ctrl+I == Tab
598 ["tab"], # Ctrl+I == Tab
599 "default_buffer_focused"
599 "default_buffer_focused"
600 " & ~has_selection"
600 " & ~has_selection"
601 " & insert_mode"
601 " & insert_mode"
602 " & cursor_in_leading_ws",
602 " & cursor_in_leading_ws",
603 ),
603 ),
604 Binding(newline_autoindent, ["c-o"], "default_buffer_focused & emacs_insert_mode"),
604 Binding(newline_autoindent, ["c-o"], "default_buffer_focused & emacs_insert_mode"),
605 Binding(open_input_in_editor, ["f2"], "default_buffer_focused"),
605 Binding(open_input_in_editor, ["f2"], "default_buffer_focused"),
606 *AUTO_MATCH_BINDINGS,
606 *AUTO_MATCH_BINDINGS,
607 *AUTO_SUGGEST_BINDINGS,
607 *AUTO_SUGGEST_BINDINGS,
608 Binding(
608 Binding(
609 display_completions_like_readline,
609 display_completions_like_readline,
610 ["c-i"],
610 ["c-i"],
611 "readline_like_completions"
611 "readline_like_completions"
612 " & default_buffer_focused"
612 " & default_buffer_focused"
613 " & ~has_selection"
613 " & ~has_selection"
614 " & insert_mode"
614 " & insert_mode"
615 " & ~cursor_in_leading_ws",
615 " & ~cursor_in_leading_ws",
616 ),
616 ),
617 Binding(win_paste, ["c-v"], "default_buffer_focused & ~vi_mode & is_windows_os"),
617 Binding(win_paste, ["c-v"], "default_buffer_focused & ~vi_mode & is_windows_os"),
618 *SIMPLE_CONTROL_BINDINGS,
618 *SIMPLE_CONTROL_BINDINGS,
619 *ALT_AND_COMOBO_CONTROL_BINDINGS,
619 *ALT_AND_COMOBO_CONTROL_BINDINGS,
620 ]
620 ]
General Comments 0
You need to be logged in to leave comments. Login now