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