##// END OF EJS Templates
Merge pull request #10489 from takluyver/i10425...
meeseeksdev[bot] -
r23582:bbb0bc29 merge
parent child Browse files
Show More
@@ -1,251 +1,256 b''
1 1 """
2 2 Module to define and register Terminal IPython shortcuts with
3 3 :mod:`prompt_toolkit`
4 4 """
5 5
6 6 # Copyright (c) IPython Development Team.
7 7 # Distributed under the terms of the Modified BSD License.
8 8
9 9 import warnings
10 10 import signal
11 11 import sys
12 12 from typing import Callable
13 13
14 14
15 15 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
16 16 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
17 17 ViInsertMode, EmacsInsertMode, HasCompletions)
18 18 from prompt_toolkit.filters.cli import ViMode, ViNavigationMode
19 19 from prompt_toolkit.keys import Keys
20 20 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
21 21
22 22 from IPython.utils.decorators import undoc
23 23
24 24 @undoc
25 25 @Condition
26 26 def cursor_in_leading_ws(cli):
27 27 before = cli.application.buffer.document.current_line_before_cursor
28 28 return (not before) or before.isspace()
29 29
30 30 def register_ipython_shortcuts(registry, shell):
31 31 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
32 32 insert_mode = ViInsertMode() | EmacsInsertMode()
33 33
34 34 if getattr(shell, 'handle_return', None):
35 35 return_handler = shell.handle_return(shell)
36 36 else:
37 37 return_handler = newline_or_execute_outer(shell)
38 38
39 39 # Ctrl+J == Enter, seemingly
40 40 registry.add_binding(Keys.ControlJ,
41 41 filter=(HasFocus(DEFAULT_BUFFER)
42 42 & ~HasSelection()
43 43 & insert_mode
44 44 ))(return_handler)
45 45
46 46 registry.add_binding(Keys.ControlBackslash)(force_exit)
47 47
48 48 registry.add_binding(Keys.ControlP,
49 49 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
50 50 ))(previous_history_or_previous_completion)
51 51
52 52 registry.add_binding(Keys.ControlN,
53 53 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
54 54 ))(next_history_or_next_completion)
55 55
56 56 registry.add_binding(Keys.ControlG,
57 57 filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions()
58 58 ))(dismiss_completion)
59 59
60 60 registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)
61 61 )(reset_buffer)
62 62
63 63 registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)
64 64 )(reset_search_buffer)
65 65
66 66 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
67 67 registry.add_binding(Keys.ControlZ, filter=supports_suspend
68 68 )(suspend_to_bg)
69 69
70 70 # Ctrl+I == Tab
71 71 registry.add_binding(Keys.ControlI,
72 72 filter=(HasFocus(DEFAULT_BUFFER)
73 73 & ~HasSelection()
74 74 & insert_mode
75 75 & cursor_in_leading_ws
76 76 ))(indent_buffer)
77 77
78 78 registry.add_binding(Keys.ControlO,
79 79 filter=(HasFocus(DEFAULT_BUFFER)
80 80 & EmacsInsertMode()))(newline_autoindent_outer(shell.input_splitter))
81 81
82 82 registry.add_binding(Keys.F2,
83 83 filter=HasFocus(DEFAULT_BUFFER)
84 84 )(open_input_in_editor)
85 85
86 86 if shell.display_completions == 'readlinelike':
87 87 registry.add_binding(Keys.ControlI,
88 88 filter=(HasFocus(DEFAULT_BUFFER)
89 89 & ~HasSelection()
90 90 & insert_mode
91 91 & ~cursor_in_leading_ws
92 92 ))(display_completions_like_readline)
93 93
94 94 if sys.platform == 'win32':
95 95 registry.add_binding(Keys.ControlV,
96 96 filter=(
97 97 HasFocus(
98 98 DEFAULT_BUFFER) & ~ViMode()
99 99 ))(win_paste)
100 100
101 101
102 102 def newline_or_execute_outer(shell):
103 103 def newline_or_execute(event):
104 104 """When the user presses return, insert a newline or execute the code."""
105 105 b = event.current_buffer
106 106 d = b.document
107 107
108 108 if b.complete_state:
109 109 cc = b.complete_state.current_completion
110 110 if cc:
111 111 b.apply_completion(cc)
112 112 else:
113 113 b.cancel_completion()
114 114 return
115 115
116 before_text = d.text[:d.cursor_position]
117 status, indent = shell.input_splitter.check_complete(before_text + '\n')
116 # If there's only one line, treat it as if the cursor is at the end.
117 # See https://github.com/ipython/ipython/issues/10425
118 if d.line_count == 1:
119 check_text = d.text
120 else:
121 check_text = d.text[:d.cursor_position]
122 status, indent = shell.input_splitter.check_complete(check_text + '\n')
118 123
119 124 if not (d.on_last_line or
120 125 d.cursor_position_row >= d.line_count - d.empty_line_count_at_the_end()
121 126 ):
122 127 b.insert_text('\n' + (' ' * (indent or 0)))
123 128 return
124 129
125 130 if (status != 'incomplete') and b.accept_action.is_returnable:
126 131 b.accept_action.validate_and_handle(event.cli, b)
127 132 else:
128 133 b.insert_text('\n' + (' ' * (indent or 0)))
129 134 return newline_or_execute
130 135
131 136
132 137 def previous_history_or_previous_completion(event):
133 138 """
134 139 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
135 140
136 141 If completer is open this still select previous completion.
137 142 """
138 143 event.current_buffer.auto_up()
139 144
140 145
141 146 def next_history_or_next_completion(event):
142 147 """
143 148 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
144 149
145 150 If completer is open this still select next completion.
146 151 """
147 152 event.current_buffer.auto_down()
148 153
149 154
150 155 def dismiss_completion(event):
151 156 b = event.current_buffer
152 157 if b.complete_state:
153 158 b.cancel_completion()
154 159
155 160
156 161 def reset_buffer(event):
157 162 b = event.current_buffer
158 163 if b.complete_state:
159 164 b.cancel_completion()
160 165 else:
161 166 b.reset()
162 167
163 168
164 169 def reset_search_buffer(event):
165 170 if event.current_buffer.document.text:
166 171 event.current_buffer.reset()
167 172 else:
168 173 event.cli.push_focus(DEFAULT_BUFFER)
169 174
170 175 def suspend_to_bg(event):
171 176 event.cli.suspend_to_background()
172 177
173 178 def force_exit(event):
174 179 """
175 180 Force exit (with a non-zero return value)
176 181 """
177 182 sys.exit("Quit")
178 183
179 184 def indent_buffer(event):
180 185 event.current_buffer.insert_text(' ' * 4)
181 186
182 187 @undoc
183 188 def newline_with_copy_margin(event):
184 189 """
185 190 DEPRECATED since IPython 6.0
186 191
187 192 See :any:`newline_autoindent_outer` for a replacement.
188 193
189 194 Preserve margin and cursor position when using
190 195 Control-O to insert a newline in EMACS mode
191 196 """
192 197 warnings.warn("`newline_with_copy_margin(event)` is deprecated since IPython 6.0. "
193 198 "see `newline_autoindent_outer(shell)(event)` for a replacement.",
194 199 DeprecationWarning, stacklevel=2)
195 200
196 201 b = event.current_buffer
197 202 cursor_start_pos = b.document.cursor_position_col
198 203 b.newline(copy_margin=True)
199 204 b.cursor_up(count=1)
200 205 cursor_end_pos = b.document.cursor_position_col
201 206 if cursor_start_pos != cursor_end_pos:
202 207 pos_diff = cursor_start_pos - cursor_end_pos
203 208 b.cursor_right(count=pos_diff)
204 209
205 210 def newline_autoindent_outer(inputsplitter) -> Callable[..., None]:
206 211 """
207 212 Return a function suitable for inserting a indented newline after the cursor.
208 213
209 214 Fancier version of deprecated ``newline_with_copy_margin`` which should
210 215 compute the correct indentation of the inserted line. That is to say, indent
211 216 by 4 extra space after a function definition, class definition, context
212 217 manager... And dedent by 4 space after ``pass``, ``return``, ``raise ...``.
213 218 """
214 219
215 220 def newline_autoindent(event):
216 221 """insert a newline after the cursor indented appropriately."""
217 222 b = event.current_buffer
218 223 d = b.document
219 224
220 225 if b.complete_state:
221 226 b.cancel_completion()
222 227 text = d.text[:d.cursor_position] + '\n'
223 228 _, indent = inputsplitter.check_complete(text)
224 229 b.insert_text('\n' + (' ' * (indent or 0)), move_cursor=False)
225 230
226 231 return newline_autoindent
227 232
228 233
229 234 def open_input_in_editor(event):
230 235 event.cli.current_buffer.tempfile_suffix = ".py"
231 236 event.cli.current_buffer.open_in_editor(event.cli)
232 237
233 238
234 239 if sys.platform == 'win32':
235 240 from IPython.core.error import TryNext
236 241 from IPython.lib.clipboard import (ClipboardEmpty,
237 242 win32_clipboard_get,
238 243 tkinter_clipboard_get)
239 244
240 245 @undoc
241 246 def win_paste(event):
242 247 try:
243 248 text = win32_clipboard_get()
244 249 except TryNext:
245 250 try:
246 251 text = tkinter_clipboard_get()
247 252 except (TryNext, ClipboardEmpty):
248 253 return
249 254 except ClipboardEmpty:
250 255 return
251 256 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
General Comments 0
You need to be logged in to leave comments. Login now