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