##// END OF EJS Templates
Config option to enable mouse support
Thomas Kluyver -
Show More
@@ -1,221 +1,226 b''
1 1 """IPython terminal interface using prompt_toolkit in place of readline"""
2 2 from __future__ import print_function
3 3
4 4 import sys
5 5
6 6 from IPython.core.interactiveshell import InteractiveShell
7 7 from IPython.utils.py3compat import PY3, cast_unicode_py2
8 8 from traitlets import Bool, Unicode, Dict
9 9
10 10 from prompt_toolkit.completion import Completer, Completion
11 11 from prompt_toolkit.enums import DEFAULT_BUFFER
12 12 from prompt_toolkit.filters import HasFocus, HasSelection
13 13 from prompt_toolkit.history import InMemoryHistory
14 14 from prompt_toolkit.shortcuts import create_prompt_application, create_eventloop
15 15 from prompt_toolkit.interface import CommandLineInterface
16 16 from prompt_toolkit.key_binding.manager import KeyBindingManager
17 17 from prompt_toolkit.key_binding.vi_state import InputMode
18 18 from prompt_toolkit.key_binding.bindings.vi import ViStateFilter
19 19 from prompt_toolkit.keys import Keys
20 20 from prompt_toolkit.layout.lexers import PygmentsLexer
21 21 from prompt_toolkit.styles import PygmentsStyle
22 22
23 23 from pygments.styles import get_style_by_name
24 24 from pygments.lexers import Python3Lexer, PythonLexer
25 25 from pygments.token import Token
26 26
27 27 from .pt_inputhooks import get_inputhook_func
28 28 from .interactiveshell import get_default_editor
29 29
30 30
31 31 class IPythonPTCompleter(Completer):
32 32 """Adaptor to provide IPython completions to prompt_toolkit"""
33 33 def __init__(self, ipy_completer):
34 34 self.ipy_completer = ipy_completer
35 35
36 36 def get_completions(self, document, complete_event):
37 37 if not document.current_line.strip():
38 38 return
39 39
40 40 used, matches = self.ipy_completer.complete(
41 41 line_buffer=document.current_line,
42 42 cursor_pos=document.cursor_position_col
43 43 )
44 44 start_pos = -len(used)
45 45 for m in matches:
46 46 yield Completion(m, start_position=start_pos)
47 47
48 48 class PTInteractiveShell(InteractiveShell):
49 49 colors_force = True
50 50
51 51 pt_cli = None
52 52
53 53 vi_mode = Bool(False, config=True,
54 54 help="Use vi style keybindings at the prompt",
55 55 )
56 56
57 mouse_support = Bool(False, config=True,
58 help="Enable mouse support in the prompt"
59 )
60
57 61 highlighting_style = Unicode('', config=True,
58 62 help="The name of a Pygments style to use for syntax highlighting"
59 63 )
60 64
61 65 highlighting_style_overrides = Dict(config=True,
62 66 help="Override highlighting format for specific tokens"
63 67 )
64 68
65 69 editor = Unicode(get_default_editor(), config=True,
66 70 help="Set the editor used by IPython (default to $EDITOR/vi/notepad)."
67 71 )
68 72
69 73 def get_prompt_tokens(self, cli):
70 74 return [
71 75 (Token.Prompt, 'In ['),
72 76 (Token.PromptNum, str(self.execution_count)),
73 77 (Token.Prompt, ']: '),
74 78 ]
75 79
76 80 def get_continuation_tokens(self, cli, width):
77 81 return [
78 82 (Token.Prompt, (' ' * (width - 2)) + ': '),
79 83 ]
80 84
81 85 def init_prompt_toolkit_cli(self):
82 86 kbmanager = KeyBindingManager.for_prompt(enable_vi_mode=self.vi_mode)
83 87 insert_mode = ViStateFilter(kbmanager.get_vi_state, InputMode.INSERT)
84 88 # Ctrl+J == Enter, seemingly
85 89 @kbmanager.registry.add_binding(Keys.ControlJ,
86 90 filter=(HasFocus(DEFAULT_BUFFER)
87 91 & ~HasSelection()
88 92 & insert_mode
89 93 ))
90 94 def _(event):
91 95 b = event.current_buffer
92 96 d = b.document
93 97 if not (d.on_last_line or d.cursor_position_row >= d.line_count
94 98 - d.empty_line_count_at_the_end()):
95 99 b.newline()
96 100 return
97 101
98 102 status, indent = self.input_splitter.check_complete(d.text)
99 103
100 104 if (status != 'incomplete') and b.accept_action.is_returnable:
101 105 b.accept_action.validate_and_handle(event.cli, b)
102 106 else:
103 107 b.insert_text('\n' + (' ' * (indent or 0)))
104 108
105 109 @kbmanager.registry.add_binding(Keys.ControlC)
106 110 def _(event):
107 111 event.current_buffer.reset()
108 112
109 113 # Pre-populate history from IPython's history database
110 114 history = InMemoryHistory()
111 115 last_cell = u""
112 116 for _, _, cell in self.history_manager.get_tail(self.history_load_length,
113 117 include_latest=True):
114 118 # Ignore blank lines and consecutive duplicates
115 119 cell = cell.rstrip()
116 120 if cell and (cell != last_cell):
117 121 history.append(cell)
118 122
119 123 style_overrides = {
120 124 Token.Prompt: '#009900',
121 125 Token.PromptNum: '#00ff00 bold',
122 126 }
123 127 if self.highlighting_style:
124 128 style_cls = get_style_by_name(self.highlighting_style)
125 129 else:
126 130 style_cls = get_style_by_name('default')
127 131 # The default theme needs to be visible on both a dark background
128 132 # and a light background, because we can't tell what the terminal
129 133 # looks like. These tweaks to the default theme help with that.
130 134 style_overrides.update({
131 135 Token.Number: '#007700',
132 136 Token.Operator: 'noinherit',
133 137 Token.String: '#BB6622',
134 138 Token.Name.Function: '#2080D0',
135 139 Token.Name.Class: 'bold #2080D0',
136 140 Token.Name.Namespace: 'bold #2080D0',
137 141 })
138 142 style_overrides.update(self.highlighting_style_overrides)
139 143 style = PygmentsStyle.from_defaults(pygments_style_cls=style_cls,
140 144 style_dict=style_overrides)
141 145
142 146 app = create_prompt_application(multiline=True,
143 147 lexer=PygmentsLexer(Python3Lexer if PY3 else PythonLexer),
144 148 get_prompt_tokens=self.get_prompt_tokens,
145 149 get_continuation_tokens=self.get_continuation_tokens,
146 150 key_bindings_registry=kbmanager.registry,
147 151 history=history,
148 152 completer=IPythonPTCompleter(self.Completer),
149 153 enable_history_search=True,
150 154 style=style,
155 mouse_support=self.mouse_support,
151 156 )
152 157
153 158 self.pt_cli = CommandLineInterface(app,
154 159 eventloop=create_eventloop(self.inputhook))
155 160
156 161 def init_io(self):
157 162 if sys.platform not in {'win32', 'cli'}:
158 163 return
159 164
160 165 import colorama
161 166 colorama.init()
162 167
163 168 # For some reason we make these wrappers around stdout/stderr.
164 169 # For now, we need to reset them so all output gets coloured.
165 170 # https://github.com/ipython/ipython/issues/8669
166 171 from IPython.utils import io
167 172 io.stdout = io.IOStream(sys.stdout)
168 173 io.stderr = io.IOStream(sys.stderr)
169 174
170 175 def __init__(self, *args, **kwargs):
171 176 super(PTInteractiveShell, self).__init__(*args, **kwargs)
172 177 self.init_prompt_toolkit_cli()
173 178 self.keep_running = True
174 179
175 180 def ask_exit(self):
176 181 self.keep_running = False
177 182
178 183 rl_next_input = None
179 184
180 185 def pre_prompt(self):
181 186 if self.rl_next_input:
182 187 self.pt_cli.application.buffer.text = cast_unicode_py2(self.rl_next_input)
183 188 self.rl_next_input = None
184 189
185 190 def interact(self):
186 191 while self.keep_running:
187 192 print(self.separate_in, end='')
188 193
189 194 try:
190 195 document = self.pt_cli.run(pre_run=self.pre_prompt)
191 196 except EOFError:
192 197 if self.ask_yes_no('Do you really want to exit ([y]/n)?','y','n'):
193 198 self.ask_exit()
194 199
195 200 else:
196 201 if document:
197 202 self.run_cell(document.text, store_history=True)
198 203
199 204 def mainloop(self):
200 205 # An extra layer of protection in case someone mashing Ctrl-C breaks
201 206 # out of our internal code.
202 207 while True:
203 208 try:
204 209 self.interact()
205 210 break
206 211 except KeyboardInterrupt:
207 212 print("\nKeyboardInterrupt escaped interact()\n")
208 213
209 214 _inputhook = None
210 215 def inputhook(self, context):
211 216 if self._inputhook is not None:
212 217 self._inputhook(context)
213 218
214 219 def enable_gui(self, gui=None):
215 220 if gui:
216 221 self._inputhook = get_inputhook_func(gui)
217 222 else:
218 223 self._inputhook = None
219 224
220 225 if __name__ == '__main__':
221 226 PTInteractiveShell.instance().interact()
General Comments 0
You need to be logged in to leave comments. Login now