##// END OF EJS Templates
Fix starting IPython in vi editing mode...
Matthias Bussonnier -
Show More
@@ -1,547 +1,548 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2
2
3 import os
3 import os
4 import sys
4 import sys
5 import warnings
5 import warnings
6 from warnings import warn
6 from warnings import warn
7
7
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
8 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.utils import io
9 from IPython.utils import io
10 from IPython.utils.py3compat import input
10 from IPython.utils.py3compat import input
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
11 from IPython.utils.terminal import toggle_set_term_title, set_term_title
12 from IPython.utils.process import abbrev_cwd
12 from IPython.utils.process import abbrev_cwd
13 from traitlets import (
13 from traitlets import (
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
14 Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union,
15 Any, validate
15 Any, validate
16 )
16 )
17
17
18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
18 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
19 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
20 from prompt_toolkit.formatted_text import PygmentsTokens
20 from prompt_toolkit.formatted_text import PygmentsTokens
21 from prompt_toolkit.history import InMemoryHistory
21 from prompt_toolkit.history import InMemoryHistory
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.output import ColorDepth
23 from prompt_toolkit.output import ColorDepth
24 from prompt_toolkit.patch_stdout import patch_stdout
24 from prompt_toolkit.patch_stdout import patch_stdout
25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
25 from prompt_toolkit.shortcuts import PromptSession, CompleteStyle, print_formatted_text
26 from prompt_toolkit.styles import DynamicStyle, merge_styles
26 from prompt_toolkit.styles import DynamicStyle, merge_styles
27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
27 from prompt_toolkit.styles.pygments import style_from_pygments_cls, style_from_pygments_dict
28
28
29 from pygments.styles import get_style_by_name
29 from pygments.styles import get_style_by_name
30 from pygments.style import Style
30 from pygments.style import Style
31 from pygments.token import Token
31 from pygments.token import Token
32
32
33 from .debugger import TerminalPdb, Pdb
33 from .debugger import TerminalPdb, Pdb
34 from .magics import TerminalMagics
34 from .magics import TerminalMagics
35 from .pt_inputhooks import get_inputhook_name_and_func
35 from .pt_inputhooks import get_inputhook_name_and_func
36 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
36 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
37 from .ptutils import IPythonPTCompleter, IPythonPTLexer
37 from .ptutils import IPythonPTCompleter, IPythonPTLexer
38 from .shortcuts import create_ipython_shortcuts
38 from .shortcuts import create_ipython_shortcuts
39
39
40 DISPLAY_BANNER_DEPRECATED = object()
40 DISPLAY_BANNER_DEPRECATED = object()
41
41
42
42
43 class _NoStyle(Style): pass
43 class _NoStyle(Style): pass
44
44
45
45
46
46
47 _style_overrides_light_bg = {
47 _style_overrides_light_bg = {
48 Token.Prompt: '#0000ff',
48 Token.Prompt: '#0000ff',
49 Token.PromptNum: '#0000ee bold',
49 Token.PromptNum: '#0000ee bold',
50 Token.OutPrompt: '#cc0000',
50 Token.OutPrompt: '#cc0000',
51 Token.OutPromptNum: '#bb0000 bold',
51 Token.OutPromptNum: '#bb0000 bold',
52 }
52 }
53
53
54 _style_overrides_linux = {
54 _style_overrides_linux = {
55 Token.Prompt: '#00cc00',
55 Token.Prompt: '#00cc00',
56 Token.PromptNum: '#00bb00 bold',
56 Token.PromptNum: '#00bb00 bold',
57 Token.OutPrompt: '#cc0000',
57 Token.OutPrompt: '#cc0000',
58 Token.OutPromptNum: '#bb0000 bold',
58 Token.OutPromptNum: '#bb0000 bold',
59 }
59 }
60
60
61 def get_default_editor():
61 def get_default_editor():
62 try:
62 try:
63 return os.environ['EDITOR']
63 return os.environ['EDITOR']
64 except KeyError:
64 except KeyError:
65 pass
65 pass
66 except UnicodeError:
66 except UnicodeError:
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
67 warn("$EDITOR environment variable is not pure ASCII. Using platform "
68 "default editor.")
68 "default editor.")
69
69
70 if os.name == 'posix':
70 if os.name == 'posix':
71 return 'vi' # the only one guaranteed to be there!
71 return 'vi' # the only one guaranteed to be there!
72 else:
72 else:
73 return 'notepad' # same in Windows!
73 return 'notepad' # same in Windows!
74
74
75 # conservatively check for tty
75 # conservatively check for tty
76 # overridden streams can result in things like:
76 # overridden streams can result in things like:
77 # - sys.stdin = None
77 # - sys.stdin = None
78 # - no isatty method
78 # - no isatty method
79 for _name in ('stdin', 'stdout', 'stderr'):
79 for _name in ('stdin', 'stdout', 'stderr'):
80 _stream = getattr(sys, _name)
80 _stream = getattr(sys, _name)
81 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
81 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
82 _is_tty = False
82 _is_tty = False
83 break
83 break
84 else:
84 else:
85 _is_tty = True
85 _is_tty = True
86
86
87
87
88 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
88 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
89
89
90 class TerminalInteractiveShell(InteractiveShell):
90 class TerminalInteractiveShell(InteractiveShell):
91 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
91 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
92 'to reserve for the completion menu'
92 'to reserve for the completion menu'
93 ).tag(config=True)
93 ).tag(config=True)
94
94
95 pt_app = None
95 pt_app = None
96 debugger_history = None
96 debugger_history = None
97
97
98 simple_prompt = Bool(_use_simple_prompt,
98 simple_prompt = Bool(_use_simple_prompt,
99 help="""Use `raw_input` for the REPL, without completion and prompt colors.
99 help="""Use `raw_input` for the REPL, without completion and prompt colors.
100
100
101 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:
102 IPython own testing machinery, and emacs inferior-shell integration through elpy.
102 IPython own testing machinery, and emacs inferior-shell integration through elpy.
103
103
104 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
104 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
105 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."""
106 ).tag(config=True)
106 ).tag(config=True)
107
107
108 @property
108 @property
109 def debugger_cls(self):
109 def debugger_cls(self):
110 return Pdb if self.simple_prompt else TerminalPdb
110 return Pdb if self.simple_prompt else TerminalPdb
111
111
112 confirm_exit = Bool(True,
112 confirm_exit = Bool(True,
113 help="""
113 help="""
114 Set to confirm when you try to exit IPython with an EOF (Control-D
114 Set to confirm when you try to exit IPython with an EOF (Control-D
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
115 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
116 you can force a direct exit without any confirmation.""",
116 you can force a direct exit without any confirmation.""",
117 ).tag(config=True)
117 ).tag(config=True)
118
118
119 editing_mode = Unicode('emacs',
119 editing_mode = Unicode('emacs',
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
120 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
121 ).tag(config=True)
121 ).tag(config=True)
122
122
123 mouse_support = Bool(False,
123 mouse_support = Bool(False,
124 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
124 help="Enable mouse support in the prompt\n(Note: prevents selecting text with the mouse)"
125 ).tag(config=True)
125 ).tag(config=True)
126
126
127 # We don't load the list of styles for the help string, because loading
127 # We don't load the list of styles for the help string, because loading
128 # Pygments plugins takes time and can cause unexpected errors.
128 # Pygments plugins takes time and can cause unexpected errors.
129 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
129 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
130 help="""The name or class of a Pygments style to use for syntax
130 help="""The name or class of a Pygments style to use for syntax
131 highlighting. To see available styles, run `pygmentize -L styles`."""
131 highlighting. To see available styles, run `pygmentize -L styles`."""
132 ).tag(config=True)
132 ).tag(config=True)
133
133
134 @validate('editing_mode')
134 @validate('editing_mode')
135 def _validate_editing_mode(self, proposal):
135 def _validate_editing_mode(self, proposal):
136 if proposal['value'].lower() == 'vim':
136 if proposal['value'].lower() == 'vim':
137 proposal['value']= 'vi'
137 proposal['value']= 'vi'
138 elif proposal['value'].lower() == 'default':
138 elif proposal['value'].lower() == 'default':
139 proposal['value']= 'emacs'
139 proposal['value']= 'emacs'
140
140
141 if hasattr(EditingMode, proposal['value'].upper()):
141 if hasattr(EditingMode, proposal['value'].upper()):
142 return proposal['value'].lower()
142 return proposal['value'].lower()
143
143
144 return self.editing_mode
144 return self.editing_mode
145
145
146
146
147 @observe('editing_mode')
147 @observe('editing_mode')
148 def _editing_mode(self, change):
148 def _editing_mode(self, change):
149 u_mode = change.new.upper()
149 u_mode = change.new.upper()
150 self.pt_app.editing_mode = u_mode
150 if self.pt_app:
151 self.pt_app.editing_mode = u_mode
151
152
152 @observe('highlighting_style')
153 @observe('highlighting_style')
153 @observe('colors')
154 @observe('colors')
154 def _highlighting_style_changed(self, change):
155 def _highlighting_style_changed(self, change):
155 self.refresh_style()
156 self.refresh_style()
156
157
157 def refresh_style(self):
158 def refresh_style(self):
158 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
159 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
159
160
160
161
161 highlighting_style_overrides = Dict(
162 highlighting_style_overrides = Dict(
162 help="Override highlighting format for specific tokens"
163 help="Override highlighting format for specific tokens"
163 ).tag(config=True)
164 ).tag(config=True)
164
165
165 true_color = Bool(False,
166 true_color = Bool(False,
166 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
167 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
167 "If your terminal supports true color, the following command "
168 "If your terminal supports true color, the following command "
168 "should print 'TRUECOLOR' in orange: "
169 "should print 'TRUECOLOR' in orange: "
169 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
170 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
170 ).tag(config=True)
171 ).tag(config=True)
171
172
172 editor = Unicode(get_default_editor(),
173 editor = Unicode(get_default_editor(),
173 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
174 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
174 ).tag(config=True)
175 ).tag(config=True)
175
176
176 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
177 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
177
178
178 prompts = Instance(Prompts)
179 prompts = Instance(Prompts)
179
180
180 @default('prompts')
181 @default('prompts')
181 def _prompts_default(self):
182 def _prompts_default(self):
182 return self.prompts_class(self)
183 return self.prompts_class(self)
183
184
184 # @observe('prompts')
185 # @observe('prompts')
185 # def _(self, change):
186 # def _(self, change):
186 # self._update_layout()
187 # self._update_layout()
187
188
188 @default('displayhook_class')
189 @default('displayhook_class')
189 def _displayhook_class_default(self):
190 def _displayhook_class_default(self):
190 return RichPromptDisplayHook
191 return RichPromptDisplayHook
191
192
192 term_title = Bool(True,
193 term_title = Bool(True,
193 help="Automatically set the terminal title"
194 help="Automatically set the terminal title"
194 ).tag(config=True)
195 ).tag(config=True)
195
196
196 term_title_format = Unicode("IPython: {cwd}",
197 term_title_format = Unicode("IPython: {cwd}",
197 help="Customize the terminal title format. This is a python format string. " +
198 help="Customize the terminal title format. This is a python format string. " +
198 "Available substitutions are: {cwd}."
199 "Available substitutions are: {cwd}."
199 ).tag(config=True)
200 ).tag(config=True)
200
201
201 display_completions = Enum(('column', 'multicolumn','readlinelike'),
202 display_completions = Enum(('column', 'multicolumn','readlinelike'),
202 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
203 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
203 "'readlinelike'. These options are for `prompt_toolkit`, see "
204 "'readlinelike'. These options are for `prompt_toolkit`, see "
204 "`prompt_toolkit` documentation for more information."
205 "`prompt_toolkit` documentation for more information."
205 ),
206 ),
206 default_value='multicolumn').tag(config=True)
207 default_value='multicolumn').tag(config=True)
207
208
208 highlight_matching_brackets = Bool(True,
209 highlight_matching_brackets = Bool(True,
209 help="Highlight matching brackets.",
210 help="Highlight matching brackets.",
210 ).tag(config=True)
211 ).tag(config=True)
211
212
212 extra_open_editor_shortcuts = Bool(False,
213 extra_open_editor_shortcuts = Bool(False,
213 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
214 help="Enable vi (v) or Emacs (C-X C-E) shortcuts to open an external editor. "
214 "This is in addition to the F2 binding, which is always enabled."
215 "This is in addition to the F2 binding, which is always enabled."
215 ).tag(config=True)
216 ).tag(config=True)
216
217
217 handle_return = Any(None,
218 handle_return = Any(None,
218 help="Provide an alternative handler to be called when the user presses "
219 help="Provide an alternative handler to be called when the user presses "
219 "Return. This is an advanced option intended for debugging, which "
220 "Return. This is an advanced option intended for debugging, which "
220 "may be changed or removed in later releases."
221 "may be changed or removed in later releases."
221 ).tag(config=True)
222 ).tag(config=True)
222
223
223 enable_history_search = Bool(True,
224 enable_history_search = Bool(True,
224 help="Allows to enable/disable the prompt toolkit history search"
225 help="Allows to enable/disable the prompt toolkit history search"
225 ).tag(config=True)
226 ).tag(config=True)
226
227
227 @observe('term_title')
228 @observe('term_title')
228 def init_term_title(self, change=None):
229 def init_term_title(self, change=None):
229 # Enable or disable the terminal title.
230 # Enable or disable the terminal title.
230 if self.term_title:
231 if self.term_title:
231 toggle_set_term_title(True)
232 toggle_set_term_title(True)
232 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
233 set_term_title(self.term_title_format.format(cwd=abbrev_cwd()))
233 else:
234 else:
234 toggle_set_term_title(False)
235 toggle_set_term_title(False)
235
236
236 def init_display_formatter(self):
237 def init_display_formatter(self):
237 super(TerminalInteractiveShell, self).init_display_formatter()
238 super(TerminalInteractiveShell, self).init_display_formatter()
238 # terminal only supports plain text
239 # terminal only supports plain text
239 self.display_formatter.active_types = ['text/plain']
240 self.display_formatter.active_types = ['text/plain']
240 # disable `_ipython_display_`
241 # disable `_ipython_display_`
241 self.display_formatter.ipython_display_formatter.enabled = False
242 self.display_formatter.ipython_display_formatter.enabled = False
242
243
243 def init_prompt_toolkit_cli(self):
244 def init_prompt_toolkit_cli(self):
244 if self.simple_prompt:
245 if self.simple_prompt:
245 # Fall back to plain non-interactive output for tests.
246 # Fall back to plain non-interactive output for tests.
246 # This is very limited.
247 # This is very limited.
247 def prompt():
248 def prompt():
248 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
249 prompt_text = "".join(x[1] for x in self.prompts.in_prompt_tokens())
249 lines = [input(prompt_text)]
250 lines = [input(prompt_text)]
250 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
251 prompt_continuation = "".join(x[1] for x in self.prompts.continuation_prompt_tokens())
251 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
252 while self.check_complete('\n'.join(lines))[0] == 'incomplete':
252 lines.append( input(prompt_continuation) )
253 lines.append( input(prompt_continuation) )
253 return '\n'.join(lines)
254 return '\n'.join(lines)
254 self.prompt_for_code = prompt
255 self.prompt_for_code = prompt
255 return
256 return
256
257
257 # Set up keyboard shortcuts
258 # Set up keyboard shortcuts
258 key_bindings = create_ipython_shortcuts(self)
259 key_bindings = create_ipython_shortcuts(self)
259
260
260 # Pre-populate history from IPython's history database
261 # Pre-populate history from IPython's history database
261 history = InMemoryHistory()
262 history = InMemoryHistory()
262 last_cell = u""
263 last_cell = u""
263 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
264 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
264 include_latest=True):
265 include_latest=True):
265 # Ignore blank lines and consecutive duplicates
266 # Ignore blank lines and consecutive duplicates
266 cell = cell.rstrip()
267 cell = cell.rstrip()
267 if cell and (cell != last_cell):
268 if cell and (cell != last_cell):
268 history.append_string(cell)
269 history.append_string(cell)
269 last_cell = cell
270 last_cell = cell
270
271
271 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
272 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
272 self.style = DynamicStyle(lambda: self._style)
273 self.style = DynamicStyle(lambda: self._style)
273
274
274 editing_mode = getattr(EditingMode, self.editing_mode.upper())
275 editing_mode = getattr(EditingMode, self.editing_mode.upper())
275
276
276 self.pt_app = PromptSession(
277 self.pt_app = PromptSession(
277 editing_mode=editing_mode,
278 editing_mode=editing_mode,
278 key_bindings=key_bindings,
279 key_bindings=key_bindings,
279 history=history,
280 history=history,
280 completer=IPythonPTCompleter(shell=self),
281 completer=IPythonPTCompleter(shell=self),
281 enable_history_search = self.enable_history_search,
282 enable_history_search = self.enable_history_search,
282 style=self.style,
283 style=self.style,
283 include_default_pygments_style=False,
284 include_default_pygments_style=False,
284 mouse_support=self.mouse_support,
285 mouse_support=self.mouse_support,
285 enable_open_in_editor=self.extra_open_editor_shortcuts,
286 enable_open_in_editor=self.extra_open_editor_shortcuts,
286 color_depth=(ColorDepth.TRUE_COLOR if self.true_color else None),
287 color_depth=(ColorDepth.TRUE_COLOR if self.true_color else None),
287 **self._extra_prompt_options())
288 **self._extra_prompt_options())
288
289
289 def _make_style_from_name_or_cls(self, name_or_cls):
290 def _make_style_from_name_or_cls(self, name_or_cls):
290 """
291 """
291 Small wrapper that make an IPython compatible style from a style name
292 Small wrapper that make an IPython compatible style from a style name
292
293
293 We need that to add style for prompt ... etc.
294 We need that to add style for prompt ... etc.
294 """
295 """
295 style_overrides = {}
296 style_overrides = {}
296 if name_or_cls == 'legacy':
297 if name_or_cls == 'legacy':
297 legacy = self.colors.lower()
298 legacy = self.colors.lower()
298 if legacy == 'linux':
299 if legacy == 'linux':
299 style_cls = get_style_by_name('monokai')
300 style_cls = get_style_by_name('monokai')
300 style_overrides = _style_overrides_linux
301 style_overrides = _style_overrides_linux
301 elif legacy == 'lightbg':
302 elif legacy == 'lightbg':
302 style_overrides = _style_overrides_light_bg
303 style_overrides = _style_overrides_light_bg
303 style_cls = get_style_by_name('pastie')
304 style_cls = get_style_by_name('pastie')
304 elif legacy == 'neutral':
305 elif legacy == 'neutral':
305 # The default theme needs to be visible on both a dark background
306 # The default theme needs to be visible on both a dark background
306 # and a light background, because we can't tell what the terminal
307 # and a light background, because we can't tell what the terminal
307 # looks like. These tweaks to the default theme help with that.
308 # looks like. These tweaks to the default theme help with that.
308 style_cls = get_style_by_name('default')
309 style_cls = get_style_by_name('default')
309 style_overrides.update({
310 style_overrides.update({
310 Token.Number: '#007700',
311 Token.Number: '#007700',
311 Token.Operator: 'noinherit',
312 Token.Operator: 'noinherit',
312 Token.String: '#BB6622',
313 Token.String: '#BB6622',
313 Token.Name.Function: '#2080D0',
314 Token.Name.Function: '#2080D0',
314 Token.Name.Class: 'bold #2080D0',
315 Token.Name.Class: 'bold #2080D0',
315 Token.Name.Namespace: 'bold #2080D0',
316 Token.Name.Namespace: 'bold #2080D0',
316 Token.Prompt: '#009900',
317 Token.Prompt: '#009900',
317 Token.PromptNum: '#ansibrightgreen bold',
318 Token.PromptNum: '#ansibrightgreen bold',
318 Token.OutPrompt: '#990000',
319 Token.OutPrompt: '#990000',
319 Token.OutPromptNum: '#ansibrightred bold',
320 Token.OutPromptNum: '#ansibrightred bold',
320 })
321 })
321
322
322 # Hack: Due to limited color support on the Windows console
323 # Hack: Due to limited color support on the Windows console
323 # the prompt colors will be wrong without this
324 # the prompt colors will be wrong without this
324 if os.name == 'nt':
325 if os.name == 'nt':
325 style_overrides.update({
326 style_overrides.update({
326 Token.Prompt: '#ansidarkgreen',
327 Token.Prompt: '#ansidarkgreen',
327 Token.PromptNum: '#ansigreen bold',
328 Token.PromptNum: '#ansigreen bold',
328 Token.OutPrompt: '#ansidarkred',
329 Token.OutPrompt: '#ansidarkred',
329 Token.OutPromptNum: '#ansired bold',
330 Token.OutPromptNum: '#ansired bold',
330 })
331 })
331 elif legacy =='nocolor':
332 elif legacy =='nocolor':
332 style_cls=_NoStyle
333 style_cls=_NoStyle
333 style_overrides = {}
334 style_overrides = {}
334 else :
335 else :
335 raise ValueError('Got unknown colors: ', legacy)
336 raise ValueError('Got unknown colors: ', legacy)
336 else :
337 else :
337 if isinstance(name_or_cls, str):
338 if isinstance(name_or_cls, str):
338 style_cls = get_style_by_name(name_or_cls)
339 style_cls = get_style_by_name(name_or_cls)
339 else:
340 else:
340 style_cls = name_or_cls
341 style_cls = name_or_cls
341 style_overrides = {
342 style_overrides = {
342 Token.Prompt: '#009900',
343 Token.Prompt: '#009900',
343 Token.PromptNum: '#ansibrightgreen bold',
344 Token.PromptNum: '#ansibrightgreen bold',
344 Token.OutPrompt: '#990000',
345 Token.OutPrompt: '#990000',
345 Token.OutPromptNum: '#ansibrightred bold',
346 Token.OutPromptNum: '#ansibrightred bold',
346 }
347 }
347 style_overrides.update(self.highlighting_style_overrides)
348 style_overrides.update(self.highlighting_style_overrides)
348 style = merge_styles([
349 style = merge_styles([
349 style_from_pygments_cls(style_cls),
350 style_from_pygments_cls(style_cls),
350 style_from_pygments_dict(style_overrides),
351 style_from_pygments_dict(style_overrides),
351 ])
352 ])
352
353
353 return style
354 return style
354
355
355 @property
356 @property
356 def pt_complete_style(self):
357 def pt_complete_style(self):
357 return {
358 return {
358 'multicolumn': CompleteStyle.MULTI_COLUMN,
359 'multicolumn': CompleteStyle.MULTI_COLUMN,
359 'column': CompleteStyle.COLUMN,
360 'column': CompleteStyle.COLUMN,
360 'readlinelike': CompleteStyle.READLINE_LIKE,
361 'readlinelike': CompleteStyle.READLINE_LIKE,
361 }[self.display_completions]
362 }[self.display_completions]
362
363
363 def _extra_prompt_options(self):
364 def _extra_prompt_options(self):
364 """
365 """
365 Return the current layout option for the current Terminal InteractiveShell
366 Return the current layout option for the current Terminal InteractiveShell
366 """
367 """
367 def get_message():
368 def get_message():
368 return PygmentsTokens(self.prompts.in_prompt_tokens())
369 return PygmentsTokens(self.prompts.in_prompt_tokens())
369
370
370 return {
371 return {
371 'complete_in_thread': False,
372 'complete_in_thread': False,
372 'lexer':IPythonPTLexer(),
373 'lexer':IPythonPTLexer(),
373 'reserve_space_for_menu':self.space_for_menu,
374 'reserve_space_for_menu':self.space_for_menu,
374 'message': get_message,
375 'message': get_message,
375 'prompt_continuation': (
376 'prompt_continuation': (
376 lambda width, lineno, is_soft_wrap:
377 lambda width, lineno, is_soft_wrap:
377 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
378 PygmentsTokens(self.prompts.continuation_prompt_tokens(width))),
378 'multiline': True,
379 'multiline': True,
379 'complete_style': self.pt_complete_style,
380 'complete_style': self.pt_complete_style,
380
381
381 # Highlight matching brackets, but only when this setting is
382 # Highlight matching brackets, but only when this setting is
382 # enabled, and only when the DEFAULT_BUFFER has the focus.
383 # enabled, and only when the DEFAULT_BUFFER has the focus.
383 'input_processors': [ConditionalProcessor(
384 'input_processors': [ConditionalProcessor(
384 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
385 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
385 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
386 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
386 Condition(lambda: self.highlight_matching_brackets))],
387 Condition(lambda: self.highlight_matching_brackets))],
387 'inputhook': self.inputhook,
388 'inputhook': self.inputhook,
388 }
389 }
389
390
390 def prompt_for_code(self):
391 def prompt_for_code(self):
391 if self.rl_next_input:
392 if self.rl_next_input:
392 default = self.rl_next_input
393 default = self.rl_next_input
393 self.rl_next_input = None
394 self.rl_next_input = None
394 else:
395 else:
395 default = ''
396 default = ''
396
397
397 with patch_stdout(raw=True):
398 with patch_stdout(raw=True):
398 text = self.pt_app.prompt(
399 text = self.pt_app.prompt(
399 default=default,
400 default=default,
400 # pre_run=self.pre_prompt,# reset_current_buffer=True,
401 # pre_run=self.pre_prompt,# reset_current_buffer=True,
401 **self._extra_prompt_options())
402 **self._extra_prompt_options())
402 return text
403 return text
403
404
404 def enable_win_unicode_console(self):
405 def enable_win_unicode_console(self):
405 if sys.version_info >= (3, 6):
406 if sys.version_info >= (3, 6):
406 # Since PEP 528, Python uses the unicode APIs for the Windows
407 # Since PEP 528, Python uses the unicode APIs for the Windows
407 # console by default, so WUC shouldn't be needed.
408 # console by default, so WUC shouldn't be needed.
408 return
409 return
409
410
410 import win_unicode_console
411 import win_unicode_console
411 win_unicode_console.enable()
412 win_unicode_console.enable()
412
413
413 def init_io(self):
414 def init_io(self):
414 if sys.platform not in {'win32', 'cli'}:
415 if sys.platform not in {'win32', 'cli'}:
415 return
416 return
416
417
417 self.enable_win_unicode_console()
418 self.enable_win_unicode_console()
418
419
419 import colorama
420 import colorama
420 colorama.init()
421 colorama.init()
421
422
422 # For some reason we make these wrappers around stdout/stderr.
423 # For some reason we make these wrappers around stdout/stderr.
423 # For now, we need to reset them so all output gets coloured.
424 # For now, we need to reset them so all output gets coloured.
424 # https://github.com/ipython/ipython/issues/8669
425 # https://github.com/ipython/ipython/issues/8669
425 # io.std* are deprecated, but don't show our own deprecation warnings
426 # io.std* are deprecated, but don't show our own deprecation warnings
426 # during initialization of the deprecated API.
427 # during initialization of the deprecated API.
427 with warnings.catch_warnings():
428 with warnings.catch_warnings():
428 warnings.simplefilter('ignore', DeprecationWarning)
429 warnings.simplefilter('ignore', DeprecationWarning)
429 io.stdout = io.IOStream(sys.stdout)
430 io.stdout = io.IOStream(sys.stdout)
430 io.stderr = io.IOStream(sys.stderr)
431 io.stderr = io.IOStream(sys.stderr)
431
432
432 def init_magics(self):
433 def init_magics(self):
433 super(TerminalInteractiveShell, self).init_magics()
434 super(TerminalInteractiveShell, self).init_magics()
434 self.register_magics(TerminalMagics)
435 self.register_magics(TerminalMagics)
435
436
436 def init_alias(self):
437 def init_alias(self):
437 # The parent class defines aliases that can be safely used with any
438 # The parent class defines aliases that can be safely used with any
438 # frontend.
439 # frontend.
439 super(TerminalInteractiveShell, self).init_alias()
440 super(TerminalInteractiveShell, self).init_alias()
440
441
441 # Now define aliases that only make sense on the terminal, because they
442 # Now define aliases that only make sense on the terminal, because they
442 # need direct access to the console in a way that we can't emulate in
443 # need direct access to the console in a way that we can't emulate in
443 # GUI or web frontend
444 # GUI or web frontend
444 if os.name == 'posix':
445 if os.name == 'posix':
445 for cmd in ['clear', 'more', 'less', 'man']:
446 for cmd in ['clear', 'more', 'less', 'man']:
446 self.alias_manager.soft_define_alias(cmd, cmd)
447 self.alias_manager.soft_define_alias(cmd, cmd)
447
448
448
449
449 def __init__(self, *args, **kwargs):
450 def __init__(self, *args, **kwargs):
450 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
451 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
451 self.init_prompt_toolkit_cli()
452 self.init_prompt_toolkit_cli()
452 self.init_term_title()
453 self.init_term_title()
453 self.keep_running = True
454 self.keep_running = True
454
455
455 self.debugger_history = InMemoryHistory()
456 self.debugger_history = InMemoryHistory()
456
457
457 def ask_exit(self):
458 def ask_exit(self):
458 self.keep_running = False
459 self.keep_running = False
459
460
460 rl_next_input = None
461 rl_next_input = None
461
462
462 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
463 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
463
464
464 if display_banner is not DISPLAY_BANNER_DEPRECATED:
465 if display_banner is not DISPLAY_BANNER_DEPRECATED:
465 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
466 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
466
467
467 self.keep_running = True
468 self.keep_running = True
468 while self.keep_running:
469 while self.keep_running:
469 print(self.separate_in, end='')
470 print(self.separate_in, end='')
470
471
471 try:
472 try:
472 code = self.prompt_for_code()
473 code = self.prompt_for_code()
473 except EOFError:
474 except EOFError:
474 if (not self.confirm_exit) \
475 if (not self.confirm_exit) \
475 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
476 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
476 self.ask_exit()
477 self.ask_exit()
477
478
478 else:
479 else:
479 if code:
480 if code:
480 self.run_cell(code, store_history=True)
481 self.run_cell(code, store_history=True)
481
482
482 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
483 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
483 # An extra layer of protection in case someone mashing Ctrl-C breaks
484 # An extra layer of protection in case someone mashing Ctrl-C breaks
484 # out of our internal code.
485 # out of our internal code.
485 if display_banner is not DISPLAY_BANNER_DEPRECATED:
486 if display_banner is not DISPLAY_BANNER_DEPRECATED:
486 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
487 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
487 while True:
488 while True:
488 try:
489 try:
489 self.interact()
490 self.interact()
490 break
491 break
491 except KeyboardInterrupt as e:
492 except KeyboardInterrupt as e:
492 print("\n%s escaped interact()\n" % type(e).__name__)
493 print("\n%s escaped interact()\n" % type(e).__name__)
493 finally:
494 finally:
494 # An interrupt during the eventloop will mess up the
495 # An interrupt during the eventloop will mess up the
495 # internal state of the prompt_toolkit library.
496 # internal state of the prompt_toolkit library.
496 # Stopping the eventloop fixes this, see
497 # Stopping the eventloop fixes this, see
497 # https://github.com/ipython/ipython/pull/9867
498 # https://github.com/ipython/ipython/pull/9867
498 if hasattr(self, '_eventloop'):
499 if hasattr(self, '_eventloop'):
499 self._eventloop.stop()
500 self._eventloop.stop()
500
501
501 _inputhook = None
502 _inputhook = None
502 def inputhook(self, context):
503 def inputhook(self, context):
503 if self._inputhook is not None:
504 if self._inputhook is not None:
504 self._inputhook(context)
505 self._inputhook(context)
505
506
506 active_eventloop = None
507 active_eventloop = None
507 def enable_gui(self, gui=None):
508 def enable_gui(self, gui=None):
508 if gui:
509 if gui:
509 self.active_eventloop, self._inputhook =\
510 self.active_eventloop, self._inputhook =\
510 get_inputhook_name_and_func(gui)
511 get_inputhook_name_and_func(gui)
511 else:
512 else:
512 self.active_eventloop = self._inputhook = None
513 self.active_eventloop = self._inputhook = None
513
514
514 # Run !system commands directly, not through pipes, so terminal programs
515 # Run !system commands directly, not through pipes, so terminal programs
515 # work correctly.
516 # work correctly.
516 system = InteractiveShell.system_raw
517 system = InteractiveShell.system_raw
517
518
518 def auto_rewrite_input(self, cmd):
519 def auto_rewrite_input(self, cmd):
519 """Overridden from the parent class to use fancy rewriting prompt"""
520 """Overridden from the parent class to use fancy rewriting prompt"""
520 if not self.show_rewritten_input:
521 if not self.show_rewritten_input:
521 return
522 return
522
523
523 tokens = self.prompts.rewrite_prompt_tokens()
524 tokens = self.prompts.rewrite_prompt_tokens()
524 if self.pt_app:
525 if self.pt_app:
525 print_formatted_text(PygmentsTokens(tokens), end='',
526 print_formatted_text(PygmentsTokens(tokens), end='',
526 style=self.pt_app.app.style)
527 style=self.pt_app.app.style)
527 print(cmd)
528 print(cmd)
528 else:
529 else:
529 prompt = ''.join(s for t, s in tokens)
530 prompt = ''.join(s for t, s in tokens)
530 print(prompt, cmd, sep='')
531 print(prompt, cmd, sep='')
531
532
532 _prompts_before = None
533 _prompts_before = None
533 def switch_doctest_mode(self, mode):
534 def switch_doctest_mode(self, mode):
534 """Switch prompts to classic for %doctest_mode"""
535 """Switch prompts to classic for %doctest_mode"""
535 if mode:
536 if mode:
536 self._prompts_before = self.prompts
537 self._prompts_before = self.prompts
537 self.prompts = ClassicPrompts(self)
538 self.prompts = ClassicPrompts(self)
538 elif self._prompts_before:
539 elif self._prompts_before:
539 self.prompts = self._prompts_before
540 self.prompts = self._prompts_before
540 self._prompts_before = None
541 self._prompts_before = None
541 # self._update_layout()
542 # self._update_layout()
542
543
543
544
544 InteractiveShellABC.register(TerminalInteractiveShell)
545 InteractiveShellABC.register(TerminalInteractiveShell)
545
546
546 if __name__ == '__main__':
547 if __name__ == '__main__':
547 TerminalInteractiveShell.instance().interact()
548 TerminalInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now