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