Zsh completion case insensitive _multi_parts function - zsh

I have a script that takes file like arguments (multi part arguments), I am fetching the possible values and putting them in an array called raw and then using
_multi_parts / "(${raw[#]})"
to autocomplete. The problem is that this is case sensitive, how can I make it so that it I type mycommand get fo and press Tab it will autocomplete to mycommand get Foo/ if Foo is one of the things in raw.
The full completion is here for reference:
_mycommand() {
local curcontext="$curcontext" state line
_arguments "1: :->command" "*: :->label"
case $state in
command)
_arguments "1: :(add get ls rm)"
;;
*)
case $words[2] in
add|get|ls|rm)
if [ -f ~/.pts ]; then
IFS=$'\n' read -d '' -r raw <<< "$(mycommand ls)"
_multi_parts / "(${raw[#]})"
fi
;;
*)
_files
;;
esac
esac
}
_mycommand "$#"
mycommand ls outputs path like names like the following:
Foo/Bar/x
Foo/Bar/y
Derp/z
Placeholder/z

Okay I figured it out:
Change lines
IFS=$'\n' read -d '' -r raw <<< "$(mycommand ls)"
_multi_parts / "(${raw[#]})"
To
IFS=$'\n' raw=($(mycommand ls))
_multi_parts -M "m:{[:lower:][:upper:]}={[:upper:][:lower:]}" / raw

Related

Emulate R lang "?" command in shell (bash/zsh)?

One helpful feature of R is being able to use "?<function>" to quickly look up functions, without having to type out "help("<function>")".
Is it possible to implement a similar behavior in shell (e.g. bash or zsh), using a non-reserved character?
That is, somecommand (no space) triggering "man somecommand"
Perhaps, for bash
command_not_found_handle() {
if [[ $1 != Z* ]]; then
echo "$1: command not found" >&2
return 127
fi
local cmd=${1#Z}
case "$(type -t "$cmd")" in
builtin|keyword) help "$cmd" ;;
file) man "$cmd" ;;
alias) alias "$cmd" ;;
function) type "$cmd" ;;
*) echo "$cmd: command not found" >&2; return 127 ;;
esac
}

zsh function case condition parse error near `)'

I'm following this blog to setup a zsh function to switch aws cli profiles
: https://mads-hartmann.com/2017/04/27/multiple-aws-profiles.html
This is the zsh function in the blog:
function aws-switch() {
case ${1} in
"")
clear)
export AWS_PROFILE=""
;;
*)
export AWS_PROFILE="${1}"
;;
esac
}
#compdef aws-switch
#description Switch the AWS profile
_aws-switch() {
local -a aws_profiles
aws_profiles=$( \
grep '\[profile' ~/.aws/config \
| awk '{sub(/]/, "", $2); print $2}' \
| while read -r profile; do echo -n "$profile "; done \
)
_arguments \
':Aws profile:($(echo ${aws_profiles}) clear)'
}
_aws-switch "$#"
I added these lines to my ~/.zshrc, when I run source ~/.zshrc
It gives /.zshrc:4: parse error near `)'
I read the zsh function doc but still not very good at understanding the syntax and how could I fix this.
Have a look at the zsh man page (man zshmisc):
case word in [ [(] pattern [ | pattern ] ... ) list (;;|;&|;|) ] ... esac
As you see, you have to separate multiple pattern by |:
case $1 in
|clear)
....

using associative arrays with zparseopts

I'm trying to use an associate array to parse options with zparseopts.
I have some working code, shared below, using normal arrays...but it's so awkward and verbose. i want to just ask if -f is present using opts as an associative array, by passing -A as my option to zparseopts, but I can't seem to make it work.
local -a opts
zparseopts -D -a opts f
if [[ ${opts[(ie)-f]} -le ${#opts} ]]; then
echo "force was passed"
else
echo "be kind"
fi
thanks for any help!
When the (i) subscript flag is used with an associative array, the substitution will return the key if a match was found, or an empty value if it was not. So you just need to test for a non-empty string:
local -A opts
zparseopts -D -A opts f
if [[ -n ${opts[(ie)-f]} ]]; then
echo "force was passed"
else
echo "be kind"
fi
To consolidate option parsing, I've used this (slightly cryptic) substitution:
flag=-f; force=${${:-true false}[(w)((${#opts[(i)$flag]}>0?1:2))]}
flag=-q; quiet=${${:-true false}[(w)((${#opts[(i)$flag]}>0?1:2))]}
...
if $force; then
...
if ! $quiet; then
...

ZSH completion removes the argument with compadd -U

I'm trying to add a completion to a custom vs function which basically open the first filename matching the argument.
OPTIONAL (If you want more information about this function you can find my medium post here => https://medium.com/#victor.boissiere/how-to-quickly-open-files-with-your-editor-1a51b3fe21bf)
In my current folder I have the following:
./example.sh
./custom/directory.sh
./custom/example.sh
Behavior
vs direct<TAB> => completes to custom/directory.sh SUCCESS
vs example<TAB> => vs
Why does it removes the argument and does not let me choose between example.sh and custom/example.sh ?
Code
_vs() {
local curcontext="$curcontext" state line expl
_arguments -C \
'*:: :->open_files'
case "$state" in
open_files)
local file=${words[CURRENT]}
compadd -U - `find . -type f -ipath "*$file*" | sed "s|^\./||"`
;;
esac
return 0
}
compdef _vs vs
I just found the solution thanks to this post => https://superuser.com/questions/1264778/changing-to-a-directory-found-somewhere-in-the-tree-hierarchy/1278919#1278919
I just needed to add compstate[insert]=menu # no expand
Solution
_vs() {
local curcontext="$curcontext" state line expl
_arguments -C \
'*:: :->open_files'
case "$state" in
open_files)
local file=${words[CURRENT]}
compadd -U - `find . -type f -ipath "*$file*" | sed "s|^\./||"`
compstate[insert]=menu # add this
;;
esac
return 0
}
compdef _vs vs

Auto complete commands with "windows like" options (starting with slash)

I'm trying to write a custom auto-complete script for zsh (or bash) for a command that has options starting with a slash.
For instance: MyCommand /foo=bar.txt /yolo=test /final=4
I've been trying to use the zsh helper _arguments but it did not work:
#compdef MyCommand
_MyCommand()
{
local curcontext="$curcontext" state line
typeset -A opt_args
_arguments \
'/foo=:foo:_files'
}
_MyCommand "$#"
But when I replace the / with -- it works well.
How can I achieve this?
You can do that using _regex_arguments like this:
matchany=/$'[^\0]##\0'/
_regex_arguments _mycommand "$matchany" \( /$'/foo='/ ':option:option:(/foo=)' "$matchany" ':file:filename:_files' \| /$'/yolo='/ ':option:option:(/yolo=)' "$matchany" ':optarg:optarg:(test this)' \| /$'/final='/ ':option:option:(/final=)' /$'[0-9]##\0'/ ':number:number:' \) \#
_mycommand "$#"
You can read more about _regex_arguments here
http://zsh.sourceforge.net/Doc/Release/Completion-System.html#Completion-Functions
or here: https://github.com/vapniks/zsh-completions/blob/master/zsh-completions-howto.org
The important thing to note here is that there is no null char (\0) at the end of the pattern matches for the option names.

Resources