How to expand environment variables using keyboard shortcut in Fish? - fish

In Bash, the shortcut Esc Ctrl-e can be used to expand an environment variable at the shell:
$ echo $PATH
/home/joe
$ $PATH<Press Esc Ctrl-e>
$ /home/joe
Is there a shortcut to achieve something similar in Fish?

You could do something like this
function bind_expand_all
# what are the tokens of the current command line
set tokens (commandline --tokenize)
# erase the current command line (replace with empty string)
commandline -r ""
for token in $tokens
# append the expanded value of each token followed by a space
commandline -a (eval echo $token)" "
end
# move the cursor to the end of the new command line
commandline -C (string length (commandline))
end
then
bind \e\ce bind_expand_all
And if this is your current command line (with the cursor at the underscore):
$ echo $HOME (date -u)_
when you hit AltCtrle, you get
$ echo /home/jackman Thu May 10 19:27:18 UTC 2018 _
To store that binding permanently, add it to your fish_user_key_bindings function (create it if it does not exist):
Key bindings are not saved between sessions by default. Bare bind statements in config.fish won't have any effect because it is sourced before the default keybindings are setup. To save custom keybindings, put the bind statements into a function called fish_user_key_bindings, which will be autoloaded.
https://fishshell.com/docs/2.7/commands.html#bind
A little nicer:
function bind_expand_all
set -l expanded
for token in (commandline --tokenize)
set expanded $expanded (eval echo $token)
end
set -l new (string join " " $expanded)
commandline -r $new
commandline -C (string length $new)
end

Related

how to seperate compadd values with line break (zsh)

I have a compadd function that is meant to list AWS instances tags and return that list as completion options.
_aws_instance_by_name() {
# TODO: Figure out partial completion
local instances=$(aws ec2 describe-instances --query "Reservations[*].Instances[*].{Name:Tags[?Key=='Name']|[0].Value}" --output text)
[[ ${DEBUG} ]] && echo -e "\033[34;1m[DEBUG]\033[0m ${instances}"
compadd ${instances}
}
compdef _aws_instance_by_name aws_instance_id_by_name
But when I hit tab I get all options in option delimited by \n
foo\nbar\nSuper fancy
How do I split each value into the returned suggestions?
#compdef aws_instance_id_by_name
# Wrap output in double quotes to
# preserve all whitespace.
local output="$( <command> )"
# Split on line breaks.
local -a instances=( "${(f)output}" )
# Use convenience function to get
# fancy completion features for
# free. No need to figure out
# partial completion yourself.
_wanted aws-instances 'AWS instance' \
compadd -a instances
If you paste the above into a text file whose name starts with _ and save it into a dir that’s in your $fpath, it will get picked up automatically when you run compinit. No need to call compdef.
Documentation:
Command substitution
Parameter expansion flags
Completion system:
Utility functions
Autoloaded files

Set and execute a command from a function

How can I write a fish function that executes a command in a string and make it appear in the history?
function qh --description 'Use peco to query command history'
if test (count $argv) = 0
set peco_flags --layout=bottom-up
else
set peco_flags --layout=bottom-up --query "$argv"
end
history | peco $peco_flags | read cmd
if test $cmd
commandline $cmd
else
commandline ''
end
end
This does not work...
It is possible to create a key binding which sets the command line and then executes it; that command will then appear in history. Example:
function whatday
commandline "echo Today is $(date +%A)"
commandline -f execute
end
bind \eq whatday
now alt-q will set the commandline to echo Today is Sunday and execute it; it appears in history.
Beyond that there are also abbreviations which allow replacing tokens with text; but the text is just static (e.g. gco -> git checkout).
There is as yet no way for an arbitrary fish function (e.g. run as part of a shell script) to append to history, only delete and read from it.

How can I make a function run every time cd successfully changes to another directory within sh on FreeBSD?

