##// END OF EJS Templates
Merge pull request #9527 from Carreau/simple-prompt-update-layout...
Thomas Kluyver -
r22443:64806cca merge
parent child Browse files
Show More
@@ -1,490 +1,492 b''
1 """IPython terminal interface using prompt_toolkit in place of readline"""
1 """IPython terminal interface using prompt_toolkit in place of readline"""
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
6 import signal
7 from warnings import warn
7 from warnings import warn
8
8
9 from IPython.core.error import TryNext
9 from IPython.core.error import TryNext
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
10 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.terminal import toggle_set_term_title, set_term_title
13 from IPython.utils.process import abbrev_cwd
13 from IPython.utils.process import abbrev_cwd
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance
15
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
16 from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode
17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
17 from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone
18 from prompt_toolkit.history import InMemoryHistory
18 from prompt_toolkit.history import InMemoryHistory
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout
20 from prompt_toolkit.interface import CommandLineInterface
20 from prompt_toolkit.interface import CommandLineInterface
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
21 from prompt_toolkit.key_binding.manager import KeyBindingManager
22 from prompt_toolkit.keys import Keys
22 from prompt_toolkit.keys import Keys
23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
24 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
24 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
25
25
26 from pygments.styles import get_style_by_name, get_all_styles
26 from pygments.styles import get_style_by_name, get_all_styles
27 from pygments.token import Token
27 from pygments.token import Token
28
28
29 from .debugger import TerminalPdb, Pdb
29 from .debugger import TerminalPdb, Pdb
30 from .magics import TerminalMagics
30 from .magics import TerminalMagics
31 from .pt_inputhooks import get_inputhook_func
31 from .pt_inputhooks import get_inputhook_func
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
34
34
35
35
36 def get_default_editor():
36 def get_default_editor():
37 try:
37 try:
38 ed = os.environ['EDITOR']
38 ed = os.environ['EDITOR']
39 if not PY3:
39 if not PY3:
40 ed = ed.decode()
40 ed = ed.decode()
41 return ed
41 return ed
42 except KeyError:
42 except KeyError:
43 pass
43 pass
44 except UnicodeError:
44 except UnicodeError:
45 warn("$EDITOR environment variable is not pure ASCII. Using platform "
45 warn("$EDITOR environment variable is not pure ASCII. Using platform "
46 "default editor.")
46 "default editor.")
47
47
48 if os.name == 'posix':
48 if os.name == 'posix':
49 return 'vi' # the only one guaranteed to be there!
49 return 'vi' # the only one guaranteed to be there!
50 else:
50 else:
51 return 'notepad' # same in Windows!
51 return 'notepad' # same in Windows!
52
52
53 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
53 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
54
54
55 class TerminalInteractiveShell(InteractiveShell):
55 class TerminalInteractiveShell(InteractiveShell):
56 colors_force = True
56 colors_force = True
57
57
58 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
58 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
59 'to reserve for the completion menu'
59 'to reserve for the completion menu'
60 ).tag(config=True)
60 ).tag(config=True)
61
61
62 def _space_for_menu_changed(self, old, new):
62 def _space_for_menu_changed(self, old, new):
63 self._update_layout()
63 self._update_layout()
64
64
65 pt_cli = None
65 pt_cli = None
66 debugger_history = None
66 debugger_history = None
67
67
68 simple_prompt = Bool(_use_simple_prompt,
68 simple_prompt = Bool(_use_simple_prompt,
69 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
69 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
70
70
71 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
71 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
72 IPython own testing machinery, and emacs inferior-shell integration through elpy.
72 IPython own testing machinery, and emacs inferior-shell integration through elpy.
73
73
74 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
74 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
75 environment variable is set, or the current terminal is not a tty.
75 environment variable is set, or the current terminal is not a tty.
76
76
77 """
77 """
78 ).tag(config=True)
78 ).tag(config=True)
79
79
80 @property
80 @property
81 def debugger_cls(self):
81 def debugger_cls(self):
82 return Pdb if self.simple_prompt else TerminalPdb
82 return Pdb if self.simple_prompt else TerminalPdb
83
83
84 autoedit_syntax = Bool(False,
84 autoedit_syntax = Bool(False,
85 help="auto editing of files with syntax errors.",
85 help="auto editing of files with syntax errors.",
86 ).tag(config=True)
86 ).tag(config=True)
87
87
88
88
89 confirm_exit = Bool(True,
89 confirm_exit = Bool(True,
90 help="""
90 help="""
91 Set to confirm when you try to exit IPython with an EOF (Control-D
91 Set to confirm when you try to exit IPython with an EOF (Control-D
92 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
92 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
93 you can force a direct exit without any confirmation.""",
93 you can force a direct exit without any confirmation.""",
94 ).tag(config=True)
94 ).tag(config=True)
95
95
96 editing_mode = Unicode('emacs',
96 editing_mode = Unicode('emacs',
97 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
97 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
98 ).tag(config=True)
98 ).tag(config=True)
99
99
100 mouse_support = Bool(False,
100 mouse_support = Bool(False,
101 help="Enable mouse support in the prompt"
101 help="Enable mouse support in the prompt"
102 ).tag(config=True)
102 ).tag(config=True)
103
103
104 highlighting_style = Unicode('default',
104 highlighting_style = Unicode('default',
105 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
105 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
106 ).tag(config=True)
106 ).tag(config=True)
107
107
108
108
109 @observe('highlighting_style')
109 @observe('highlighting_style')
110 def _highlighting_style_changed(self, change):
110 def _highlighting_style_changed(self, change):
111 self._style = self._make_style_from_name(self.highlighting_style)
111 self._style = self._make_style_from_name(self.highlighting_style)
112
112
113 highlighting_style_overrides = Dict(
113 highlighting_style_overrides = Dict(
114 help="Override highlighting format for specific tokens"
114 help="Override highlighting format for specific tokens"
115 ).tag(config=True)
115 ).tag(config=True)
116
116
117 editor = Unicode(get_default_editor(),
117 editor = Unicode(get_default_editor(),
118 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
118 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
119 ).tag(config=True)
119 ).tag(config=True)
120
120
121 prompts = Instance(Prompts)
121 prompts = Instance(Prompts)
122
122
123 def _prompts_default(self):
123 def _prompts_default(self):
124 return Prompts(self)
124 return Prompts(self)
125
125
126 @observe('prompts')
126 @observe('prompts')
127 def _(self, change):
127 def _(self, change):
128 self._update_layout()
128 self._update_layout()
129
129
130 def _displayhook_class_default(self):
130 def _displayhook_class_default(self):
131 return RichPromptDisplayHook
131 return RichPromptDisplayHook
132
132
133 term_title = Bool(True,
133 term_title = Bool(True,
134 help="Automatically set the terminal title"
134 help="Automatically set the terminal title"
135 ).tag(config=True)
135 ).tag(config=True)
136
136
137 display_completions_in_columns = Bool(False,
137 display_completions_in_columns = Bool(False,
138 help="Display a multi column completion menu.",
138 help="Display a multi column completion menu.",
139 ).tag(config=True)
139 ).tag(config=True)
140
140
141 highlight_matching_brackets = Bool(True,
141 highlight_matching_brackets = Bool(True,
142 help="Highlight matching brackets .",
142 help="Highlight matching brackets .",
143 ).tag(config=True)
143 ).tag(config=True)
144
144
145 @observe('term_title')
145 @observe('term_title')
146 def init_term_title(self, change=None):
146 def init_term_title(self, change=None):
147 # Enable or disable the terminal title.
147 # Enable or disable the terminal title.
148 if self.term_title:
148 if self.term_title:
149 toggle_set_term_title(True)
149 toggle_set_term_title(True)
150 set_term_title('IPython: ' + abbrev_cwd())
150 set_term_title('IPython: ' + abbrev_cwd())
151 else:
151 else:
152 toggle_set_term_title(False)
152 toggle_set_term_title(False)
153
153
154 def init_prompt_toolkit_cli(self):
154 def init_prompt_toolkit_cli(self):
155 self._app = None
155 if self.simple_prompt:
156 if self.simple_prompt:
156 # Fall back to plain non-interactive output for tests.
157 # Fall back to plain non-interactive output for tests.
157 # This is very limited, and only accepts a single line.
158 # This is very limited, and only accepts a single line.
158 def prompt():
159 def prompt():
159 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
160 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
160 self.prompt_for_code = prompt
161 self.prompt_for_code = prompt
161 return
162 return
162
163
163 kbmanager = KeyBindingManager.for_prompt()
164 kbmanager = KeyBindingManager.for_prompt()
164 insert_mode = ViInsertMode() | EmacsInsertMode()
165 insert_mode = ViInsertMode() | EmacsInsertMode()
165 # Ctrl+J == Enter, seemingly
166 # Ctrl+J == Enter, seemingly
166 @kbmanager.registry.add_binding(Keys.ControlJ,
167 @kbmanager.registry.add_binding(Keys.ControlJ,
167 filter=(HasFocus(DEFAULT_BUFFER)
168 filter=(HasFocus(DEFAULT_BUFFER)
168 & ~HasSelection()
169 & ~HasSelection()
169 & insert_mode
170 & insert_mode
170 ))
171 ))
171 def _(event):
172 def _(event):
172 b = event.current_buffer
173 b = event.current_buffer
173 d = b.document
174 d = b.document
174 if not (d.on_last_line or d.cursor_position_row >= d.line_count
175 if not (d.on_last_line or d.cursor_position_row >= d.line_count
175 - d.empty_line_count_at_the_end()):
176 - d.empty_line_count_at_the_end()):
176 b.newline()
177 b.newline()
177 return
178 return
178
179
179 status, indent = self.input_splitter.check_complete(d.text)
180 status, indent = self.input_splitter.check_complete(d.text)
180
181
181 if (status != 'incomplete') and b.accept_action.is_returnable:
182 if (status != 'incomplete') and b.accept_action.is_returnable:
182 b.accept_action.validate_and_handle(event.cli, b)
183 b.accept_action.validate_and_handle(event.cli, b)
183 else:
184 else:
184 b.insert_text('\n' + (' ' * (indent or 0)))
185 b.insert_text('\n' + (' ' * (indent or 0)))
185
186
186 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
187 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
187 def _reset_buffer(event):
188 def _reset_buffer(event):
188 event.current_buffer.reset()
189 event.current_buffer.reset()
189
190
190 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
191 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
191 def _reset_search_buffer(event):
192 def _reset_search_buffer(event):
192 if event.current_buffer.document.text:
193 if event.current_buffer.document.text:
193 event.current_buffer.reset()
194 event.current_buffer.reset()
194 else:
195 else:
195 event.cli.push_focus(DEFAULT_BUFFER)
196 event.cli.push_focus(DEFAULT_BUFFER)
196
197
197 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
198 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
198
199
199 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
200 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
200 def _suspend_to_bg(event):
201 def _suspend_to_bg(event):
201 event.cli.suspend_to_background()
202 event.cli.suspend_to_background()
202
203
203 @Condition
204 @Condition
204 def cursor_in_leading_ws(cli):
205 def cursor_in_leading_ws(cli):
205 before = cli.application.buffer.document.current_line_before_cursor
206 before = cli.application.buffer.document.current_line_before_cursor
206 return (not before) or before.isspace()
207 return (not before) or before.isspace()
207
208
208 # Ctrl+I == Tab
209 # Ctrl+I == Tab
209 @kbmanager.registry.add_binding(Keys.ControlI,
210 @kbmanager.registry.add_binding(Keys.ControlI,
210 filter=(HasFocus(DEFAULT_BUFFER)
211 filter=(HasFocus(DEFAULT_BUFFER)
211 & ~HasSelection()
212 & ~HasSelection()
212 & insert_mode
213 & insert_mode
213 & cursor_in_leading_ws
214 & cursor_in_leading_ws
214 ))
215 ))
215 def _indent_buffer(event):
216 def _indent_buffer(event):
216 event.current_buffer.insert_text(' ' * 4)
217 event.current_buffer.insert_text(' ' * 4)
217
218
218 # Pre-populate history from IPython's history database
219 # Pre-populate history from IPython's history database
219 history = InMemoryHistory()
220 history = InMemoryHistory()
220 last_cell = u""
221 last_cell = u""
221 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
222 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
222 include_latest=True):
223 include_latest=True):
223 # Ignore blank lines and consecutive duplicates
224 # Ignore blank lines and consecutive duplicates
224 cell = cell.rstrip()
225 cell = cell.rstrip()
225 if cell and (cell != last_cell):
226 if cell and (cell != last_cell):
226 history.append(cell)
227 history.append(cell)
227
228
228 self._style = self._make_style_from_name(self.highlighting_style)
229 self._style = self._make_style_from_name(self.highlighting_style)
229 style = DynamicStyle(lambda: self._style)
230 style = DynamicStyle(lambda: self._style)
230
231
231 editing_mode = getattr(EditingMode, self.editing_mode.upper())
232 editing_mode = getattr(EditingMode, self.editing_mode.upper())
232
233
233 self._app = create_prompt_application(
234 self._app = create_prompt_application(
234 editing_mode=editing_mode,
235 editing_mode=editing_mode,
235 key_bindings_registry=kbmanager.registry,
236 key_bindings_registry=kbmanager.registry,
236 history=history,
237 history=history,
237 completer=IPythonPTCompleter(self.Completer),
238 completer=IPythonPTCompleter(self.Completer),
238 enable_history_search=True,
239 enable_history_search=True,
239 style=style,
240 style=style,
240 mouse_support=self.mouse_support,
241 mouse_support=self.mouse_support,
241 **self._layout_options()
242 **self._layout_options()
242 )
243 )
243 self._eventloop = create_eventloop(self.inputhook)
244 self._eventloop = create_eventloop(self.inputhook)
244 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
245 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
245
246
246 def _make_style_from_name(self, name):
247 def _make_style_from_name(self, name):
247 """
248 """
248 Small wrapper that make an IPython compatible style from a style name
249 Small wrapper that make an IPython compatible style from a style name
249
250
250 We need that to add style for prompt ... etc.
251 We need that to add style for prompt ... etc.
251 """
252 """
252 style_cls = get_style_by_name(name)
253 style_cls = get_style_by_name(name)
253 style_overrides = {
254 style_overrides = {
254 Token.Prompt: '#009900',
255 Token.Prompt: '#009900',
255 Token.PromptNum: '#00ff00 bold',
256 Token.PromptNum: '#00ff00 bold',
256 Token.OutPrompt: '#990000',
257 Token.OutPrompt: '#990000',
257 Token.OutPromptNum: '#ff0000 bold',
258 Token.OutPromptNum: '#ff0000 bold',
258 }
259 }
259 if name == 'default':
260 if name == 'default':
260 style_cls = get_style_by_name('default')
261 style_cls = get_style_by_name('default')
261 # The default theme needs to be visible on both a dark background
262 # The default theme needs to be visible on both a dark background
262 # and a light background, because we can't tell what the terminal
263 # and a light background, because we can't tell what the terminal
263 # looks like. These tweaks to the default theme help with that.
264 # looks like. These tweaks to the default theme help with that.
264 style_overrides.update({
265 style_overrides.update({
265 Token.Number: '#007700',
266 Token.Number: '#007700',
266 Token.Operator: 'noinherit',
267 Token.Operator: 'noinherit',
267 Token.String: '#BB6622',
268 Token.String: '#BB6622',
268 Token.Name.Function: '#2080D0',
269 Token.Name.Function: '#2080D0',
269 Token.Name.Class: 'bold #2080D0',
270 Token.Name.Class: 'bold #2080D0',
270 Token.Name.Namespace: 'bold #2080D0',
271 Token.Name.Namespace: 'bold #2080D0',
271 })
272 })
272 style_overrides.update(self.highlighting_style_overrides)
273 style_overrides.update(self.highlighting_style_overrides)
273 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
274 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
274 style_dict=style_overrides)
275 style_dict=style_overrides)
275
276
276 return style
277 return style
277
278
278 def _layout_options(self):
279 def _layout_options(self):
279 """
280 """
280 Return the current layout option for the current Terminal InteractiveShell
281 Return the current layout option for the current Terminal InteractiveShell
281 """
282 """
282 return {
283 return {
283 'lexer':IPythonPTLexer(),
284 'lexer':IPythonPTLexer(),
284 'reserve_space_for_menu':self.space_for_menu,
285 'reserve_space_for_menu':self.space_for_menu,
285 'get_prompt_tokens':self.prompts.in_prompt_tokens,
286 'get_prompt_tokens':self.prompts.in_prompt_tokens,
286 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
287 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
287 'multiline':True,
288 'multiline':True,
288 'display_completions_in_columns': self.display_completions_in_columns,
289 'display_completions_in_columns': self.display_completions_in_columns,
289
290
290 # Highlight matching brackets, but only when this setting is
291 # Highlight matching brackets, but only when this setting is
291 # enabled, and only when the DEFAULT_BUFFER has the focus.
292 # enabled, and only when the DEFAULT_BUFFER has the focus.
292 'extra_input_processors': [ConditionalProcessor(
293 'extra_input_processors': [ConditionalProcessor(
293 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
294 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
294 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
295 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
295 Condition(lambda cli: self.highlight_matching_brackets))],
296 Condition(lambda cli: self.highlight_matching_brackets))],
296 }
297 }
297
298
298 def _update_layout(self):
299 def _update_layout(self):
299 """
300 """
300 Ask for a re computation of the application layout, if for example ,
301 Ask for a re computation of the application layout, if for example ,
301 some configuration options have changed.
302 some configuration options have changed.
302 """
303 """
303 self._app.layout = create_prompt_layout(**self._layout_options())
304 if self._app:
305 self._app.layout = create_prompt_layout(**self._layout_options())
304
306
305 def prompt_for_code(self):
307 def prompt_for_code(self):
306 document = self.pt_cli.run(
308 document = self.pt_cli.run(
307 pre_run=self.pre_prompt, reset_current_buffer=True)
309 pre_run=self.pre_prompt, reset_current_buffer=True)
308 return document.text
310 return document.text
309
311
310 def init_io(self):
312 def init_io(self):
311 if sys.platform not in {'win32', 'cli'}:
313 if sys.platform not in {'win32', 'cli'}:
312 return
314 return
313
315
314 import colorama
316 import colorama
315 colorama.init()
317 colorama.init()
316
318
317 # For some reason we make these wrappers around stdout/stderr.
319 # For some reason we make these wrappers around stdout/stderr.
318 # For now, we need to reset them so all output gets coloured.
320 # For now, we need to reset them so all output gets coloured.
319 # https://github.com/ipython/ipython/issues/8669
321 # https://github.com/ipython/ipython/issues/8669
320 from IPython.utils import io
322 from IPython.utils import io
321 io.stdout = io.IOStream(sys.stdout)
323 io.stdout = io.IOStream(sys.stdout)
322 io.stderr = io.IOStream(sys.stderr)
324 io.stderr = io.IOStream(sys.stderr)
323
325
324 def init_magics(self):
326 def init_magics(self):
325 super(TerminalInteractiveShell, self).init_magics()
327 super(TerminalInteractiveShell, self).init_magics()
326 self.register_magics(TerminalMagics)
328 self.register_magics(TerminalMagics)
327
329
328 def init_alias(self):
330 def init_alias(self):
329 # The parent class defines aliases that can be safely used with any
331 # The parent class defines aliases that can be safely used with any
330 # frontend.
332 # frontend.
331 super(TerminalInteractiveShell, self).init_alias()
333 super(TerminalInteractiveShell, self).init_alias()
332
334
333 # Now define aliases that only make sense on the terminal, because they
335 # Now define aliases that only make sense on the terminal, because they
334 # need direct access to the console in a way that we can't emulate in
336 # need direct access to the console in a way that we can't emulate in
335 # GUI or web frontend
337 # GUI or web frontend
336 if os.name == 'posix':
338 if os.name == 'posix':
337 for cmd in ['clear', 'more', 'less', 'man']:
339 for cmd in ['clear', 'more', 'less', 'man']:
338 self.alias_manager.soft_define_alias(cmd, cmd)
340 self.alias_manager.soft_define_alias(cmd, cmd)
339
341
340
342
341 def __init__(self, *args, **kwargs):
343 def __init__(self, *args, **kwargs):
342 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
344 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
343 self.init_prompt_toolkit_cli()
345 self.init_prompt_toolkit_cli()
344 self.init_term_title()
346 self.init_term_title()
345 self.keep_running = True
347 self.keep_running = True
346
348
347 self.debugger_history = InMemoryHistory()
349 self.debugger_history = InMemoryHistory()
348
350
349 def ask_exit(self):
351 def ask_exit(self):
350 self.keep_running = False
352 self.keep_running = False
351
353
352 rl_next_input = None
354 rl_next_input = None
353
355
354 def pre_prompt(self):
356 def pre_prompt(self):
355 if self.rl_next_input:
357 if self.rl_next_input:
356 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
358 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
357 self.rl_next_input = None
359 self.rl_next_input = None
358
360
359 def interact(self):
361 def interact(self):
360 while self.keep_running:
362 while self.keep_running:
361 print(self.separate_in, end='')
363 print(self.separate_in, end='')
362
364
363 try:
365 try:
364 code = self.prompt_for_code()
366 code = self.prompt_for_code()
365 except EOFError:
367 except EOFError:
366 if (not self.confirm_exit) \
368 if (not self.confirm_exit) \
367 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
369 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
368 self.ask_exit()
370 self.ask_exit()
369
371
370 else:
372 else:
371 if code:
373 if code:
372 self.run_cell(code, store_history=True)
374 self.run_cell(code, store_history=True)
373 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
375 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
374 self.edit_syntax_error()
376 self.edit_syntax_error()
375
377
376 def mainloop(self):
378 def mainloop(self):
377 # An extra layer of protection in case someone mashing Ctrl-C breaks
379 # An extra layer of protection in case someone mashing Ctrl-C breaks
378 # out of our internal code.
380 # out of our internal code.
379 while True:
381 while True:
380 try:
382 try:
381 self.interact()
383 self.interact()
382 break
384 break
383 except KeyboardInterrupt:
385 except KeyboardInterrupt:
384 print("\nKeyboardInterrupt escaped interact()\n")
386 print("\nKeyboardInterrupt escaped interact()\n")
385
387
386 if hasattr(self, '_eventloop'):
388 if hasattr(self, '_eventloop'):
387 self._eventloop.close()
389 self._eventloop.close()
388
390
389 _inputhook = None
391 _inputhook = None
390 def inputhook(self, context):
392 def inputhook(self, context):
391 if self._inputhook is not None:
393 if self._inputhook is not None:
392 self._inputhook(context)
394 self._inputhook(context)
393
395
394 def enable_gui(self, gui=None):
396 def enable_gui(self, gui=None):
395 if gui:
397 if gui:
396 self._inputhook = get_inputhook_func(gui)
398 self._inputhook = get_inputhook_func(gui)
397 else:
399 else:
398 self._inputhook = None
400 self._inputhook = None
399
401
400 # Methods to support auto-editing of SyntaxErrors:
402 # Methods to support auto-editing of SyntaxErrors:
401
403
402 def edit_syntax_error(self):
404 def edit_syntax_error(self):
403 """The bottom half of the syntax error handler called in the main loop.
405 """The bottom half of the syntax error handler called in the main loop.
404
406
405 Loop until syntax error is fixed or user cancels.
407 Loop until syntax error is fixed or user cancels.
406 """
408 """
407
409
408 while self.SyntaxTB.last_syntax_error:
410 while self.SyntaxTB.last_syntax_error:
409 # copy and clear last_syntax_error
411 # copy and clear last_syntax_error
410 err = self.SyntaxTB.clear_err_state()
412 err = self.SyntaxTB.clear_err_state()
411 if not self._should_recompile(err):
413 if not self._should_recompile(err):
412 return
414 return
413 try:
415 try:
414 # may set last_syntax_error again if a SyntaxError is raised
416 # may set last_syntax_error again if a SyntaxError is raised
415 self.safe_execfile(err.filename, self.user_ns)
417 self.safe_execfile(err.filename, self.user_ns)
416 except:
418 except:
417 self.showtraceback()
419 self.showtraceback()
418 else:
420 else:
419 try:
421 try:
420 with open(err.filename) as f:
422 with open(err.filename) as f:
421 # This should be inside a display_trap block and I
423 # This should be inside a display_trap block and I
422 # think it is.
424 # think it is.
423 sys.displayhook(f.read())
425 sys.displayhook(f.read())
424 except:
426 except:
425 self.showtraceback()
427 self.showtraceback()
426
428
427 def _should_recompile(self, e):
429 def _should_recompile(self, e):
428 """Utility routine for edit_syntax_error"""
430 """Utility routine for edit_syntax_error"""
429
431
430 if e.filename in ('<ipython console>', '<input>', '<string>',
432 if e.filename in ('<ipython console>', '<input>', '<string>',
431 '<console>', '<BackgroundJob compilation>',
433 '<console>', '<BackgroundJob compilation>',
432 None):
434 None):
433 return False
435 return False
434 try:
436 try:
435 if (self.autoedit_syntax and
437 if (self.autoedit_syntax and
436 not self.ask_yes_no(
438 not self.ask_yes_no(
437 'Return to editor to correct syntax error? '
439 'Return to editor to correct syntax error? '
438 '[Y/n] ', 'y')):
440 '[Y/n] ', 'y')):
439 return False
441 return False
440 except EOFError:
442 except EOFError:
441 return False
443 return False
442
444
443 def int0(x):
445 def int0(x):
444 try:
446 try:
445 return int(x)
447 return int(x)
446 except TypeError:
448 except TypeError:
447 return 0
449 return 0
448
450
449 # always pass integer line and offset values to editor hook
451 # always pass integer line and offset values to editor hook
450 try:
452 try:
451 self.hooks.fix_error_editor(e.filename,
453 self.hooks.fix_error_editor(e.filename,
452 int0(e.lineno), int0(e.offset),
454 int0(e.lineno), int0(e.offset),
453 e.msg)
455 e.msg)
454 except TryNext:
456 except TryNext:
455 warn('Could not open editor')
457 warn('Could not open editor')
456 return False
458 return False
457 return True
459 return True
458
460
459 # Run !system commands directly, not through pipes, so terminal programs
461 # Run !system commands directly, not through pipes, so terminal programs
460 # work correctly.
462 # work correctly.
461 system = InteractiveShell.system_raw
463 system = InteractiveShell.system_raw
462
464
463 def auto_rewrite_input(self, cmd):
465 def auto_rewrite_input(self, cmd):
464 """Overridden from the parent class to use fancy rewriting prompt"""
466 """Overridden from the parent class to use fancy rewriting prompt"""
465 if not self.show_rewritten_input:
467 if not self.show_rewritten_input:
466 return
468 return
467
469
468 tokens = self.prompts.rewrite_prompt_tokens()
470 tokens = self.prompts.rewrite_prompt_tokens()
469 if self.pt_cli:
471 if self.pt_cli:
470 self.pt_cli.print_tokens(tokens)
472 self.pt_cli.print_tokens(tokens)
471 print(cmd)
473 print(cmd)
472 else:
474 else:
473 prompt = ''.join(s for t, s in tokens)
475 prompt = ''.join(s for t, s in tokens)
474 print(prompt, cmd, sep='')
476 print(prompt, cmd, sep='')
475
477
476 _prompts_before = None
478 _prompts_before = None
477 def switch_doctest_mode(self, mode):
479 def switch_doctest_mode(self, mode):
478 """Switch prompts to classic for %doctest_mode"""
480 """Switch prompts to classic for %doctest_mode"""
479 if mode:
481 if mode:
480 self._prompts_before = self.prompts
482 self._prompts_before = self.prompts
481 self.prompts = ClassicPrompts(self)
483 self.prompts = ClassicPrompts(self)
482 elif self._prompts_before:
484 elif self._prompts_before:
483 self.prompts = self._prompts_before
485 self.prompts = self._prompts_before
484 self._prompts_before = None
486 self._prompts_before = None
485
487
486
488
487 InteractiveShellABC.register(TerminalInteractiveShell)
489 InteractiveShellABC.register(TerminalInteractiveShell)
488
490
489 if __name__ == '__main__':
491 if __name__ == '__main__':
490 TerminalInteractiveShell.instance().interact()
492 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now