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