##// END OF EJS Templates
Backport PR #10223: Fix error tab completing in the debugger...
Min RK -
Show More
@@ -1,85 +1,88 b''
1 from IPython.core.debugger import Pdb
1 from IPython.core.debugger import Pdb
2
2
3 from IPython.core.completer import IPCompleter
3 from IPython.core.completer import IPCompleter
4 from .ptutils import IPythonPTCompleter
4 from .ptutils import IPythonPTCompleter
5
5
6 from prompt_toolkit.token import Token
6 from prompt_toolkit.token import Token
7 from prompt_toolkit.shortcuts import create_prompt_application
7 from prompt_toolkit.shortcuts import create_prompt_application
8 from prompt_toolkit.interface import CommandLineInterface
8 from prompt_toolkit.interface import CommandLineInterface
9 from prompt_toolkit.enums import EditingMode
9 from prompt_toolkit.enums import EditingMode
10 import sys
10 import sys
11
11
12
12
13 class TerminalPdb(Pdb):
13 class TerminalPdb(Pdb):
14 def __init__(self, *args, **kwargs):
14 def __init__(self, *args, **kwargs):
15 Pdb.__init__(self, *args, **kwargs)
15 Pdb.__init__(self, *args, **kwargs)
16 self._ptcomp = None
16 self._ptcomp = None
17 self.pt_init()
17 self.pt_init()
18
18
19 def pt_init(self):
19 def pt_init(self):
20 def get_prompt_tokens(cli):
20 def get_prompt_tokens(cli):
21 return [(Token.Prompt, self.prompt)]
21 return [(Token.Prompt, self.prompt)]
22
22
23 def patch_stdout(**kwargs):
24 return self.pt_cli.patch_stdout_context(**kwargs)
25
23 if self._ptcomp is None:
26 if self._ptcomp is None:
24 compl = IPCompleter(shell=self.shell,
27 compl = IPCompleter(shell=self.shell,
25 namespace={},
28 namespace={},
26 global_namespace={},
29 global_namespace={},
27 use_readline=False,
30 use_readline=False,
28 parent=self.shell,
31 parent=self.shell,
29 )
32 )
30 self._ptcomp = IPythonPTCompleter(compl)
33 self._ptcomp = IPythonPTCompleter(compl, patch_stdout=patch_stdout)
31
34
32 self._pt_app = create_prompt_application(
35 self._pt_app = create_prompt_application(
33 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
36 editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()),
34 history=self.shell.debugger_history,
37 history=self.shell.debugger_history,
35 completer= self._ptcomp,
38 completer= self._ptcomp,
36 enable_history_search=True,
39 enable_history_search=True,
37 mouse_support=self.shell.mouse_support,
40 mouse_support=self.shell.mouse_support,
38 get_prompt_tokens=get_prompt_tokens
41 get_prompt_tokens=get_prompt_tokens
39 )
42 )
40 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
43 self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop)
41
44
42 def cmdloop(self, intro=None):
45 def cmdloop(self, intro=None):
43 """Repeatedly issue a prompt, accept input, parse an initial prefix
46 """Repeatedly issue a prompt, accept input, parse an initial prefix
44 off the received input, and dispatch to action methods, passing them
47 off the received input, and dispatch to action methods, passing them
45 the remainder of the line as argument.
48 the remainder of the line as argument.
46
49
47 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
50 override the same methods from cmd.Cmd to provide prompt toolkit replacement.
48 """
51 """
49 if not self.use_rawinput:
52 if not self.use_rawinput:
50 raise ValueError('Sorry ipdb does not support use_rawinput=False')
53 raise ValueError('Sorry ipdb does not support use_rawinput=False')
51
54
52 self.preloop()
55 self.preloop()
53
56
54 try:
57 try:
55 if intro is not None:
58 if intro is not None:
56 self.intro = intro
59 self.intro = intro
57 if self.intro:
60 if self.intro:
58 self.stdout.write(str(self.intro)+"\n")
61 self.stdout.write(str(self.intro)+"\n")
59 stop = None
62 stop = None
60 while not stop:
63 while not stop:
61 if self.cmdqueue:
64 if self.cmdqueue:
62 line = self.cmdqueue.pop(0)
65 line = self.cmdqueue.pop(0)
63 else:
66 else:
64 self._ptcomp.ipy_completer.namespace = self.curframe_locals
67 self._ptcomp.ipy_completer.namespace = self.curframe_locals
65 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
68 self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals
66 try:
69 try:
67 line = self.pt_cli.run(reset_current_buffer=True).text
70 line = self.pt_cli.run(reset_current_buffer=True).text
68 except EOFError:
71 except EOFError:
69 line = 'EOF'
72 line = 'EOF'
70 line = self.precmd(line)
73 line = self.precmd(line)
71 stop = self.onecmd(line)
74 stop = self.onecmd(line)
72 stop = self.postcmd(stop, line)
75 stop = self.postcmd(stop, line)
73 self.postloop()
76 self.postloop()
74 except Exception:
77 except Exception:
75 raise
78 raise
76
79
77
80
78 def set_trace(frame=None):
81 def set_trace(frame=None):
79 """
82 """
80 Start debugging from `frame`.
83 Start debugging from `frame`.
81
84
82 If frame is not specified, debugging starts from caller's frame.
85 If frame is not specified, debugging starts from caller's frame.
83 """
86 """
84 TerminalPdb().set_trace(frame or sys._getframe().f_back)
87 TerminalPdb().set_trace(frame or sys._getframe().f_back)
85
88
@@ -1,508 +1,512 b''
1 """IPython terminal interface using prompt_toolkit"""
1 """IPython terminal interface using prompt_toolkit"""
2 from __future__ import print_function
2 from __future__ import print_function
3
3
4 import os
4 import os
5 import sys
5 import sys
6 import warnings
6 import warnings
7 from warnings import warn
7 from warnings import warn
8
8
9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
9 from IPython.core.interactiveshell import InteractiveShell, InteractiveShellABC
10 from IPython.utils import io
10 from IPython.utils import io
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input, string_types
11 from IPython.utils.py3compat import PY3, cast_unicode_py2, input, string_types
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, Type, default, Enum, Union
14 from traitlets import Bool, Unicode, Dict, Integer, observe, Instance, Type, default, Enum, Union
15
15
16 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
16 from prompt_toolkit.enums import DEFAULT_BUFFER, EditingMode
17 from prompt_toolkit.filters import (HasFocus, Condition, IsDone)
17 from prompt_toolkit.filters import (HasFocus, Condition, 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, create_output
19 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop, create_prompt_layout, create_output
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.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
22 from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor
23 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
23 from prompt_toolkit.styles import PygmentsStyle, DynamicStyle
24
24
25 from pygments.styles import get_style_by_name, get_all_styles
25 from pygments.styles import get_style_by_name, get_all_styles
26 from pygments.style import Style
26 from pygments.style import Style
27 from pygments.token import Token
27 from pygments.token import Token
28
28
29 from .debugger import TerminalPdb, Pdb
29 from .debugger import TerminalPdb, Pdb
30 from .magics import TerminalMagics
30 from .magics import TerminalMagics
31 from .pt_inputhooks import get_inputhook_name_and_func
31 from .pt_inputhooks import get_inputhook_name_and_func
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
32 from .prompts import Prompts, ClassicPrompts, RichPromptDisplayHook
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
33 from .ptutils import IPythonPTCompleter, IPythonPTLexer
34 from .shortcuts import register_ipython_shortcuts
34 from .shortcuts import register_ipython_shortcuts
35
35
36 DISPLAY_BANNER_DEPRECATED = object()
36 DISPLAY_BANNER_DEPRECATED = object()
37
37
38
38
39 from pygments.style import Style
39 from pygments.style import Style
40
40
41 class _NoStyle(Style): pass
41 class _NoStyle(Style): pass
42
42
43
43
44
44
45 _style_overrides_light_bg = {
45 _style_overrides_light_bg = {
46 Token.Prompt: '#0000ff',
46 Token.Prompt: '#0000ff',
47 Token.PromptNum: '#0000ee bold',
47 Token.PromptNum: '#0000ee bold',
48 Token.OutPrompt: '#cc0000',
48 Token.OutPrompt: '#cc0000',
49 Token.OutPromptNum: '#bb0000 bold',
49 Token.OutPromptNum: '#bb0000 bold',
50 }
50 }
51
51
52 _style_overrides_linux = {
52 _style_overrides_linux = {
53 Token.Prompt: '#00cc00',
53 Token.Prompt: '#00cc00',
54 Token.PromptNum: '#00bb00 bold',
54 Token.PromptNum: '#00bb00 bold',
55 Token.OutPrompt: '#cc0000',
55 Token.OutPrompt: '#cc0000',
56 Token.OutPromptNum: '#bb0000 bold',
56 Token.OutPromptNum: '#bb0000 bold',
57 }
57 }
58
58
59
59
60
60
61 def get_default_editor():
61 def get_default_editor():
62 try:
62 try:
63 ed = os.environ['EDITOR']
63 ed = os.environ['EDITOR']
64 if not PY3:
64 if not PY3:
65 ed = ed.decode()
65 ed = ed.decode()
66 return ed
66 return ed
67 except KeyError:
67 except KeyError:
68 pass
68 pass
69 except UnicodeError:
69 except UnicodeError:
70 warn("$EDITOR environment variable is not pure ASCII. Using platform "
70 warn("$EDITOR environment variable is not pure ASCII. Using platform "
71 "default editor.")
71 "default editor.")
72
72
73 if os.name == 'posix':
73 if os.name == 'posix':
74 return 'vi' # the only one guaranteed to be there!
74 return 'vi' # the only one guaranteed to be there!
75 else:
75 else:
76 return 'notepad' # same in Windows!
76 return 'notepad' # same in Windows!
77
77
78 # conservatively check for tty
78 # conservatively check for tty
79 # overridden streams can result in things like:
79 # overridden streams can result in things like:
80 # - sys.stdin = None
80 # - sys.stdin = None
81 # - no isatty method
81 # - no isatty method
82 for _name in ('stdin', 'stdout', 'stderr'):
82 for _name in ('stdin', 'stdout', 'stderr'):
83 _stream = getattr(sys, _name)
83 _stream = getattr(sys, _name)
84 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
84 if not _stream or not hasattr(_stream, 'isatty') or not _stream.isatty():
85 _is_tty = False
85 _is_tty = False
86 break
86 break
87 else:
87 else:
88 _is_tty = True
88 _is_tty = True
89
89
90
90
91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
91 _use_simple_prompt = ('IPY_TEST_SIMPLE_PROMPT' in os.environ) or (not _is_tty)
92
92
93 class TerminalInteractiveShell(InteractiveShell):
93 class TerminalInteractiveShell(InteractiveShell):
94 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
94 space_for_menu = Integer(6, help='Number of line at the bottom of the screen '
95 'to reserve for the completion menu'
95 'to reserve for the completion menu'
96 ).tag(config=True)
96 ).tag(config=True)
97
97
98 def _space_for_menu_changed(self, old, new):
98 def _space_for_menu_changed(self, old, new):
99 self._update_layout()
99 self._update_layout()
100
100
101 pt_cli = None
101 pt_cli = None
102 debugger_history = None
102 debugger_history = None
103 _pt_app = None
103 _pt_app = None
104
104
105 simple_prompt = Bool(_use_simple_prompt,
105 simple_prompt = Bool(_use_simple_prompt,
106 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
106 help="""Use `raw_input` for the REPL, without completion, multiline input, and prompt colors.
107
107
108 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
108 Useful when controlling IPython as a subprocess, and piping STDIN/OUT/ERR. Known usage are:
109 IPython own testing machinery, and emacs inferior-shell integration through elpy.
109 IPython own testing machinery, and emacs inferior-shell integration through elpy.
110
110
111 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
111 This mode default to `True` if the `IPY_TEST_SIMPLE_PROMPT`
112 environment variable is set, or the current terminal is not a tty.
112 environment variable is set, or the current terminal is not a tty.
113
113
114 """
114 """
115 ).tag(config=True)
115 ).tag(config=True)
116
116
117 @property
117 @property
118 def debugger_cls(self):
118 def debugger_cls(self):
119 return Pdb if self.simple_prompt else TerminalPdb
119 return Pdb if self.simple_prompt else TerminalPdb
120
120
121 confirm_exit = Bool(True,
121 confirm_exit = Bool(True,
122 help="""
122 help="""
123 Set to confirm when you try to exit IPython with an EOF (Control-D
123 Set to confirm when you try to exit IPython with an EOF (Control-D
124 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
124 in Unix, Control-Z/Enter in Windows). By typing 'exit' or 'quit',
125 you can force a direct exit without any confirmation.""",
125 you can force a direct exit without any confirmation.""",
126 ).tag(config=True)
126 ).tag(config=True)
127
127
128 editing_mode = Unicode('emacs',
128 editing_mode = Unicode('emacs',
129 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
129 help="Shortcut style to use at the prompt. 'vi' or 'emacs'.",
130 ).tag(config=True)
130 ).tag(config=True)
131
131
132 mouse_support = Bool(False,
132 mouse_support = Bool(False,
133 help="Enable mouse support in the prompt"
133 help="Enable mouse support in the prompt"
134 ).tag(config=True)
134 ).tag(config=True)
135
135
136 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
136 highlighting_style = Union([Unicode('legacy'), Type(klass=Style)],
137 help="""The name or class of a Pygments style to use for syntax
137 help="""The name or class of a Pygments style to use for syntax
138 highlighting: \n %s""" % ', '.join(get_all_styles())
138 highlighting: \n %s""" % ', '.join(get_all_styles())
139 ).tag(config=True)
139 ).tag(config=True)
140
140
141
141
142 @observe('highlighting_style')
142 @observe('highlighting_style')
143 @observe('colors')
143 @observe('colors')
144 def _highlighting_style_changed(self, change):
144 def _highlighting_style_changed(self, change):
145 self.refresh_style()
145 self.refresh_style()
146
146
147 def refresh_style(self):
147 def refresh_style(self):
148 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
148 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
149
149
150
150
151 highlighting_style_overrides = Dict(
151 highlighting_style_overrides = Dict(
152 help="Override highlighting format for specific tokens"
152 help="Override highlighting format for specific tokens"
153 ).tag(config=True)
153 ).tag(config=True)
154
154
155 true_color = Bool(False,
155 true_color = Bool(False,
156 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
156 help=("Use 24bit colors instead of 256 colors in prompt highlighting. "
157 "If your terminal supports true color, the following command "
157 "If your terminal supports true color, the following command "
158 "should print 'TRUECOLOR' in orange: "
158 "should print 'TRUECOLOR' in orange: "
159 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
159 "printf \"\\x1b[38;2;255;100;0mTRUECOLOR\\x1b[0m\\n\"")
160 ).tag(config=True)
160 ).tag(config=True)
161
161
162 editor = Unicode(get_default_editor(),
162 editor = Unicode(get_default_editor(),
163 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
163 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
164 ).tag(config=True)
164 ).tag(config=True)
165
165
166 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
166 prompts_class = Type(Prompts, help='Class used to generate Prompt token for prompt_toolkit').tag(config=True)
167
167
168 prompts = Instance(Prompts)
168 prompts = Instance(Prompts)
169
169
170 @default('prompts')
170 @default('prompts')
171 def _prompts_default(self):
171 def _prompts_default(self):
172 return self.prompts_class(self)
172 return self.prompts_class(self)
173
173
174 @observe('prompts')
174 @observe('prompts')
175 def _(self, change):
175 def _(self, change):
176 self._update_layout()
176 self._update_layout()
177
177
178 @default('displayhook_class')
178 @default('displayhook_class')
179 def _displayhook_class_default(self):
179 def _displayhook_class_default(self):
180 return RichPromptDisplayHook
180 return RichPromptDisplayHook
181
181
182 term_title = Bool(True,
182 term_title = Bool(True,
183 help="Automatically set the terminal title"
183 help="Automatically set the terminal title"
184 ).tag(config=True)
184 ).tag(config=True)
185
185
186 display_completions = Enum(('column', 'multicolumn','readlinelike'),
186 display_completions = Enum(('column', 'multicolumn','readlinelike'),
187 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
187 help= ( "Options for displaying tab completions, 'column', 'multicolumn', and "
188 "'readlinelike'. These options are for `prompt_toolkit`, see "
188 "'readlinelike'. These options are for `prompt_toolkit`, see "
189 "`prompt_toolkit` documentation for more information."
189 "`prompt_toolkit` documentation for more information."
190 ),
190 ),
191 default_value='multicolumn').tag(config=True)
191 default_value='multicolumn').tag(config=True)
192
192
193 highlight_matching_brackets = Bool(True,
193 highlight_matching_brackets = Bool(True,
194 help="Highlight matching brackets .",
194 help="Highlight matching brackets .",
195 ).tag(config=True)
195 ).tag(config=True)
196
196
197 @observe('term_title')
197 @observe('term_title')
198 def init_term_title(self, change=None):
198 def init_term_title(self, change=None):
199 # Enable or disable the terminal title.
199 # Enable or disable the terminal title.
200 if self.term_title:
200 if self.term_title:
201 toggle_set_term_title(True)
201 toggle_set_term_title(True)
202 set_term_title('IPython: ' + abbrev_cwd())
202 set_term_title('IPython: ' + abbrev_cwd())
203 else:
203 else:
204 toggle_set_term_title(False)
204 toggle_set_term_title(False)
205
205
206 def init_display_formatter(self):
206 def init_display_formatter(self):
207 super(TerminalInteractiveShell, self).init_display_formatter()
207 super(TerminalInteractiveShell, self).init_display_formatter()
208 # terminal only supports plain text
208 # terminal only supports plain text
209 self.display_formatter.active_types = ['text/plain']
209 self.display_formatter.active_types = ['text/plain']
210
210
211 def init_prompt_toolkit_cli(self):
211 def init_prompt_toolkit_cli(self):
212 if self.simple_prompt:
212 if self.simple_prompt:
213 # Fall back to plain non-interactive output for tests.
213 # Fall back to plain non-interactive output for tests.
214 # This is very limited, and only accepts a single line.
214 # This is very limited, and only accepts a single line.
215 def prompt():
215 def prompt():
216 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
216 return cast_unicode_py2(input('In [%d]: ' % self.execution_count))
217 self.prompt_for_code = prompt
217 self.prompt_for_code = prompt
218 return
218 return
219
219
220 # Set up keyboard shortcuts
220 # Set up keyboard shortcuts
221 kbmanager = KeyBindingManager.for_prompt()
221 kbmanager = KeyBindingManager.for_prompt()
222 register_ipython_shortcuts(kbmanager.registry, self)
222 register_ipython_shortcuts(kbmanager.registry, self)
223
223
224 # Pre-populate history from IPython's history database
224 # Pre-populate history from IPython's history database
225 history = InMemoryHistory()
225 history = InMemoryHistory()
226 last_cell = u""
226 last_cell = u""
227 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
227 for __, ___, cell in self.history_manager.get_tail(self.history_load_length,
228 include_latest=True):
228 include_latest=True):
229 # Ignore blank lines and consecutive duplicates
229 # Ignore blank lines and consecutive duplicates
230 cell = cell.rstrip()
230 cell = cell.rstrip()
231 if cell and (cell != last_cell):
231 if cell and (cell != last_cell):
232 history.append(cell)
232 history.append(cell)
233 last_cell = cell
233 last_cell = cell
234
234
235 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
235 self._style = self._make_style_from_name_or_cls(self.highlighting_style)
236 style = DynamicStyle(lambda: self._style)
236 style = DynamicStyle(lambda: self._style)
237
237
238 editing_mode = getattr(EditingMode, self.editing_mode.upper())
238 editing_mode = getattr(EditingMode, self.editing_mode.upper())
239
239
240 def patch_stdout(**kwargs):
241 return self.pt_cli.patch_stdout_context(**kwargs)
242
240 self._pt_app = create_prompt_application(
243 self._pt_app = create_prompt_application(
241 editing_mode=editing_mode,
244 editing_mode=editing_mode,
242 key_bindings_registry=kbmanager.registry,
245 key_bindings_registry=kbmanager.registry,
243 history=history,
246 history=history,
244 completer=IPythonPTCompleter(shell=self),
247 completer=IPythonPTCompleter(shell=self,
248 patch_stdout=patch_stdout),
245 enable_history_search=True,
249 enable_history_search=True,
246 style=style,
250 style=style,
247 mouse_support=self.mouse_support,
251 mouse_support=self.mouse_support,
248 **self._layout_options()
252 **self._layout_options()
249 )
253 )
250 self._eventloop = create_eventloop(self.inputhook)
254 self._eventloop = create_eventloop(self.inputhook)
251 self.pt_cli = CommandLineInterface(
255 self.pt_cli = CommandLineInterface(
252 self._pt_app, eventloop=self._eventloop,
256 self._pt_app, eventloop=self._eventloop,
253 output=create_output(true_color=self.true_color))
257 output=create_output(true_color=self.true_color))
254
258
255 def _make_style_from_name_or_cls(self, name_or_cls):
259 def _make_style_from_name_or_cls(self, name_or_cls):
256 """
260 """
257 Small wrapper that make an IPython compatible style from a style name
261 Small wrapper that make an IPython compatible style from a style name
258
262
259 We need that to add style for prompt ... etc.
263 We need that to add style for prompt ... etc.
260 """
264 """
261 style_overrides = {}
265 style_overrides = {}
262 if name_or_cls == 'legacy':
266 if name_or_cls == 'legacy':
263 legacy = self.colors.lower()
267 legacy = self.colors.lower()
264 if legacy == 'linux':
268 if legacy == 'linux':
265 style_cls = get_style_by_name('monokai')
269 style_cls = get_style_by_name('monokai')
266 style_overrides = _style_overrides_linux
270 style_overrides = _style_overrides_linux
267 elif legacy == 'lightbg':
271 elif legacy == 'lightbg':
268 style_overrides = _style_overrides_light_bg
272 style_overrides = _style_overrides_light_bg
269 style_cls = get_style_by_name('pastie')
273 style_cls = get_style_by_name('pastie')
270 elif legacy == 'neutral':
274 elif legacy == 'neutral':
271 # The default theme needs to be visible on both a dark background
275 # The default theme needs to be visible on both a dark background
272 # and a light background, because we can't tell what the terminal
276 # and a light background, because we can't tell what the terminal
273 # looks like. These tweaks to the default theme help with that.
277 # looks like. These tweaks to the default theme help with that.
274 style_cls = get_style_by_name('default')
278 style_cls = get_style_by_name('default')
275 style_overrides.update({
279 style_overrides.update({
276 Token.Number: '#007700',
280 Token.Number: '#007700',
277 Token.Operator: 'noinherit',
281 Token.Operator: 'noinherit',
278 Token.String: '#BB6622',
282 Token.String: '#BB6622',
279 Token.Name.Function: '#2080D0',
283 Token.Name.Function: '#2080D0',
280 Token.Name.Class: 'bold #2080D0',
284 Token.Name.Class: 'bold #2080D0',
281 Token.Name.Namespace: 'bold #2080D0',
285 Token.Name.Namespace: 'bold #2080D0',
282 Token.Prompt: '#009900',
286 Token.Prompt: '#009900',
283 Token.PromptNum: '#00ff00 bold',
287 Token.PromptNum: '#00ff00 bold',
284 Token.OutPrompt: '#990000',
288 Token.OutPrompt: '#990000',
285 Token.OutPromptNum: '#ff0000 bold',
289 Token.OutPromptNum: '#ff0000 bold',
286 })
290 })
287 elif legacy =='nocolor':
291 elif legacy =='nocolor':
288 style_cls=_NoStyle
292 style_cls=_NoStyle
289 style_overrides = {}
293 style_overrides = {}
290 else :
294 else :
291 raise ValueError('Got unknown colors: ', legacy)
295 raise ValueError('Got unknown colors: ', legacy)
292 else :
296 else :
293 if isinstance(name_or_cls, string_types):
297 if isinstance(name_or_cls, string_types):
294 style_cls = get_style_by_name(name_or_cls)
298 style_cls = get_style_by_name(name_or_cls)
295 else:
299 else:
296 style_cls = name_or_cls
300 style_cls = name_or_cls
297 style_overrides = {
301 style_overrides = {
298 Token.Prompt: '#009900',
302 Token.Prompt: '#009900',
299 Token.PromptNum: '#00ff00 bold',
303 Token.PromptNum: '#00ff00 bold',
300 Token.OutPrompt: '#990000',
304 Token.OutPrompt: '#990000',
301 Token.OutPromptNum: '#ff0000 bold',
305 Token.OutPromptNum: '#ff0000 bold',
302 }
306 }
303 style_overrides.update(self.highlighting_style_overrides)
307 style_overrides.update(self.highlighting_style_overrides)
304 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
308 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
305 style_dict=style_overrides)
309 style_dict=style_overrides)
306
310
307 return style
311 return style
308
312
309 def _layout_options(self):
313 def _layout_options(self):
310 """
314 """
311 Return the current layout option for the current Terminal InteractiveShell
315 Return the current layout option for the current Terminal InteractiveShell
312 """
316 """
313 return {
317 return {
314 'lexer':IPythonPTLexer(),
318 'lexer':IPythonPTLexer(),
315 'reserve_space_for_menu':self.space_for_menu,
319 'reserve_space_for_menu':self.space_for_menu,
316 'get_prompt_tokens':self.prompts.in_prompt_tokens,
320 'get_prompt_tokens':self.prompts.in_prompt_tokens,
317 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
321 'get_continuation_tokens':self.prompts.continuation_prompt_tokens,
318 'multiline':True,
322 'multiline':True,
319 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
323 'display_completions_in_columns': (self.display_completions == 'multicolumn'),
320
324
321 # Highlight matching brackets, but only when this setting is
325 # Highlight matching brackets, but only when this setting is
322 # enabled, and only when the DEFAULT_BUFFER has the focus.
326 # enabled, and only when the DEFAULT_BUFFER has the focus.
323 'extra_input_processors': [ConditionalProcessor(
327 'extra_input_processors': [ConditionalProcessor(
324 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
328 processor=HighlightMatchingBracketProcessor(chars='[](){}'),
325 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
329 filter=HasFocus(DEFAULT_BUFFER) & ~IsDone() &
326 Condition(lambda cli: self.highlight_matching_brackets))],
330 Condition(lambda cli: self.highlight_matching_brackets))],
327 }
331 }
328
332
329 def _update_layout(self):
333 def _update_layout(self):
330 """
334 """
331 Ask for a re computation of the application layout, if for example ,
335 Ask for a re computation of the application layout, if for example ,
332 some configuration options have changed.
336 some configuration options have changed.
333 """
337 """
334 if self._pt_app:
338 if self._pt_app:
335 self._pt_app.layout = create_prompt_layout(**self._layout_options())
339 self._pt_app.layout = create_prompt_layout(**self._layout_options())
336
340
337 def prompt_for_code(self):
341 def prompt_for_code(self):
338 document = self.pt_cli.run(
342 document = self.pt_cli.run(
339 pre_run=self.pre_prompt, reset_current_buffer=True)
343 pre_run=self.pre_prompt, reset_current_buffer=True)
340 return document.text
344 return document.text
341
345
342 def enable_win_unicode_console(self):
346 def enable_win_unicode_console(self):
343 if sys.version_info >= (3, 6):
347 if sys.version_info >= (3, 6):
344 # Since PEP 528, Python uses the unicode APIs for the Windows
348 # Since PEP 528, Python uses the unicode APIs for the Windows
345 # console by default, so WUC shouldn't be needed.
349 # console by default, so WUC shouldn't be needed.
346 return
350 return
347
351
348 import win_unicode_console
352 import win_unicode_console
349
353
350 if PY3:
354 if PY3:
351 win_unicode_console.enable()
355 win_unicode_console.enable()
352 else:
356 else:
353 # https://github.com/ipython/ipython/issues/9768
357 # https://github.com/ipython/ipython/issues/9768
354 from win_unicode_console.streams import (TextStreamWrapper,
358 from win_unicode_console.streams import (TextStreamWrapper,
355 stdout_text_transcoded, stderr_text_transcoded)
359 stdout_text_transcoded, stderr_text_transcoded)
356
360
357 class LenientStrStreamWrapper(TextStreamWrapper):
361 class LenientStrStreamWrapper(TextStreamWrapper):
358 def write(self, s):
362 def write(self, s):
359 if isinstance(s, bytes):
363 if isinstance(s, bytes):
360 s = s.decode(self.encoding, 'replace')
364 s = s.decode(self.encoding, 'replace')
361
365
362 self.base.write(s)
366 self.base.write(s)
363
367
364 stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded)
368 stdout_text_str = LenientStrStreamWrapper(stdout_text_transcoded)
365 stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded)
369 stderr_text_str = LenientStrStreamWrapper(stderr_text_transcoded)
366
370
367 win_unicode_console.enable(stdout=stdout_text_str,
371 win_unicode_console.enable(stdout=stdout_text_str,
368 stderr=stderr_text_str)
372 stderr=stderr_text_str)
369
373
370 def init_io(self):
374 def init_io(self):
371 if sys.platform not in {'win32', 'cli'}:
375 if sys.platform not in {'win32', 'cli'}:
372 return
376 return
373
377
374 self.enable_win_unicode_console()
378 self.enable_win_unicode_console()
375
379
376 import colorama
380 import colorama
377 colorama.init()
381 colorama.init()
378
382
379 # For some reason we make these wrappers around stdout/stderr.
383 # For some reason we make these wrappers around stdout/stderr.
380 # For now, we need to reset them so all output gets coloured.
384 # For now, we need to reset them so all output gets coloured.
381 # https://github.com/ipython/ipython/issues/8669
385 # https://github.com/ipython/ipython/issues/8669
382 # io.std* are deprecated, but don't show our own deprecation warnings
386 # io.std* are deprecated, but don't show our own deprecation warnings
383 # during initialization of the deprecated API.
387 # during initialization of the deprecated API.
384 with warnings.catch_warnings():
388 with warnings.catch_warnings():
385 warnings.simplefilter('ignore', DeprecationWarning)
389 warnings.simplefilter('ignore', DeprecationWarning)
386 io.stdout = io.IOStream(sys.stdout)
390 io.stdout = io.IOStream(sys.stdout)
387 io.stderr = io.IOStream(sys.stderr)
391 io.stderr = io.IOStream(sys.stderr)
388
392
389 def init_magics(self):
393 def init_magics(self):
390 super(TerminalInteractiveShell, self).init_magics()
394 super(TerminalInteractiveShell, self).init_magics()
391 self.register_magics(TerminalMagics)
395 self.register_magics(TerminalMagics)
392
396
393 def init_alias(self):
397 def init_alias(self):
394 # The parent class defines aliases that can be safely used with any
398 # The parent class defines aliases that can be safely used with any
395 # frontend.
399 # frontend.
396 super(TerminalInteractiveShell, self).init_alias()
400 super(TerminalInteractiveShell, self).init_alias()
397
401
398 # Now define aliases that only make sense on the terminal, because they
402 # Now define aliases that only make sense on the terminal, because they
399 # need direct access to the console in a way that we can't emulate in
403 # need direct access to the console in a way that we can't emulate in
400 # GUI or web frontend
404 # GUI or web frontend
401 if os.name == 'posix':
405 if os.name == 'posix':
402 for cmd in ['clear', 'more', 'less', 'man']:
406 for cmd in ['clear', 'more', 'less', 'man']:
403 self.alias_manager.soft_define_alias(cmd, cmd)
407 self.alias_manager.soft_define_alias(cmd, cmd)
404
408
405
409
406 def __init__(self, *args, **kwargs):
410 def __init__(self, *args, **kwargs):
407 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
411 super(TerminalInteractiveShell, self).__init__(*args, **kwargs)
408 self.init_prompt_toolkit_cli()
412 self.init_prompt_toolkit_cli()
409 self.init_term_title()
413 self.init_term_title()
410 self.keep_running = True
414 self.keep_running = True
411
415
412 self.debugger_history = InMemoryHistory()
416 self.debugger_history = InMemoryHistory()
413
417
414 def ask_exit(self):
418 def ask_exit(self):
415 self.keep_running = False
419 self.keep_running = False
416
420
417 rl_next_input = None
421 rl_next_input = None
418
422
419 def pre_prompt(self):
423 def pre_prompt(self):
420 if self.rl_next_input:
424 if self.rl_next_input:
421 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
425 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
422 self.rl_next_input = None
426 self.rl_next_input = None
423
427
424 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
428 def interact(self, display_banner=DISPLAY_BANNER_DEPRECATED):
425
429
426 if display_banner is not DISPLAY_BANNER_DEPRECATED:
430 if display_banner is not DISPLAY_BANNER_DEPRECATED:
427 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
431 warn('interact `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
428
432
429 self.keep_running = True
433 self.keep_running = True
430 while self.keep_running:
434 while self.keep_running:
431 print(self.separate_in, end='')
435 print(self.separate_in, end='')
432
436
433 try:
437 try:
434 code = self.prompt_for_code()
438 code = self.prompt_for_code()
435 except EOFError:
439 except EOFError:
436 if (not self.confirm_exit) \
440 if (not self.confirm_exit) \
437 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
441 or self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
438 self.ask_exit()
442 self.ask_exit()
439
443
440 else:
444 else:
441 if code:
445 if code:
442 self.run_cell(code, store_history=True)
446 self.run_cell(code, store_history=True)
443
447
444 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
448 def mainloop(self, display_banner=DISPLAY_BANNER_DEPRECATED):
445 # An extra layer of protection in case someone mashing Ctrl-C breaks
449 # An extra layer of protection in case someone mashing Ctrl-C breaks
446 # out of our internal code.
450 # out of our internal code.
447 if display_banner is not DISPLAY_BANNER_DEPRECATED:
451 if display_banner is not DISPLAY_BANNER_DEPRECATED:
448 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
452 warn('mainloop `display_banner` argument is deprecated since IPython 5.0. Call `show_banner()` if needed.', DeprecationWarning, stacklevel=2)
449 while True:
453 while True:
450 try:
454 try:
451 self.interact()
455 self.interact()
452 break
456 break
453 except KeyboardInterrupt as e:
457 except KeyboardInterrupt as e:
454 print("\n%s escaped interact()\n" % type(e).__name__)
458 print("\n%s escaped interact()\n" % type(e).__name__)
455 finally:
459 finally:
456 # An interrupt during the eventloop will mess up the
460 # An interrupt during the eventloop will mess up the
457 # internal state of the prompt_toolkit library.
461 # internal state of the prompt_toolkit library.
458 # Stopping the eventloop fixes this, see
462 # Stopping the eventloop fixes this, see
459 # https://github.com/ipython/ipython/pull/9867
463 # https://github.com/ipython/ipython/pull/9867
460 if hasattr(self, '_eventloop'):
464 if hasattr(self, '_eventloop'):
461 self._eventloop.stop()
465 self._eventloop.stop()
462
466
463 _inputhook = None
467 _inputhook = None
464 def inputhook(self, context):
468 def inputhook(self, context):
465 if self._inputhook is not None:
469 if self._inputhook is not None:
466 self._inputhook(context)
470 self._inputhook(context)
467
471
468 active_eventloop = None
472 active_eventloop = None
469 def enable_gui(self, gui=None):
473 def enable_gui(self, gui=None):
470 if gui:
474 if gui:
471 self.active_eventloop, self._inputhook =\
475 self.active_eventloop, self._inputhook =\
472 get_inputhook_name_and_func(gui)
476 get_inputhook_name_and_func(gui)
473 else:
477 else:
474 self.active_eventloop = self._inputhook = None
478 self.active_eventloop = self._inputhook = None
475
479
476 # Run !system commands directly, not through pipes, so terminal programs
480 # Run !system commands directly, not through pipes, so terminal programs
477 # work correctly.
481 # work correctly.
478 system = InteractiveShell.system_raw
482 system = InteractiveShell.system_raw
479
483
480 def auto_rewrite_input(self, cmd):
484 def auto_rewrite_input(self, cmd):
481 """Overridden from the parent class to use fancy rewriting prompt"""
485 """Overridden from the parent class to use fancy rewriting prompt"""
482 if not self.show_rewritten_input:
486 if not self.show_rewritten_input:
483 return
487 return
484
488
485 tokens = self.prompts.rewrite_prompt_tokens()
489 tokens = self.prompts.rewrite_prompt_tokens()
486 if self.pt_cli:
490 if self.pt_cli:
487 self.pt_cli.print_tokens(tokens)
491 self.pt_cli.print_tokens(tokens)
488 print(cmd)
492 print(cmd)
489 else:
493 else:
490 prompt = ''.join(s for t, s in tokens)
494 prompt = ''.join(s for t, s in tokens)
491 print(prompt, cmd, sep='')
495 print(prompt, cmd, sep='')
492
496
493 _prompts_before = None
497 _prompts_before = None
494 def switch_doctest_mode(self, mode):
498 def switch_doctest_mode(self, mode):
495 """Switch prompts to classic for %doctest_mode"""
499 """Switch prompts to classic for %doctest_mode"""
496 if mode:
500 if mode:
497 self._prompts_before = self.prompts
501 self._prompts_before = self.prompts
498 self.prompts = ClassicPrompts(self)
502 self.prompts = ClassicPrompts(self)
499 elif self._prompts_before:
503 elif self._prompts_before:
500 self.prompts = self._prompts_before
504 self.prompts = self._prompts_before
501 self._prompts_before = None
505 self._prompts_before = None
502 self._update_layout()
506 self._update_layout()
503
507
504
508
505 InteractiveShellABC.register(TerminalInteractiveShell)
509 InteractiveShellABC.register(TerminalInteractiveShell)
506
510
507 if __name__ == '__main__':
511 if __name__ == '__main__':
508 TerminalInteractiveShell.instance().interact()
512 TerminalInteractiveShell.instance().interact()
@@ -1,112 +1,115 b''
1 """prompt-toolkit utilities
1 """prompt-toolkit utilities
2
2
3 Everything in this module is a private API,
3 Everything in this module is a private API,
4 not to be used outside IPython.
4 not to be used outside IPython.
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 import unicodedata
10 import unicodedata
11 from wcwidth import wcwidth
11 from wcwidth import wcwidth
12
12
13 from IPython.utils.py3compat import PY3
13 from IPython.utils.py3compat import PY3
14
14
15 from IPython.core.completer import IPCompleter
15 from IPython.core.completer import IPCompleter
16 from prompt_toolkit.completion import Completer, Completion
16 from prompt_toolkit.completion import Completer, Completion
17 from prompt_toolkit.layout.lexers import Lexer
17 from prompt_toolkit.layout.lexers import Lexer
18 from prompt_toolkit.layout.lexers import PygmentsLexer
18 from prompt_toolkit.layout.lexers import PygmentsLexer
19
19
20 import pygments.lexers as pygments_lexers
20 import pygments.lexers as pygments_lexers
21
21
22
22
23 class IPythonPTCompleter(Completer):
23 class IPythonPTCompleter(Completer):
24 """Adaptor to provide IPython completions to prompt_toolkit"""
24 """Adaptor to provide IPython completions to prompt_toolkit"""
25 def __init__(self, ipy_completer=None, shell=None):
25 def __init__(self, ipy_completer=None, shell=None, patch_stdout=None):
26 if shell is None and ipy_completer is None:
26 if shell is None and ipy_completer is None:
27 raise TypeError("Please pass shell=an InteractiveShell instance.")
27 raise TypeError("Please pass shell=an InteractiveShell instance.")
28 self._ipy_completer = ipy_completer
28 self._ipy_completer = ipy_completer
29 self.shell = shell
29 self.shell = shell
30 if patch_stdout is None:
31 raise TypeError("Please pass patch_stdout")
32 self.patch_stdout = patch_stdout
30
33
31 @property
34 @property
32 def ipy_completer(self):
35 def ipy_completer(self):
33 if self._ipy_completer:
36 if self._ipy_completer:
34 return self._ipy_completer
37 return self._ipy_completer
35 else:
38 else:
36 return self.shell.Completer
39 return self.shell.Completer
37
40
38 def get_completions(self, document, complete_event):
41 def get_completions(self, document, complete_event):
39 if not document.current_line.strip():
42 if not document.current_line.strip():
40 return
43 return
41
44
42 # Some bits of our completion system may print stuff (e.g. if a module
45 # Some bits of our completion system may print stuff (e.g. if a module
43 # is imported). This context manager ensures that doesn't interfere with
46 # is imported). This context manager ensures that doesn't interfere with
44 # the prompt.
47 # the prompt.
45 with self.shell.pt_cli.patch_stdout_context():
48 with self.patch_stdout():
46 used, matches = self.ipy_completer.complete(
49 used, matches = self.ipy_completer.complete(
47 line_buffer=document.current_line,
50 line_buffer=document.current_line,
48 cursor_pos=document.cursor_position_col
51 cursor_pos=document.cursor_position_col
49 )
52 )
50 start_pos = -len(used)
53 start_pos = -len(used)
51 for m in matches:
54 for m in matches:
52 if not m:
55 if not m:
53 # Guard against completion machinery giving us an empty string.
56 # Guard against completion machinery giving us an empty string.
54 continue
57 continue
55
58
56 m = unicodedata.normalize('NFC', m)
59 m = unicodedata.normalize('NFC', m)
57
60
58 # When the first character of the completion has a zero length,
61 # When the first character of the completion has a zero length,
59 # then it's probably a decomposed unicode character. E.g. caused by
62 # then it's probably a decomposed unicode character. E.g. caused by
60 # the "\dot" completion. Try to compose again with the previous
63 # the "\dot" completion. Try to compose again with the previous
61 # character.
64 # character.
62 if wcwidth(m[0]) == 0:
65 if wcwidth(m[0]) == 0:
63 if document.cursor_position + start_pos > 0:
66 if document.cursor_position + start_pos > 0:
64 char_before = document.text[document.cursor_position + start_pos - 1]
67 char_before = document.text[document.cursor_position + start_pos - 1]
65 m = unicodedata.normalize('NFC', char_before + m)
68 m = unicodedata.normalize('NFC', char_before + m)
66
69
67 # Yield the modified completion instead, if this worked.
70 # Yield the modified completion instead, if this worked.
68 if wcwidth(m[0:1]) == 1:
71 if wcwidth(m[0:1]) == 1:
69 yield Completion(m, start_position=start_pos - 1)
72 yield Completion(m, start_position=start_pos - 1)
70 continue
73 continue
71
74
72 # TODO: Use Jedi to determine meta_text
75 # TODO: Use Jedi to determine meta_text
73 # (Jedi currently has a bug that results in incorrect information.)
76 # (Jedi currently has a bug that results in incorrect information.)
74 # meta_text = ''
77 # meta_text = ''
75 # yield Completion(m, start_position=start_pos,
78 # yield Completion(m, start_position=start_pos,
76 # display_meta=meta_text)
79 # display_meta=meta_text)
77 yield Completion(m, start_position=start_pos)
80 yield Completion(m, start_position=start_pos)
78
81
79 class IPythonPTLexer(Lexer):
82 class IPythonPTLexer(Lexer):
80 """
83 """
81 Wrapper around PythonLexer and BashLexer.
84 Wrapper around PythonLexer and BashLexer.
82 """
85 """
83 def __init__(self):
86 def __init__(self):
84 l = pygments_lexers
87 l = pygments_lexers
85 self.python_lexer = PygmentsLexer(l.Python3Lexer if PY3 else l.PythonLexer)
88 self.python_lexer = PygmentsLexer(l.Python3Lexer if PY3 else l.PythonLexer)
86 self.shell_lexer = PygmentsLexer(l.BashLexer)
89 self.shell_lexer = PygmentsLexer(l.BashLexer)
87
90
88 self.magic_lexers = {
91 self.magic_lexers = {
89 'HTML': PygmentsLexer(l.HtmlLexer),
92 'HTML': PygmentsLexer(l.HtmlLexer),
90 'html': PygmentsLexer(l.HtmlLexer),
93 'html': PygmentsLexer(l.HtmlLexer),
91 'javascript': PygmentsLexer(l.JavascriptLexer),
94 'javascript': PygmentsLexer(l.JavascriptLexer),
92 'js': PygmentsLexer(l.JavascriptLexer),
95 'js': PygmentsLexer(l.JavascriptLexer),
93 'perl': PygmentsLexer(l.PerlLexer),
96 'perl': PygmentsLexer(l.PerlLexer),
94 'ruby': PygmentsLexer(l.RubyLexer),
97 'ruby': PygmentsLexer(l.RubyLexer),
95 'latex': PygmentsLexer(l.TexLexer),
98 'latex': PygmentsLexer(l.TexLexer),
96 }
99 }
97
100
98 def lex_document(self, cli, document):
101 def lex_document(self, cli, document):
99 text = document.text.lstrip()
102 text = document.text.lstrip()
100
103
101 lexer = self.python_lexer
104 lexer = self.python_lexer
102
105
103 if text.startswith('!') or text.startswith('%%bash'):
106 if text.startswith('!') or text.startswith('%%bash'):
104 lexer = self.shell_lexer
107 lexer = self.shell_lexer
105
108
106 elif text.startswith('%%'):
109 elif text.startswith('%%'):
107 for magic, l in self.magic_lexers.items():
110 for magic, l in self.magic_lexers.items():
108 if text.startswith('%%' + magic):
111 if text.startswith('%%' + magic):
109 lexer = l
112 lexer = l
110 break
113 break
111
114
112 return lexer.lex_document(cli, document)
115 return lexer.lex_document(cli, document)
General Comments 0
You need to be logged in to leave comments. Login now