OILS / frontend / py_readline.py View on Github | oils.pub

195 lines, 102 significant
1"""
2py_readline.py: GNU readline wrapper that's also implemented in C++
3"""
4
5try:
6 import line_input
7except ImportError:
8 # Note: build/ovm-compile.sh doesn't build pyext/line_input.c unless shell
9 # var $HAVE_READLINE is set
10 # On the other hand, cpp/frontend_pyreadline.cc uses -D HAVE_READLINE, a
11 # C++ preprocessor var
12 line_input = None
13
14from typing import Optional, TYPE_CHECKING
15if TYPE_CHECKING:
16 from core.completion import ReadlineCallback
17 from core.comp_ui import _IDisplay
18 from core.state import Mem
19
20
21class Readline(object):
22 """A thin wrapper around GNU readline to make it usable from C++."""
23
24 def __init__(self):
25 # type: () -> None
26 assert line_input is not None
27
28 def prompt_input(self, prompt):
29 # type: (str) -> str
30 """
31 Print prompt, read line, and return it with trailing newline. Or raise
32 EOFError.
33 """
34 # Add trailing newline to make GNU readline conform to Python's
35 # f.readline() interface
36 return raw_input(prompt) + '\n'
37
38 def parse_and_bind(self, s):
39 # type: (str) -> None
40 line_input.parse_and_bind(s)
41
42 def read_init_file(self, s):
43 # type: (str) -> None
44 line_input.read_init_file(s)
45
46 def add_history(self, line):
47 # type: (str) -> None
48 line_input.add_history(line)
49
50 def read_history_file(self, path=None):
51 # type: (Optional[str]) -> None
52 line_input.read_history_file(path)
53
54 def write_history_file(self, path=None):
55 # type: (Optional[str]) -> None
56 line_input.write_history_file(path)
57
58 def set_completer(self, completer=None):
59 # type: (Optional[ReadlineCallback]) -> None
60 line_input.set_completer(completer)
61
62 def set_completer_delims(self, delims):
63 # type: (str) -> None
64 line_input.set_completer_delims(delims)
65
66 def set_completion_display_matches_hook(self, display=None):
67 # type: (Optional[_IDisplay]) -> None
68 hook = None
69 if display is not None:
70 hook = lambda *args: display.PrintCandidates(*args)
71
72 line_input.set_completion_display_matches_hook(hook)
73
74 def get_line_buffer(self):
75 # type: () -> str
76 return line_input.get_line_buffer()
77
78 def get_begidx(self):
79 # type: () -> int
80 return line_input.get_begidx()
81
82 def get_endidx(self):
83 # type: () -> int
84 return line_input.get_endidx()
85
86 def clear_history(self):
87 # type: () -> None
88 line_input.clear_history()
89
90 def get_history_item(self, pos):
91 # type: (int) -> str
92 return line_input.get_history_item(pos)
93
94 def remove_history_item(self, pos):
95 # type: (int) -> None
96 line_input.remove_history_item(pos)
97
98 def get_current_history_length(self):
99 # type: () -> int
100 return line_input.get_current_history_length()
101
102 def resize_terminal(self):
103 # type: () -> None
104 line_input.resize_terminal()
105
106 def list_funmap_names(self):
107 # type: () -> None
108 line_input.list_funmap_names()
109
110 def function_dumper(self, print_readably):
111 # type: (bool) -> None
112 line_input.function_dumper(print_readably)
113
114 def macro_dumper(self, print_readably):
115 # type: (bool) -> None
116 line_input.macro_dumper(print_readably)
117
118 def variable_dumper(self, print_readably):
119 # type: (bool) -> None
120 line_input.variable_dumper(print_readably)
121
122 def query_bindings(self, fn_name):
123 # type: (str) -> None
124 line_input.query_bindings(fn_name)
125
126 def unbind_rl_function(self, fn_name):
127 # type: (str) -> None
128 line_input.unbind_rl_function(fn_name)
129
130 def use_temp_keymap(self, fn_name):
131 # type: (str) -> None
132 line_input.use_temp_keymap(fn_name)
133
134 def restore_orig_keymap(self):
135 # type: () -> None
136 line_input.restore_orig_keymap()
137
138 def print_shell_cmd_map(self):
139 # type: () -> None
140 line_input.print_shell_cmd_map()
141
142 def unbind_keyseq(self, keyseq):
143 # type: (str) -> None
144 line_input.unbind_keyseq(keyseq)
145
146 def bind_shell_command(self, bindseq):
147 # type: (str) -> None
148 import sys
149 print("default encoding: %s" % sys.getdefaultencoding())
150 cmdseq_split = bindseq.strip().split(":", 1)
151 if len(cmdseq_split) != 2:
152 raise ValueError("%s: missing colon separator" % bindseq)
153
154 # Below checks prevent need to do so in C, but also ensure rl_generic_bind
155 # will not try to incorrectly xfree `cmd`/`data`, which doesn't belong to it
156 keyseq = cmdseq_split[0].rstrip()
157 if len(keyseq) <= 2:
158 raise ValueError("%s: empty/invalid key sequence" % keyseq)
159 if keyseq[0] != '"' or keyseq[-1] != '"':
160 raise ValueError("%s: missing double-quotes around the key sequence" % keyseq)
161 keyseq = keyseq[1:-1]
162
163 cmd = cmdseq_split[1]
164 print("type of cmd string: %s" % type(cmd)) # REMOVE ME
165 line_input.bind_shell_command(keyseq, cmd)
166
167 def set_bind_shell_command_hook(self, hook):
168 # type: (Callable[[str, str, int], (int, str, str)]) -> None
169
170 if hook is None:
171 raise ValueError("missing bind shell command hook function")
172
173 line_input.set_bind_shell_command_hook(hook)
174
175
176def MaybeGetReadline():
177 # type: () -> Optional[Readline]
178 """Returns a readline "module" if we were built with readline support."""
179 if line_input is not None:
180 return Readline()
181
182 return None
183
184
185if __name__ == '__main__':
186 import sys
187 readline = MaybeGetReadline()
188 try:
189 prompt_str = sys.argv[1]
190 except IndexError:
191 prompt_str = '! '
192
193 while True:
194 x = readline.prompt_input(prompt_str)
195 print(x)