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