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

142 lines, 94 significant
1#!/usr/bin/env python2
2"""
3builtin_lib.py - Builtins that are bindings to libraries, e.g. 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.ui import ErrorFormatter
21 from core import shell
22
23
24class Bind(vm._Builtin):
25 """For :, true, false."""
26
27 def __init__(self, readline, errfmt):
28 # type: (Optional[Readline], ErrorFormatter) -> None
29 self.readline = readline
30 self.errfmt = errfmt
31
32 def Run(self, cmd_val):
33 # type: (cmd_value.Argv) -> int
34 self.errfmt.Print_("warning: bind isn't implemented",
35 blame_loc=cmd_val.arg_locs[0])
36 return 1
37
38
39class History(vm._Builtin):
40 """Show interactive command history."""
41
42 def __init__(
43 self,
44 readline, # type: Optional[Readline]
45 sh_files, # type: shell.ShellFiles
46 errfmt, # type: ErrorFormatter
47 f, # type: mylib.Writer
48 ):
49 # type: (...) -> None
50 self.readline = readline
51 self.sh_files = sh_files
52 self.errfmt = errfmt
53 self.f = f # this hook is for unit testing only
54
55 def Run(self, cmd_val):
56 # type: (cmd_value.Argv) -> int
57 # NOTE: This builtin doesn't do anything in non-interactive mode in bash?
58 # It silently exits zero.
59 # zsh -c 'history' produces an error.
60 readline = self.readline
61 if not readline:
62 e_usage("is disabled because Oil wasn't compiled with 'readline'",
63 loc.Missing)
64
65 attrs, arg_r = flag_util.ParseCmdVal('history', cmd_val)
66 arg = arg_types.history(attrs.attrs)
67
68 # Clear all history
69 if arg.c:
70 readline.clear_history()
71 return 0
72
73 if arg.a:
74 hist_file = self.sh_files.HistoryFile()
75 if hist_file is None:
76 return 1
77
78 try:
79 readline.write_history_file(hist_file)
80 except (IOError, OSError) as e:
81 self.errfmt.Print_(
82 'Error writing HISTFILE %r: %s' %
83 (hist_file, pyutil.strerror(e)), loc.Missing)
84 return 1
85
86 return 0
87
88 if arg.r:
89 hist_file = self.sh_files.HistoryFile()
90 if hist_file is None:
91 return 1
92
93 try:
94 readline.read_history_file(hist_file)
95 except (IOError, OSError) as e:
96 self.errfmt.Print_(
97 'Error reading HISTFILE %r: %s' %
98 (hist_file, pyutil.strerror(e)), loc.Missing)
99 return 1
100
101 return 0
102
103 # Delete history entry by id number
104 arg_d = mops.BigTruncate(arg.d)
105 if arg_d >= 0:
106 cmd_index = arg_d - 1
107
108 try:
109 readline.remove_history_item(cmd_index)
110 except ValueError:
111 e_usage("couldn't find item %d" % arg_d, loc.Missing)
112
113 return 0
114
115 # Returns 0 items in non-interactive mode?
116 num_items = readline.get_current_history_length()
117 #log('len = %d', num_items)
118
119 num_arg, num_arg_loc = arg_r.Peek2()
120
121 if num_arg is None:
122 start_index = 1
123 else:
124 try:
125 num_to_show = int(num_arg)
126 except ValueError:
127 e_usage('got invalid argument %r' % num_arg, num_arg_loc)
128 start_index = max(1, num_items + 1 - num_to_show)
129
130 arg_r.Next()
131 if not arg_r.AtEnd():
132 e_usage('got too many arguments', loc.Missing)
133
134 # TODO:
135 # - Exclude lines that don't parse from the history! bash and zsh don't do
136 # that.
137 # - Consolidate multiline commands.
138
139 for i in xrange(start_index, num_items + 1): # 1-based index
140 item = readline.get_history_item(i)
141 self.f.write('%5d %s\n' % (i, item))
142 return 0