I'm using sh as my shell on FreeBSD but I want to be able to have a pretty prompt like the one bash gives me on Ubuntu. There are two things that the FreeBSD implementation of sh seems to lack as far as PS1 escape characters go:
The \w works but does not expand $HOME to ~, so this is something I have already hacked up myself
I can use PS1 to update the prompt on the terminal, but as far as I can tell it is not possible to use the PS1 variable to update the title bar as well. ESC and BEL fail to set the title as one would expect if they were using bash or ksh
Here is my .shrc file
update_prompt() {
case "$PWD" in
"$HOME"*)
pretty_pwd="~${PWD#*"${HOME}"}"
;;
"/usr$HOME"*)
pretty_pwd="~${PWD#*"/usr${HOME}"}"
;;
*)
pretty_pwd="$PWD"
;;
esac
case "$TERM" in
xterm*|rxvt*)
PS1="[$USER#\\h $pretty_pwd]\\$ "
;;
*)
;;
esac
printf "\\033]0;[%s#$(hostname -s): %s]\\007" "$USER" "$pretty_pwd"
}
update_prompt
So when I fire up a terminal or log in via ssh, it gives the pretty prompt that I like. But now I need this function to run every time that cd is executed and returns an exit status of 0.
I was going to use an alias that was something like:
alias cd="cd $1 && update_prompt"
but that was before I realized that aliases do not except arguments. How might I go about doing something like this?
You can use a function instead of an alias:
cd() {
command cd "$#" && update_prompt
}
Just put it into ~/.shrc. You have to use command here to let sh know that you are referring to the actual cd builtin command instead of the function you've just defined.
Refer to the sh(1) manual page for the details on how to make sh(1) source the ~/.shrc file when it starts:
Therefore, a user should place commands that are to be executed only at login
time in the .profile file, and commands that are executed for every shell
inside the ENV file. The user can set the ENV variable to some file by placing
the following line in the file .profile in the home directory, substituting for
.shrc the filename desired:
ENV=$HOME/.shrc; export ENV
I use this trick in my cd alias manager. Here's a link to the source code of the function: https://github.com/0mp/goat/blob/v2.5.0/libgoat.sh#L31-L57
You can do it with alias+arguments if you swap the commands:
$ alias cd="echo change; cd"
$ pwd
/nas
$ cd /
change
$ pwd
/
$ cd /etc
change
$ pwd
/etc
$

Oh-my-zsh aliases do not autocomplete

I am a bit out of my wits researching this... and I just have to ask.
I have oh-my-zsh on my Mavericks machine and have everything updated. I also have Xcode and Brew. All updated. According to this page: https://github.com/robbyrussell/oh-my-zsh/wiki/Cheatsheet am I not supposed to just type, say: "g" [tab] and get "git"? or type "md" [tab] and get "mkdir -p"? Right now I just get a list of options I can tab through (or arrow through)... I thought it would autocomplete. What am I missing?
I have a feeling it might be related to my $PATH but that is where I also get confused... where should it point to?
I greatly appreciate and enlightenment.
# Path to your oh-my-zsh configuration.
#ZSH=$HOME/.oh-my-zsh
export ZSH=$HOME/.oh-my-zsh
# Set name of the theme to load.
# Look in ~/.oh-my-zsh/themes/
# Optionally, if you set this to "random", it'll load a random theme each
# time that oh-my-zsh is loaded.
#ZSH_THEME="af-magic"
ZSH_THEME="agnoster"
# Set to this to use case-sensitive completion
# CASE_SENSITIVE="true"
# Comment this out to disable weekly auto-update checks
# DISABLE_AUTO_UPDATE="true"
# Uncomment following line if you want to disable colors in ls
# DISABLE_LS_COLORS="true"
# Uncomment following line if you want to disable autosetting terminal title.
# DISABLE_AUTO_TITLE="true"
# Uncomment following line if you want red dots to be displayed while waiting for completion
COMPLETION_WAITING_DOTS="true"
# Which plugins would you like to load? (plugins can be found in ~/.oh-my-zsh/plugins/*)
# Example format: plugins=(rails git textmate ruby lighthouse)
plugins=(git textmate sublime zsh-syntax-highlighting)
source $ZSH/oh-my-zsh.sh
#export PATH=/usr/local/bin:/usr/local/sbin:/usr/local/git/bin:/usr/bin:/bin:/usr/sbin:/sbin:/opt/X11/bin:/usr/X11/$
#export PATH=$HOME/bin:/usr/local/bin:$PATH
export PATH=/usr/local/bin:$PATH
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"
zstyle ':completion:*' list-prompt ''
zstyle ':completion:*' select-prompt ''
autoload -Uz compinit
compinit
Aliases are essentially just short names for commands. Before executing a command zsh internally replaces aliases by their values. But by default aliases are not expanded when using completion (Tab), they are handled just like any other command.
For example:
alias ll='ls -l'
alias la='ls -al'
If you now type lTab, zsh will present you every command starting with an l, including the aliases ll and la. If you type llTab it will probably just add an space after ll (assuming there are no other commands starting with ll).
When you run ll somedir it does the exact same thing as ls -l somedir. You can even add other options of ls: ll -t somedir runs ls -l -t somedir.
That being said, if you want to expand aliases when typing Tab, zsh can do so.
There are two ways:
You can call the _expand_alias widget. In emacs mode (bindkey -e) this is bound to ^Xa (press Control+X then A).
You can add _expand_alias to the completer style. It seems that oh-my-zsh does not change this style from its default value, so adding
zstyle ':completion:*' completer _expand_alias _complete _ignored
to your ~/.zshrc should work.
(To be save you can print the current setting with zstyle -L ':completion:*' completer, _expand_alias has to come before _complete)
If you now type llTab, zsh will replace it with ls -l immediatelly.
Note: in both cases the cursor has to be in or just after the alias for the replacement to happen. This also means you have to type the whole alias or backspace if automatically completed (_completer adds a space after successful completion)

