Show More
@@ -0,0 +1,73 b'' | |||
|
1 | from IPython.core.debugger import Pdb | |
|
2 | ||
|
3 | from IPython.core.completer import IPCompleter | |
|
4 | from .ptutils import IPythonPTCompleter | |
|
5 | ||
|
6 | from prompt_toolkit.token import Token | |
|
7 | from prompt_toolkit.shortcuts import create_prompt_application | |
|
8 | from prompt_toolkit.interface import CommandLineInterface | |
|
9 | from prompt_toolkit.enums import EditingMode | |
|
10 | ||
|
11 | class TerminalPdb(Pdb): | |
|
12 | def __init__(self, *args, **kwargs): | |
|
13 | Pdb.__init__(self, *args, **kwargs) | |
|
14 | self._ptcomp = None | |
|
15 | self.pt_init() | |
|
16 | ||
|
17 | def pt_init(self): | |
|
18 | def get_prompt_tokens(cli): | |
|
19 | return [(Token.Prompt, self.prompt)] | |
|
20 | ||
|
21 | if self._ptcomp is None: | |
|
22 | compl = IPCompleter(shell=self.shell, | |
|
23 | namespace={}, | |
|
24 | global_namespace={}, | |
|
25 | use_readline=False, | |
|
26 | parent=self.shell, | |
|
27 | ) | |
|
28 | self._ptcomp = IPythonPTCompleter(compl) | |
|
29 | ||
|
30 | self._pt_app = create_prompt_application( | |
|
31 | editing_mode=getattr(EditingMode, self.shell.editing_mode.upper()), | |
|
32 | history=self.shell.debugger_history, | |
|
33 | completer= self._ptcomp, | |
|
34 | enable_history_search=True, | |
|
35 | mouse_support=self.shell.mouse_support, | |
|
36 | get_prompt_tokens=get_prompt_tokens | |
|
37 | ) | |
|
38 | self.pt_cli = CommandLineInterface(self._pt_app, eventloop=self.shell._eventloop) | |
|
39 | ||
|
40 | def cmdloop(self, intro=None): | |
|
41 | """Repeatedly issue a prompt, accept input, parse an initial prefix | |
|
42 | off the received input, and dispatch to action methods, passing them | |
|
43 | the remainder of the line as argument. | |
|
44 | ||
|
45 | override the same methods from cmd.Cmd to provide prompt toolkit replacement. | |
|
46 | """ | |
|
47 | if not self.use_rawinput: | |
|
48 | raise ValueError('Sorry ipdb does not support use_rawinput=False') | |
|
49 | ||
|
50 | self.preloop() | |
|
51 | ||
|
52 | try: | |
|
53 | if intro is not None: | |
|
54 | self.intro = intro | |
|
55 | if self.intro: | |
|
56 | self.stdout.write(str(self.intro)+"\n") | |
|
57 | stop = None | |
|
58 | while not stop: | |
|
59 | if self.cmdqueue: | |
|
60 | line = self.cmdqueue.pop(0) | |
|
61 | else: | |
|
62 | self._ptcomp.ipy_completer.namespace = self.curframe_locals | |
|
63 | self._ptcomp.ipy_completer.global_namespace = self.curframe.f_globals | |
|
64 | try: | |
|
65 | line = self.pt_cli.run(reset_current_buffer=True).text | |
|
66 | except EOFError: | |
|
67 | line = 'EOF' | |
|
68 | line = self.precmd(line) | |
|
69 | stop = self.onecmd(line) | |
|
70 | stop = self.postcmd(stop, line) | |
|
71 | self.postloop() | |
|
72 | except Exception: | |
|
73 | raise |
@@ -0,0 +1,62 b'' | |||
|
1 | import unicodedata | |
|
2 | from wcwidth import wcwidth | |
|
3 | ||
|
4 | from IPython.utils.py3compat import PY3 | |
|
5 | ||
|
6 | from prompt_toolkit.completion import Completer, Completion | |
|
7 | from prompt_toolkit.layout.lexers import Lexer | |
|
8 | from prompt_toolkit.layout.lexers import PygmentsLexer | |
|
9 | ||
|
10 | from pygments.lexers import Python3Lexer, BashLexer, PythonLexer | |
|
11 | ||
|
12 | class IPythonPTCompleter(Completer): | |
|
13 | """Adaptor to provide IPython completions to prompt_toolkit""" | |
|
14 | def __init__(self, ipy_completer): | |
|
15 | self.ipy_completer = ipy_completer | |
|
16 | ||
|
17 | def get_completions(self, document, complete_event): | |
|
18 | if not document.current_line.strip(): | |
|
19 | return | |
|
20 | ||
|
21 | used, matches = self.ipy_completer.complete( | |
|
22 | line_buffer=document.current_line, | |
|
23 | cursor_pos=document.cursor_position_col | |
|
24 | ) | |
|
25 | start_pos = -len(used) | |
|
26 | for m in matches: | |
|
27 | m = unicodedata.normalize('NFC', m) | |
|
28 | ||
|
29 | # When the first character of the completion has a zero length, | |
|
30 | # then it's probably a decomposed unicode character. E.g. caused by | |
|
31 | # the "\dot" completion. Try to compose again with the previous | |
|
32 | # character. | |
|
33 | if wcwidth(m[0]) == 0: | |
|
34 | if document.cursor_position + start_pos > 0: | |
|
35 | char_before = document.text[document.cursor_position + start_pos - 1] | |
|
36 | m = unicodedata.normalize('NFC', char_before + m) | |
|
37 | ||
|
38 | # Yield the modified completion instead, if this worked. | |
|
39 | if wcwidth(m[0:1]) == 1: | |
|
40 | yield Completion(m, start_position=start_pos - 1) | |
|
41 | continue | |
|
42 | ||
|
43 | # TODO: Use Jedi to determine meta_text | |
|
44 | # (Jedi currently has a bug that results in incorrect information.) | |
|
45 | # meta_text = '' | |
|
46 | # yield Completion(m, start_position=start_pos, | |
|
47 | # display_meta=meta_text) | |
|
48 | yield Completion(m, start_position=start_pos) | |
|
49 | ||
|
50 | class IPythonPTLexer(Lexer): | |
|
51 | """ | |
|
52 | Wrapper around PythonLexer and BashLexer. | |
|
53 | """ | |
|
54 | def __init__(self): | |
|
55 | self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer) | |
|
56 | self.shell_lexer = PygmentsLexer(BashLexer) | |
|
57 | ||
|
58 | def lex_document(self, cli, document): | |
|
59 | if document.text.startswith('!'): | |
|
60 | return self.shell_lexer.lex_document(cli, document) | |
|
61 | else: | |
|
62 | return self.python_lexer.lex_document(cli, document) |
@@ -652,6 +652,9 b' class IPCompleter(Completer):' | |||
|
652 | 652 | self.dict_key_matches, |
|
653 | 653 | ] |
|
654 | 654 | |
|
655 | # This is set externally by InteractiveShell | |
|
656 | self.custom_completers = None | |
|
657 | ||
|
655 | 658 | def all_completions(self, text): |
|
656 | 659 | """ |
|
657 | 660 | Wrapper around the complete method for the benefit of emacs. |
@@ -1072,6 +1075,9 b' class IPCompleter(Completer):' | |||
|
1072 | 1075 | return u'', [] |
|
1073 | 1076 | |
|
1074 | 1077 | def dispatch_custom_completer(self, text): |
|
1078 | if not self.custom_completers: | |
|
1079 | return | |
|
1080 | ||
|
1075 | 1081 | line = self.line_buffer |
|
1076 | 1082 | if not line.strip(): |
|
1077 | 1083 | return None |
@@ -37,6 +37,7 b' from IPython.utils import coloransi, py3compat' | |||
|
37 | 37 | from IPython.core.excolors import exception_colors |
|
38 | 38 | from IPython.testing.skipdoctest import skip_doctest |
|
39 | 39 | |
|
40 | ||
|
40 | 41 | prompt = 'ipdb> ' |
|
41 | 42 | |
|
42 | 43 | #We have to check this directly from sys.argv, config struct not yet available |
@@ -120,12 +121,6 b' class Tracer(object):' | |||
|
120 | 121 | sys.excepthook = functools.partial(BdbQuit_excepthook, |
|
121 | 122 | excepthook=sys.excepthook) |
|
122 | 123 | def_colors = 'NoColor' |
|
123 | try: | |
|
124 | # Limited tab completion support | |
|
125 | import readline | |
|
126 | readline.parse_and_bind('tab: complete') | |
|
127 | except ImportError: | |
|
128 | pass | |
|
129 | 124 | else: |
|
130 | 125 | # In ipython, we use its custom exception handler mechanism |
|
131 | 126 | def_colors = ip.colors |
@@ -205,7 +200,7 b' class Pdb(OldPdb, object):' | |||
|
205 | 200 | except (TypeError, ValueError): |
|
206 | 201 | raise ValueError("Context must be a positive integer") |
|
207 | 202 | |
|
208 | OldPdb.__init__(self,completekey,stdin,stdout) | |
|
203 | OldPdb.__init__(self, completekey, stdin, stdout) | |
|
209 | 204 | |
|
210 | 205 | # IPython changes... |
|
211 | 206 | self.shell = get_ipython() |
@@ -245,31 +240,17 b' class Pdb(OldPdb, object):' | |||
|
245 | 240 | self.parser = PyColorize.Parser() |
|
246 | 241 | |
|
247 | 242 | # Set the prompt - the default prompt is '(Pdb)' |
|
248 | Colors = cst.active_colors | |
|
249 | if color_scheme == 'NoColor': | |
|
250 | self.prompt = prompt | |
|
251 | else: | |
|
252 | # The colour markers are wrapped by bytes 01 and 02 so that readline | |
|
253 | # can calculate the width. | |
|
254 | self.prompt = u'\x01%s\x02%s\x01%s\x02' % (Colors.prompt, prompt, Colors.Normal) | |
|
243 | self.prompt = prompt | |
|
255 | 244 | |
|
256 | 245 | def set_colors(self, scheme): |
|
257 | 246 | """Shorthand access to the color table scheme selector method.""" |
|
258 | 247 | self.color_scheme_table.set_active_scheme(scheme) |
|
259 | 248 | |
|
260 | 249 | def interaction(self, frame, traceback): |
|
261 | self.shell.set_completer_frame(frame) | |
|
262 | while True: | |
|
263 | try: | |
|
264 | OldPdb.interaction(self, frame, traceback) | |
|
265 | break | |
|
266 | except KeyboardInterrupt: | |
|
267 | self.shell.write('\n' + self.shell.get_exception_only()) | |
|
268 | break | |
|
269 | finally: | |
|
270 | # Pdb sets readline delimiters, so set them back to our own | |
|
271 | if self.shell.readline is not None: | |
|
272 | self.shell.readline.set_completer_delims(self.shell.readline_delims) | |
|
250 | try: | |
|
251 | OldPdb.interaction(self, frame, traceback) | |
|
252 | except KeyboardInterrupt: | |
|
253 | sys.stdout.write('\n' + self.shell.get_exception_only()) | |
|
273 | 254 | |
|
274 | 255 | def parseline(self, line): |
|
275 | 256 | if line.startswith("!!"): |
@@ -284,18 +265,15 b' class Pdb(OldPdb, object):' | |||
|
284 | 265 | |
|
285 | 266 | def new_do_up(self, arg): |
|
286 | 267 | OldPdb.do_up(self, arg) |
|
287 | self.shell.set_completer_frame(self.curframe) | |
|
288 | 268 | do_u = do_up = decorate_fn_with_doc(new_do_up, OldPdb.do_up) |
|
289 | 269 | |
|
290 | 270 | def new_do_down(self, arg): |
|
291 | 271 | OldPdb.do_down(self, arg) |
|
292 | self.shell.set_completer_frame(self.curframe) | |
|
293 | 272 | |
|
294 | 273 | do_d = do_down = decorate_fn_with_doc(new_do_down, OldPdb.do_down) |
|
295 | 274 | |
|
296 | 275 | def new_do_frame(self, arg): |
|
297 | 276 | OldPdb.do_frame(self, arg) |
|
298 | self.shell.set_completer_frame(self.curframe) | |
|
299 | 277 | |
|
300 | 278 | def new_do_quit(self, arg): |
|
301 | 279 | |
@@ -312,9 +290,6 b' class Pdb(OldPdb, object):' | |||
|
312 | 290 | self.msg("Restart doesn't make sense here. Using 'quit' instead.") |
|
313 | 291 | return self.do_quit(arg) |
|
314 | 292 | |
|
315 | def postloop(self): | |
|
316 | self.shell.set_completer_frame(None) | |
|
317 | ||
|
318 | 293 | def print_stack_trace(self, context=None): |
|
319 | 294 | if context is None: |
|
320 | 295 | context = self.context |
@@ -42,6 +42,7 b' from IPython.core.autocall import ExitAutocall' | |||
|
42 | 42 | from IPython.core.builtin_trap import BuiltinTrap |
|
43 | 43 | from IPython.core.events import EventManager, available_events |
|
44 | 44 | from IPython.core.compilerop import CachingCompiler, check_linecache_ipython |
|
45 | from IPython.core.debugger import Pdb | |
|
45 | 46 | from IPython.core.display_trap import DisplayTrap |
|
46 | 47 | from IPython.core.displayhook import DisplayHook |
|
47 | 48 | from IPython.core.displaypub import DisplayPublisher |
@@ -1584,6 +1585,8 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1584 | 1585 | # Things related to exception handling and tracebacks (not debugging) |
|
1585 | 1586 | #------------------------------------------------------------------------- |
|
1586 | 1587 | |
|
1588 | debugger_cls = Pdb | |
|
1589 | ||
|
1587 | 1590 | def init_traceback_handlers(self, custom_exceptions): |
|
1588 | 1591 | # Syntax error handler. |
|
1589 | 1592 | self.SyntaxTB = ultratb.SyntaxTB(color_scheme='NoColor') |
@@ -1594,7 +1597,8 b' class InteractiveShell(SingletonConfigurable):' | |||
|
1594 | 1597 | self.InteractiveTB = ultratb.AutoFormattedTB(mode = 'Plain', |
|
1595 | 1598 | color_scheme='NoColor', |
|
1596 | 1599 | tb_offset = 1, |
|
1597 |
check_cache=check_linecache_ipython |
|
|
1600 | check_cache=check_linecache_ipython, | |
|
1601 | debugger_cls=self.debugger_cls) | |
|
1598 | 1602 | |
|
1599 | 1603 | # The instance will store a pointer to the system-wide exception hook, |
|
1600 | 1604 | # so that runtime code (such as magics) can access it. This is because |
@@ -809,7 +809,7 b' class VerboseTB(TBTools):' | |||
|
809 | 809 | |
|
810 | 810 | def __init__(self, color_scheme='Linux', call_pdb=False, ostream=None, |
|
811 | 811 | tb_offset=0, long_header=False, include_vars=True, |
|
812 | check_cache=None): | |
|
812 | check_cache=None, debugger_cls = None): | |
|
813 | 813 | """Specify traceback offset, headers and color scheme. |
|
814 | 814 | |
|
815 | 815 | Define how many frames to drop from the tracebacks. Calling it with |
@@ -830,6 +830,8 b' class VerboseTB(TBTools):' | |||
|
830 | 830 | check_cache = linecache.checkcache |
|
831 | 831 | self.check_cache = check_cache |
|
832 | 832 | |
|
833 | self.debugger_cls = debugger_cls or debugger.Pdb | |
|
834 | ||
|
833 | 835 | def format_records(self, records, last_unique, recursion_repeat): |
|
834 | 836 | """Format the stack frames of the traceback""" |
|
835 | 837 | frames = [] |
@@ -1217,7 +1219,7 b' class VerboseTB(TBTools):' | |||
|
1217 | 1219 | |
|
1218 | 1220 | if force or self.call_pdb: |
|
1219 | 1221 | if self.pdb is None: |
|
1220 |
self.pdb = |
|
|
1222 | self.pdb = self.debugger_cls( | |
|
1221 | 1223 | self.color_scheme_table.active_scheme_name) |
|
1222 | 1224 | # the system displayhook may have changed, restore the original |
|
1223 | 1225 | # for pdb |
@@ -1278,7 +1280,7 b' class FormattedTB(VerboseTB, ListTB):' | |||
|
1278 | 1280 | def __init__(self, mode='Plain', color_scheme='Linux', call_pdb=False, |
|
1279 | 1281 | ostream=None, |
|
1280 | 1282 | tb_offset=0, long_header=False, include_vars=False, |
|
1281 | check_cache=None): | |
|
1283 | check_cache=None, debugger_cls=None): | |
|
1282 | 1284 | |
|
1283 | 1285 | # NEVER change the order of this list. Put new modes at the end: |
|
1284 | 1286 | self.valid_modes = ['Plain', 'Context', 'Verbose'] |
@@ -1287,7 +1289,7 b' class FormattedTB(VerboseTB, ListTB):' | |||
|
1287 | 1289 | VerboseTB.__init__(self, color_scheme=color_scheme, call_pdb=call_pdb, |
|
1288 | 1290 | ostream=ostream, tb_offset=tb_offset, |
|
1289 | 1291 | long_header=long_header, include_vars=include_vars, |
|
1290 | check_cache=check_cache) | |
|
1292 | check_cache=check_cache, debugger_cls=debugger_cls) | |
|
1291 | 1293 | |
|
1292 | 1294 | # Different types of tracebacks are joined with different separators to |
|
1293 | 1295 | # form a single string. They are taken from this dict |
@@ -4,18 +4,15 b' from __future__ import print_function' | |||
|
4 | 4 | import os |
|
5 | 5 | import sys |
|
6 | 6 | import signal |
|
7 | import unicodedata | |
|
8 | 7 | from warnings import warn |
|
9 | from wcwidth import wcwidth | |
|
10 | 8 | |
|
11 | 9 | from IPython.core.error import TryNext |
|
12 | 10 | from IPython.core.interactiveshell import InteractiveShell |
|
13 |
from IPython.utils.py3compat import |
|
|
11 | from IPython.utils.py3compat import cast_unicode_py2, input | |
|
14 | 12 | from IPython.utils.terminal import toggle_set_term_title, set_term_title |
|
15 | 13 | from IPython.utils.process import abbrev_cwd |
|
16 | 14 | from traitlets import Bool, Unicode, Dict, Integer, observe |
|
17 | 15 | |
|
18 | from prompt_toolkit.completion import Completer, Completion | |
|
19 | 16 | from prompt_toolkit.enums import DEFAULT_BUFFER, SEARCH_BUFFER, EditingMode |
|
20 | 17 | from prompt_toolkit.filters import HasFocus, HasSelection, Condition, ViInsertMode, EmacsInsertMode, IsDone |
|
21 | 18 | from prompt_toolkit.history import InMemoryHistory |
@@ -23,70 +20,16 b' from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop' | |||
|
23 | 20 | from prompt_toolkit.interface import CommandLineInterface |
|
24 | 21 | from prompt_toolkit.key_binding.manager import KeyBindingManager |
|
25 | 22 | from prompt_toolkit.keys import Keys |
|
26 | from prompt_toolkit.layout.lexers import Lexer | |
|
27 | from prompt_toolkit.layout.lexers import PygmentsLexer | |
|
28 | 23 | from prompt_toolkit.layout.processors import ConditionalProcessor, HighlightMatchingBracketProcessor |
|
29 | 24 | from prompt_toolkit.styles import PygmentsStyle, DynamicStyle |
|
30 | 25 | |
|
31 | 26 | from pygments.styles import get_style_by_name, get_all_styles |
|
32 | from pygments.lexers import Python3Lexer, BashLexer, PythonLexer | |
|
33 | 27 | from pygments.token import Token |
|
34 | 28 | |
|
29 | from .debugger import TerminalPdb | |
|
35 | 30 | from .pt_inputhooks import get_inputhook_func |
|
36 | 31 | from .interactiveshell import get_default_editor, TerminalMagics |
|
37 | ||
|
38 | ||
|
39 | class IPythonPTCompleter(Completer): | |
|
40 | """Adaptor to provide IPython completions to prompt_toolkit""" | |
|
41 | def __init__(self, ipy_completer): | |
|
42 | self.ipy_completer = ipy_completer | |
|
43 | ||
|
44 | def get_completions(self, document, complete_event): | |
|
45 | if not document.current_line.strip(): | |
|
46 | return | |
|
47 | ||
|
48 | used, matches = self.ipy_completer.complete( | |
|
49 | line_buffer=document.current_line, | |
|
50 | cursor_pos=document.cursor_position_col | |
|
51 | ) | |
|
52 | start_pos = -len(used) | |
|
53 | for m in matches: | |
|
54 | m = unicodedata.normalize('NFC', m) | |
|
55 | ||
|
56 | # When the first character of the completion has a zero length, | |
|
57 | # then it's probably a decomposed unicode character. E.g. caused by | |
|
58 | # the "\dot" completion. Try to compose again with the previous | |
|
59 | # character. | |
|
60 | if wcwidth(m[0]) == 0: | |
|
61 | if document.cursor_position + start_pos > 0: | |
|
62 | char_before = document.text[document.cursor_position + start_pos - 1] | |
|
63 | m = unicodedata.normalize('NFC', char_before + m) | |
|
64 | ||
|
65 | # Yield the modified completion instead, if this worked. | |
|
66 | if wcwidth(m[0:1]) == 1: | |
|
67 | yield Completion(m, start_position=start_pos - 1) | |
|
68 | continue | |
|
69 | ||
|
70 | # TODO: Use Jedi to determine meta_text | |
|
71 | # (Jedi currently has a bug that results in incorrect information.) | |
|
72 | # meta_text = '' | |
|
73 | # yield Completion(m, start_position=start_pos, | |
|
74 | # display_meta=meta_text) | |
|
75 | yield Completion(m, start_position=start_pos) | |
|
76 | ||
|
77 | class IPythonPTLexer(Lexer): | |
|
78 | """ | |
|
79 | Wrapper around PythonLexer and BashLexer. | |
|
80 | """ | |
|
81 | def __init__(self): | |
|
82 | self.python_lexer = PygmentsLexer(Python3Lexer if PY3 else PythonLexer) | |
|
83 | self.shell_lexer = PygmentsLexer(BashLexer) | |
|
84 | ||
|
85 | def lex_document(self, cli, document): | |
|
86 | if document.text.startswith('!'): | |
|
87 | return self.shell_lexer.lex_document(cli, document) | |
|
88 | else: | |
|
89 | return self.python_lexer.lex_document(cli, document) | |
|
32 | from .ptutils import IPythonPTCompleter, IPythonPTLexer | |
|
90 | 33 | |
|
91 | 34 | |
|
92 | 35 | class TerminalInteractiveShell(InteractiveShell): |
@@ -100,6 +43,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
100 | 43 | self._update_layout() |
|
101 | 44 | |
|
102 | 45 | pt_cli = None |
|
46 | debugger_history = None | |
|
47 | debugger_cls = TerminalPdb | |
|
103 | 48 | |
|
104 | 49 | autoedit_syntax = Bool(False, |
|
105 | 50 | help="auto editing of files with syntax errors.", |
@@ -362,6 +307,8 b' class TerminalInteractiveShell(InteractiveShell):' | |||
|
362 | 307 | self.init_term_title() |
|
363 | 308 | self.keep_running = True |
|
364 | 309 | |
|
310 | self.debugger_history = InMemoryHistory() | |
|
311 | ||
|
365 | 312 | def ask_exit(self): |
|
366 | 313 | self.keep_running = False |
|
367 | 314 |
General Comments 0
You need to be logged in to leave comments.
Login now