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