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