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