##// END OF EJS Templates
Pull shortcut definitions out to a separate module
Thomas Kluyver -
Show More
@@ -0,0 +1,165 b''
1 import signal
2 import sys
3
4 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER
5 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
6 ViInsertMode, EmacsInsertMode, HasCompletions)
7 from prompt_toolkit.filters.cli import ViMode
8 from prompt_toolkit.keys import Keys
9 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
10
11 @Condition
12 def cursor_in_leading_ws(cli):
13 before = cli.application.buffer.document.current_line_before_cursor
14 return (not before) or before.isspace()
15
16 def register_ipython_shortcuts(registry, shell):
17 """Set up the prompt_toolkit keyboard shortcuts for IPython"""
18 insert_mode = ViInsertMode() | EmacsInsertMode()
19
20 # Ctrl+J == Enter, seemingly
21 registry.add_binding(Keys.ControlJ,
22 filter=(HasFocus(DEFAULT_BUFFER)
23 & ~HasSelection()
24 & insert_mode
25 ))(newline_or_execute_outer(shell))
26
27 registry.add_binding(Keys.ControlP,
28 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
29 ))(previous_history_or_previous_completion)
30
31 registry.add_binding(Keys.ControlN,
32 filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)
33 ))(next_history_or_next_completion)
34
35 registry.add_binding(Keys.ControlG,
36 filter=(HasFocus(DEFAULT_BUFFER) & HasCompletions()
37 ))(dismiss_completion)
38
39 registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER)
40 )(reset_buffer)
41
42 registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER)
43 )(reset_search_buffer)
44
45 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
46 registry.add_binding(Keys.ControlZ, filter=supports_suspend
47 )(suspend_to_bg)
48
49 # Ctrl+I == Tab
50 registry.add_binding(Keys.ControlI,
51 filter=(HasFocus(DEFAULT_BUFFER)
52 & ~HasSelection()
53 & insert_mode
54 & cursor_in_leading_ws
55 ))(indent_buffer)
56
57 if shell.display_completions == 'readlinelike':
58 registry.add_binding(Keys.ControlI,
59 filter=(HasFocus(DEFAULT_BUFFER)
60 & ~HasSelection()
61 & insert_mode
62 & ~cursor_in_leading_ws
63 ))(display_completions_like_readline)
64
65 if sys.platform == 'win32':
66 registry.add_binding(Keys.ControlV,
67 filter=(
68 HasFocus(
69 DEFAULT_BUFFER) & ~ViMode()
70 ))(win_paste)
71
72
73 def newline_or_execute_outer(shell):
74 def newline_or_execute(event):
75 """When the user presses return, insert a newline or execute the code."""
76 b = event.current_buffer
77 d = b.document
78
79 if b.complete_state:
80 cc = b.complete_state.current_completion
81 if cc:
82 b.apply_completion(cc)
83 else:
84 b.cancel_completion()
85 return
86
87 if not (d.on_last_line or d.cursor_position_row >= d.line_count
88 - d.empty_line_count_at_the_end()):
89 b.newline()
90 return
91
92 status, indent = shell.input_splitter.check_complete(d.text + '\n')
93
94 if (status != 'incomplete') and b.accept_action.is_returnable:
95 b.accept_action.validate_and_handle(event.cli, b)
96 else:
97 b.insert_text('\n' + (' ' * (indent or 0)))
98 return newline_or_execute
99
100
101 def previous_history_or_previous_completion(event):
102 """
103 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
104
105 If completer is open this still select previous completion.
106 """
107 event.current_buffer.auto_up()
108
109
110 def next_history_or_next_completion(event):
111 """
112 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
113
114 If completer is open this still select next completion.
115 """
116 event.current_buffer.auto_down()
117
118
119 def dismiss_completion(event):
120 b = event.current_buffer
121 if b.complete_state:
122 b.cancel_completion()
123
124
125 def reset_buffer(event):
126 b = event.current_buffer
127 if b.complete_state:
128 b.cancel_completion()
129 else:
130 b.reset()
131
132
133 def reset_search_buffer(event):
134 if event.current_buffer.document.text:
135 event.current_buffer.reset()
136 else:
137 event.cli.push_focus(DEFAULT_BUFFER)
138
139 def suspend_to_bg(event):
140 event.cli.suspend_to_background()
141
142 def indent_buffer(event):
143 event.current_buffer.insert_text(' ' * 4)
144
145
146
147
148 if sys.platform == 'win32':
149 from IPython.core.error import TryNext
150 from IPython.lib.clipboard import (ClipboardEmpty,
151 win32_clipboard_get,
152 tkinter_clipboard_get)
153
154
155 def win_paste(event):
156 try:
157 text = win32_clipboard_get()
158 except TryNext:
159 try:
160 text = tkinter_clipboard_get()
161 except (TryNext, ClipboardEmpty):
162 return
163 except ClipboardEmpty:
164 return
165 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
@@ -1,579 +1,455 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import signal
7 from warnings import warn
6 from warnings import warn
8
7
9 from IPython.core.error import TryNext
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
9 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
10 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
11 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
12 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum
15
13
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
14 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, HasSelection, Condition,
15 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
18 ViInsertMode, EmacsInsertMode, IsDone, HasCompletions)
19 from prompt_toolkit.filters.cli import ViMode
20 from prompt_toolkit.history import InMemoryHistory
16 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
17 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
22 from prompt_toolkit.interface import CommandLineInterface
18 from prompt_toolkit.interface import CommandLineInterface
23 from prompt_toolkit.key_binding.manager import KeyBindingManager
19 from prompt_toolkit.key_binding.manager import KeyBindingManager
24 from prompt_toolkit.keys import Keys
25 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
20 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
26 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
21 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
27 from prompt_toolkit.key_binding.bindings.completion import display_completions_like_readline
28
22
29 from pygments.styles import get_style_by_name, get_all_styles
23 from pygments.styles import get_style_by_name, get_all_styles
30 from pygments.token import Token
24 from pygments.token import Token
31
25
32 from .debugger import TerminalPdb, Pdb
26 from .debugger import TerminalPdb, Pdb
33 from .magics import TerminalMagics
27 from .magics import TerminalMagics
34 from .pt_inputhooks import get_inputhook_func
28 from .pt_inputhooks import get_inputhook_func
35 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
29 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .ptutils import IPythonPTCompleter, IPythonPTLexer
30 from .ptutils import IPythonPTCompleter, IPythonPTLexer
31 from .shortcuts import register_ipython_shortcuts
37
32
38 DISPLAY_BANNER_DEPRECATED = object()
33 DISPLAY_BANNER_DEPRECATED = object()
39
34
40
35
41 from pygments.style import Style
36 from pygments.style import Style
42
37
43 class _NoStyle(Style): pass
38 class _NoStyle(Style): pass
44
39
45
40
46
41
47 _style_overrides_light_bg = {
42 _style_overrides_light_bg = {
48 Token.Prompt: '#0000ff',
43 Token.Prompt: '#0000ff',
49 Token.PromptNum: '#0000ee bold',
44 Token.PromptNum: '#0000ee bold',
50 Token.OutPrompt: '#cc0000',
45 Token.OutPrompt: '#cc0000',
51 Token.OutPromptNum: '#bb0000 bold',
46 Token.OutPromptNum: '#bb0000 bold',
52 }
47 }
53
48
54 _style_overrides_linux = {
49 _style_overrides_linux = {
55 Token.Prompt: '#00cc00',
50 Token.Prompt: '#00cc00',
56 Token.PromptNum: '#00bb00 bold',
51 Token.PromptNum: '#00bb00 bold',
57 Token.OutPrompt: '#cc0000',
52 Token.OutPrompt: '#cc0000',
58 Token.OutPromptNum: '#bb0000 bold',
53 Token.OutPromptNum: '#bb0000 bold',
59 }
54 }
60
55
61
56
62
57
63 def get_default_editor():
58 def get_default_editor():
64 try:
59 try:
65 ed = os.environ['EDITOR']
60 ed = os.environ['EDITOR']
66 if not PY3:
61 if not PY3:
67 ed = ed.decode()
62 ed = ed.decode()
68 return ed
63 return ed
69 except KeyError:
64 except KeyError:
70 pass
65 pass
71 except UnicodeError:
66 except UnicodeError:
72 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
73 "default editor.")
68 "default editor.")
74
69
75 if os.name == 'posix':
70 if os.name == 'posix':
76 return 'vi' # the only one guaranteed to be there!
71 return 'vi' # the only one guaranteed to be there!
77 else:
72 else:
78 return 'notepad' # same in Windows!
73 return 'notepad' # same in Windows!
79
74
80
75
81 if sys.stdin and sys.stdout and sys.stderr:
76 if sys.stdin and sys.stdout and sys.stderr:
82 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
77 _is_tty = (sys.stdin.isatty()) and (sys.stdout.isatty()) and (sys.stderr.isatty())
83 else:
78 else:
84 _is_tty = False
79 _is_tty = False
85
80
86
81
87 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
82 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
88
83
89 class TerminalInteractiveShell(InteractiveShell):
84 class TerminalInteractiveShell(InteractiveShell):
90 colors_force = True
85 colors_force = True
91
86
92 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
87 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
93 'to reserve for the completion menu'
88 'to reserve for the completion menu'
94 ).tag(config=True)
89 ).tag(config=True)
95
90
96 def _space_for_menu_changed(self, old, new):
91 def _space_for_menu_changed(self, old, new):
97 self._update_layout()
92 self._update_layout()
98
93
99 pt_cli = None
94 pt_cli = None
100 debugger_history = None
95 debugger_history = None
101 _pt_app = None
96 _pt_app = None
102
97
103 simple_prompt = Bool(_use_simple_prompt,
98 simple_prompt = Bool(_use_simple_prompt,
104 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
99 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
105
100
106 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
101 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
107 IPython own testing machinery, and emacs inferior-shell integration through elpy.
102 IPython own testing machinery, and emacs inferior-shell integration through elpy.
108
103
109 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
104 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
110 environment variable is set, or the current terminal is not a tty.
105 environment variable is set, or the current terminal is not a tty.
111
106
112 """
107 """
113 ).tag(config=True)
108 ).tag(config=True)
114
109
115 @property
110 @property
116 def debugger_cls(self):
111 def debugger_cls(self):
117 return Pdb if self.simple_prompt else TerminalPdb
112 return Pdb if self.simple_prompt else TerminalPdb
118
113
119 confirm_exit = Bool(True,
114 confirm_exit = Bool(True,
120 help="""
115 help="""
121 Set to confirm when you try to exit IPython with an EOF (Control-D
116 Set to confirm when you try to exit IPython with an EOF (Control-D
122 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
117 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
123 you can force a direct exit without any confirmation.""",
118 you can force a direct exit without any confirmation.""",
124 ).tag(config=True)
119 ).tag(config=True)
125
120
126 editing_mode = Unicode('emacs',
121 editing_mode = Unicode('emacs',
127 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
122 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
128 ).tag(config=True)
123 ).tag(config=True)
129
124
130 mouse_support = Bool(False,
125 mouse_support = Bool(False,
131 help="Enable mouse support in the prompt"
126 help="Enable mouse support in the prompt"
132 ).tag(config=True)
127 ).tag(config=True)
133
128
134 highlighting_style = Unicode('legacy',
129 highlighting_style = Unicode('legacy',
135 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
130 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
136 ).tag(config=True)
131 ).tag(config=True)
137
132
138
133
139 @observe('highlighting_style')
134 @observe('highlighting_style')
140 @observe('colors')
135 @observe('colors')
141 def _highlighting_style_changed(self, change):
136 def _highlighting_style_changed(self, change):
142 self.refresh_style()
137 self.refresh_style()
143
138
144 def refresh_style(self):
139 def refresh_style(self):
145 self._style = self._make_style_from_name(self.highlighting_style)
140 self._style = self._make_style_from_name(self.highlighting_style)
146
141
147
142
148 highlighting_style_overrides = Dict(
143 highlighting_style_overrides = Dict(
149 help="Override highlighting format for specific tokens"
144 help="Override highlighting format for specific tokens"
150 ).tag(config=True)
145 ).tag(config=True)
151
146
152 editor = Unicode(get_default_editor(),
147 editor = Unicode(get_default_editor(),
153 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
148 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
154 ).tag(config=True)
149 ).tag(config=True)
155
150
156 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
151 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
157
152
158 prompts = Instance(Prompts)
153 prompts = Instance(Prompts)
159
154
160 @default('prompts')
155 @default('prompts')
161 def _prompts_default(self):
156 def _prompts_default(self):
162 return self.prompts_class(self)
157 return self.prompts_class(self)
163
158
164 @observe('prompts')
159 @observe('prompts')
165 def _(self, change):
160 def _(self, change):
166 self._update_layout()
161 self._update_layout()
167
162
168 @default('displayhook_class')
163 @default('displayhook_class')
169 def _displayhook_class_default(self):
164 def _displayhook_class_default(self):
170 return RichPromptDisplayHook
165 return RichPromptDisplayHook
171
166
172 term_title = Bool(True,
167 term_title = Bool(True,
173 help="Automatically set the terminal title"
168 help="Automatically set the terminal title"
174 ).tag(config=True)
169 ).tag(config=True)
175
170
176 # Leaving that for beta/rc tester, shoudl remove for 5.0.0 final.
171 # Leaving that for beta/rc tester, shoudl remove for 5.0.0 final.
177 display_completions_in_columns = Bool(None,
172 display_completions_in_columns = Bool(None,
178 help="DEPRECATED", allow_none=True
173 help="DEPRECATED", allow_none=True
179 ).tag(config=True)
174 ).tag(config=True)
180
175
181 @observe('display_completions_in_columns')
176 @observe('display_completions_in_columns')
182 def _display_completions_in_columns_changed(self, new):
177 def _display_completions_in_columns_changed(self, new):
183 raise DeprecationWarning("The `display_completions_in_columns` Boolean has been replaced by the enum `display_completions`"
178 raise DeprecationWarning("The `display_completions_in_columns` Boolean has been replaced by the enum `display_completions`"
184 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
179 "with the following acceptable value: 'column', 'multicolumn','readlinelike'. ")
185
180
186 display_completions = Enum(('column', 'multicolumn','readlinelike'), default_value='multicolumn').tag(config=True)
181 display_completions = Enum(('column', 'multicolumn','readlinelike'), default_value='multicolumn').tag(config=True)
187
182
188 highlight_matching_brackets = Bool(True,
183 highlight_matching_brackets = Bool(True,
189 help="Highlight matching brackets .",
184 help="Highlight matching brackets .",
190 ).tag(config=True)
185 ).tag(config=True)
191
186
192 @observe('term_title')
187 @observe('term_title')
193 def init_term_title(self, change=None):
188 def init_term_title(self, change=None):
194 # Enable or disable the terminal title.
189 # Enable or disable the terminal title.
195 if self.term_title:
190 if self.term_title:
196 toggle_set_term_title(True)
191 toggle_set_term_title(True)
197 set_term_title('IPython: ' + abbrev_cwd())
192 set_term_title('IPython: ' + abbrev_cwd())
198 else:
193 else:
199 toggle_set_term_title(False)
194 toggle_set_term_title(False)
200
195
201 def init_display_formatter(self):
196 def init_display_formatter(self):
202 super(TerminalInteractiveShell, self).init_display_formatter()
197 super(TerminalInteractiveShell, self).init_display_formatter()
203 # terminal only supports plain text
198 # terminal only supports plain text
204 self.display_formatter.active_types = ['text/plain']
199 self.display_formatter.active_types = ['text/plain']
205
200
206 def init_prompt_toolkit_cli(self):
201 def init_prompt_toolkit_cli(self):
207 if self.simple_prompt:
202 if self.simple_prompt:
208 # Fall back to plain non-interactive output for tests.
203 # Fall back to plain non-interactive output for tests.
209 # This is very limited, and only accepts a single line.
204 # This is very limited, and only accepts a single line.
210 def prompt():
205 def prompt():
211 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
206 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
212 self.prompt_for_code = prompt
207 self.prompt_for_code = prompt
213 return
208 return
214
209
210 # Set up keyboard shortcuts
215 kbmanager = KeyBindingManager.for_prompt()
211 kbmanager = KeyBindingManager.for_prompt()
216 insert_mode = ViInsertMode() | EmacsInsertMode()
212 register_ipython_shortcuts(kbmanager.registry, self)
217 # Ctrl+J == Enter, seemingly
218 @kbmanager.registry.add_binding(Keys.ControlJ,
219 filter=(HasFocus(DEFAULT_BUFFER)
220 & ~HasSelection()
221 & insert_mode
222 ))
223 def _(event):
224 b = event.current_buffer
225 d = b.document
226
227 if b.complete_state:
228 cc = b.complete_state.current_completion
229 if cc:
230 b.apply_completion(cc)
231 else:
232 b.cancel_completion()
233 return
234
235 if not (d.on_last_line or d.cursor_position_row >= d.line_count
236 - d.empty_line_count_at_the_end()):
237 b.newline()
238 return
239
240 status, indent = self.input_splitter.check_complete(d.text + '\n')
241
242 if (status != 'incomplete') and b.accept_action.is_returnable:
243 b.accept_action.validate_and_handle(event.cli, b)
244 else:
245 b.insert_text('\n' + (' ' * (indent or 0)))
246
247 @kbmanager.registry.add_binding(Keys.ControlP, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
248 def _previous_history_or_previous_completion(event):
249 """
250 Control-P in vi edit mode on readline is history next, unlike default prompt toolkit.
251
252 If completer is open this still select previous completion.
253 """
254 event.current_buffer.auto_up()
255
256 @kbmanager.registry.add_binding(Keys.ControlN, filter=(ViInsertMode() & HasFocus(DEFAULT_BUFFER)))
257 def _next_history_or_next_completion(event):
258 """
259 Control-N in vi edit mode on readline is history previous, unlike default prompt toolkit.
260
261 If completer is open this still select next completion.
262 """
263 event.current_buffer.auto_down()
264
265 @kbmanager.registry.add_binding(Keys.ControlG, filter=(
266 HasFocus(DEFAULT_BUFFER) & HasCompletions()
267 ))
268 def _dismiss_completion(event):
269 b = event.current_buffer
270 if b.complete_state:
271 b.cancel_completion()
272
273 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
274 def _reset_buffer(event):
275 b = event.current_buffer
276 if b.complete_state:
277 b.cancel_completion()
278 else:
279 b.reset()
280
281 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
282 def _reset_search_buffer(event):
283 if event.current_buffer.document.text:
284 event.current_buffer.reset()
285 else:
286 event.cli.push_focus(DEFAULT_BUFFER)
287
288 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
289
290 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
291 def _suspend_to_bg(event):
292 event.cli.suspend_to_background()
293
294 @Condition
295 def cursor_in_leading_ws(cli):
296 before = cli.application.buffer.document.current_line_before_cursor
297 return (not before) or before.isspace()
298
299 # Ctrl+I == Tab
300 @kbmanager.registry.add_binding(Keys.ControlI,
301 filter=(HasFocus(DEFAULT_BUFFER)
302 & ~HasSelection()
303 & insert_mode
304 & cursor_in_leading_ws
305 ))
306 def _indent_buffer(event):
307 event.current_buffer.insert_text(' ' * 4)
308
309
310 if self.display_completions == 'readlinelike':
311 @kbmanager.registry.add_binding(Keys.ControlI,
312 filter=(HasFocus(DEFAULT_BUFFER)
313 & ~HasSelection()
314 & insert_mode
315 & ~cursor_in_leading_ws
316 ))
317 def _disaply_compl(ev):
318 display_completions_like_readline(ev)
319
320
321 if sys.platform == 'win32':
322 from IPython.lib.clipboard import (ClipboardEmpty,
323 win32_clipboard_get, tkinter_clipboard_get)
324 @kbmanager.registry.add_binding(Keys.ControlV,
325 filter=(HasFocus(DEFAULT_BUFFER) & ~ViMode()))
326 def _paste(event):
327 try:
328 text = win32_clipboard_get()
329 except TryNext:
330 try:
331 text = tkinter_clipboard_get()
332 except (TryNext, ClipboardEmpty):
333 return
334 except ClipboardEmpty:
335 return
336 event.current_buffer.insert_text(text.replace('\t', ' ' * 4))
337
213
338 # Pre-populate history from IPython's history database
214 # Pre-populate history from IPython's history database
339 history = InMemoryHistory()
215 history = InMemoryHistory()
340 last_cell = u""
216 last_cell = u""
341 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
217 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
342 include_latest=True):
218 include_latest=True):
343 # Ignore blank lines and consecutive duplicates
219 # Ignore blank lines and consecutive duplicates
344 cell = cell.rstrip()
220 cell = cell.rstrip()
345 if cell and (cell != last_cell):
221 if cell and (cell != last_cell):
346 history.append(cell)
222 history.append(cell)
347
223
348 self._style = self._make_style_from_name(self.highlighting_style)
224 self._style = self._make_style_from_name(self.highlighting_style)
349 style = DynamicStyle(lambda: self._style)
225 style = DynamicStyle(lambda: self._style)
350
226
351 editing_mode = getattr(EditingMode, self.editing_mode.upper())
227 editing_mode = getattr(EditingMode, self.editing_mode.upper())
352
228
353 self._pt_app = create_prompt_application(
229 self._pt_app = create_prompt_application(
354 editing_mode=editing_mode,
230 editing_mode=editing_mode,
355 key_bindings_registry=kbmanager.registry,
231 key_bindings_registry=kbmanager.registry,
356 history=history,
232 history=history,
357 completer=IPythonPTCompleter(self.Completer),
233 completer=IPythonPTCompleter(self.Completer),
358 enable_history_search=True,
234 enable_history_search=True,
359 style=style,
235 style=style,
360 mouse_support=self.mouse_support,
236 mouse_support=self.mouse_support,
361 **self._layout_options()
237 **self._layout_options()
362 )
238 )
363 self._eventloop = create_eventloop(self.inputhook)
239 self._eventloop = create_eventloop(self.inputhook)
364 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self._eventloop)
240 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self._eventloop)
365
241
366 def _make_style_from_name(self, name):
242 def _make_style_from_name(self, name):
367 """
243 """
368 Small wrapper that make an IPython compatible style from a style name
244 Small wrapper that make an IPython compatible style from a style name
369
245
370 We need that to add style for prompt ... etc.
246 We need that to add style for prompt ... etc.
371 """
247 """
372 style_overrides = {}
248 style_overrides = {}
373 if name == 'legacy':
249 if name == 'legacy':
374 legacy = self.colors.lower()
250 legacy = self.colors.lower()
375 if legacy == 'linux':
251 if legacy == 'linux':
376 style_cls = get_style_by_name('monokai')
252 style_cls = get_style_by_name('monokai')
377 style_overrides = _style_overrides_linux
253 style_overrides = _style_overrides_linux
378 elif legacy == 'lightbg':
254 elif legacy == 'lightbg':
379 style_overrides = _style_overrides_light_bg
255 style_overrides = _style_overrides_light_bg
380 style_cls = get_style_by_name('pastie')
256 style_cls = get_style_by_name('pastie')
381 elif legacy == 'neutral':
257 elif legacy == 'neutral':
382 # The default theme needs to be visible on both a dark background
258 # The default theme needs to be visible on both a dark background
383 # and a light background, because we can't tell what the terminal
259 # and a light background, because we can't tell what the terminal
384 # looks like. These tweaks to the default theme help with that.
260 # looks like. These tweaks to the default theme help with that.
385 style_cls = get_style_by_name('default')
261 style_cls = get_style_by_name('default')
386 style_overrides.update({
262 style_overrides.update({
387 Token.Number: '#007700',
263 Token.Number: '#007700',
388 Token.Operator: 'noinherit',
264 Token.Operator: 'noinherit',
389 Token.String: '#BB6622',
265 Token.String: '#BB6622',
390 Token.Name.Function: '#2080D0',
266 Token.Name.Function: '#2080D0',
391 Token.Name.Class: 'bold #2080D0',
267 Token.Name.Class: 'bold #2080D0',
392 Token.Name.Namespace: 'bold #2080D0',
268 Token.Name.Namespace: 'bold #2080D0',
393 Token.Prompt: '#009900',
269 Token.Prompt: '#009900',
394 Token.PromptNum: '#00ff00 bold',
270 Token.PromptNum: '#00ff00 bold',
395 Token.OutPrompt: '#990000',
271 Token.OutPrompt: '#990000',
396 Token.OutPromptNum: '#ff0000 bold',
272 Token.OutPromptNum: '#ff0000 bold',
397 })
273 })
398 elif legacy =='nocolor':
274 elif legacy =='nocolor':
399 style_cls=_NoStyle
275 style_cls=_NoStyle
400 style_overrides = {}
276 style_overrides = {}
401 else :
277 else :
402 raise ValueError('Got unknown colors: ', legacy)
278 raise ValueError('Got unknown colors: ', legacy)
403 else :
279 else :
404 style_cls = get_style_by_name(name)
280 style_cls = get_style_by_name(name)
405 style_overrides = {
281 style_overrides = {
406 Token.Prompt: '#009900',
282 Token.Prompt: '#009900',
407 Token.PromptNum: '#00ff00 bold',
283 Token.PromptNum: '#00ff00 bold',
408 Token.OutPrompt: '#990000',
284 Token.OutPrompt: '#990000',
409 Token.OutPromptNum: '#ff0000 bold',
285 Token.OutPromptNum: '#ff0000 bold',
410 }
286 }
411 style_overrides.update(self.highlighting_style_overrides)
287 style_overrides.update(self.highlighting_style_overrides)
412 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
288 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
413 style_dict=style_overrides)
289 style_dict=style_overrides)
414
290
415 return style
291 return style
416
292
417 def _layout_options(self):
293 def _layout_options(self):
418 """
294 """
419 Return the current layout option for the current Terminal InteractiveShell
295 Return the current layout option for the current Terminal InteractiveShell
420 """
296 """
421 return {
297 return {
422 'lexer':IPythonPTLexer(),
298 'lexer':IPythonPTLexer(),
423 'reserve_space_for_menu':self.space_for_menu,
299 'reserve_space_for_menu':self.space_for_menu,
424 'get_prompt_tokens':self.prompts.in_prompt_tokens,
300 'get_prompt_tokens':self.prompts.in_prompt_tokens,
425 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
301 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
426 'multiline':True,
302 'multiline':True,
427 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
303 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
428
304
429 # Highlight matching brackets, but only when this setting is
305 # Highlight matching brackets, but only when this setting is
430 # enabled, and only when the DEFAULT_BUFFER has the focus.
306 # enabled, and only when the DEFAULT_BUFFER has the focus.
431 'extra_input_processors': [ConditionalProcessor(
307 'extra_input_processors': [ConditionalProcessor(
432 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
308 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
433 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
309 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
434 Condition(lambda cli: self.highlight_matching_brackets))],
310 Condition(lambda cli: self.highlight_matching_brackets))],
435 }
311 }
436
312
437 def _update_layout(self):
313 def _update_layout(self):
438 """
314 """
439 Ask for a re computation of the application layout, if for example ,
315 Ask for a re computation of the application layout, if for example ,
440 some configuration options have changed.
316 some configuration options have changed.
441 """
317 """
442 if self._pt_app:
318 if self._pt_app:
443 self._pt_app.layout = create_prompt_layout(**self._layout_options())
319 self._pt_app.layout = create_prompt_layout(**self._layout_options())
444
320
445 def prompt_for_code(self):
321 def prompt_for_code(self):
446 document = self.pt_cli.run(
322 document = self.pt_cli.run(
447 pre_run=self.pre_prompt, reset_current_buffer=True)
323 pre_run=self.pre_prompt, reset_current_buffer=True)
448 return document.text
324 return document.text
449
325
450 def init_io(self):
326 def init_io(self):
451 if sys.platform not in {'win32', 'cli'}:
327 if sys.platform not in {'win32', 'cli'}:
452 return
328 return
453
329
454 import win_unicode_console
330 import win_unicode_console
455 import colorama
331 import colorama
456
332
457 win_unicode_console.enable()
333 win_unicode_console.enable()
458 colorama.init()
334 colorama.init()
459
335
460 # For some reason we make these wrappers around stdout/stderr.
336 # For some reason we make these wrappers around stdout/stderr.
461 # For now, we need to reset them so all output gets coloured.
337 # For now, we need to reset them so all output gets coloured.
462 # https://github.com/ipython/ipython/issues/8669
338 # https://github.com/ipython/ipython/issues/8669
463 from IPython.utils import io
339 from IPython.utils import io
464 io.stdout = io.IOStream(sys.stdout)
340 io.stdout = io.IOStream(sys.stdout)
465 io.stderr = io.IOStream(sys.stderr)
341 io.stderr = io.IOStream(sys.stderr)
466
342
467 def init_magics(self):
343 def init_magics(self):
468 super(TerminalInteractiveShell, self).init_magics()
344 super(TerminalInteractiveShell, self).init_magics()
469 self.register_magics(TerminalMagics)
345 self.register_magics(TerminalMagics)
470
346
471 def init_alias(self):
347 def init_alias(self):
472 # The parent class defines aliases that can be safely used with any
348 # The parent class defines aliases that can be safely used with any
473 # frontend.
349 # frontend.
474 super(TerminalInteractiveShell, self).init_alias()
350 super(TerminalInteractiveShell, self).init_alias()
475
351
476 # Now define aliases that only make sense on the terminal, because they
352 # Now define aliases that only make sense on the terminal, because they
477 # need direct access to the console in a way that we can't emulate in
353 # need direct access to the console in a way that we can't emulate in
478 # GUI or web frontend
354 # GUI or web frontend
479 if os.name == 'posix':
355 if os.name == 'posix':
480 for cmd in ['clear', 'more', 'less', 'man']:
356 for cmd in ['clear', 'more', 'less', 'man']:
481 self.alias_manager.soft_define_alias(cmd, cmd)
357 self.alias_manager.soft_define_alias(cmd, cmd)
482
358
483
359
484 def __init__(self, *args, **kwargs):
360 def __init__(self, *args, **kwargs):
485 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
361 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
486 self.init_prompt_toolkit_cli()
362 self.init_prompt_toolkit_cli()
487 self.init_term_title()
363 self.init_term_title()
488 self.keep_running = True
364 self.keep_running = True
489
365
490 self.debugger_history = InMemoryHistory()
366 self.debugger_history = InMemoryHistory()
491
367
492 def ask_exit(self):
368 def ask_exit(self):
493 self.keep_running = False
369 self.keep_running = False
494
370
495 rl_next_input = None
371 rl_next_input = None
496
372
497 def pre_prompt(self):
373 def pre_prompt(self):
498 if self.rl_next_input:
374 if self.rl_next_input:
499 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
375 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
500 self.rl_next_input = None
376 self.rl_next_input = None
501
377
502 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
378 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
503
379
504 if display_banner is not DISPLAY_BANNER_DEPRECATED:
380 if display_banner is not DISPLAY_BANNER_DEPRECATED:
505 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
381 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
506
382
507 while self.keep_running:
383 while self.keep_running:
508 print(self.separate_in, end='')
384 print(self.separate_in, end='')
509
385
510 try:
386 try:
511 code = self.prompt_for_code()
387 code = self.prompt_for_code()
512 except EOFError:
388 except EOFError:
513 if (not self.confirm_exit) \
389 if (not self.confirm_exit) \
514 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
390 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
515 self.ask_exit()
391 self.ask_exit()
516
392
517 else:
393 else:
518 if code:
394 if code:
519 self.run_cell(code, store_history=True)
395 self.run_cell(code, store_history=True)
520
396
521 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
397 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
522 # An extra layer of protection in case someone mashing Ctrl-C breaks
398 # An extra layer of protection in case someone mashing Ctrl-C breaks
523 # out of our internal code.
399 # out of our internal code.
524 if display_banner is not DISPLAY_BANNER_DEPRECATED:
400 if display_banner is not DISPLAY_BANNER_DEPRECATED:
525 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
401 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
526 while True:
402 while True:
527 try:
403 try:
528 self.interact()
404 self.interact()
529 break
405 break
530 except KeyboardInterrupt:
406 except KeyboardInterrupt:
531 print("\nKeyboardInterrupt escaped interact()\n")
407 print("\nKeyboardInterrupt escaped interact()\n")
532
408
533 if hasattr(self, '_eventloop'):
409 if hasattr(self, '_eventloop'):
534 self._eventloop.close()
410 self._eventloop.close()
535
411
536 _inputhook = None
412 _inputhook = None
537 def inputhook(self, context):
413 def inputhook(self, context):
538 if self._inputhook is not None:
414 if self._inputhook is not None:
539 self._inputhook(context)
415 self._inputhook(context)
540
416
541 def enable_gui(self, gui=None):
417 def enable_gui(self, gui=None):
542 if gui:
418 if gui:
543 self._inputhook = get_inputhook_func(gui)
419 self._inputhook = get_inputhook_func(gui)
544 else:
420 else:
545 self._inputhook = None
421 self._inputhook = None
546
422
547 # Run !system commands directly, not through pipes, so terminal programs
423 # Run !system commands directly, not through pipes, so terminal programs
548 # work correctly.
424 # work correctly.
549 system = InteractiveShell.system_raw
425 system = InteractiveShell.system_raw
550
426
551 def auto_rewrite_input(self, cmd):
427 def auto_rewrite_input(self, cmd):
552 """Overridden from the parent class to use fancy rewriting prompt"""
428 """Overridden from the parent class to use fancy rewriting prompt"""
553 if not self.show_rewritten_input:
429 if not self.show_rewritten_input:
554 return
430 return
555
431
556 tokens = self.prompts.rewrite_prompt_tokens()
432 tokens = self.prompts.rewrite_prompt_tokens()
557 if self.pt_cli:
433 if self.pt_cli:
558 self.pt_cli.print_tokens(tokens)
434 self.pt_cli.print_tokens(tokens)
559 print(cmd)
435 print(cmd)
560 else:
436 else:
561 prompt = ''.join(s for t, s in tokens)
437 prompt = ''.join(s for t, s in tokens)
562 print(prompt, cmd, sep='')
438 print(prompt, cmd, sep='')
563
439
564 _prompts_before = None
440 _prompts_before = None
565 def switch_doctest_mode(self, mode):
441 def switch_doctest_mode(self, mode):
566 """Switch prompts to classic for %doctest_mode"""
442 """Switch prompts to classic for %doctest_mode"""
567 if mode:
443 if mode:
568 self._prompts_before = self.prompts
444 self._prompts_before = self.prompts
569 self.prompts = ClassicPrompts(self)
445 self.prompts = ClassicPrompts(self)
570 elif self._prompts_before:
446 elif self._prompts_before:
571 self.prompts = self._prompts_before
447 self.prompts = self._prompts_before
572 self._prompts_before = None
448 self._prompts_before = None
573 self._update_layout()
449 self._update_layout()
574
450
575
451
576 InteractiveShellABC.register(TerminalInteractiveShell)
452 InteractiveShellABC.register(TerminalInteractiveShell)
577
453
578 if __name__ == '__main__':
454 if __name__ == '__main__':
579 TerminalInteractiveShell.instance().interact()
455 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now