OILS / builtin / readline_osh.py View on Github | oilshell.org

180 lines, 120 significant
1#!/usr/bin/env python2
2"""
3readline_osh.py - Builtins that are dependent on GNU readline.
4"""
5from __future__ import print_function
6
7from _devbuild.gen import arg_types
8from _devbuild.gen.syntax_asdl import loc
9from core.error import e_usage
10from core import pyutil
11from core import vm
12from frontend import flag_util
13from mycpp import mops
14from mycpp import mylib
15
16from typing import Optional, TYPE_CHECKING
17if TYPE_CHECKING:
18 from _devbuild.gen.runtime_asdl import cmd_value
19 from frontend.py_readline import Readline
20 from core import shell
21 from display import ui
22
23
24class Bind(vm._Builtin):
25 """Interactive interface to readline bindings"""
26
27 def __init__(self, readline, errfmt):
28 # type: (Optional[Readline], ui.ErrorFormatter) -> None
29 self.readline = readline
30 self.errfmt = errfmt
31
32 def Run(self, cmd_val):
33 # type: (cmd_value.Argv) -> int
34 readline = self.readline
35 if not readline:
36 e_usage("is disabled because Oil wasn't compiled with 'readline'",
37 loc.Missing)
38
39 attrs, arg_r = flag_util.ParseCmdVal('bind', cmd_val)
40
41 print(attrs)
42 print(attrs.attrs)
43 print(arg_r)
44
45 arg = arg_types.bind(attrs.attrs)
46 print(arg)
47
48 if arg.m:
49 print("Using keymap: " + arg.m)
50
51 if arg.l:
52 readline.list_funmap_names()
53
54 if arg.p:
55 readline.function_dumper(True)
56
57 if arg.P:
58 readline.function_dumper(False)
59
60 if arg.s:
61 readline.macro_dumper(True)
62
63 if arg.S:
64 readline.macro_dumper(False)
65
66 if arg.v:
67 readline.variable_dumper(True)
68
69 if arg.V:
70 readline.variable_dumper(False)
71
72 self.errfmt.Print_("warning: bind isn't implemented",
73 blame_loc=cmd_val.arg_locs[0])
74 return 1
75
76
77class History(vm._Builtin):
78 """Show interactive command history."""
79
80 def __init__(
81 self,
82 readline, # type: Optional[Readline]
83 sh_files, # type: shell.ShellFiles
84 errfmt, # type: ui.ErrorFormatter
85 f, # type: mylib.Writer
86 ):
87 # type: (...) -> None
88 self.readline = readline
89 self.sh_files = sh_files
90 self.errfmt = errfmt
91 self.f = f # this hook is for unit testing only
92
93 def Run(self, cmd_val):
94 # type: (cmd_value.Argv) -> int
95 # NOTE: This builtin doesn't do anything in non-interactive mode in bash?
96 # It silently exits zero.
97 # zsh -c 'history' produces an error.
98 readline = self.readline
99 if not readline:
100 e_usage("is disabled because Oils wasn't compiled with 'readline'",
101 loc.Missing)
102
103 attrs, arg_r = flag_util.ParseCmdVal('history', cmd_val)
104 arg = arg_types.history(attrs.attrs)
105
106 # Clear all history
107 if arg.c:
108 readline.clear_history()
109 return 0
110
111 if arg.a:
112 hist_file = self.sh_files.HistoryFile()
113 if hist_file is None:
114 return 1
115
116 try:
117 readline.write_history_file(hist_file)
118 except (IOError, OSError) as e:
119 self.errfmt.Print_(
120 'Error writing HISTFILE %r: %s' %
121 (hist_file, pyutil.strerror(e)), loc.Missing)
122 return 1
123
124 return 0
125
126 if arg.r:
127 hist_file = self.sh_files.HistoryFile()
128 if hist_file is None:
129 return 1
130
131 try:
132 readline.read_history_file(hist_file)
133 except (IOError, OSError) as e:
134 self.errfmt.Print_(
135 'Error reading HISTFILE %r: %s' %
136 (hist_file, pyutil.strerror(e)), loc.Missing)
137 return 1
138
139 return 0
140
141 # Delete history entry by id number
142 arg_d = mops.BigTruncate(arg.d)
143 if arg_d >= 0:
144 cmd_index = arg_d - 1
145
146 try:
147 readline.remove_history_item(cmd_index)
148 except ValueError:
149 e_usage("couldn't find item %d" % arg_d, loc.Missing)
150
151 return 0
152
153 # Returns 0 items in non-interactive mode?
154 num_items = readline.get_current_history_length()
155 #log('len = %d', num_items)
156
157 num_arg, num_arg_loc = arg_r.Peek2()
158
159 if num_arg is None:
160 start_index = 1
161 else:
162 try:
163 num_to_show = int(num_arg)
164 except ValueError:
165 e_usage('got invalid argument %r' % num_arg, num_arg_loc)
166 start_index = max(1, num_items + 1 - num_to_show)
167
168 arg_r.Next()
169 if not arg_r.AtEnd():
170 e_usage('got too many arguments', loc.Missing)
171
172 # TODO:
173 # - Exclude lines that don't parse from the history! bash and zsh don't do
174 # that.
175 # - Consolidate multiline commands.
176
177 for i in xrange(start_index, num_items + 1): # 1-based index
178 item = readline.get_history_item(i)
179 self.f.write('%5d %s\n' % (i, item))
180 return 0