##// END OF EJS Templates
Use new prompt machinery to generate Out prompts
Thomas Kluyver -
Show More
@@ -1,293 +1,293 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Displayhook for IPython.
2 """Displayhook for IPython.
3
3
4 This defines a callable class that IPython uses for `sys.displayhook`.
4 This defines a callable class that IPython uses for `sys.displayhook`.
5 """
5 """
6
6
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10 from __future__ import print_function
10 from __future__ import print_function
11
11
12 import sys
12 import sys
13 import io as _io
13 import io as _io
14 import tokenize
14 import tokenize
15
15
16 from traitlets.config.configurable import Configurable
16 from traitlets.config.configurable import Configurable
17 from IPython.utils.py3compat import builtin_mod, cast_unicode_py2
17 from IPython.utils.py3compat import builtin_mod, cast_unicode_py2
18 from traitlets import Instance, Float
18 from traitlets import Instance, Float
19 from warnings import warn
19 from warnings import warn
20
20
21 # TODO: Move the various attributes (cache_size, [others now moved]). Some
21 # TODO: Move the various attributes (cache_size, [others now moved]). Some
22 # of these are also attributes of InteractiveShell. They should be on ONE object
22 # of these are also attributes of InteractiveShell. They should be on ONE object
23 # only and the other objects should ask that one object for their values.
23 # only and the other objects should ask that one object for their values.
24
24
25 class DisplayHook(Configurable):
25 class DisplayHook(Configurable):
26 """The custom IPython displayhook to replace sys.displayhook.
26 """The custom IPython displayhook to replace sys.displayhook.
27
27
28 This class does many things, but the basic idea is that it is a callable
28 This class does many things, but the basic idea is that it is a callable
29 that gets called anytime user code returns a value.
29 that gets called anytime user code returns a value.
30 """
30 """
31
31
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
32 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
33 allow_none=True)
33 allow_none=True)
34 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
34 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
35 allow_none=True)
35 allow_none=True)
36 cull_fraction = Float(0.2)
36 cull_fraction = Float(0.2)
37
37
38 def __init__(self, shell=None, cache_size=1000, **kwargs):
38 def __init__(self, shell=None, cache_size=1000, **kwargs):
39 super(DisplayHook, self).__init__(shell=shell, **kwargs)
39 super(DisplayHook, self).__init__(shell=shell, **kwargs)
40 cache_size_min = 3
40 cache_size_min = 3
41 if cache_size <= 0:
41 if cache_size <= 0:
42 self.do_full_cache = 0
42 self.do_full_cache = 0
43 cache_size = 0
43 cache_size = 0
44 elif cache_size < cache_size_min:
44 elif cache_size < cache_size_min:
45 self.do_full_cache = 0
45 self.do_full_cache = 0
46 cache_size = 0
46 cache_size = 0
47 warn('caching was disabled (min value for cache size is %s).' %
47 warn('caching was disabled (min value for cache size is %s).' %
48 cache_size_min,level=3)
48 cache_size_min,level=3)
49 else:
49 else:
50 self.do_full_cache = 1
50 self.do_full_cache = 1
51
51
52 self.cache_size = cache_size
52 self.cache_size = cache_size
53
53
54 # we need a reference to the user-level namespace
54 # we need a reference to the user-level namespace
55 self.shell = shell
55 self.shell = shell
56
56
57 self._,self.__,self.___ = '','',''
57 self._,self.__,self.___ = '','',''
58
58
59 # these are deliberately global:
59 # these are deliberately global:
60 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
60 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
61 self.shell.user_ns.update(to_user_ns)
61 self.shell.user_ns.update(to_user_ns)
62
62
63 @property
63 @property
64 def prompt_count(self):
64 def prompt_count(self):
65 return self.shell.execution_count
65 return self.shell.execution_count
66
66
67 #-------------------------------------------------------------------------
67 #-------------------------------------------------------------------------
68 # Methods used in __call__. Override these methods to modify the behavior
68 # Methods used in __call__. Override these methods to modify the behavior
69 # of the displayhook.
69 # of the displayhook.
70 #-------------------------------------------------------------------------
70 #-------------------------------------------------------------------------
71
71
72 def check_for_underscore(self):
72 def check_for_underscore(self):
73 """Check if the user has set the '_' variable by hand."""
73 """Check if the user has set the '_' variable by hand."""
74 # If something injected a '_' variable in __builtin__, delete
74 # If something injected a '_' variable in __builtin__, delete
75 # ipython's automatic one so we don't clobber that. gettext() in
75 # ipython's automatic one so we don't clobber that. gettext() in
76 # particular uses _, so we need to stay away from it.
76 # particular uses _, so we need to stay away from it.
77 if '_' in builtin_mod.__dict__:
77 if '_' in builtin_mod.__dict__:
78 try:
78 try:
79 del self.shell.user_ns['_']
79 del self.shell.user_ns['_']
80 except KeyError:
80 except KeyError:
81 pass
81 pass
82
82
83 def quiet(self):
83 def quiet(self):
84 """Should we silence the display hook because of ';'?"""
84 """Should we silence the display hook because of ';'?"""
85 # do not print output if input ends in ';'
85 # do not print output if input ends in ';'
86
86
87 try:
87 try:
88 cell = cast_unicode_py2(self.shell.history_manager.input_hist_parsed[-1])
88 cell = cast_unicode_py2(self.shell.history_manager.input_hist_parsed[-1])
89 except IndexError:
89 except IndexError:
90 # some uses of ipshellembed may fail here
90 # some uses of ipshellembed may fail here
91 return False
91 return False
92
92
93 sio = _io.StringIO(cell)
93 sio = _io.StringIO(cell)
94 tokens = list(tokenize.generate_tokens(sio.readline))
94 tokens = list(tokenize.generate_tokens(sio.readline))
95
95
96 for token in reversed(tokens):
96 for token in reversed(tokens):
97 if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):
97 if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):
98 continue
98 continue
99 if (token[0] == tokenize.OP) and (token[1] == ';'):
99 if (token[0] == tokenize.OP) and (token[1] == ';'):
100 return True
100 return True
101 else:
101 else:
102 return False
102 return False
103
103
104 def start_displayhook(self):
104 def start_displayhook(self):
105 """Start the displayhook, initializing resources."""
105 """Start the displayhook, initializing resources."""
106 pass
106 pass
107
107
108 def write_output_prompt(self):
108 def write_output_prompt(self):
109 """Write the output prompt.
109 """Write the output prompt.
110
110
111 The default implementation simply writes the prompt to
111 The default implementation simply writes the prompt to
112 ``io.stdout``.
112 ``io.stdout``.
113 """
113 """
114 # Use write, not print which adds an extra space.
114 # Use write, not print which adds an extra space.
115 sys.stdout.write(self.shell.separate_out)
115 sys.stdout.write(self.shell.separate_out)
116 outprompt = self.shell.prompt_manager.render('out')
116 outprompt = 'Out[{}]: '.format(self.shell.execution_count)
117 if self.do_full_cache:
117 if self.do_full_cache:
118 sys.stdout.write(outprompt)
118 sys.stdout.write(outprompt)
119
119
120 def compute_format_data(self, result):
120 def compute_format_data(self, result):
121 """Compute format data of the object to be displayed.
121 """Compute format data of the object to be displayed.
122
122
123 The format data is a generalization of the :func:`repr` of an object.
123 The format data is a generalization of the :func:`repr` of an object.
124 In the default implementation the format data is a :class:`dict` of
124 In the default implementation the format data is a :class:`dict` of
125 key value pair where the keys are valid MIME types and the values
125 key value pair where the keys are valid MIME types and the values
126 are JSON'able data structure containing the raw data for that MIME
126 are JSON'able data structure containing the raw data for that MIME
127 type. It is up to frontends to determine pick a MIME to to use and
127 type. It is up to frontends to determine pick a MIME to to use and
128 display that data in an appropriate manner.
128 display that data in an appropriate manner.
129
129
130 This method only computes the format data for the object and should
130 This method only computes the format data for the object and should
131 NOT actually print or write that to a stream.
131 NOT actually print or write that to a stream.
132
132
133 Parameters
133 Parameters
134 ----------
134 ----------
135 result : object
135 result : object
136 The Python object passed to the display hook, whose format will be
136 The Python object passed to the display hook, whose format will be
137 computed.
137 computed.
138
138
139 Returns
139 Returns
140 -------
140 -------
141 (format_dict, md_dict) : dict
141 (format_dict, md_dict) : dict
142 format_dict is a :class:`dict` whose keys are valid MIME types and values are
142 format_dict is a :class:`dict` whose keys are valid MIME types and values are
143 JSON'able raw data for that MIME type. It is recommended that
143 JSON'able raw data for that MIME type. It is recommended that
144 all return values of this should always include the "text/plain"
144 all return values of this should always include the "text/plain"
145 MIME type representation of the object.
145 MIME type representation of the object.
146 md_dict is a :class:`dict` with the same MIME type keys
146 md_dict is a :class:`dict` with the same MIME type keys
147 of metadata associated with each output.
147 of metadata associated with each output.
148
148
149 """
149 """
150 return self.shell.display_formatter.format(result)
150 return self.shell.display_formatter.format(result)
151
151
152 def write_format_data(self, format_dict, md_dict=None):
152 def write_format_data(self, format_dict, md_dict=None):
153 """Write the format data dict to the frontend.
153 """Write the format data dict to the frontend.
154
154
155 This default version of this method simply writes the plain text
155 This default version of this method simply writes the plain text
156 representation of the object to ``io.stdout``. Subclasses should
156 representation of the object to ``io.stdout``. Subclasses should
157 override this method to send the entire `format_dict` to the
157 override this method to send the entire `format_dict` to the
158 frontends.
158 frontends.
159
159
160 Parameters
160 Parameters
161 ----------
161 ----------
162 format_dict : dict
162 format_dict : dict
163 The format dict for the object passed to `sys.displayhook`.
163 The format dict for the object passed to `sys.displayhook`.
164 md_dict : dict (optional)
164 md_dict : dict (optional)
165 The metadata dict to be associated with the display data.
165 The metadata dict to be associated with the display data.
166 """
166 """
167 if 'text/plain' not in format_dict:
167 if 'text/plain' not in format_dict:
168 # nothing to do
168 # nothing to do
169 return
169 return
170 # We want to print because we want to always make sure we have a
170 # We want to print because we want to always make sure we have a
171 # newline, even if all the prompt separators are ''. This is the
171 # newline, even if all the prompt separators are ''. This is the
172 # standard IPython behavior.
172 # standard IPython behavior.
173 result_repr = format_dict['text/plain']
173 result_repr = format_dict['text/plain']
174 if '\n' in result_repr:
174 if '\n' in result_repr:
175 # So that multi-line strings line up with the left column of
175 # So that multi-line strings line up with the left column of
176 # the screen, instead of having the output prompt mess up
176 # the screen, instead of having the output prompt mess up
177 # their first line.
177 # their first line.
178 # We use the prompt template instead of the expanded prompt
178 # We use the prompt template instead of the expanded prompt
179 # because the expansion may add ANSI escapes that will interfere
179 # because the expansion may add ANSI escapes that will interfere
180 # with our ability to determine whether or not we should add
180 # with our ability to determine whether or not we should add
181 # a newline.
181 # a newline.
182 prompt_template = self.shell.prompt_manager.out_template
182 prompt_template = self.shell.prompt_manager.out_template
183 if prompt_template and not prompt_template.endswith('\n'):
183 if prompt_template and not prompt_template.endswith('\n'):
184 # But avoid extraneous empty lines.
184 # But avoid extraneous empty lines.
185 result_repr = '\n' + result_repr
185 result_repr = '\n' + result_repr
186
186
187 print(result_repr)
187 print(result_repr)
188
188
189 def update_user_ns(self, result):
189 def update_user_ns(self, result):
190 """Update user_ns with various things like _, __, _1, etc."""
190 """Update user_ns with various things like _, __, _1, etc."""
191
191
192 # Avoid recursive reference when displaying _oh/Out
192 # Avoid recursive reference when displaying _oh/Out
193 if result is not self.shell.user_ns['_oh']:
193 if result is not self.shell.user_ns['_oh']:
194 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
194 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
195 self.cull_cache()
195 self.cull_cache()
196 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
196 # Don't overwrite '_' and friends if '_' is in __builtin__ (otherwise
197 # we cause buggy behavior for things like gettext).
197 # we cause buggy behavior for things like gettext).
198
198
199 if '_' not in builtin_mod.__dict__:
199 if '_' not in builtin_mod.__dict__:
200 self.___ = self.__
200 self.___ = self.__
201 self.__ = self._
201 self.__ = self._
202 self._ = result
202 self._ = result
203 self.shell.push({'_':self._,
203 self.shell.push({'_':self._,
204 '__':self.__,
204 '__':self.__,
205 '___':self.___}, interactive=False)
205 '___':self.___}, interactive=False)
206
206
207 # hackish access to top-level namespace to create _1,_2... dynamically
207 # hackish access to top-level namespace to create _1,_2... dynamically
208 to_main = {}
208 to_main = {}
209 if self.do_full_cache:
209 if self.do_full_cache:
210 new_result = '_'+repr(self.prompt_count)
210 new_result = '_'+repr(self.prompt_count)
211 to_main[new_result] = result
211 to_main[new_result] = result
212 self.shell.push(to_main, interactive=False)
212 self.shell.push(to_main, interactive=False)
213 self.shell.user_ns['_oh'][self.prompt_count] = result
213 self.shell.user_ns['_oh'][self.prompt_count] = result
214
214
215 def fill_exec_result(self, result):
215 def fill_exec_result(self, result):
216 if self.exec_result is not None:
216 if self.exec_result is not None:
217 self.exec_result.result = result
217 self.exec_result.result = result
218
218
219 def log_output(self, format_dict):
219 def log_output(self, format_dict):
220 """Log the output."""
220 """Log the output."""
221 if 'text/plain' not in format_dict:
221 if 'text/plain' not in format_dict:
222 # nothing to do
222 # nothing to do
223 return
223 return
224 if self.shell.logger.log_output:
224 if self.shell.logger.log_output:
225 self.shell.logger.log_write(format_dict['text/plain'], 'output')
225 self.shell.logger.log_write(format_dict['text/plain'], 'output')
226 self.shell.history_manager.output_hist_reprs[self.prompt_count] = \
226 self.shell.history_manager.output_hist_reprs[self.prompt_count] = \
227 format_dict['text/plain']
227 format_dict['text/plain']
228
228
229 def finish_displayhook(self):
229 def finish_displayhook(self):
230 """Finish up all displayhook activities."""
230 """Finish up all displayhook activities."""
231 sys.stdout.write(self.shell.separate_out2)
231 sys.stdout.write(self.shell.separate_out2)
232 sys.stdout.flush()
232 sys.stdout.flush()
233
233
234 def __call__(self, result=None):
234 def __call__(self, result=None):
235 """Printing with history cache management.
235 """Printing with history cache management.
236
236
237 This is invoked everytime the interpreter needs to print, and is
237 This is invoked everytime the interpreter needs to print, and is
238 activated by setting the variable sys.displayhook to it.
238 activated by setting the variable sys.displayhook to it.
239 """
239 """
240 self.check_for_underscore()
240 self.check_for_underscore()
241 if result is not None and not self.quiet():
241 if result is not None and not self.quiet():
242 self.start_displayhook()
242 self.start_displayhook()
243 self.write_output_prompt()
243 self.write_output_prompt()
244 format_dict, md_dict = self.compute_format_data(result)
244 format_dict, md_dict = self.compute_format_data(result)
245 self.update_user_ns(result)
245 self.update_user_ns(result)
246 self.fill_exec_result(result)
246 self.fill_exec_result(result)
247 if format_dict:
247 if format_dict:
248 self.write_format_data(format_dict, md_dict)
248 self.write_format_data(format_dict, md_dict)
249 self.log_output(format_dict)
249 self.log_output(format_dict)
250 self.finish_displayhook()
250 self.finish_displayhook()
251
251
252 def cull_cache(self):
252 def cull_cache(self):
253 """Output cache is full, cull the oldest entries"""
253 """Output cache is full, cull the oldest entries"""
254 oh = self.shell.user_ns.get('_oh', {})
254 oh = self.shell.user_ns.get('_oh', {})
255 sz = len(oh)
255 sz = len(oh)
256 cull_count = max(int(sz * self.cull_fraction), 2)
256 cull_count = max(int(sz * self.cull_fraction), 2)
257 warn('Output cache limit (currently {sz} entries) hit.\n'
257 warn('Output cache limit (currently {sz} entries) hit.\n'
258 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))
258 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))
259
259
260 for i, n in enumerate(sorted(oh)):
260 for i, n in enumerate(sorted(oh)):
261 if i >= cull_count:
261 if i >= cull_count:
262 break
262 break
263 self.shell.user_ns.pop('_%i' % n, None)
263 self.shell.user_ns.pop('_%i' % n, None)
264 oh.pop(n, None)
264 oh.pop(n, None)
265
265
266
266
267 def flush(self):
267 def flush(self):
268 if not self.do_full_cache:
268 if not self.do_full_cache:
269 raise ValueError("You shouldn't have reached the cache flush "
269 raise ValueError("You shouldn't have reached the cache flush "
270 "if full caching is not enabled!")
270 "if full caching is not enabled!")
271 # delete auto-generated vars from global namespace
271 # delete auto-generated vars from global namespace
272
272
273 for n in range(1,self.prompt_count + 1):
273 for n in range(1,self.prompt_count + 1):
274 key = '_'+repr(n)
274 key = '_'+repr(n)
275 try:
275 try:
276 del self.shell.user_ns[key]
276 del self.shell.user_ns[key]
277 except: pass
277 except: pass
278 # In some embedded circumstances, the user_ns doesn't have the
278 # In some embedded circumstances, the user_ns doesn't have the
279 # '_oh' key set up.
279 # '_oh' key set up.
280 oh = self.shell.user_ns.get('_oh', None)
280 oh = self.shell.user_ns.get('_oh', None)
281 if oh is not None:
281 if oh is not None:
282 oh.clear()
282 oh.clear()
283
283
284 # Release our own references to objects:
284 # Release our own references to objects:
285 self._, self.__, self.___ = '', '', ''
285 self._, self.__, self.___ = '', '', ''
286
286
287 if '_' not in builtin_mod.__dict__:
287 if '_' not in builtin_mod.__dict__:
288 self.shell.user_ns.update({'_':None,'__':None, '___':None})
288 self.shell.user_ns.update({'_':None,'__':None, '___':None})
289 import gc
289 import gc
290 # TODO: Is this really needed?
290 # TODO: Is this really needed?
291 # IronPython blocks here forever
291 # IronPython blocks here forever
292 if sys.platform != "cli":
292 if sys.platform != "cli":
293 gc.collect()
293 gc.collect()
@@ -1,36 +1,50 b''
1 from pygments.token import Token
1 from pygments.token import Token
2 import sys
3
4 from IPython.core.displayhook import DisplayHook
2
5
3 class Prompts(object):
6 class Prompts(object):
4 def __init__(self, shell):
7 def __init__(self, shell):
5 self.shell = shell
8 self.shell = shell
6
9
7 def in_prompt_tokens(self, cli=None):
10 def in_prompt_tokens(self, cli=None):
8 return [
11 return [
9 (Token.Prompt, 'In ['),
12 (Token.Prompt, 'In ['),
10 (Token.PromptNum, str(self.shell.execution_count)),
13 (Token.PromptNum, str(self.shell.execution_count)),
11 (Token.Prompt, ']: '),
14 (Token.Prompt, ']: '),
12 ]
15 ]
13
16
14 def _width(self):
17 def _width(self):
15 in_tokens = self.in_prompt_tokens()
18 in_tokens = self.in_prompt_tokens()
16 return sum(len(s) for (t, s) in in_tokens)
19 return sum(len(s) for (t, s) in in_tokens)
17
20
18 def continuation_prompt_tokens(self, cli=None, width=None):
21 def continuation_prompt_tokens(self, cli=None, width=None):
19 if width is None:
22 if width is None:
20 width = self._width()
23 width = self._width()
21 return [
24 return [
22 (Token.Prompt, (' ' * (width - 5)) + '...: '),
25 (Token.Prompt, (' ' * (width - 5)) + '...: '),
23 ]
26 ]
24
27
25 def rewrite_prompt_tokens(self):
28 def rewrite_prompt_tokens(self):
26 width = self._width()
29 width = self._width()
27 return [
30 return [
28 (Token.Prompt, ('-' * (width - 2)) + '> '),
31 (Token.Prompt, ('-' * (width - 2)) + '> '),
29 ]
32 ]
30
33
31 def out_prompt_tokens(self):
34 def out_prompt_tokens(self):
32 return [
35 return [
33 (Token.OutPrompt, 'Out['),
36 (Token.OutPrompt, 'Out['),
34 (Token.OutPromptNum, str(self.shell.execution_count)),
37 (Token.OutPromptNum, str(self.shell.execution_count)),
35 (Token.OutPrompt, ']: '),
38 (Token.OutPrompt, ']: '),
36 ]
39 ]
40
41 class RichPromptDisplayHook(DisplayHook):
42 """Subclass of base display hook using coloured prompt"""
43 def write_output_prompt(self):
44 sys.stdout.write(self.shell.separate_out)
45 if self.do_full_cache:
46 tokens = self.shell.prompts.out_prompt_tokens()
47 if self.shell.pt_cli:
48 self.shell.pt_cli.print_tokens(tokens)
49 else:
50 print(*(s for t, s in tokens), sep='')
@@ -1,451 +1,456 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
10 from IPython.core.interactiveshell import InteractiveShell
11 from IPython.utils.py3compat import cast_unicode_py2, input
11 from IPython.utils.py3compat import 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 .pt_inputhooks import get_inputhook_func
30 from .pt_inputhooks import get_inputhook_func
31 from .interactiveshell import get_default_editor, TerminalMagics
31 from .interactiveshell import get_default_editor, TerminalMagics
32 from .prompts import Prompts
32 from .prompts import Prompts, RichPromptDisplayHook
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
34
34
35 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
35 _use_simple_prompt = 'IPY_TEST_SIMPLE_PROMPT' in os.environ or not sys.stdin.isatty()
36
36
37 class TerminalInteractiveShell(InteractiveShell):
37 class TerminalInteractiveShell(InteractiveShell):
38 colors_force = True
38 colors_force = True
39
39
40 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
40 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
41 'to reserve for the completion menu'
41 'to reserve for the completion menu'
42 ).tag(config=True)
42 ).tag(config=True)
43
43
44 def _space_for_menu_changed(self, old, new):
44 def _space_for_menu_changed(self, old, new):
45 self._update_layout()
45 self._update_layout()
46
46
47 pt_cli = None
47 pt_cli = None
48 debugger_history = None
48 debugger_history = None
49
49
50 simple_prompt = Bool(_use_simple_prompt,
50 simple_prompt = Bool(_use_simple_prompt,
51 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
51 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
52
52
53 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
53 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
54 IPython own testing machinery, and emacs inferior-shell integration through elpy.
54 IPython own testing machinery, and emacs inferior-shell integration through elpy.
55
55
56 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
56 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
57 environment variable is set, or the current terminal is not a tty.
57 environment variable is set, or the current terminal is not a tty.
58
58
59 """
59 """
60 ).tag(config=True)
60 ).tag(config=True)
61
61
62 @property
62 @property
63 def debugger_cls(self):
63 def debugger_cls(self):
64 return Pdb if self.simple_prompt else TerminalPdb
64 return Pdb if self.simple_prompt else TerminalPdb
65
65
66 autoedit_syntax = Bool(False,
66 autoedit_syntax = Bool(False,
67 help="auto editing of files with syntax errors.",
67 help="auto editing of files with syntax errors.",
68 ).tag(config=True)
68 ).tag(config=True)
69
69
70
70
71 confirm_exit = Bool(True,
71 confirm_exit = Bool(True,
72 help="""
72 help="""
73 Set to confirm when you try to exit IPython with an EOF (Control-D
73 Set to confirm when you try to exit IPython with an EOF (Control-D
74 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
74 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
75 you can force a direct exit without any confirmation.""",
75 you can force a direct exit without any confirmation.""",
76 ).tag(config=True)
76 ).tag(config=True)
77
77
78 editing_mode = Unicode('emacs',
78 editing_mode = Unicode('emacs',
79 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
79 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
80 ).tag(config=True)
80 ).tag(config=True)
81
81
82 mouse_support = Bool(False,
82 mouse_support = Bool(False,
83 help="Enable mouse support in the prompt"
83 help="Enable mouse support in the prompt"
84 ).tag(config=True)
84 ).tag(config=True)
85
85
86 highlighting_style = Unicode('default',
86 highlighting_style = Unicode('default',
87 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
87 help="The name of a Pygments style to use for syntax highlighting: \n %s" % ', '.join(get_all_styles())
88 ).tag(config=True)
88 ).tag(config=True)
89
89
90
90
91 @observe('highlighting_style')
91 @observe('highlighting_style')
92 def _highlighting_style_changed(self, change):
92 def _highlighting_style_changed(self, change):
93 self._style = self._make_style_from_name(self.highlighting_style)
93 self._style = self._make_style_from_name(self.highlighting_style)
94
94
95 highlighting_style_overrides = Dict(
95 highlighting_style_overrides = Dict(
96 help="Override highlighting format for specific tokens"
96 help="Override highlighting format for specific tokens"
97 ).tag(config=True)
97 ).tag(config=True)
98
98
99 editor = Unicode(get_default_editor(),
99 editor = Unicode(get_default_editor(),
100 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
100 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
101 ).tag(config=True)
101 ).tag(config=True)
102
102
103 prompts = Instance(Prompts)
103 prompts = Instance(Prompts)
104
104
105 def _prompts_default(self):
105 def _prompts_default(self):
106 return Prompts(self)
106 return Prompts(self)
107
107
108 def _displayhook_class_default(self):
109 return RichPromptDisplayHook
110
108 term_title = Bool(True,
111 term_title = Bool(True,
109 help="Automatically set the terminal title"
112 help="Automatically set the terminal title"
110 ).tag(config=True)
113 ).tag(config=True)
111
114
112 display_completions_in_columns = Bool(False,
115 display_completions_in_columns = Bool(False,
113 help="Display a multi column completion menu.",
116 help="Display a multi column completion menu.",
114 ).tag(config=True)
117 ).tag(config=True)
115
118
116 highlight_matching_brackets = Bool(True,
119 highlight_matching_brackets = Bool(True,
117 help="Highlight matching brackets .",
120 help="Highlight matching brackets .",
118 ).tag(config=True)
121 ).tag(config=True)
119
122
120 @observe('term_title')
123 @observe('term_title')
121 def init_term_title(self, change=None):
124 def init_term_title(self, change=None):
122 # Enable or disable the terminal title.
125 # Enable or disable the terminal title.
123 if self.term_title:
126 if self.term_title:
124 toggle_set_term_title(True)
127 toggle_set_term_title(True)
125 set_term_title('IPython: ' + abbrev_cwd())
128 set_term_title('IPython: ' + abbrev_cwd())
126 else:
129 else:
127 toggle_set_term_title(False)
130 toggle_set_term_title(False)
128
131
129 def init_prompt_toolkit_cli(self):
132 def init_prompt_toolkit_cli(self):
130 if self.simple_prompt:
133 if self.simple_prompt:
131 # Fall back to plain non-interactive output for tests.
134 # Fall back to plain non-interactive output for tests.
132 # This is very limited, and only accepts a single line.
135 # This is very limited, and only accepts a single line.
133 def prompt():
136 def prompt():
134 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
137 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
135 self.prompt_for_code = prompt
138 self.prompt_for_code = prompt
136 return
139 return
137
140
138 kbmanager = KeyBindingManager.for_prompt()
141 kbmanager = KeyBindingManager.for_prompt()
139 insert_mode = ViInsertMode() | EmacsInsertMode()
142 insert_mode = ViInsertMode() | EmacsInsertMode()
140 # Ctrl+J == Enter, seemingly
143 # Ctrl+J == Enter, seemingly
141 @kbmanager.registry.add_binding(Keys.ControlJ,
144 @kbmanager.registry.add_binding(Keys.ControlJ,
142 filter=(HasFocus(DEFAULT_BUFFER)
145 filter=(HasFocus(DEFAULT_BUFFER)
143 & ~HasSelection()
146 & ~HasSelection()
144 & insert_mode
147 & insert_mode
145 ))
148 ))
146 def _(event):
149 def _(event):
147 b = event.current_buffer
150 b = event.current_buffer
148 d = b.document
151 d = b.document
149 if not (d.on_last_line or d.cursor_position_row >= d.line_count
152 if not (d.on_last_line or d.cursor_position_row >= d.line_count
150 - d.empty_line_count_at_the_end()):
153 - d.empty_line_count_at_the_end()):
151 b.newline()
154 b.newline()
152 return
155 return
153
156
154 status, indent = self.input_splitter.check_complete(d.text)
157 status, indent = self.input_splitter.check_complete(d.text)
155
158
156 if (status != 'incomplete') and b.accept_action.is_returnable:
159 if (status != 'incomplete') and b.accept_action.is_returnable:
157 b.accept_action.validate_and_handle(event.cli, b)
160 b.accept_action.validate_and_handle(event.cli, b)
158 else:
161 else:
159 b.insert_text('\n' + (' ' * (indent or 0)))
162 b.insert_text('\n' + (' ' * (indent or 0)))
160
163
161 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
164 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(DEFAULT_BUFFER))
162 def _reset_buffer(event):
165 def _reset_buffer(event):
163 event.current_buffer.reset()
166 event.current_buffer.reset()
164
167
165 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
168 @kbmanager.registry.add_binding(Keys.ControlC, filter=HasFocus(SEARCH_BUFFER))
166 def _reset_search_buffer(event):
169 def _reset_search_buffer(event):
167 if event.current_buffer.document.text:
170 if event.current_buffer.document.text:
168 event.current_buffer.reset()
171 event.current_buffer.reset()
169 else:
172 else:
170 event.cli.push_focus(DEFAULT_BUFFER)
173 event.cli.push_focus(DEFAULT_BUFFER)
171
174
172 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
175 supports_suspend = Condition(lambda cli: hasattr(signal, 'SIGTSTP'))
173
176
174 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
177 @kbmanager.registry.add_binding(Keys.ControlZ, filter=supports_suspend)
175 def _suspend_to_bg(event):
178 def _suspend_to_bg(event):
176 event.cli.suspend_to_background()
179 event.cli.suspend_to_background()
177
180
178 @Condition
181 @Condition
179 def cursor_in_leading_ws(cli):
182 def cursor_in_leading_ws(cli):
180 before = cli.application.buffer.document.current_line_before_cursor
183 before = cli.application.buffer.document.current_line_before_cursor
181 return (not before) or before.isspace()
184 return (not before) or before.isspace()
182
185
183 # Ctrl+I == Tab
186 # Ctrl+I == Tab
184 @kbmanager.registry.add_binding(Keys.ControlI,
187 @kbmanager.registry.add_binding(Keys.ControlI,
185 filter=(HasFocus(DEFAULT_BUFFER)
188 filter=(HasFocus(DEFAULT_BUFFER)
186 & ~HasSelection()
189 & ~HasSelection()
187 & insert_mode
190 & insert_mode
188 & cursor_in_leading_ws
191 & cursor_in_leading_ws
189 ))
192 ))
190 def _indent_buffer(event):
193 def _indent_buffer(event):
191 event.current_buffer.insert_text(' ' * 4)
194 event.current_buffer.insert_text(' ' * 4)
192
195
193 # Pre-populate history from IPython's history database
196 # Pre-populate history from IPython's history database
194 history = InMemoryHistory()
197 history = InMemoryHistory()
195 last_cell = u""
198 last_cell = u""
196 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
199 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
197 include_latest=True):
200 include_latest=True):
198 # Ignore blank lines and consecutive duplicates
201 # Ignore blank lines and consecutive duplicates
199 cell = cell.rstrip()
202 cell = cell.rstrip()
200 if cell and (cell != last_cell):
203 if cell and (cell != last_cell):
201 history.append(cell)
204 history.append(cell)
202
205
203 self._style = self._make_style_from_name(self.highlighting_style)
206 self._style = self._make_style_from_name(self.highlighting_style)
204 style = DynamicStyle(lambda: self._style)
207 style = DynamicStyle(lambda: self._style)
205
208
206 editing_mode = getattr(EditingMode, self.editing_mode.upper())
209 editing_mode = getattr(EditingMode, self.editing_mode.upper())
207
210
208 self._app = create_prompt_application(
211 self._app = create_prompt_application(
209 editing_mode=editing_mode,
212 editing_mode=editing_mode,
210 key_bindings_registry=kbmanager.registry,
213 key_bindings_registry=kbmanager.registry,
211 history=history,
214 history=history,
212 completer=IPythonPTCompleter(self.Completer),
215 completer=IPythonPTCompleter(self.Completer),
213 enable_history_search=True,
216 enable_history_search=True,
214 style=style,
217 style=style,
215 mouse_support=self.mouse_support,
218 mouse_support=self.mouse_support,
216 **self._layout_options()
219 **self._layout_options()
217 )
220 )
218 self._eventloop = create_eventloop(self.inputhook)
221 self._eventloop = create_eventloop(self.inputhook)
219 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
222 self.pt_cli = CommandLineInterface(self._app, eventloop=self._eventloop)
220
223
221 def _make_style_from_name(self, name):
224 def _make_style_from_name(self, name):
222 """
225 """
223 Small wrapper that make an IPython compatible style from a style name
226 Small wrapper that make an IPython compatible style from a style name
224
227
225 We need that to add style for prompt ... etc.
228 We need that to add style for prompt ... etc.
226 """
229 """
227 style_cls = get_style_by_name(name)
230 style_cls = get_style_by_name(name)
228 style_overrides = {
231 style_overrides = {
229 Token.Prompt: '#009900',
232 Token.Prompt: '#009900',
230 Token.PromptNum: '#00ff00 bold',
233 Token.PromptNum: '#00ff00 bold',
234 Token.OutPrompt: '#990000',
235 Token.OutPromptNum: '#ff0000 bold',
231 }
236 }
232 if name == 'default':
237 if name == 'default':
233 style_cls = get_style_by_name('default')
238 style_cls = get_style_by_name('default')
234 # The default theme needs to be visible on both a dark background
239 # The default theme needs to be visible on both a dark background
235 # and a light background, because we can't tell what the terminal
240 # and a light background, because we can't tell what the terminal
236 # looks like. These tweaks to the default theme help with that.
241 # looks like. These tweaks to the default theme help with that.
237 style_overrides.update({
242 style_overrides.update({
238 Token.Number: '#007700',
243 Token.Number: '#007700',
239 Token.Operator: 'noinherit',
244 Token.Operator: 'noinherit',
240 Token.String: '#BB6622',
245 Token.String: '#BB6622',
241 Token.Name.Function: '#2080D0',
246 Token.Name.Function: '#2080D0',
242 Token.Name.Class: 'bold #2080D0',
247 Token.Name.Class: 'bold #2080D0',
243 Token.Name.Namespace: 'bold #2080D0',
248 Token.Name.Namespace: 'bold #2080D0',
244 })
249 })
245 style_overrides.update(self.highlighting_style_overrides)
250 style_overrides.update(self.highlighting_style_overrides)
246 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
251 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
247 style_dict=style_overrides)
252 style_dict=style_overrides)
248
253
249 return style
254 return style
250
255
251 def _layout_options(self):
256 def _layout_options(self):
252 """
257 """
253 Return the current layout option for the current Terminal InteractiveShell
258 Return the current layout option for the current Terminal InteractiveShell
254 """
259 """
255 return {
260 return {
256 'lexer':IPythonPTLexer(),
261 'lexer':IPythonPTLexer(),
257 'reserve_space_for_menu':self.space_for_menu,
262 'reserve_space_for_menu':self.space_for_menu,
258 'get_prompt_tokens':self.prompts.in_prompt_tokens,
263 'get_prompt_tokens':self.prompts.in_prompt_tokens,
259 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
264 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
260 'multiline':True,
265 'multiline':True,
261 'display_completions_in_columns': self.display_completions_in_columns,
266 'display_completions_in_columns': self.display_completions_in_columns,
262
267
263 # Highlight matching brackets, but only when this setting is
268 # Highlight matching brackets, but only when this setting is
264 # enabled, and only when the DEFAULT_BUFFER has the focus.
269 # enabled, and only when the DEFAULT_BUFFER has the focus.
265 'extra_input_processors': [ConditionalProcessor(
270 'extra_input_processors': [ConditionalProcessor(
266 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
271 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
267 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
272 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
268 Condition(lambda cli: self.highlight_matching_brackets))],
273 Condition(lambda cli: self.highlight_matching_brackets))],
269 }
274 }
270
275
271 def _update_layout(self):
276 def _update_layout(self):
272 """
277 """
273 Ask for a re computation of the application layout, if for example ,
278 Ask for a re computation of the application layout, if for example ,
274 some configuration options have changed.
279 some configuration options have changed.
275 """
280 """
276 self._app.layout = create_prompt_layout(**self._layout_options())
281 self._app.layout = create_prompt_layout(**self._layout_options())
277
282
278 def prompt_for_code(self):
283 def prompt_for_code(self):
279 document = self.pt_cli.run(
284 document = self.pt_cli.run(
280 pre_run=self.pre_prompt, reset_current_buffer=True)
285 pre_run=self.pre_prompt, reset_current_buffer=True)
281 return document.text
286 return document.text
282
287
283 def init_io(self):
288 def init_io(self):
284 if sys.platform not in {'win32', 'cli'}:
289 if sys.platform not in {'win32', 'cli'}:
285 return
290 return
286
291
287 import colorama
292 import colorama
288 colorama.init()
293 colorama.init()
289
294
290 # For some reason we make these wrappers around stdout/stderr.
295 # For some reason we make these wrappers around stdout/stderr.
291 # For now, we need to reset them so all output gets coloured.
296 # For now, we need to reset them so all output gets coloured.
292 # https://github.com/ipython/ipython/issues/8669
297 # https://github.com/ipython/ipython/issues/8669
293 from IPython.utils import io
298 from IPython.utils import io
294 io.stdout = io.IOStream(sys.stdout)
299 io.stdout = io.IOStream(sys.stdout)
295 io.stderr = io.IOStream(sys.stderr)
300 io.stderr = io.IOStream(sys.stderr)
296
301
297 def init_magics(self):
302 def init_magics(self):
298 super(TerminalInteractiveShell, self).init_magics()
303 super(TerminalInteractiveShell, self).init_magics()
299 self.register_magics(TerminalMagics)
304 self.register_magics(TerminalMagics)
300
305
301 def init_alias(self):
306 def init_alias(self):
302 # The parent class defines aliases that can be safely used with any
307 # The parent class defines aliases that can be safely used with any
303 # frontend.
308 # frontend.
304 super(TerminalInteractiveShell, self).init_alias()
309 super(TerminalInteractiveShell, self).init_alias()
305
310
306 # Now define aliases that only make sense on the terminal, because they
311 # Now define aliases that only make sense on the terminal, because they
307 # need direct access to the console in a way that we can't emulate in
312 # need direct access to the console in a way that we can't emulate in
308 # GUI or web frontend
313 # GUI or web frontend
309 if os.name == 'posix':
314 if os.name == 'posix':
310 for cmd in ['clear', 'more', 'less', 'man']:
315 for cmd in ['clear', 'more', 'less', 'man']:
311 self.alias_manager.soft_define_alias(cmd, cmd)
316 self.alias_manager.soft_define_alias(cmd, cmd)
312
317
313
318
314 def __init__(self, *args, **kwargs):
319 def __init__(self, *args, **kwargs):
315 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
320 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
316 self.init_prompt_toolkit_cli()
321 self.init_prompt_toolkit_cli()
317 self.init_term_title()
322 self.init_term_title()
318 self.keep_running = True
323 self.keep_running = True
319
324
320 self.debugger_history = InMemoryHistory()
325 self.debugger_history = InMemoryHistory()
321
326
322 def ask_exit(self):
327 def ask_exit(self):
323 self.keep_running = False
328 self.keep_running = False
324
329
325 rl_next_input = None
330 rl_next_input = None
326
331
327 def pre_prompt(self):
332 def pre_prompt(self):
328 if self.rl_next_input:
333 if self.rl_next_input:
329 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
334 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
330 self.rl_next_input = None
335 self.rl_next_input = None
331
336
332 def interact(self):
337 def interact(self):
333 while self.keep_running:
338 while self.keep_running:
334 print(self.separate_in, end='')
339 print(self.separate_in, end='')
335
340
336 try:
341 try:
337 code = self.prompt_for_code()
342 code = self.prompt_for_code()
338 except EOFError:
343 except EOFError:
339 if (not self.confirm_exit) \
344 if (not self.confirm_exit) \
340 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
345 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
341 self.ask_exit()
346 self.ask_exit()
342
347
343 else:
348 else:
344 if code:
349 if code:
345 self.run_cell(code, store_history=True)
350 self.run_cell(code, store_history=True)
346 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
351 if self.autoedit_syntax and self.SyntaxTB.last_syntax_error:
347 self.edit_syntax_error()
352 self.edit_syntax_error()
348
353
349 def mainloop(self):
354 def mainloop(self):
350 # An extra layer of protection in case someone mashing Ctrl-C breaks
355 # An extra layer of protection in case someone mashing Ctrl-C breaks
351 # out of our internal code.
356 # out of our internal code.
352 while True:
357 while True:
353 try:
358 try:
354 self.interact()
359 self.interact()
355 break
360 break
356 except KeyboardInterrupt:
361 except KeyboardInterrupt:
357 print("\nKeyboardInterrupt escaped interact()\n")
362 print("\nKeyboardInterrupt escaped interact()\n")
358
363
359 if hasattr(self, '_eventloop'):
364 if hasattr(self, '_eventloop'):
360 self._eventloop.close()
365 self._eventloop.close()
361
366
362 _inputhook = None
367 _inputhook = None
363 def inputhook(self, context):
368 def inputhook(self, context):
364 if self._inputhook is not None:
369 if self._inputhook is not None:
365 self._inputhook(context)
370 self._inputhook(context)
366
371
367 def enable_gui(self, gui=None):
372 def enable_gui(self, gui=None):
368 if gui:
373 if gui:
369 self._inputhook = get_inputhook_func(gui)
374 self._inputhook = get_inputhook_func(gui)
370 else:
375 else:
371 self._inputhook = None
376 self._inputhook = None
372
377
373 # Methods to support auto-editing of SyntaxErrors:
378 # Methods to support auto-editing of SyntaxErrors:
374
379
375 def edit_syntax_error(self):
380 def edit_syntax_error(self):
376 """The bottom half of the syntax error handler called in the main loop.
381 """The bottom half of the syntax error handler called in the main loop.
377
382
378 Loop until syntax error is fixed or user cancels.
383 Loop until syntax error is fixed or user cancels.
379 """
384 """
380
385
381 while self.SyntaxTB.last_syntax_error:
386 while self.SyntaxTB.last_syntax_error:
382 # copy and clear last_syntax_error
387 # copy and clear last_syntax_error
383 err = self.SyntaxTB.clear_err_state()
388 err = self.SyntaxTB.clear_err_state()
384 if not self._should_recompile(err):
389 if not self._should_recompile(err):
385 return
390 return
386 try:
391 try:
387 # may set last_syntax_error again if a SyntaxError is raised
392 # may set last_syntax_error again if a SyntaxError is raised
388 self.safe_execfile(err.filename, self.user_ns)
393 self.safe_execfile(err.filename, self.user_ns)
389 except:
394 except:
390 self.showtraceback()
395 self.showtraceback()
391 else:
396 else:
392 try:
397 try:
393 with open(err.filename) as f:
398 with open(err.filename) as f:
394 # This should be inside a display_trap block and I
399 # This should be inside a display_trap block and I
395 # think it is.
400 # think it is.
396 sys.displayhook(f.read())
401 sys.displayhook(f.read())
397 except:
402 except:
398 self.showtraceback()
403 self.showtraceback()
399
404
400 def _should_recompile(self, e):
405 def _should_recompile(self, e):
401 """Utility routine for edit_syntax_error"""
406 """Utility routine for edit_syntax_error"""
402
407
403 if e.filename in ('<ipython console>', '<input>', '<string>',
408 if e.filename in ('<ipython console>', '<input>', '<string>',
404 '<console>', '<BackgroundJob compilation>',
409 '<console>', '<BackgroundJob compilation>',
405 None):
410 None):
406 return False
411 return False
407 try:
412 try:
408 if (self.autoedit_syntax and
413 if (self.autoedit_syntax and
409 not self.ask_yes_no(
414 not self.ask_yes_no(
410 'Return to editor to correct syntax error? '
415 'Return to editor to correct syntax error? '
411 '[Y/n] ', 'y')):
416 '[Y/n] ', 'y')):
412 return False
417 return False
413 except EOFError:
418 except EOFError:
414 return False
419 return False
415
420
416 def int0(x):
421 def int0(x):
417 try:
422 try:
418 return int(x)
423 return int(x)
419 except TypeError:
424 except TypeError:
420 return 0
425 return 0
421
426
422 # always pass integer line and offset values to editor hook
427 # always pass integer line and offset values to editor hook
423 try:
428 try:
424 self.hooks.fix_error_editor(e.filename,
429 self.hooks.fix_error_editor(e.filename,
425 int0(e.lineno), int0(e.offset),
430 int0(e.lineno), int0(e.offset),
426 e.msg)
431 e.msg)
427 except TryNext:
432 except TryNext:
428 warn('Could not open editor')
433 warn('Could not open editor')
429 return False
434 return False
430 return True
435 return True
431
436
432 # Run !system commands directly, not through pipes, so terminal programs
437 # Run !system commands directly, not through pipes, so terminal programs
433 # work correctly.
438 # work correctly.
434 system = InteractiveShell.system_raw
439 system = InteractiveShell.system_raw
435
440
436 def auto_rewrite_input(self, cmd):
441 def auto_rewrite_input(self, cmd):
437 """Overridden from the parent class to use fancy rewriting prompt"""
442 """Overridden from the parent class to use fancy rewriting prompt"""
438 if not self.show_rewritten_input:
443 if not self.show_rewritten_input:
439 return
444 return
440
445
441 tokens = self.prompts.rewrite_prompt_tokens()
446 tokens = self.prompts.rewrite_prompt_tokens()
442 if self.pt_cli:
447 if self.pt_cli:
443 self.pt_cli.print_tokens(tokens)
448 self.pt_cli.print_tokens(tokens)
444 print(cmd)
449 print(cmd)
445 else:
450 else:
446 prompt = ''.join(s for t, s in tokens)
451 prompt = ''.join(s for t, s in tokens)
447 print(prompt, cmd, sep='')
452 print(prompt, cmd, sep='')
448
453
449
454
450 if __name__ == '__main__':
455 if __name__ == '__main__':
451 TerminalInteractiveShell.instance().interact()
456 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now