##// END OF EJS Templates
Merge pull request #9487 from takluyver/ptk-ipdb...
Matthias Bussonnier -
r22396:97429bde merge
parent child Browse files
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 = debugger.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 PY3, cast_unicode_py2, input
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