MacVim: create new file from command line by using `alias mvim="open -a macvim"`

When I use vim newfilename to open a file and this file does not exit, vim will create a new file with the name newfilename.
However, MacVim does not work in this way --- i.e. mvim newfilename (alias mvim="open -a macvim") will lead to an error: newfilename does not exist
Is there a way to configure MacVim such that mvim newfilename (alias mvim="open -a macvim") will create a new file and open it?
I'm guessing the error message comes from open, not from vim. You can replace your alias with a function;
mvim () {
local f
for f; do
test -e "$f" || touch "$f"
done
open -a macvim "$#"
}
This will create empty files if necessary before opening them.
edit Didn't see #Peter Lyons' comment about this; credit should go to him for first suggesting this solution. I'll be happy to remove this answer if Peter wants to submit his.
You don't need the mvim alias to the open command, you can instead use the mvim launcher script that comes bundled with most MacVim Snaphots. After adding that mvim to your path, then runing mvim newfile, will now open a newfile buffer in an new MacVim window just like gvim would.
The MacVim mvim script as linked to above:
#!/bin/sh
#
# This shell script passes all its arguments to the binary inside the
# MacVim.app application bundle. If you make links to this script as view,
# gvim, etc., then it will peek at the name used to call it and set options
# appropriately.
#
# Based on a script by Wout Mertens and suggestions from Laurent Bihanic. This
# version is the fault of Benji Fisher, 16 May 2005 (with modifications by Nico
# Weber and Bjorn Winckler, Aug 13 2007).
# First, check "All the Usual Suspects" for the location of the Vim.app bundle.
# You can short-circuit this by setting the VIM_APP_DIR environment variable
# or by un-commenting and editing the following line:
# VIM_APP_DIR=/Applications
if [ -z "$VIM_APP_DIR" ]
then
myDir="`dirname "$0"`"
myAppDir="$myDir/../Applications"
for i in ~/Applications ~/Applications/vim $myDir $myDir/vim $myAppDir $myAppDir/vim /Applications /Applications/vim /Applications/Utilities /Applications/Utilities/vim; do
if [ -x "$i/MacVim.app" ]; then
VIM_APP_DIR="$i"
break
fi
done
fi
if [ -z "$VIM_APP_DIR" ]
then
echo "Sorry, cannot find MacVim.app. Try setting the VIM_APP_DIR environment variable to the directory containing MacVim.app."
exit 1
fi
binary="$VIM_APP_DIR/MacVim.app/Contents/MacOS/Vim"
# Next, peek at the name used to invoke this script, and set options
# accordingly.
name="`basename "$0"`"
gui=
opts=
# GUI mode, implies forking
case "$name" in m*|g*|rm*|rg*) gui=true ;; esac
# Restricted mode
case "$name" in r*) opts="$opts -Z";; esac
# vimdiff, view, and ex mode
case "$name" in
*vimdiff)
opts="$opts -dO"
;;
*view)
opts="$opts -R"
;;
*ex)
opts="$opts -e"
;;
esac
# Last step: fire up vim.
# The program should fork by default when started in GUI mode, but it does
# not; we work around this when this script is invoked as "gvim" or "rgview"
# etc., but not when it is invoked as "vim -g".
if [ "$gui" ]; then
# Note: this isn't perfect, because any error output goes to the
# terminal instead of the console log.
# But if you use open instead, you will need to fully qualify the
# path names for any filenames you specify, which is hard.
exec "$binary" -g $opts ${1:+"$#"}
else
exec "$binary" $opts ${1:+"$#"}
fi