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

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