##// END OF EJS Templates
please formatter
Matthias Bussonnier -
Show More
@@ -1,608 +1,608 b''
1 """
1 """
2 Module to define and register Terminal IPython shortcuts with
2 Module to define and register Terminal IPython shortcuts with
3 :mod:`prompt_toolkit`
3 :mod:`prompt_toolkit`
4 """
4 """
5
5
6 # Copyright (c) IPython Development Team.
6 # Copyright (c) IPython Development Team.
7 # Distributed under the terms of the Modified BSD License.
7 # Distributed under the terms of the Modified BSD License.
8
8
9 import warnings
9 import warnings
10 import signal
10 import signal
11 import sys
11 import sys
12 import re
12 import re
13 import os
13 import os
14 from typing import Callable
14 from typing import Callable
15
15
16
16
17 from prompt_toolkit.application.current import get_app
17 from prompt_toolkit.application.current import get_app
18 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
18 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
19 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
19 from prompt_toolkit.filters import (has_focus, has_selection, Condition,
20 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
20 vi_insert_mode, emacs_insert_mode, has_completions, vi_mode)
21 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
21 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
22 from prompt_toolkit.key_binding import KeyBindings
22 from prompt_toolkit.key_binding import KeyBindings
23 from prompt_toolkit.key_binding.bindings import named_commands as nc
23 from prompt_toolkit.key_binding.bindings import named_commands as nc
24 from prompt_toolkit.key_binding.vi_state import InputMode, ViState
24 from prompt_toolkit.key_binding.vi_state import InputMode, ViState
25
25
26 from IPython.utils.decorators import undoc
26 from IPython.utils.decorators import undoc
27
27
28 @undoc
28 @undoc
29 @Condition
29 @Condition
30 def cursor_in_leading_ws():
30 def cursor_in_leading_ws():
31 before = get_app().current_buffer.document.current_line_before_cursor
31 before = get_app().current_buffer.document.current_line_before_cursor
32 return (not before) or before.isspace()
32 return (not before) or before.isspace()
33
33
34
34
35 # Needed for to accept autosuggestions in vi insert mode
35 # Needed for to accept autosuggestions in vi insert mode
36 def _apply_autosuggest(event):
36 def _apply_autosuggest(event):
37 """
37 """
38 Apply autosuggestion if at end of line.
38 Apply autosuggestion if at end of line.
39 """
39 """
40 b = event.current_buffer
40 b = event.current_buffer
41 d = b.document
41 d = b.document
42 after_cursor = d.text[d.cursor_position :]
42 after_cursor = d.text[d.cursor_position :]
43 lines = after_cursor.split("\n")
43 lines = after_cursor.split("\n")
44 end_of_current_line = lines[0].strip()
44 end_of_current_line = lines[0].strip()
45 suggestion = b.suggestion
45 suggestion = b.suggestion
46 if (suggestion is not None) and (suggestion.text) and (end_of_current_line == ""):
46 if (suggestion is not None) and (suggestion.text) and (end_of_current_line == ""):
47 b.insert_text(suggestion.text)
47 b.insert_text(suggestion.text)
48 else:
48 else:
49 nc.end_of_line(event)
49 nc.end_of_line(event)
50
50
51 def create_ipython_shortcuts(shell):
51 def create_ipython_shortcuts(shell):
52 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
52 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
53
53
54 kb = KeyBindings()
54 kb = KeyBindings()
55 insert_mode = vi_insert_mode | emacs_insert_mode
55 insert_mode = vi_insert_mode | emacs_insert_mode
56
56
57 if getattr(shell, 'handle_return', None):
57 if getattr(shell, 'handle_return', None):
58 return_handler = shell.handle_return(shell)
58 return_handler = shell.handle_return(shell)
59 else:
59 else:
60 return_handler = newline_or_execute_outer(shell)
60 return_handler = newline_or_execute_outer(shell)
61
61
62 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
62 kb.add('enter', filter=(has_focus(DEFAULT_BUFFER)
63 & ~has_selection
63 & ~has_selection
64 & insert_mode
64 & insert_mode
65 ))(return_handler)
65 ))(return_handler)
66
66
67 def reformat_and_execute(event):
67 def reformat_and_execute(event):
68 reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
68 reformat_text_before_cursor(event.current_buffer, event.current_buffer.document, shell)
69 event.current_buffer.validate_and_handle()
69 event.current_buffer.validate_and_handle()
70
70
71 @Condition
71 @Condition
72 def ebivim():
72 def ebivim():
73 return shell.emacs_bindings_in_vi_insert_mode
73 return shell.emacs_bindings_in_vi_insert_mode
74
74
75 kb.add('escape', 'enter', filter=(has_focus(DEFAULT_BUFFER)
75 kb.add(
76 & ~has_selection
76 "escape",
77 & insert_mode
77 "enter",
78 & ebivim
78 filter=(has_focus(DEFAULT_BUFFER) & ~has_selection & insert_mode & ebivim),
79 ))(reformat_and_execute)
79 )(reformat_and_execute)
80
80
81 kb.add("c-\\")(quit)
81 kb.add("c-\\")(quit)
82
82
83 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
83 kb.add('c-p', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
84 )(previous_history_or_previous_completion)
84 )(previous_history_or_previous_completion)
85
85
86 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
86 kb.add('c-n', filter=(vi_insert_mode & has_focus(DEFAULT_BUFFER))
87 )(next_history_or_next_completion)
87 )(next_history_or_next_completion)
88
88
89 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
89 kb.add('c-g', filter=(has_focus(DEFAULT_BUFFER) & has_completions)
90 )(dismiss_completion)
90 )(dismiss_completion)
91
91
92 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
92 kb.add('c-c', filter=has_focus(DEFAULT_BUFFER))(reset_buffer)
93
93
94 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
94 kb.add('c-c', filter=has_focus(SEARCH_BUFFER))(reset_search_buffer)
95
95
96 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
96 supports_suspend = Condition(lambda: hasattr(signal, 'SIGTSTP'))
97 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
97 kb.add('c-z', filter=supports_suspend)(suspend_to_bg)
98
98
99 # Ctrl+I == Tab
99 # Ctrl+I == Tab
100 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
100 kb.add('tab', filter=(has_focus(DEFAULT_BUFFER)
101 & ~has_selection
101 & ~has_selection
102 & insert_mode
102 & insert_mode
103 & cursor_in_leading_ws
103 & cursor_in_leading_ws
104 ))(indent_buffer)
104 ))(indent_buffer)
105 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
105 kb.add('c-o', filter=(has_focus(DEFAULT_BUFFER) & emacs_insert_mode)
106 )(newline_autoindent_outer(shell.input_transformer_manager))
106 )(newline_autoindent_outer(shell.input_transformer_manager))
107
107
108 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
108 kb.add('f2', filter=has_focus(DEFAULT_BUFFER))(open_input_in_editor)
109
109
110 @Condition
110 @Condition
111 def auto_match():
111 def auto_match():
112 return shell.auto_match
112 return shell.auto_match
113
113
114 def all_quotes_paired(quote, buf):
114 def all_quotes_paired(quote, buf):
115 paired = True
115 paired = True
116 i = 0
116 i = 0
117 while i < len(buf):
117 while i < len(buf):
118 c = buf[i]
118 c = buf[i]
119 if c == quote:
119 if c == quote:
120 paired = not paired
120 paired = not paired
121 elif c == "\\":
121 elif c == "\\":
122 i += 1
122 i += 1
123 i += 1
123 i += 1
124 return paired
124 return paired
125
125
126 focused_insert = (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER)
126 focused_insert = (vi_insert_mode | emacs_insert_mode) & has_focus(DEFAULT_BUFFER)
127 _preceding_text_cache = {}
127 _preceding_text_cache = {}
128 _following_text_cache = {}
128 _following_text_cache = {}
129
129
130 def preceding_text(pattern):
130 def preceding_text(pattern):
131 if pattern in _preceding_text_cache:
131 if pattern in _preceding_text_cache:
132 return _preceding_text_cache[pattern]
132 return _preceding_text_cache[pattern]
133
133
134 if callable(pattern):
134 if callable(pattern):
135
135
136 def _preceding_text():
136 def _preceding_text():
137 app = get_app()
137 app = get_app()
138 before_cursor = app.current_buffer.document.current_line_before_cursor
138 before_cursor = app.current_buffer.document.current_line_before_cursor
139 return bool(pattern(before_cursor))
139 return bool(pattern(before_cursor))
140
140
141 else:
141 else:
142 m = re.compile(pattern)
142 m = re.compile(pattern)
143
143
144 def _preceding_text():
144 def _preceding_text():
145 app = get_app()
145 app = get_app()
146 before_cursor = app.current_buffer.document.current_line_before_cursor
146 before_cursor = app.current_buffer.document.current_line_before_cursor
147 return bool(m.match(before_cursor))
147 return bool(m.match(before_cursor))
148
148
149 condition = Condition(_preceding_text)
149 condition = Condition(_preceding_text)
150 _preceding_text_cache[pattern] = condition
150 _preceding_text_cache[pattern] = condition
151 return condition
151 return condition
152
152
153 def following_text(pattern):
153 def following_text(pattern):
154 try:
154 try:
155 return _following_text_cache[pattern]
155 return _following_text_cache[pattern]
156 except KeyError:
156 except KeyError:
157 pass
157 pass
158 m = re.compile(pattern)
158 m = re.compile(pattern)
159
159
160 def _following_text():
160 def _following_text():
161 app = get_app()
161 app = get_app()
162 return bool(m.match(app.current_buffer.document.current_line_after_cursor))
162 return bool(m.match(app.current_buffer.document.current_line_after_cursor))
163
163
164 condition = Condition(_following_text)
164 condition = Condition(_following_text)
165 _following_text_cache[pattern] = condition
165 _following_text_cache[pattern] = condition
166 return condition
166 return condition
167
167
168 @Condition
168 @Condition
169 def not_inside_unclosed_string():
169 def not_inside_unclosed_string():
170 app = get_app()
170 app = get_app()
171 s = app.current_buffer.document.text_before_cursor
171 s = app.current_buffer.document.text_before_cursor
172 # remove escaped quotes
172 # remove escaped quotes
173 s = s.replace('\\"', "").replace("\\'", "")
173 s = s.replace('\\"', "").replace("\\'", "")
174 # remove triple-quoted string literals
174 # remove triple-quoted string literals
175 s = re.sub(r"(?:\"\"\"[\s\S]*\"\"\"|'''[\s\S]*''')", "", s)
175 s = re.sub(r"(?:\"\"\"[\s\S]*\"\"\"|'''[\s\S]*''')", "", s)
176 # remove single-quoted string literals
176 # remove single-quoted string literals
177 s = re.sub(r"""(?:"[^"]*["\n]|'[^']*['\n])""", "", s)
177 s = re.sub(r"""(?:"[^"]*["\n]|'[^']*['\n])""", "", s)
178 return not ('"' in s or "'" in s)
178 return not ('"' in s or "'" in s)
179
179
180 # auto match
180 # auto match
181 @kb.add("(", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
181 @kb.add("(", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
182 def _(event):
182 def _(event):
183 event.current_buffer.insert_text("()")
183 event.current_buffer.insert_text("()")
184 event.current_buffer.cursor_left()
184 event.current_buffer.cursor_left()
185
185
186 @kb.add("[", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
186 @kb.add("[", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
187 def _(event):
187 def _(event):
188 event.current_buffer.insert_text("[]")
188 event.current_buffer.insert_text("[]")
189 event.current_buffer.cursor_left()
189 event.current_buffer.cursor_left()
190
190
191 @kb.add("{", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
191 @kb.add("{", filter=focused_insert & auto_match & following_text(r"[,)}\]]|$"))
192 def _(event):
192 def _(event):
193 event.current_buffer.insert_text("{}")
193 event.current_buffer.insert_text("{}")
194 event.current_buffer.cursor_left()
194 event.current_buffer.cursor_left()
195
195
196 @kb.add(
196 @kb.add(
197 '"',
197 '"',
198 filter=focused_insert
198 filter=focused_insert
199 & auto_match
199 & auto_match
200 & not_inside_unclosed_string
200 & not_inside_unclosed_string
201 & preceding_text(lambda line: all_quotes_paired('"', line))
201 & preceding_text(lambda line: all_quotes_paired('"', line))
202 & following_text(r"[,)}\]]|$"),
202 & following_text(r"[,)}\]]|$"),
203 )
203 )
204 def _(event):
204 def _(event):
205 event.current_buffer.insert_text('""')
205 event.current_buffer.insert_text('""')
206 event.current_buffer.cursor_left()
206 event.current_buffer.cursor_left()
207
207
208 @kb.add(
208 @kb.add(
209 "'",
209 "'",
210 filter=focused_insert
210 filter=focused_insert
211 & auto_match
211 & auto_match
212 & not_inside_unclosed_string
212 & not_inside_unclosed_string
213 & preceding_text(lambda line: all_quotes_paired("'", line))
213 & preceding_text(lambda line: all_quotes_paired("'", line))
214 & following_text(r"[,)}\]]|$"),
214 & following_text(r"[,)}\]]|$"),
215 )
215 )
216 def _(event):
216 def _(event):
217 event.current_buffer.insert_text("''")
217 event.current_buffer.insert_text("''")
218 event.current_buffer.cursor_left()
218 event.current_buffer.cursor_left()
219
219
220 @kb.add(
220 @kb.add(
221 '"',
221 '"',
222 filter=focused_insert
222 filter=focused_insert
223 & auto_match
223 & auto_match
224 & not_inside_unclosed_string
224 & not_inside_unclosed_string
225 & preceding_text(r'^.*""$'),
225 & preceding_text(r'^.*""$'),
226 )
226 )
227 def _(event):
227 def _(event):
228 event.current_buffer.insert_text('""""')
228 event.current_buffer.insert_text('""""')
229 event.current_buffer.cursor_left(3)
229 event.current_buffer.cursor_left(3)
230
230
231 @kb.add(
231 @kb.add(
232 "'",
232 "'",
233 filter=focused_insert
233 filter=focused_insert
234 & auto_match
234 & auto_match
235 & not_inside_unclosed_string
235 & not_inside_unclosed_string
236 & preceding_text(r"^.*''$"),
236 & preceding_text(r"^.*''$"),
237 )
237 )
238 def _(event):
238 def _(event):
239 event.current_buffer.insert_text("''''")
239 event.current_buffer.insert_text("''''")
240 event.current_buffer.cursor_left(3)
240 event.current_buffer.cursor_left(3)
241
241
242 # raw string
242 # raw string
243 @kb.add(
243 @kb.add(
244 "(", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
244 "(", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
245 )
245 )
246 def _(event):
246 def _(event):
247 matches = re.match(
247 matches = re.match(
248 r".*(r|R)[\"'](-*)",
248 r".*(r|R)[\"'](-*)",
249 event.current_buffer.document.current_line_before_cursor,
249 event.current_buffer.document.current_line_before_cursor,
250 )
250 )
251 dashes = matches.group(2) or ""
251 dashes = matches.group(2) or ""
252 event.current_buffer.insert_text("()" + dashes)
252 event.current_buffer.insert_text("()" + dashes)
253 event.current_buffer.cursor_left(len(dashes) + 1)
253 event.current_buffer.cursor_left(len(dashes) + 1)
254
254
255 @kb.add(
255 @kb.add(
256 "[", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
256 "[", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
257 )
257 )
258 def _(event):
258 def _(event):
259 matches = re.match(
259 matches = re.match(
260 r".*(r|R)[\"'](-*)",
260 r".*(r|R)[\"'](-*)",
261 event.current_buffer.document.current_line_before_cursor,
261 event.current_buffer.document.current_line_before_cursor,
262 )
262 )
263 dashes = matches.group(2) or ""
263 dashes = matches.group(2) or ""
264 event.current_buffer.insert_text("[]" + dashes)
264 event.current_buffer.insert_text("[]" + dashes)
265 event.current_buffer.cursor_left(len(dashes) + 1)
265 event.current_buffer.cursor_left(len(dashes) + 1)
266
266
267 @kb.add(
267 @kb.add(
268 "{", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
268 "{", filter=focused_insert & auto_match & preceding_text(r".*(r|R)[\"'](-*)$")
269 )
269 )
270 def _(event):
270 def _(event):
271 matches = re.match(
271 matches = re.match(
272 r".*(r|R)[\"'](-*)",
272 r".*(r|R)[\"'](-*)",
273 event.current_buffer.document.current_line_before_cursor,
273 event.current_buffer.document.current_line_before_cursor,
274 )
274 )
275 dashes = matches.group(2) or ""
275 dashes = matches.group(2) or ""
276 event.current_buffer.insert_text("{}" + dashes)
276 event.current_buffer.insert_text("{}" + dashes)
277 event.current_buffer.cursor_left(len(dashes) + 1)
277 event.current_buffer.cursor_left(len(dashes) + 1)
278
278
279 # just move cursor
279 # just move cursor
280 @kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)"))
280 @kb.add(")", filter=focused_insert & auto_match & following_text(r"^\)"))
281 @kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]"))
281 @kb.add("]", filter=focused_insert & auto_match & following_text(r"^\]"))
282 @kb.add("}", filter=focused_insert & auto_match & following_text(r"^\}"))
282 @kb.add("}", filter=focused_insert & auto_match & following_text(r"^\}"))
283 @kb.add('"', filter=focused_insert & auto_match & following_text('^"'))
283 @kb.add('"', filter=focused_insert & auto_match & following_text('^"'))
284 @kb.add("'", filter=focused_insert & auto_match & following_text("^'"))
284 @kb.add("'", filter=focused_insert & auto_match & following_text("^'"))
285 def _(event):
285 def _(event):
286 event.current_buffer.cursor_right()
286 event.current_buffer.cursor_right()
287
287
288 @kb.add(
288 @kb.add(
289 "backspace",
289 "backspace",
290 filter=focused_insert
290 filter=focused_insert
291 & preceding_text(r".*\($")
291 & preceding_text(r".*\($")
292 & auto_match
292 & auto_match
293 & following_text(r"^\)"),
293 & following_text(r"^\)"),
294 )
294 )
295 @kb.add(
295 @kb.add(
296 "backspace",
296 "backspace",
297 filter=focused_insert
297 filter=focused_insert
298 & preceding_text(r".*\[$")
298 & preceding_text(r".*\[$")
299 & auto_match
299 & auto_match
300 & following_text(r"^\]"),
300 & following_text(r"^\]"),
301 )
301 )
302 @kb.add(
302 @kb.add(
303 "backspace",
303 "backspace",
304 filter=focused_insert
304 filter=focused_insert
305 & preceding_text(r".*\{$")
305 & preceding_text(r".*\{$")
306 & auto_match
306 & auto_match
307 & following_text(r"^\}"),
307 & following_text(r"^\}"),
308 )
308 )
309 @kb.add(
309 @kb.add(
310 "backspace",
310 "backspace",
311 filter=focused_insert
311 filter=focused_insert
312 & preceding_text('.*"$')
312 & preceding_text('.*"$')
313 & auto_match
313 & auto_match
314 & following_text('^"'),
314 & following_text('^"'),
315 )
315 )
316 @kb.add(
316 @kb.add(
317 "backspace",
317 "backspace",
318 filter=focused_insert
318 filter=focused_insert
319 & preceding_text(r".*'$")
319 & preceding_text(r".*'$")
320 & auto_match
320 & auto_match
321 & following_text(r"^'"),
321 & following_text(r"^'"),
322 )
322 )
323 def _(event):
323 def _(event):
324 event.current_buffer.delete()
324 event.current_buffer.delete()
325 event.current_buffer.delete_before_cursor()
325 event.current_buffer.delete_before_cursor()
326
326
327 if shell.display_completions == "readlinelike":
327 if shell.display_completions == "readlinelike":
328 kb.add(
328 kb.add(
329 "c-i",
329 "c-i",
330 filter=(
330 filter=(
331 has_focus(DEFAULT_BUFFER)
331 has_focus(DEFAULT_BUFFER)
332 & ~has_selection
332 & ~has_selection
333 & insert_mode
333 & insert_mode
334 & ~cursor_in_leading_ws
334 & ~cursor_in_leading_ws
335 ),
335 ),
336 )(display_completions_like_readline)
336 )(display_completions_like_readline)
337
337
338 if sys.platform == "win32":
338 if sys.platform == "win32":
339 kb.add("c-v", filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
339 kb.add("c-v", filter=(has_focus(DEFAULT_BUFFER) & ~vi_mode))(win_paste)
340
340
341 focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode
341 focused_insert_vi = has_focus(DEFAULT_BUFFER) & vi_insert_mode
342
342
343 @kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode))
343 @kb.add("end", filter=has_focus(DEFAULT_BUFFER) & (ebivim | ~vi_insert_mode))
344 def _(event):
344 def _(event):
345 _apply_autosuggest(event)
345 _apply_autosuggest(event)
346
346
347 @kb.add("c-e", filter=focused_insert_vi & ebivim)
347 @kb.add("c-e", filter=focused_insert_vi & ebivim)
348 def _(event):
348 def _(event):
349 _apply_autosuggest(event)
349 _apply_autosuggest(event)
350
350
351 @kb.add("c-f", filter=focused_insert_vi)
351 @kb.add("c-f", filter=focused_insert_vi)
352 def _(event):
352 def _(event):
353 b = event.current_buffer
353 b = event.current_buffer
354 suggestion = b.suggestion
354 suggestion = b.suggestion
355 if suggestion:
355 if suggestion:
356 b.insert_text(suggestion.text)
356 b.insert_text(suggestion.text)
357 else:
357 else:
358 nc.forward_char(event)
358 nc.forward_char(event)
359
359
360 @kb.add("escape", "f", filter=focused_insert_vi & ebivim)
360 @kb.add("escape", "f", filter=focused_insert_vi & ebivim)
361 def _(event):
361 def _(event):
362 b = event.current_buffer
362 b = event.current_buffer
363 suggestion = b.suggestion
363 suggestion = b.suggestion
364 if suggestion:
364 if suggestion:
365 t = re.split(r"(\S+\s+)", suggestion.text)
365 t = re.split(r"(\S+\s+)", suggestion.text)
366 b.insert_text(next((x for x in t if x), ""))
366 b.insert_text(next((x for x in t if x), ""))
367 else:
367 else:
368 nc.forward_word(event)
368 nc.forward_word(event)
369
369
370 # Simple Control keybindings
370 # Simple Control keybindings
371 key_cmd_dict = {
371 key_cmd_dict = {
372 "c-a": nc.beginning_of_line,
372 "c-a": nc.beginning_of_line,
373 "c-b": nc.backward_char,
373 "c-b": nc.backward_char,
374 "c-k": nc.kill_line,
374 "c-k": nc.kill_line,
375 "c-w": nc.backward_kill_word,
375 "c-w": nc.backward_kill_word,
376 "c-y": nc.yank,
376 "c-y": nc.yank,
377 "c-_": nc.undo,
377 "c-_": nc.undo,
378 }
378 }
379
379
380 for key, cmd in key_cmd_dict.items():
380 for key, cmd in key_cmd_dict.items():
381 kb.add(key, filter=focused_insert_vi & ebivim)(cmd)
381 kb.add(key, filter=focused_insert_vi & ebivim)(cmd)
382
382
383 # Alt and Combo Control keybindings
383 # Alt and Combo Control keybindings
384 keys_cmd_dict = {
384 keys_cmd_dict = {
385 # Control Combos
385 # Control Combos
386 ("c-x", "c-e"): nc.edit_and_execute,
386 ("c-x", "c-e"): nc.edit_and_execute,
387 ("c-x", "e"): nc.edit_and_execute,
387 ("c-x", "e"): nc.edit_and_execute,
388 # Alt
388 # Alt
389 ("escape", "b"): nc.backward_word,
389 ("escape", "b"): nc.backward_word,
390 ("escape", "c"): nc.capitalize_word,
390 ("escape", "c"): nc.capitalize_word,
391 ("escape", "d"): nc.kill_word,
391 ("escape", "d"): nc.kill_word,
392 ("escape", "h"): nc.backward_kill_word,
392 ("escape", "h"): nc.backward_kill_word,
393 ("escape", "l"): nc.downcase_word,
393 ("escape", "l"): nc.downcase_word,
394 ("escape", "u"): nc.uppercase_word,
394 ("escape", "u"): nc.uppercase_word,
395 ("escape", "y"): nc.yank_pop,
395 ("escape", "y"): nc.yank_pop,
396 ("escape", "."): nc.yank_last_arg,
396 ("escape", "."): nc.yank_last_arg,
397 }
397 }
398
398
399 for keys, cmd in keys_cmd_dict.items():
399 for keys, cmd in keys_cmd_dict.items():
400 kb.add(*keys, filter=focused_insert_vi & ebivim)(cmd)
400 kb.add(*keys, filter=focused_insert_vi & ebivim)(cmd)
401
401
402 def get_input_mode(self):
402 def get_input_mode(self):
403 app = get_app()
403 app = get_app()
404 app.ttimeoutlen = shell.ttimeoutlen
404 app.ttimeoutlen = shell.ttimeoutlen
405 app.timeoutlen = shell.timeoutlen
405 app.timeoutlen = shell.timeoutlen
406
406
407 return self._input_mode
407 return self._input_mode
408
408
409 def set_input_mode(self, mode):
409 def set_input_mode(self, mode):
410 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
410 shape = {InputMode.NAVIGATION: 2, InputMode.REPLACE: 4}.get(mode, 6)
411 cursor = "\x1b[{} q".format(shape)
411 cursor = "\x1b[{} q".format(shape)
412
412
413 sys.stdout.write(cursor)
413 sys.stdout.write(cursor)
414 sys.stdout.flush()
414 sys.stdout.flush()
415
415
416 self._input_mode = mode
416 self._input_mode = mode
417
417
418 if shell.editing_mode == "vi" and shell.modal_cursor:
418 if shell.editing_mode == "vi" and shell.modal_cursor:
419 ViState._input_mode = InputMode.INSERT
419 ViState._input_mode = InputMode.INSERT
420 ViState.input_mode = property(get_input_mode, set_input_mode)
420 ViState.input_mode = property(get_input_mode, set_input_mode)
421
421
422 return kb
422 return kb
423
423
424
424
425 def reformat_text_before_cursor(buffer, document, shell):
425 def reformat_text_before_cursor(buffer, document, shell):
426 text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
426 text = buffer.delete_before_cursor(len(document.text[:document.cursor_position]))
427 try:
427 try:
428 formatted_text = shell.reformat_handler(text)
428 formatted_text = shell.reformat_handler(text)
429 buffer.insert_text(formatted_text)
429 buffer.insert_text(formatted_text)
430 except Exception as e:
430 except Exception as e:
431 buffer.insert_text(text)
431 buffer.insert_text(text)
432
432
433
433
434 def newline_or_execute_outer(shell):
434 def newline_or_execute_outer(shell):
435
435
436 def newline_or_execute(event):
436 def newline_or_execute(event):
437 """When the user presses return, insert a newline or execute the code."""
437 """When the user presses return, insert a newline or execute the code."""
438 b = event.current_buffer
438 b = event.current_buffer
439 d = b.document
439 d = b.document
440
440
441 if b.complete_state:
441 if b.complete_state:
442 cc = b.complete_state.current_completion
442 cc = b.complete_state.current_completion
443 if cc:
443 if cc:
444 b.apply_completion(cc)
444 b.apply_completion(cc)
445 else:
445 else:
446 b.cancel_completion()
446 b.cancel_completion()
447 return
447 return
448
448
449 # If there's only one line, treat it as if the cursor is at the end.
449 # If there's only one line, treat it as if the cursor is at the end.
450 # See https://github.com/ipython/ipython/issues/10425
450 # See https://github.com/ipython/ipython/issues/10425
451 if d.line_count == 1:
451 if d.line_count == 1:
452 check_text = d.text
452 check_text = d.text
453 else:
453 else:
454 check_text = d.text[:d.cursor_position]
454 check_text = d.text[:d.cursor_position]
455 status, indent = shell.check_complete(check_text)
455 status, indent = shell.check_complete(check_text)
456
456
457 # if all we have after the cursor is whitespace: reformat current text
457 # if all we have after the cursor is whitespace: reformat current text
458 # before cursor
458 # before cursor
459 after_cursor = d.text[d.cursor_position:]
459 after_cursor = d.text[d.cursor_position:]
460 reformatted = False
460 reformatted = False
461 if not after_cursor.strip():
461 if not after_cursor.strip():
462 reformat_text_before_cursor(b, d, shell)
462 reformat_text_before_cursor(b, d, shell)
463 reformatted = True
463 reformatted = True
464 if not (d.on_last_line or
464 if not (d.on_last_line or
465 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
465 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
466 ):
466 ):
467 if shell.autoindent:
467 if shell.autoindent:
468 b.insert_text('\n' + indent)
468 b.insert_text('\n' + indent)
469 else:
469 else:
470 b.insert_text('\n')
470 b.insert_text('\n')
471 return
471 return
472
472
473 if (status != 'incomplete') and b.accept_handler:
473 if (status != 'incomplete') and b.accept_handler:
474 if not reformatted:
474 if not reformatted:
475 reformat_text_before_cursor(b, d, shell)
475 reformat_text_before_cursor(b, d, shell)
476 b.validate_and_handle()
476 b.validate_and_handle()
477 else:
477 else:
478 if shell.autoindent:
478 if shell.autoindent:
479 b.insert_text('\n' + indent)
479 b.insert_text('\n' + indent)
480 else:
480 else:
481 b.insert_text('\n')
481 b.insert_text('\n')
482 return newline_or_execute
482 return newline_or_execute
483
483
484
484
485 def previous_history_or_previous_completion(event):
485 def previous_history_or_previous_completion(event):
486 """
486 """
487 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
487 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
488
488
489 If completer is open this still select previous completion.
489 If completer is open this still select previous completion.
490 """
490 """
491 event.current_buffer.auto_up()
491 event.current_buffer.auto_up()
492
492
493
493
494 def next_history_or_next_completion(event):
494 def next_history_or_next_completion(event):
495 """
495 """
496 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
496 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
497
497
498 If completer is open this still select next completion.
498 If completer is open this still select next completion.
499 """
499 """
500 event.current_buffer.auto_down()
500 event.current_buffer.auto_down()
501
501
502
502
503 def dismiss_completion(event):
503 def dismiss_completion(event):
504 b = event.current_buffer
504 b = event.current_buffer
505 if b.complete_state:
505 if b.complete_state:
506 b.cancel_completion()
506 b.cancel_completion()
507
507
508
508
509 def reset_buffer(event):
509 def reset_buffer(event):
510 b = event.current_buffer
510 b = event.current_buffer
511 if b.complete_state:
511 if b.complete_state:
512 b.cancel_completion()
512 b.cancel_completion()
513 else:
513 else:
514 b.reset()
514 b.reset()
515
515
516
516
517 def reset_search_buffer(event):
517 def reset_search_buffer(event):
518 if event.current_buffer.document.text:
518 if event.current_buffer.document.text:
519 event.current_buffer.reset()
519 event.current_buffer.reset()
520 else:
520 else:
521 event.app.layout.focus(DEFAULT_BUFFER)
521 event.app.layout.focus(DEFAULT_BUFFER)
522
522
523 def suspend_to_bg(event):
523 def suspend_to_bg(event):
524 event.app.suspend_to_background()
524 event.app.suspend_to_background()
525
525
526 def quit(event):
526 def quit(event):
527 """
527 """
528 On platforms that support SIGQUIT, send SIGQUIT to the current process.
528 On platforms that support SIGQUIT, send SIGQUIT to the current process.
529 On other platforms, just exit the process with a message.
529 On other platforms, just exit the process with a message.
530 """
530 """
531 sigquit = getattr(signal, "SIGQUIT", None)
531 sigquit = getattr(signal, "SIGQUIT", None)
532 if sigquit is not None:
532 if sigquit is not None:
533 os.kill(0, signal.SIGQUIT)
533 os.kill(0, signal.SIGQUIT)
534 else:
534 else:
535 sys.exit("Quit")
535 sys.exit("Quit")
536
536
537 def indent_buffer(event):
537 def indent_buffer(event):
538 event.current_buffer.insert_text(' ' * 4)
538 event.current_buffer.insert_text(' ' * 4)
539
539
540 @undoc
540 @undoc
541 def newline_with_copy_margin(event):
541 def newline_with_copy_margin(event):
542 """
542 """
543 DEPRECATED since IPython 6.0
543 DEPRECATED since IPython 6.0
544
544
545 See :any:`newline_autoindent_outer` for a replacement.
545 See :any:`newline_autoindent_outer` for a replacement.
546
546
547 Preserve margin and cursor position when using
547 Preserve margin and cursor position when using
548 Control-O to insert a newline in EMACS mode
548 Control-O to insert a newline in EMACS mode
549 """
549 """
550 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
550 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
551 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
551 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
552 DeprecationWarning, stacklevel=2)
552 DeprecationWarning, stacklevel=2)
553
553
554 b = event.current_buffer
554 b = event.current_buffer
555 cursor_start_pos = b.document.cursor_position_col
555 cursor_start_pos = b.document.cursor_position_col
556 b.newline(copy_margin=True)
556 b.newline(copy_margin=True)
557 b.cursor_up(count=1)
557 b.cursor_up(count=1)
558 cursor_end_pos = b.document.cursor_position_col
558 cursor_end_pos = b.document.cursor_position_col
559 if cursor_start_pos != cursor_end_pos:
559 if cursor_start_pos != cursor_end_pos:
560 pos_diff = cursor_start_pos - cursor_end_pos
560 pos_diff = cursor_start_pos - cursor_end_pos
561 b.cursor_right(count=pos_diff)
561 b.cursor_right(count=pos_diff)
562
562
563 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
563 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
564 """
564 """
565 Return a function suitable for inserting a indented newline after the cursor.
565 Return a function suitable for inserting a indented newline after the cursor.
566
566
567 Fancier version of deprecated ``newline_with_copy_margin`` which should
567 Fancier version of deprecated ``newline_with_copy_margin`` which should
568 compute the correct indentation of the inserted line. That is to say, indent
568 compute the correct indentation of the inserted line. That is to say, indent
569 by 4 extra space after a function definition, class definition, context
569 by 4 extra space after a function definition, class definition, context
570 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
570 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
571 """
571 """
572
572
573 def newline_autoindent(event):
573 def newline_autoindent(event):
574 """insert a newline after the cursor indented appropriately."""
574 """insert a newline after the cursor indented appropriately."""
575 b = event.current_buffer
575 b = event.current_buffer
576 d = b.document
576 d = b.document
577
577
578 if b.complete_state:
578 if b.complete_state:
579 b.cancel_completion()
579 b.cancel_completion()
580 text = d.text[:d.cursor_position] + '\n'
580 text = d.text[:d.cursor_position] + '\n'
581 _, indent = inputsplitter.check_complete(text)
581 _, indent = inputsplitter.check_complete(text)
582 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
582 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
583
583
584 return newline_autoindent
584 return newline_autoindent
585
585
586
586
587 def open_input_in_editor(event):
587 def open_input_in_editor(event):
588 event.app.current_buffer.open_in_editor()
588 event.app.current_buffer.open_in_editor()
589
589
590
590
591 if sys.platform == 'win32':
591 if sys.platform == 'win32':
592 from IPython.core.error import TryNext
592 from IPython.core.error import TryNext
593 from IPython.lib.clipboard import (ClipboardEmpty,
593 from IPython.lib.clipboard import (ClipboardEmpty,
594 win32_clipboard_get,
594 win32_clipboard_get,
595 tkinter_clipboard_get)
595 tkinter_clipboard_get)
596
596
597 @undoc
597 @undoc
598 def win_paste(event):
598 def win_paste(event):
599 try:
599 try:
600 text = win32_clipboard_get()
600 text = win32_clipboard_get()
601 except TryNext:
601 except TryNext:
602 try:
602 try:
603 text = tkinter_clipboard_get()
603 text = tkinter_clipboard_get()
604 except (TryNext, ClipboardEmpty):
604 except (TryNext, ClipboardEmpty):
605 return
605 return
606 except ClipboardEmpty:
606 except ClipboardEmpty:
607 return
607 return
608 event.current_buffer.insert_text(text.replace("\t", " " * 4))
608 event.current_buffer.insert_text(text.replace("\t", " " * 4))
General Comments 0
You need to be logged in to leave comments. Login now