Skip to content

Commit

Permalink
compgen now works with variables and filenames.
Browse files Browse the repository at this point in the history
Also, we only print items that match the prefix.

Added spec tests.  All unit and spec tests pass.
  • Loading branch information
Andy Chu committed Sep 15, 2018
1 parent 493bb5f commit 30b48c4
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 32 deletions.
2 changes: 1 addition & 1 deletion core/cmd_exec.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ def _RunBuiltin(self, builtin_id, argv):
status = comp_builtins.Complete(argv, self, self.funcs, self.comp_lookup)

elif builtin_id == builtin_e.COMPGEN:
status = comp_builtins.CompGen(argv, self.funcs)
status = comp_builtins.CompGen(argv, self.funcs, self.mem)

elif builtin_id == builtin_e.COMPOPT:
status = comp_builtins.CompOpt(argv)
Expand Down
48 changes: 35 additions & 13 deletions core/comp_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
comp_builtins.py - Completion builtins
"""

import os

from core import args
from core import completion
from core import util
Expand Down Expand Up @@ -69,34 +71,49 @@ def Complete(argv, ex, funcs, comp_lookup):
_DefineOptions(COMPGEN_SPEC)


def CompGen(argv, funcs):
# state = args.State(argv)
# state.Rest() -> []
# state.AtEnd()
def CompGen(argv, funcs, mem):
"""Print completions on stdout."""

arg_r = args.Reader(argv)
arg = COMPGEN_SPEC.Parse(arg_r)
status = 0

if arg_r.AtEnd():
prefix = ''
else:
prefix = arg_r.Peek()
arg_r.Next()
if not arg_r.AtEnd():
raise args.UsageError('Extra arguments')

matched = False

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 func_name.startswith(prefix):
print(func_name)
matched = True

# Useful command to see what bash has:
# env -i -- bash --norc --noprofile -c 'compgen -v'
if 'variable' in arg.actions:
# bash uses compgen -v, which is the same as compgen -A variable
raise NotImplementedError
for var_name in mem.VarsWithPrefix(prefix):
print(var_name)
matched = True

if 'file' in arg.actions:
# git uses compgen -f, which is the same as compgen -A file
# TODO: listing '.' could be a capability?
for filename in os.listdir('.'):
if filename.startswith(prefix):
print(filename)
matched = True

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

return status
return 0 if matched else 1


COMPOPT_SPEC = args.FlagsAndOptions() # for -o
Expand All @@ -106,6 +123,11 @@ def CompGen(argv, funcs):
def CompOpt(argv):
arg_r = args.Reader(argv)
arg = COMPOPT_SPEC.Parse(arg_r)

# NOTE: This is supposed to fail if a completion isn't being generated?
# The executor should have a mode?


log('arg %s', arg)
return 0

11 changes: 10 additions & 1 deletion core/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -826,11 +826,20 @@ def GetExported(self):
# Search from globals up. Names higher on the stack will overwrite names
# lower on the stack.
for scope in self.var_stack:
for name, cell in scope.vars.items():
for name, cell in scope.vars.iteritems():
if cell.exported and cell.val.tag == value_e.Str:
exported[name] = cell.val.s
return exported

def VarsWithPrefix(self, prefix):
"""For compgen -A variable."""

# Look up the stack, yielding all variables. Bash seems to do this.
for scope in self.var_stack:
for name, _ in scope.vars.iteritems():
if name.startswith(prefix):
yield name


def SetLocalString(mem, name, s):
"""Set a local string.
Expand Down
38 changes: 24 additions & 14 deletions spec/builtin-compgen.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@ ek () { echo hello; }
__ec () { echo hi; }
_ab () { expr 10 % 3; }
compgen -A function
echo --
compgen -A function _
## status: 0
## STDOUT:
__ec
_ab
add
div
ek
--
__ec
_ab
## END

#### Invalid syntax
Expand Down Expand Up @@ -53,36 +58,41 @@ three
two
## END

#### compgen -v with local vars
v1_global=0
f() {
local v2_local=0
compgen -v v
}
f
## STDOUT:
v1_global
v2_local
## END

#### compgen -v on unknown var
compgen -v __nonexistent__
## status: 1
## stdout-json: ""

#### compgen -v P
cd > /dev/null # for some reason in bash, this makes PIPESTATUS appear!
compgen -v P
compgen -v P | grep -E 'PATH|PWD' | sort
## STDOUT:
PATH
PIPESTATUS
PPID
PS4
PWD
## END

#### Three compgens combined
mkdir -p $TMP/compgen2
touch $TMP/compgen2/{P1,P2}_FILE
touch $TMP/compgen2/PA_FILE_{1,2}
cd $TMP/compgen2 # depends on previous test above!
P_FUNC() { echo P; }
PA_FUNC() { echo P; }
Q_FUNC() { echo Q; }
compgen -A function -A file -A variable P
compgen -A function -A file -A variable PA
## STDOUT:
P_FUNC
PA_FUNC
PATH
PIPESTATUS
PPID
PS4
PWD
P1_FILE
P2_FILE
PA_FILE_1
PA_FILE_2
## END
4 changes: 2 additions & 2 deletions spec/osh-only.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

#### --debug-file
$SH --debug-file $TMP/debug.txt -c 'true'
wc -l < $TMP/debug.txt # It prints one line
## stdout: 1
grep 'Debug file' $TMP/debug.txt >/dev/null && echo yes
## stdout: yes

#### debug-completion option
set -o debug-completion
Expand Down
2 changes: 1 addition & 1 deletion test/spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ builtin-bash() {

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

Expand Down

0 comments on commit 30b48c4

Please sign in to comment.