Permalink
Browse files

Implement set -o vi/emacs (#120)

  • Loading branch information...
BatmanAoD authored and andychu committed May 26, 2018
1 parent fc29443 commit c16d264fe5bfe04b36e899e48ff386092e0e7f5a
Showing with 45 additions and 14 deletions.
  1. +4 −2 bin/oil.py
  2. +2 −2 core/builtin.py
  3. +2 −1 core/cmd_exec.py
  4. +3 −2 core/cmd_exec_test.py
  5. +20 −7 core/state.py
  6. +14 −0 spec/builtins.test.sh
View
@@ -80,8 +80,10 @@ def _tlog(msg):
from core import util
if HAVE_READLINE:
import readline
from core import completion
else:
readline = None
completion = None
from tools import deps
@@ -232,8 +234,8 @@ def OshMain(argv0, argv, login_shell):
builtin.SetExecOpts(exec_opts, opts.opt_changes)
fd_state = process.FdState()
ex = cmd_exec.Executor(mem, fd_state, status_lines, funcs, completion,
comp_lookup, exec_opts, arena)
ex = cmd_exec.Executor(mem, fd_state, status_lines, funcs, readline,
completion, comp_lookup, exec_opts, arena)
# NOTE: The rc file can contain both commands and functions... ideally we
# would only want to save nodes/lines for the functions.
View
@@ -533,7 +533,7 @@ def Cd(argv, mem, dir_stack):
return 1
# Set $PWD.
# `-L` is the default behavior; no need to check it
# '-L' is the default behavior; no need to check it
# TODO: ensure that if multiple flags are provided, the *last* one overrides
# the others
pwd = os.path.realpath(dest_dir) if arg.P else dest_dir
@@ -647,7 +647,7 @@ def Pwd(argv, mem):
pwd = mem.GetVar('PWD').s
# `-L` is the default behavior; no need to check it
# '-L' is the default behavior; no need to check it
# TODO: ensure that if multiple flags are provided, the *last* one overrides
# the others
if arg.P:
View
@@ -104,7 +104,7 @@ class Executor(object):
It also does some double-dispatch by passing itself into Eval() for
CompoundWord/WordPart.
"""
def __init__(self, mem, fd_state, status_lines, funcs, completion,
def __init__(self, mem, fd_state, status_lines, funcs, readline, completion,
comp_lookup, exec_opts, arena):
"""
Args:
@@ -127,6 +127,7 @@ def __init__(self, mem, fd_state, status_lines, funcs, completion,
self.comp_lookup = comp_lookup
# This is for shopt and set -o. They are initialized by flags.
self.exec_opts = exec_opts
self.exec_opts.readline = readline
self.arena = arena
self.splitter = legacy.SplitContext(self.mem)
View
@@ -44,8 +44,9 @@ def InitExecutor(arena=None):
funcs = {}
comp_funcs = {}
exec_opts = state.ExecOpts(mem)
return cmd_exec.Executor(mem, fd_state, status_lines, funcs, completion,
comp_funcs, exec_opts, arena)
# For the tests, we do not use 'readline'.
return cmd_exec.Executor(mem, fd_state, status_lines, funcs, None,
completion, comp_funcs, exec_opts, arena)
def InitEvaluator():
View
@@ -87,6 +87,9 @@ def Disable(self):
(None, 'strict-errexit'),
(None, 'strict-array'),
(None, 'vi'),
(None, 'emacs'),
# TODO: Add strict-arg-parse? For example, 'trap 1 2 3' shouldn't be
# valid, because it has an extra argument. Builtins are inconsistent about
# checking this.
@@ -146,8 +149,8 @@ def __init__(self, mem):
# shopt -s / -u. NOTE: bash uses $BASHOPTS rather than $SHELLOPTS for
# these.
self.nullglob = False
self.failglob = False
self.nullglob = False
self.failglob = False
#
# OSH-specific options that are not yet implemented.
@@ -161,6 +164,10 @@ def __init__(self, mem):
# Don't need flags -e and -n. -e is $'\n', and -n is write.
self.sane_echo = False
# Used for 'set -o vi/emacs'
# Set by the Executor, if available
self.readline = None
def _InitOptionsFromEnv(self, shellopts):
# e.g. errexit:nounset:pipefail
lookup = set(shellopts.split(':'))
@@ -199,6 +206,12 @@ def _SetOption(self, opt_name, b):
raise args.UsageError('Invalid option %r' % opt_name)
if opt_name == 'errexit':
self.errexit.Set(b)
elif opt_name in ('vi', 'emacs'):
if self.readline:
self.readline.parse_and_bind("set editing-mode " + opt_name);
else:
# TODO error message copied from 'cmd_exec.py'; refactor?
util.error('Oil was not built with readline/completion.')
else:
# strict-control-flow -> strict_control_flow
opt_name = opt_name.replace('-', '_')
@@ -309,7 +322,7 @@ class DirStack(object):
def __init__(self):
self.stack = []
self.Reset()
def Reset(self):
self.stack[:] = [os.getcwd()]
@@ -565,15 +578,15 @@ def _FindCellAndNamespace(self, name, lookup_mode, is_read=False):
namespace = self.var_stack[0].vars
return namespace.get(name), namespace
else:
else:
raise AssertionError(lookup_mode)
def SetVar(self, lval, value, new_flags, lookup_mode):
"""
Args:
lval: lvalue
val: value, or None if only changing flags
new_flags: tuple of flags to set: ReadOnly | Exported
new_flags: tuple of flags to set: ReadOnly | Exported
() means no flags to start with
None means unchanged?
scope:
@@ -784,7 +797,7 @@ def GetExported(self):
# TODO: This is run on every SimpleCommand. Should we have a dirty flag?
# We have to notice these things:
# - If an exported variable is changed.
# - If the set of exported variables changes.
# - If the set of exported variables changes.
exported = {}
# Search from globals up. Names higher on the stack will overwrite names
@@ -801,7 +814,7 @@ def SetLocalString(mem, name, s):
Used for:
1) for loop iteration variables
2) temporary environments like FOO=bar BAR=$FOO cmd,
2) temporary environments like FOO=bar BAR=$FOO cmd,
3) read builtin
"""
assert isinstance(s, str)
View
@@ -136,6 +136,20 @@ eval "a=3"
echo $a
# stdout: 3
### Set readline mode
set -o emacs
echo $?
set -o vi
echo $?
## STDOUT:
0
0
## END
### Set invalid option
set -o invalid 2>/dev/null
## status: 2
# OK mksh status: 1
### Source
lib=$TMP/spec-test-lib.sh
echo 'LIBVAR=libvar' > $lib

0 comments on commit c16d264

Please sign in to comment.