Skip to content

Commit

Permalink
[completion] Misc fixes motivated by bash_completion.osh.
Browse files Browse the repository at this point in the history
- Print registered completion hooks
- Stubs for -P and -S
- Stubs for various completion types
- Shell functions for tracing the script
- Rename spec test file.  Add cases for invalid usage.
  • Loading branch information
Andy Chu committed Sep 23, 2018
1 parent 83237b7 commit b5a5b03
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 26 deletions.
86 changes: 67 additions & 19 deletions core/comp_builtins.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ def _DefineActions(spec):
# NOTE: git-completion.bash uses -f and -v.
# My ~/.bashrc on Ubuntu uses -d, -u, -j, -v, -a, -c, -b
spec.InitActions()
spec.Action(None, 'function')
spec.Action('a', 'alias')
spec.Action('b', 'binding')
spec.Action('c', 'command')
Expand All @@ -41,10 +40,12 @@ def _DefineActions(spec):
spec.Action('j', 'job')
spec.Action('u', 'user')
spec.Action('v', 'variable')
spec.Action(None, 'function')
spec.Action(None, 'helptopic') # help
spec.Action(None, 'setopt') # set -o
spec.Action(None, 'shopt') # shopt -s
spec.Action(None, 'signal') # kill -s
spec.Action(None, 'stopped')


# git-completion.sh uses complete -o and complete -F
Expand All @@ -57,6 +58,12 @@ def _DefineActions(spec):
help='Define the compspec for an empty line')
COMPLETE_SPEC.ShortFlag('-D',
help='Define the compspec that applies when nothing else matches')
COMPLETE_SPEC.ShortFlag('-P', args.Str,
help='Prefix is added at the beginning of each possible completion after '
'all other options have been applied.')
COMPLETE_SPEC.ShortFlag('-S', args.Str,
help='Suffix is appended to each possible completion after '
'all other options have been applied.')
COMPLETE_SPEC.ShortFlag('-F', args.Str, help='Complete with this function')


Expand All @@ -72,31 +79,72 @@ def Complete(argv, ex, funcs, comp_lookup):
#log('arg %s', arg)

commands = arg_r.Rest()

if arg.D:
commands.append('__fallback') # if the command doesn't match anything
if arg.E:
commands.append('__empty') # empty line

if not commands:
comp_lookup.PrintSpecs()
return 0

for command in commands:
# NOTE: bash doesn't actually check the name until completion time, but
# obviously it's better to check here.
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

chain = completion.ShellFuncAction(ex, func)
comp_lookup.RegisterName(command, chain)

# TODO: Some feedback would be nice?
# Should we show an error like this? Or maybe a warning? Maybe
# comp_lookup has readline_mod?
# util.error('Oil was not built with readline/completion.')
actions = []
# NOTE: bash doesn't actually check the name until completion time, but
# obviously it's better to check here.
if arg.F:
func_name = arg.F
func = funcs.get(func_name)
if func is None:
util.error('Function %r not found', func_name)
return 1
actions.append(completion.ShellFuncAction(ex, func))

for name in arg.actions:
if name == 'alias':
actions.append(completion.Directory())
elif name == 'binding':
actions.append(completion.Directory())
elif name == 'command':
actions.append(completion.Directory())
elif name == 'directory':
actions.append(completion.Directory())
elif name == 'file':
actions.append(completion.Directory())
elif name == 'job':
actions.append(completion.User())
elif name == 'user':
actions.append(completion.User())
elif name == 'variable':
actions.append(completion.User())
elif name == 'function':
actions.append(completion.User())
elif name == 'helptopic':
actions.append(completion.User())
elif name == 'setopt':
actions.append(completion.User())
elif name == 'shopt':
actions.append(completion.User())
elif name == 'signal':
actions.append(completion.User())
elif name == 'stopped':
actions.append(completion.User())
else:
pass

return 0
if not actions:
util.error('No actions defined in completion: %s' % argv)
return 1

chain = completion.ChainedCompleter(actions)
for command in commands:
comp_lookup.RegisterName(command, chain)

patterns = []
for pat in patterns:
comp_lookup.RegisterGlob(pat, action)

return 0


COMPGEN_SPEC = args.FlagsAndOptions() # for -o and -A
Expand Down
26 changes: 22 additions & 4 deletions core/completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ def __init__(self):

def PrintSpecs(self):
for name in sorted(self.lookup):
print('%s %s' % (name, self.lookup[name]))
print('%-15r %s' % (name, self.lookup[name]))
print('---')
print('%s' % self.empty_comp)
print('%s' % self.first_comp)
print('%s' % self.patterns)
print('empty = %s' % self.empty_comp)
print('first = %s' % self.first_comp)
print('patterns = %s' % self.patterns)

def RegisterName(self, name, chain):
"""
Expand Down Expand Up @@ -204,6 +204,20 @@ def Matches(self, words, index, prefix):
yield name + ' ' # full word


class Directory(CompletionAction):
"""complete -A directory"""

def Matches(self, words, index, prefix):
raise NotImplementedError('-A directory')


class User(CompletionAction):
"""complete -A user"""

def Matches(self, words, index, prefix):
raise NotImplementedError('-A user')


class FileSystemAction(CompletionAction):
"""Complete paths from the file system.
Expand Down Expand Up @@ -247,6 +261,10 @@ def __init__(self, ex, func):
self.ex = ex
self.func = func

def __repr__(self):
# TODO: Add file and line number here!
return '<ShellFuncAction %r>' % (self.func.name,)

def Matches(self, words, index, prefix):
# TODO:
# - Set COMP_CWORD etc. in ex.mem -- in the global namespace I guess
Expand Down
12 changes: 11 additions & 1 deletion scripts/complete.sh
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,24 @@ list-distro() {

# After running this, source testdata/completion/git-completion.bash
fresh-osh-with-dump() {
env -i OSH_CRASH_DUMP_DIR=_tmp bin/osh "$@"
env -i OSH_CRASH_DUMP_DIR=_tmp \
bin/osh --debug-file _tmp/debug "$@"
}

osh-trace() {
env -i OSH_CRASH_DUMP_DIR=_tmp PS4='+${LINENO} ' \
bin/osh -x --debug-file _tmp/debug "$@"
}

bash-completion() {
# This is a patched version
fresh-osh-with-dump /usr/share/bash-completion/bash_completion.osh
}

bash-completion-trace() {
osh-trace /usr/share/bash-completion/bash_completion.osh
}

# This should do nothing
git-completion() {
fresh-osh-with-dump testdata/completion/git-completion.bash
Expand Down
12 changes: 12 additions & 0 deletions spec/builtin-compgen.test.sh → spec/builtin-completion.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,15 @@ PATH
PA_FILE_1
PA_FILE_2
## END

#### complete with nonexistent function
complete -F invalidZZ -D
echo status=$?
## stdout: status=1
## BUG bash stdout: status=0

#### complete with no action
complete foo
echo status=$?
## stdout: status=1
## BUG bash stdout: status=0
4 changes: 2 additions & 2 deletions test/spec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ builtin-bash() {
}

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

Expand Down

0 comments on commit b5a5b03

Please sign in to comment.