Skip to content

Commit

Permalink
Sketch out the usage of complete/compgen/compopt by git-completion.bash.
Browse files Browse the repository at this point in the history
- Added parsing of "actions" for compgen -A function / compgen -f.  This
  is like set -o errexit / set -e.
- Added some spec tests, but they don't test much.  They only test
  command line parsing.

Addresses issue #175.
  • Loading branch information
Andy Chu committed Sep 14, 2018
1 parent 38a0f4d commit 1308d11
Show file tree
Hide file tree
Showing 7 changed files with 144 additions and 28 deletions.
62 changes: 59 additions & 3 deletions core/args.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ class _Attributes(object):
"""Object to hold flags"""

def __init__(self, defaults):
self.opt_changes = [] # special name
self.opt_changes = [] # for set -o, etc.
self.actions = [] # for compgen -A
self.saw_double_dash = False # for set --
for name, v in defaults.iteritems():
self.Set(name, v)
Expand Down Expand Up @@ -243,6 +244,41 @@ def OnMatch(self, prefix, suffix, state, out):
out.opt_changes.append((attr_name, b))


class SetAction(_Action):
""" For compgen -f """

def __init__(self, name):
self.name = name

def OnMatch(self, prefix, suffix, state, out):
out.actions.append(self.name)


class SetNamedAction(_Action):
""" For compgen -A file """

def __init__(self):
self.names = []

def Add(self, name):
self.names.append(name)

def OnMatch(self, prefix, suffix, state, out):
"""Called when the flag matches."""
#log('SetNamedOption %r %r %r', prefix, suffix, state)
state.Next() # always advance
try:
arg = state.Peek()
except IndexError:
raise UsageError('Expected argument for action')

attr_name = arg
# Validate the option name against a list of valid names.
if attr_name not in self.names:
raise UsageError('Invalid action name %r' % arg)
out.actions.append(attr_name)


# How to parse the value. TODO: Use ASDL for this.
Str = 1
Int = 2
Expand All @@ -257,7 +293,7 @@ class FlagsAndOptions(object):
Usage:
spec = FlagsAndOptions()
spec.ShortFlag(...)
spec.Option('-u', 'nounset')
spec.Option('u', 'nounset')
spec.Parse(argv)
"""

Expand All @@ -269,6 +305,9 @@ def __init__(self):

self.actions_short['o'] = SetNamedOption() # -o and +o

def InitActions(self):
self.actions_short['A'] = SetNamedAction() # -A

def ShortFlag(self, short_name, arg_type=None, default=None,
quit_parsing_flags=False, help=None):
""" -c """
Expand Down Expand Up @@ -298,7 +337,8 @@ def LongFlag(self, long_name, arg_type=None, default=None):
self.attr_names[name] = default

def Option(self, short_flag, name):
"""
"""Register an option that can be -e or -o errexit.
Args:
short_flag: 'e'
name: errexit
Expand All @@ -310,6 +350,22 @@ def Option(self, short_flag, name):

self.actions_short['o'].Add(attr_name)

def Action(self, short_flag, name):
"""Register an action that can be -f or -A file.
For the compgen builtin.
Args:
short_flag: 'f'
name: 'file'
"""
attr_name = name
if short_flag:
assert not short_flag.startswith('-'), short_flag
self.actions_short[short_flag] = SetAction(attr_name)

self.actions_short['A'].Add(attr_name)

def Parse(self, argv):
"""Return attributes and an index.
Expand Down
1 change: 1 addition & 0 deletions core/builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"shopt": builtin_e.SHOPT,
"complete": builtin_e.COMPLETE,
"compgen": builtin_e.COMPGEN,
"compopt": builtin_e.COMPOPT,

"true": builtin_e.TRUE,
"false": builtin_e.FALSE,
Expand Down
3 changes: 3 additions & 0 deletions core/cmd_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,9 @@ def _RunBuiltin(self, builtin_id, argv):
elif builtin_id == builtin_e.COMPGEN:
status = comp_builtins.CompGen(argv, self.funcs)

elif builtin_id == builtin_e.COMPOPT:
status = comp_builtins.CompOpt(argv)

elif builtin_id == builtin_e.COLON: # special builtin like 'true'
status = 0

