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