##// END OF EJS Templates
Python 2.7 support & highlighting in prompt_toolkit interface
Thomas Kluyver -
Show More
@@ -1,153 +1,157 b''
1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 from __future__ import print_function
3
1 4 from IPython.core.interactiveshell import InteractiveShell
5 from IPython.utils.py3compat import PY3
2 6 from traitlets import Bool, Unicode, Dict
3 7
4 8 from prompt_toolkit.completion import Completer, Completion
5 9 from prompt_toolkit.enums import DEFAULT_BUFFER
6 10 from prompt_toolkit.filters import HasFocus, HasSelection
7 11 from prompt_toolkit.history import InMemoryHistory
8 12 from prompt_toolkit.shortcuts import create_prompt_application
9 13 from prompt_toolkit.interface import CommandLineInterface
10 14 from prompt_toolkit.key_binding.manager import KeyBindingManager
11 15 from prompt_toolkit.key_binding.vi_state import InputMode
12 16 from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
13 17 from prompt_toolkit.keys import Keys
14 18 from prompt_toolkit.layout.lexers import PygmentsLexer
15 19 from prompt_toolkit.styles import PygmentsStyle
16 20
17 21 from pygments.styles import get_style_by_name
18 from pygments.lexers import Python3Lexer
22 from pygments.lexers import Python3Lexer, PythonLexer
19 23 from pygments.token import Token
20 24
21 25
22 26 class IPythonPTCompleter(Completer):
23 27 """Adaptor to provide IPython completions to prompt_toolkit"""
24 28 def __init__(self, ipy_completer):
25 29 self.ipy_completer = ipy_completer
26 30
27 31 def get_completions(self, document, complete_event):
28 32 if not document.current_line.strip():
29 33 return
30 34
31 35 used, matches = self.ipy_completer.complete(
32 36 line_buffer=document.current_line,
33 37 cursor_pos=document.cursor_position_col
34 38 )
35 39 start_pos = -len(used)
36 40 for m in matches:
37 41 yield Completion(m, start_position=start_pos)
38 42
39 43
40 44 class PTInteractiveShell(InteractiveShell):
41 45 colors_force = True
42 46
43 47 pt_cli = None
44 48
45 49 vi_mode = Bool(False, config=True,
46 50 help="Use vi style keybindings at the prompt",
47 51 )
48 52
49 53 highlighting_style = Unicode('', config=True,
50 54 help="The name of a Pygments style to use for syntax highlighting"
51 55 )
52 56
53 57 highlighting_style_overrides = Dict(config=True,
54 58 help="Override highlighting format for specific tokens"
55 59 )
56 60
57 61 def get_prompt_tokens(self, cli):
58 62 return [
59 63 (Token.Prompt, 'In ['),
60 64 (Token.PromptNum, str(self.execution_count)),
61 65 (Token.Prompt, ']: '),
62 66 ]
63 67
64 68
65 69 def init_prompt_toolkit_cli(self):
66 70 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
67 71 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
68 72 # Ctrl+J == Enter, seemingly
69 73 @kbmanager.registry.add_binding(Keys.ControlJ,
70 74 filter=(HasFocus(DEFAULT_BUFFER)
71 75 & ~HasSelection()
72 76 & insert_mode
73 77 ))
74 78 def _(event):
75 79 b = event.current_buffer
76 80 if not b.document.on_last_line:
77 81 b.newline()
78 82 return
79 83
80 84 status, indent = self.input_splitter.check_complete(b.document.text)
81 85
82 86 if (status != 'incomplete') and b.accept_action.is_returnable:
83 87 b.accept_action.validate_and_handle(event.cli, b)
84 88 else:
85 89 b.insert_text('\n' + (' ' * (indent or 0)))
86 90
87 91 @kbmanager.registry.add_binding(Keys.ControlC)
88 92 def _(event):
89 93 event.current_buffer.reset()
90 94
91 95 # Pre-populate history from IPython's history database
92 96 history = InMemoryHistory()
93 97 last_cell = u""
94 98 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
95 99 include_latest=True):
96 100 # Ignore blank lines and consecutive duplicates
97 101 cell = cell.rstrip()
98 102 if cell and (cell != last_cell):
99 103 history.append(cell)
100 104
101 105 style_overrides = {
102 106 Token.Prompt: '#009900',
103 107 Token.PromptNum: '#00ff00 bold',
104 108 }
105 109 if self.highlighting_style:
106 110 style_cls = get_style_by_name(self.highlighting_style)
107 111 else:
108 112 style_cls = get_style_by_name('default')
109 113 style_overrides.update({
110 114 Token.Number: '#007700',
111 115 Token.Operator: 'noinherit',
112 116 Token.String: '#BB6622',
113 117 })
114 118 style_overrides.update(self.highlighting_style_overrides)
115 119 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
116 120 style_dict=style_overrides)
117 121
118 122 app = create_prompt_application(multiline=True,
119 lexer=PygmentsLexer(Python3Lexer),
123 lexer=PygmentsLexer(Python3Lexer if PY3 else PythonLexer),
120 124 get_prompt_tokens=self.get_prompt_tokens,
121 125 key_bindings_registry=kbmanager.registry,
122 126 history=history,
123 127 completer=IPythonPTCompleter(self.Completer),
124 128 enable_history_search=True,
125 129 style=style,
126 130 )
127 131
128 132 self.pt_cli = CommandLineInterface(app)
129 133
130 134 def __init__(self, *args, **kwargs):
131 135 super(PTInteractiveShell, self).__init__(*args, **kwargs)
132 136 self.init_prompt_toolkit_cli()
133 137 self.keep_running = True
134 138
135 139 def ask_exit(self):
136 140 self.keep_running = False
137 141
138 142 def interact(self):
139 143 while self.keep_running:
140 144 print(self.separate_in, end='')
141 145 try:
142 146 document = self.pt_cli.run()
143 147 except EOFError:
144 148 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
145 149 self.ask_exit()
146 150
147 151 else:
148 152 if document:
149 153 self.run_cell(document.text, store_history=True)
150 154
151 155
152 156 if __name__ == '__main__':
153 157 PTInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now