Expand Down
82 changes: 59 additions & 23 deletions core/comp_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,22 @@
"""

from core import args
from core import builtin
from core import util

COMPLETE_SPEC = builtin.BUILTIN_DEF.Register('complete')
COMPLETE_SPEC.ShortFlag('-p', help='Print existing completions')
log = util.log


def _DefineOptions(spec):
spec.Option(None, 'bashdefault') # used in git
spec.Option(None, 'default') # used in git
spec.Option(None, 'filenames') # used in git
spec.Option(None, 'nospace') # used in git


# git-completion.sh uses complete -o and complete -F
COMPLETE_SPEC = args.FlagsAndOptions()
COMPLETE_SPEC.ShortFlag('-F', args.Str, help='Register a completion function')
_DefineOptions(COMPLETE_SPEC)


def Complete(argv, ex, funcs, completion, comp_lookup):
Expand All @@ -18,44 +29,69 @@ def Complete(argv, ex, funcs, completion, comp_lookup):
needs an Executor.
"""
arg, i = COMPLETE_SPEC.Parse(argv)
# TODO: process arg.opt_changes
log('arg %s', arg)

command = argv[i] # e.g. 'grep'
func_name = arg.F

# NOTE: bash doesn't actually check the name until completion time, but
# obviously it's better to check here.
func = funcs.get(func_name)
if func is None:
print('Function %r not found' % func_name)
return 1

if completion:
chain = completion.ShellFuncAction(ex, func)
comp_lookup.RegisterName(command, chain)
# TODO: Some feedback would be nice?
if arg.F:
func_name = arg.F
func = funcs.get(func_name)
if func is None:
print('Function %r not found' % func_name)
return 1

if completion:
chain = completion.ShellFuncAction(ex, func)
comp_lookup.RegisterName(command, chain)
# TODO: Some feedback would be nice?
else:
util.error('Oil was not built with readline/completion.')
else:
util.error('Oil was not built with readline/completion.')
pass

return 0


COMPGEN_SPEC = builtin.BUILTIN_DEF.Register('compgen')
COMPGEN_SPEC.ShortFlag('-A', args.Str)
COMPGEN_SPEC = args.FlagsAndOptions() # for -o and -A
COMPGEN_SPEC.InitActions()
COMPGEN_SPEC.Action(None, 'function')
COMPGEN_SPEC.Action('f', 'file')
COMPGEN_SPEC.Action('v', 'variable')
_DefineOptions(COMPGEN_SPEC)


def CompGen(argv, funcs):
arg, i = COMPGEN_SPEC.Parse(argv)
status = 0

if arg.A:
if arg.A == 'function':
for func_name in sorted(funcs):
print(func_name)
else:
raise args.UsageError('compgen: %s: invalid action name' % arg.A)
else:
if 'function' in arg.actions:
for func_name in sorted(funcs):
print(func_name)

if 'file' in arg.actions:
# bash uses compgen -f, which is the same as compgen -A file
raise NotImplementedError

if 'variable' in arg.actions:
# bash uses compgen -v, which is the same as compgen -A variable
raise NotImplementedError

if not arg.actions:
util.warn('*** command without -A not implemented ***')
status = 1

return status


COMPOPT_SPEC = args.FlagsAndOptions() # for -o
_DefineOptions(COMPOPT_SPEC)


def CompOpt(argv):
arg, i = COMPOPT_SPEC.Parse(argv)
log('arg %s', arg)
return 0

2 changes: 1 addition & 1 deletion core/runtime.asdl
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ module runtime
| EXPORT | UNSET | SET | SHOPT
| TRAP | UMASK
| SOURCE | DOT | EVAL | EXEC | WAIT | JOBS
| COMPLETE | COMPGEN | DEBUG_LINE
| COMPLETE | COMPGEN | COMPOPT | DEBUG_LINE
| TRUE | FALSE
| COLON
| TEST | BRACKET | GETOPTS
Expand Down
19 changes: 19 additions & 0 deletions spec/builtin-compgen.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,22 @@ ek
compgen -A foo
echo status=$?
## stdout: status=2

#### complete -o -F (git)
foo() { echo foo; }
wrapper=foo
complete -o default -o nospace -F $wrapper git
## status: 0

#### compopt -o (git)
# NOTE: Have to be executing a completion function
compopt -o filenames +o nospace
## status: 1

#### compgen -f
compgen -f /non-existing-dir/
## status: 1

#### compgen -v
compgen -v __gitcomp_builtin
## status: 1
3 changes: 2 additions & 1 deletion test/spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,8 @@ builtin-bash() {

# This is bash/OSH only
builtin-compgen() {
sh-spec spec/builtin-compgen.test.sh $BASH $OSH_LIST "$@"
sh-spec spec/builtin-compgen.test.sh --osh-failures-allowed 1 \
$BASH $OSH_LIST "$@"
}

builtins-special() {
Expand Down

0 comments on commit 1308d11

Please sign in to comment.