I have a little helper command in ~/.zshrc
called stfu
.
stfu() {
if [ -z "$1" ]; then
echo "Usage: stfu <program> [arguments...]"
return 1
fi
nohup "$@" &>/dev/null &
disown
}
complete -W "$(ls /usr/bin)" stfu
stfu
will run some other command but also detach it from the terminal and make any output shut up. I use it for things such as starting a browser from the terminal without worrying about CTRL+Z
, bg
, and disown
.
$ stfu firefox -safe-mode
# Will not output stuff to the terminal, and
# I can close the terminal too.
Here’s my issue:
On the second argument and above, when I hit tab, how do I let autocomplete suggest me the arguments and command line switches for the command I’m passing in?
e.g. stfu ls -<tab>
should show me whatever ls’s completion function is, rather than listing every /usr/bin
command again.
# Intended completion
$ stfu cat -<TAB>
-e -- equivalent to -vE
--help -- display help and exit
--number -n -- number all output lines
--number-nonblank -b -- number nonempty output lines, overrides -n
--show-all -A -- equivalent to -vET
--show-ends -E -- display $ at end of each line
--show-nonprinting -v -- use ^ and M- notation, except for LFD and TAB
--show-tabs -T -- display TAB characters as ^I
--squeeze-blank -s -- suppress repeated empty output lines
-t -- equivalent to -vT
-u -- ignored
# Actual completion
$ stfu cat <tab>
...a list of all /usr/bin commands
$ stfu cat -<tab>
...nothing, since no /usr/bin commands start with -
(repost, prev was removed)
EDIT: Solved.
I needed to set the curcontext
to the second word. Below is my (iffily annotated) zsh implementation, enjoy >:)
stfu() {
if [ -z "$1" ]; then
echo "Usage: stfu <program> [arguments...]"
return 1
fi
nohup "$@" &>/dev/null &
disown
}
#complete -W "$(ls /usr/bin)" stfu
_stfu() {
# Curcontext looks like this:
# $ stfu <tab>
# :complete:stfu:
local curcontext="$curcontext"
#typeset -A opt_args # idk what this does, i removed it
_arguments \
'1: :_command_names -e' \
'*::args:->args'
case $state in
args)
# idk where CURRENT came from
if (( CURRENT > 1 )); then
# $words is magic that splits up the "words" in a shell command.
# 1. stfu
# 2. yourSubCommand
# 3. argument 1 to that subcommand
local cmd=${words[2]}
# We update the autocompletion curcontext to
# pay attention to your subcommand instead
curcontext="$cmd"
# Call completion function
_normal
fi
;;
esac
}
compdef _stfu stfu
Deduced via docs (look for The Dispatcher), this dude’s docs, stackoverflow and overreliance on ChatGPT.
EDIT: Best solution (Andy)
stfu() {
if [ -z "$1" ]; then
echo "Usage: stfu <program> [arguments...]"
return 1
fi
nohup "$@" &>/dev/null &
disown
}
_stfu () {
# shift autocomplete to right
shift words
(( CURRENT-=1 ))
_normal
}
compdef _stfu stfu
Glad you have it working. This may also work:
_stfu () { shift words (( CURRENT-=1 )) _normal -P } compdef _stfu stfu
Stunningly simple, solely a shift. I love MVPs… we can possibly even remove the
-P
completion func switch :P
For people using bash that are thinking “how do I do that”:
The
bash-complete
package adds the_command
function for recursive completion on commands that accept other commands with their own arguments. It’s what sudo uses last I checked. You can addcomplete -F _command stfu
to your bashrc to link it to the stfu command.https://man.archlinux.org/man/bash.1#Programmable_Completion