Can I get zsh-style path interpolation/completion in windows git bash? - bash

On my Linux machine I have used a nice zsh feature to complete multipart paths with a single tab press:
$ cd /h/p<tab>
->
$ cd /home/plato
I am currently developing in a Windows environment, using Git Bash.
Is there any way I can replicate this behavior, complete paths without typing out the first parts completely?

You can use the complete command to run a custom function when you press tab. Add these lines to .bashrc:
# Solution written by izabera on freenode. tyvm!
#.bashrc
myfunction () {
local path oldpath ng=$(shopt -p nullglob)
shopt -s nullglob
printf -v path %q "${COMP_WORDS[COMP_CWORD]}"
oldpath=$path
path=${path//\//*/} path=${path#\*}
eval "COMPREPLY=($path*)"
if (( ${#COMPREPLY[#]} == 0 )); then COMPREPLY=("$oldpath"); fi
eval "$ng"
}
complete -D -F myfunction -o bashdefault -o default -o filenames
edit:
the -D flag was introduced in bash 4.1, the current bash version used by git-bash is 3.1.1.
The flag sets the completion function as the default, for any command that does not already have a completion function defined.
On 3.1.1, the best you can do is configure specific commands:
#.bashrc
myfunction () { ... }
complete -F myfunction -o bashdefault -o default -o filenames cd ls cp mv node npm

Disclosure: I am the author of the project
This project enables the desired completion in bash: https://github.com/sio/bash-complete-partial-path
It works just fine on Windows with Git bash. Hope you'll find it useful

Related

Makefile autocompletion on Mac

Makefile's targets are available by completion on Linux but, AFAICS, not on Mac OS (10.8.5).
Is it possible to get completion working with this OS?
This seems to achieve simple bash completions for me on El Capitan:
# .bashrc
function _makefile_targets {
local curr_arg;
local targets;
# Find makefile targets available in the current directory
targets=''
if [[ -e "$(pwd)/Makefile" ]]; then
targets=$( \
grep -oE '^[a-zA-Z0-9_-]+:' Makefile \
| sed 's/://' \
| tr '\n' ' ' \
)
fi
# Filter targets based on user input to the bash completion
curr_arg=${COMP_WORDS[COMP_CWORD]}
COMPREPLY=( $(compgen -W "${targets[#]}" -- $curr_arg ) );
}
complete -F _makefile_targets make
Here's how this works:
complete -F [function name] [command name] -- this bash builtin register a new completion for [command name] which is generated by the bash function [function name]. So in my code above, if you type make [TAB][TAB] into your shell, you'll trigger the _makefile_targets() function.
if [[ -e "$(pwd)/Makefile" ]]; then -- make sure there's a Makefile in the current directory, otherwise don't try a bash completion.
grep -oE '^[a-zA-Z0-9_-]+:' Makefile -- filter every line of Makefile using the regex for a target name like "test:". -o means only return the part of the line that matches. For example, given a Makefile target like "test: build package", only "test:" will be returned
| sed 's/://' -- taking the grep results, remove the colon from the end of line
| tr '\n' ' ' -- smoosh all targets onto one line, separated by one space
Inside a bash completion function, complete sets several environment variables for you. COMP_WORDS is an array of the list of available bash completion choises based on what the user typed. COMP_CWORD is the index of the currently selected word.
Another very magical builtin compgen will take a list of space separately strings and filter them using the currently selected word. I'm not at all clear how that works.
So, the bottom line is that the last two lines in the function filter our list of makefile targets (stored inside $targets) and shoves them into an array COMPREPLY. The bash completion reads and displays COMPREPLY as choices in the shell.
Inspired by:
https://gist.github.com/tlrobinson/1073865
http://www.thegeekstuff.com/2013/12/bash-completion-complete/ (Esp 9.)
This worked for me on Catalina in the standard zsh terminal
Edit the file named .zshrc in you home directory this can be done with this command in the terminal nano ~/.zshrc
Add the following lines
zstyle ':completion:*:*:make:*' tag-order 'targets'
autoload -U compinit && compinit
exit and save by pressing ctrl + x and then save Y
For macOS Monterey, the following works:
Edit the .zprofile file in your home directory. This can be done with a text editor. Path: ~/.zprofile
Add the following lines to the end of the file:
zstyle ':completion:*:*:make:*' tag-order 'targets'
autoload -Uz compinit && compinit
After that, everything works correctly, tips work when you press TAB.
This method does not display anything extra when entering the terminal, unlike the methods indicated above.
If you use bash and homebrew:
brew install bash-completion
follow their documentation:
Add the following line to your ~/.bash_profile:
[[ -r "$(brew --prefix)/etc/profile.d/bash_completion.sh" ]] && . "$(brew --prefix)/etc/profile.d/bash_completion.sh"
restart your terminal or
source ~/.bash_profile

How to activate FULL $var completion

under Debians BASH I can do the following:
foo=/path/to/some/file
cat $f[TAB]
As you can see, I can autocomplete the variable $foo by pressing TAB.
Under Ubuntu (11) the behaviour is not the same (I've activated '/etc/bash_completion' in my bashrc).
This is working:
echo $f[TAB]
This one is not working:
cat $f[TAB]
Can anyone give me a hint, how to get the last one working?
You can remove the completion specification provided for cat by /etc/bash_completion by doing:
complete -r cat
or you can see if modifying the completion options provided in /etc/bash_completion work in the version of Bash you're using:
complete -F _longopt -o filenames -o bashdefault cat
If you're using the latest patched version of Bash 4.2, there is an option that may help (I haven't tried it):
shopt -s direxpand

Executing Bash functions from within Vim - how can I do it? [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Commands executed from vim are not recognizing bash command aliases
I have the following function in my ~/.bashrc file (on my Ubunut box)
# convert tex to pdf, no bib
function t2p {
latex $1 && latex $1 && dvips $1 -o $1.ps && ps2pdf $1.ps && rm -f $1.dvi }
# convert tex to pdf, with bib
function tb2p {
latex $1 && bibtex $1 && latex $1 && latex $1 && dvips $1 -o $1.ps && ps2pdf $1.ps && rm -f $1.dvi }
For example, to convert a tex file f.tex to a pdf file and bibtex it in the right order, I call tb2p f. This works very well if I'm in a Bash terminal. However, to get to the Bash prompt from within Vim I actually have to execute the command :sh first.
To simplify the above procedure I tried to execute the functions t2p and tb2p inside Vim by :!t2p f. Vim then tells me that it cannot find the function t2p. I did some Googling and read that I should put these functions into the file /etc/bash.bashrc to make them visible to Vim. Unfortunately, this didn't work in my case. Vim still doesn't know about the function.
At the end of the day I would like to be able to call my functions from within Vim by using a keyboard shortcut. My questions are therefore as follows:
How can I let Vim know about ~/.bashrc functions?
How do I setup a keyboard shortcut in ~/.vimrc for a function in ~/.bashrc?
Thank you very, very much.
Try using :!bash -c t2p in Vim. If your alias is limited to interactive shells also add the -i flag.
Vim runs bash commands with the -c argument, which makes the shell non-interactive and non-login, and that bypasses reading the startup files.
You can override this with an environment variable.
From man bash:
When bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV
in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to
read and execute. Bash behaves as if the following command were executed:
if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi
but the value of the PATH variable is not used to search for the file name.
Alternatively, vim itself can be asked to run command shells as login shells. From vim :help shell we get the following:
On Unix the command normally runs in a non-interactive
shell. If you want an interactive shell to be used
(to use aliases) set 'shellcmdflag' to "-ic".
For Win32 also see |:!start|.
Try adding set shell=bash\ --login to your .vimrc.
To convert the current file, type :!tb2p %, the % will be expanded by Vim for you when executing the script.
Once it's working, you can add a mapping to make the whole process even faster:
nnoremap <F12> :!tb2p %<CR>
You can always define your functions in a separate file and put that file in a folder
in your PATH environment variable.
In my case my personal functions that I would use go to ~/bin
In your case for t2p:
create a file t2p in ~/bin with the contents:
#!/bin/bash
# convert tex to pdf, no bib
latex $1 && latex $1 && dvips $1 -o $1.ps && ps2pdf $1.ps && rm -f $1.dvi
then make the file executable:
> chmod +x t2p
make sure ~/bin is in your path, by putting the following in your ~/.bashrc:
export PATH=${HOME}/bin:${PATH}

bash completion of makefile target

Suppose I have a simple makefile like:
hello:
echo "hello world"
bye:
echo "bye bye"
Then in bash I want something like:
make h < tab >
so it can complete to
make hello
I found a simple way like creating empty files hello and bye but I'm looking for something more sophisticated.
Add this in your ~/.bash_profile file or ~/.bashrc file
complete -W "\`grep -oE '^[a-zA-Z0-9_.-]+:([^=]|$)' ?akefile | sed 's/[^a-zA-Z0-9_.-]*$//'\`" make
This searches for a target in your Makefile titled 'Makefile' or 'makefile' (note the capital ? wildcard in ?akefile) using grep, and pipes it over to the complete command in bash which is used to specify how arguments are autocompleted. The -W flag denotes that the input to the complete command will be a wordlist which is accomplished by passing the results of grep through sed which arranges it into the desirable wordlist format.
Caveats and gotchas:
Your make file is named 'GNUMakefile' or anything else other than 'Makefile' or 'makefile'. If you frequently encounter such titles consider changing the regular expression ?akefile accordingly.
Forgetting to source your ~/.bash_profile or ~/.bashrc file after making the changes. I add this seemingly trivial detail since, to the uninitiated it is unfamiliar.
For any change to your bash files to take effect, source them using the command
source ~/.bashrc
or
source ~/.bash_profile
PS. You also now have the added ability to display the possible make targets by pressing [Tab] twice just like in bash completion. Just make sure you add a space after the command make before typing [Tab] twice.
This answer from 2010 is outdated - the project mentioned here seems to have been discontinued.
Could this be what you're looking for?
http://freshmeat.net/projects/bashcompletion/
make [Tab] would complete on all
targets in Makefile. This project was
conceived to produce programmable
completion routines for the most
common Linux/UNIX commands, reducing
the amount of typing sysadmins and
programmers need to do on a daily
basis.
There's a useful package called bash-completion available for most every OS. It includes Makefile completion.
(If you're using macOS and Homebrew, you can get this via brew install bash-completion.)
This seems to be default in at least Debian Lenny:
$ grep Makefile /etc/bash_completion
# make reads `GNUmakefile', then `makefile', then `Makefile'
elif [ -f ${makef_dir}/Makefile ]; then
makef=${makef_dir}/Makefile
# before we scan for targets, see if a Makefile name was
# deal with included Makefiles
The header of this file states:
# The latest version of this software can be obtained here:
#
# http://bash-completion.alioth.debian.org/
#
# RELEASE: 20080617.5
Here is a completion script that looks at the .PHONY: declaration.
_make_phony_words() {
local opt_revert
if [ -n "${BASH_VERSION:-}" ]; then
shopt -q nullglob || {
opt_revert=1 ; shopt -s nullglob ;
}
elif [ -n "${ZSH_VERSION:-}" ]; then
[[ -o nullglob ]] || {
opt_revert=1 ; setopt nullglob
}
fi
for f in ./?akefile ./*.make ; do
sed -nEe '/^.PHONY/ { s/^.PHONY:[ ]?// ; p ; } ' "$f" | tr ' ' $'\n' | sort -u
done
if [ -n "$opt_revert" ]; then
[ -n "${ZSH_VERSION:-}" ] && unsetopt nullglob
[ -n "${BASH_VERSION:-}" ] && shopt -u nullglob
fi
unset opt_revert
}
_make_phony_complete() {
local cur="${COMP_WORDS[COMP_CWORD]}"
COMPREPLY+=( $(compgen -W "$( _make_phony_words )" -- ${cur}) )
}
complete -F _make_phony_complete make
Makefile completion on steroids!
I had 2 problems with the normal completions:
Problem #1
Sometimes you have targets you want to call like make greet:hi and make greet:hola sort of like namespacing Makefile target names. So your Makefile ends up looking like:
greet\:hola:
echo "hola world"
# OR a .PHONY target
.PHONY: greet\:hi
greet\:hi:
echo "hi world"
In this case the auto-completions after : don't show up as it uses \: in the Makefile as shown above.
Problem #2
There wasn't a way to navigate through the list of all Makefile targets that match my input using arrow keys (or CTRL-p / CTRL-n) in my bash shell.
Basically, I wanted to use fuzzy search like approach on the targets (i.e. fzf).
FZF Repo: https://github.com/junegunn/fzf
Solution
Install FZF Dependency
Using Homebrew
You can use Homebrew (on macOS or Linux)
to install fzf.
brew install fzf
$(brew --prefix)/opt/fzf/install
Using Linux package managers
Package Manager
Linux Distribution
Command
APK
Alpine Linux
sudo apk add fzf
APT
Debian 9+/Ubuntu 19.10+
sudo apt-get install fzf
Conda
conda install -c conda-forge fzf
DNF
Fedora
sudo dnf install fzf
Nix
NixOS, etc.
nix-env -iA nixpkgs.fzf
Pacman
Arch Linux
sudo pacman -S fzf
pkg
FreeBSD
pkg install fzf
pkgin
NetBSD
pkgin install fzf
pkg_add
OpenBSD
pkg_add fzf
XBPS
Void Linux
sudo xbps-install -S fzf
Zypper
openSUSE
sudo zypper install fzf
FZF and : compatible auto-complete command
Put this in your .bashrc
complete -W "\`grep -oE '^[a-zA-Z0-9_.-]+[\\:]*[a-zA-Z0-9_.-]+:([^=]|$)' ?akefile | sort | uniq | sed 's/[^a-zA-Z0-9_.-]*$//' | sed 's/[\]//g' | fzf\`" make
Now just typing make and then hitting the key will work!
DEMO: in action!
Then you can use as following:
make using fzf
I added so I follow "include" directives in Makefile. So my .bashrc looks like this:
function followMakefile() {
grep -oE '^[a-zA-Z0-9_.-]+:([^=]|$)' ?akefile | sed 's/[^a-zA-Z0-9_.-]*$//'
for x in `grep -E '^include' ?akefile | sed 's/include //'`
do
grep -oE '^[a-zA-Z0-9_.-]+:([^=]|$)' $x | sed 's/[^a-zA-Z0-9_.-]*$//'
done
}
complete -W "\`followMakefile\`" make
In Ubuntu 10.04, source the following file:
. /etc/bash_completion
or uncomment it in
/etc/bash.bashrc

Unable to find a substitute command for Bash's complete in Zsh

I put the newest git-completion.bash to my .zshrc and I get
/Users/Masi/bin/shells/git/git-completion.bash:2116: command not found: complete
/Users/Masi/bin/shells/git/git-completion.bash:2118: command not found: complete
The lines are
complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
|| complete -o default -o nospace -F _git git
complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
|| complete -o default -o nospace -F _gitk gitk
Which command is a substitute for Bash's complete in Zsh?
I do believe it's a problem, since complete is a built-in Bash command. It's part of Bash's programmable completion feature.
Git's completion script has been updated to work with ZSH too. The latest version should "just work".
Bash and Zsh have different completion systems. Using bash completion functions with zsh might be possible, but only though a compatibility layer. I suggest you read man zshcompsys.

Resources