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