Zsh completion for global aliases - zsh

Is there a way to get tab completion for global aliases in zsh? Defined as:
% alias -g zshplugins=~/.zshplugins
% nvim zshpl[tab] would not tab complete.
I use global aliases mainly to not have to enter the path to a file nor restrict myself to a single command (e.g., alias zshplugin="nvim ~/.zshplugins")
I understand that these are also meant to be used inside arbitrary one-liners (a global alias for | grep -i, for instance) and don't make sense to suggest on every tab stroke, but If there's some workaround to include these in directory/file completion, that would be great.

You should use the shell variable but not global alias.
But if you want, add following code to your zshrc
_complete_alias() {
[[ -n $PREFIX ]] && compadd -- ${(M)${(k)galiases}:#$PREFIX*}
return 1
}
zstyle ':completion:*' completer _complete_alias _complete _ignored

Would zshpl[tab] complete at the beginning of the line (leaving out nvim)? This what I should work. An alias is not meant to be used as a shortcut for a file name. Shell variables can be used for the latter (and there is a completion on them too). I suggest that you define
zshplugins=~/.zshplugins
and then do
nvim $zshpl[tab]

Related

Zsh not recognizing alias from within function

Here's my .zshrc:
alias inst="adb install -r -d"
notify() {
start=$(date +%s)
"$#"
notify-send "\"$(echo $#)\" took $(($(date +%s) - start)) seconds to finish"
}
When I run the command notify inst app.apk, I get an error:
notify:2: command not found: inst
0.00s user 0.00s system 0% cpu 0.001 total
Can anyone shed some light on why this doesn't work, and hopefully a way to make it work?
When the shell processes commands, among other things (e.g. PATH search) it will check to see if the first token/argument (whitespace delimited) belongs to an alias loaded in the current environment. If the alias you are trying to substitute is not the first token, the substitution will not happen. If your alias happens to not be the name of an executable on the PATH or current directory, the error will propagate back up to a command not found.
Since your question is about the Z Shell, zsh actually provides a lesser-known feature called global aliasing. If an alias is declared with the -g flag, zsh will make the appropriate substitution for not only the first token, but any token, regardless of order.
alias -g inst="adb install -r -d" should do the trick.
Keep in mind that this is a zsh only feature for portability reasons, and make sure that whatever script your writing has a shebang line that invokes the zsh shell: #!/usr/bin/env zsh
I would also recommend to not use zsh global aliasing in important or production scripts. For personal use, it is perfectly fine.
Don't use alias for scripting
First, according to the advanced bash scripting guide:
In a script, aliases have very limited usefulness.
So you may consider not using alias but a function for instance (still from the same page, 2 paragraph lower):
Almost invariably, whatever we would like an alias to do could be accomplished much more effectively with a function.
A hacky solution
If this is for a quick script for yourself using aliases you have in your .zshrc, there is still a way out.
alias foo='echo hello'
bar() {
`alias "$#" | cut -d\' -f2`
}
bar foo # => hello
Alias replacement
from alias man page:
The first word of each simple command, if unquoted, is checked to see if it has an alias. If so, that word is replaced by the text of the alias. The alias name and the replacement text can contain any valid shell input, including shell metacharacters, with the exception that the alias name can not contain `='.
The first word of the replacement text is tested for aliases, but a word that is identical to an alias being expanded is not expanded a second time. This means that one can alias ls to "ls -F", for instance, and Bash does not try to recursively expand the replacement text.
Aliases are not expanded when the shell is not interactive, unless the expand_aliases shell option is set using shopt .
The rules concerning the definition and use of aliases are somewhat confusing. Bash always reads at least one complete line of input before executing any of the commands on that line. Aliases are expanded when a command is read, not when it is executed. Therefore, an alias definition appearing on the same line as another command does not take effect until the next line of input is read. The commands following the alias definition on that line are not affected by the new alias. This behavior is also an issue when functions are executed. Aliases are expanded when a function definition is read, not when the function is executed, because a function definition is itself a compound command. As a consequence, aliases defined in a function are not available until after that function is executed. To be safe, always put alias definitions on a separate line, and do not use alias in compound commands.

Reload aliases on change automatically without closing the shell window

I have my aliases stored in ~/.zsh_aliases and sourced in ~/.zshrc:
# Access custom aliases in the shell
[ -e "${HOME}/.zsh_aliases" ] && source "${HOME}/.zsh_aliases"
However, when changing the name of an alias, I have to always close the current shell window and open a new one for the change to become active.
Can Zsh automatically reload aliases on change to make them available without having to close the shell window?
You do not actually need to close and reopen your terminal for that, just running source ~/.zsh_aliases (loads the new and changed aliases) or maybe exec zsh (replaces the current shell with a new one) would work, too.
If you really want to re-source ~/.zsh_aliases whenever it is modified, I would suggest adding the following to your ~/.zshrc:
# File containing aliases;
ALIAS_FILE="${HOME}/.zsh_aliases
reload_aliases () {
# do nothing if there is no $ALIAS_FILE
[[ -e ALIAS_FILE ]] || return 1
# check if $ALIAS_FILE has been modified since last reload
# the modifier `(:A)` resolves any symbolic links
if [[ $LAST_ALIAS_RELOAD < $(stat -c %Y ${ALIAS_FILE}(:A)) ]]; then
# remove all aliases; optional!
# only do this if all of your aliases are defined in $ALIAS_FILE
# also affects aliases defined on the command line
unalias -m '*'
# load aliases
source $ALIAS_FILE
# update date of last reload
LAST_ALIAS_RELOAD=$(date +%s)
fi
}
# make reload_aliases to be run before each prompt
autoload -Uz add-zsh-hook
add-zsh-hook precmd reload_aliases
Note, that any changes will only be available on a new prompt. That means, if you modify ~/.zsh_aliases, you need to press at least Enter once in the all terminals for the changes to take effect.
I use an alias, thusly: -
alias vialias='vi ~/.oh-my-zsh/custom/alias.zsh ; source ~/.oh-my-zsh/custom/alias.zsh'
When I run vialias, I edit my aliases, then when I leave vi, the change(s) take effect.
To simplify the accepted answer add:
source ~/.zsh_aliases
in the ~/.zshrc below the plugins section.
Then add an alias inside the ~/.zsh_aliases like so:
alias f="exec zsh"
To refresh zsh & aliases type f

Zsh expand alias to result of function as string

Really quirky title, I know.
Basically, I have this:
alias vv="xclip -selection clipboard -o"
which prints out anything in my clipboard, such as a repository location in ssh-form (git#github.username/repname.git).
Now I'd like to:
git clone vv
I tried several variations of the above, such as trying various switches on the alias, or using different expansions, but with no luck.
Any suggestions?
Global alias might do it... actually it does it:
alias -g vv="$(date)" # replace 'date' with your command of choice
Notice:
it is a global alias, so it works anywhere in the command line (not just the beginning)
$(...) will do command substitution and expand it as a variable, see man zshexpn and search for $(...). By default zsh will not break the results using white-spaces.
[...]
I initially wrote a suggestion to create a (zsh) widget to insert the clipboard into the command line with a given key combination, then I realized that you would just likely hit "Ctrl-Shift-V" or something... :-S
[...]
FYI, this is how you would do this using a zsh widget:
that inserts the clipboard content on the command line, and binding it to some key, as it would allow you to see what you are doing before hitting enter. Place the following into your $fpath, inside a file called insert-clipboard (needs to be loaded with KSH_AUTOLOAD set)
#! /bin/zsh
## Inserts the output of the command into the cmd line buffer
zmodload -i zsh/parameter
insert-clipboard() {
LBUFFER+="$(date)" # REPLACE date BY YOUR COMMAND!
}
At your .zshrc
autoload insert-clipboard # as written, it needs KSH_AUTOLOAD set....
zle -N insert-clipboard
bindkey '^Xu' insert-clipboard # pick a key combination you like...

ZSH auto_vim (like auto_cd)

zsh has a feature (auto_cd) where just typing the directory name will automatically go to (cd) that directory. I'm curious if there would be a way to configure zsh to do something similar with file names, automatically open files with vim if I type only a file name?
There are three possibilities I can think of. First is suffix aliases which may automatically translate
% *.ps
to
% screen -d -m okular *.ps
after you do
alias -s ps='screen -d -m okular'
. But you need to define this alias for every file suffix. It is also processed before most expansions so if
% *.p?
matches same files as *.ps it won’t open anything.
Second is command_not_found handler:
function command_not_found_handler()
{
emulate -L zsh
for file in $# ; do test -e $file && xdg-open $file:A ; done
}
. But this does not work for absolute or relative paths, only for something that does not contain forward slashes.
Third is a hack overriding accept-line widget:
function xdg-open()
{
emulate -L zsh
for arg in $# ; do
command xdg-open $arg
endfor
}
function _-accept-line()
{
emulate -L zsh
FILE="${(z)BUFFER[1]}"
whence $FILE &>/dev/null || BUFFER="xdg-open $BUFFER"
zle .accept-line
}
zle -N accept-line _-accept-line
. The above alters the history (I can show how to avoid this) and is rather hackish. Good it does not disable suffix aliases (whence '*.ps' returns the value of the alias), I used to think it does. It does disable autocd though. I can avoid this (just || test -d $FILE after whence test), but who knows how many other things are getting corrupt as well. If you are fine with the first and second solutions better to use them.
I guess you can use "fasd_cd" which has an alias v which uses viminfo file to identifi files which you have opened at least once. In my environment it works like a charm.
Fast cd has other amazing stuff you will love!
Don't forget to set this alias on vim to open the last edited file:
alias lvim="vim -c \"normal '0\""

Zsh: Make a certain set of aliases take precedence in completion suggestions

I have a couple of functions set in my zsh profile which let me save aliases to directories on the fly. It's pretty simple - just adds an alias to cd to the dir to a ~/.dirs file which gets sourced.
Is it possible in zsh to make aliases which come from this file take precedence in suggested tab completions? For example, there's an alias in there called 'printeffect'.. If I type 'printe' and hit tab, the first completion suggestion is 'printenv'.. I want it to be 'printeffect'.
This should work:
an array holding your current special aliases, you only need to redefine the array when you switch dirs
typeset -U MY_DIR_ALIASES
MY_DIR_ALIASES=(foo1 foo2 foo3 foo-etc)
a custom completer function _foo (file name is _foo) completing when the cursors in a -commands- position:
_foo() {
cmds=( $MY_DIR_ALIASES )
_describe -commands- cmds
}
Or if you are saving this inside a file named _foo:
#compdef -command-
# -*-shell-script-*-
cmds=( $MY_DIR_ALIASES )
_describe -commands- cmds
making this completer function be ran before any other:
zstyle ':completion:::::' completer _foo _complete _approximate

Resources