How do I yank into the system register from v-imode? - zsh

I'd like to yank text from a zsh command while in vi-mode with y and paste it into my web-browser/text-editor, etc.
Currently it seems that yanking text while in vi-mode only allows pasting back into zsh- has anyone had success yanking into the system clipboard?

Create a widget that executes the internal vi-yank widget and copies the zle clipboard (current position in kill ring) to the X11 clipboard using xclip(1):
function vi-yank-xclip {
zle vi-yank
echo "$CUTBUFFER" | xclip -i
}
.Replace xclip -i with pbcoby if running Mac OSX.
Make that widget known to zle and bind it to y:
zle -N vi-yank-xclip
bindkey -M vicmd 'y' vi-yank-xclip

Related

How to make zsh keybind between emacs mode and vi mode?

I want to bind a key to toggle emacs mode and vi mode,which I use oh-my-zsh plugins(vi-mode).
I tried Is there a way to switch Bash or zsh from Emacs mode to vi mode with a keystroke?
I also try to bindkey like
bindkey '^[e' 'set -o emacs'
bindkey '^[v' 'set -o vi'
But it's not work for me.
Does any way to toggle vi/emacs or keybind to set keymap?
Thanks a lot !
bindkey is used for binding keys to ZLE widgets not any random command. So what you have guessed at is not going to work. You could write a custom ZLE widget to switch keymaps:
select-emacs() { set -o emacs }
zle -N select-emacs
bindkey '^[e' select-emacs
In practical terms, I wouldn't recommend this. If you want a hybrid approach, it is better to select emacs mode but bind a key to vi-cmd-mode. In fact Ctrl-X,Ctrl-V is bound to this by default. You might even bind the escape key to vi-cmd-mode - where emacs key sequences involve an initial escape press, that can mostly be replaced by Alt. If you're used to typing it with the actual escape key, you may be able to replace it by a custom widget in vi command mode.
I finally "found out" how to toggle vi and emacs mode with a singel key, e.g. [alt]+[i] in zsh
# in the .zshrc
# toggle vi and emacs mode
vi-mode() { set -o vi; }
emacs-mode() { set -o emacs; }
zle -N vi-mode
zle -N emacs-mode
bindkey '\ei' vi-mode # switch to vi "insert" mode
bindkey -M viins 'jk' vi-cmd-mode # (optionally) switch to vi "cmd" mode
bindkey -M viins '\ei' emacs-mode # switch to emacs mode
now you can toggle from emacs-mode to vi-mode and from vi-mode (both insert or normal mode) to emacs-mode

Using bindkey to call a function in zsh requires pressing enter after function runs

I'm new to zsh and am trying to bind a key sequence to a function with the following in my .zshrc:
say_hello(){
echo "hello"
}
zle -N say_hello
bindkey '^Y' say_hello
Pressing Ctrl-Y will call the function and I'll see "hello" printed to the terminal but after I need to press Enter again before I'm given another zsh prompt. Calling the function by just typing in say_hello at the zsh prompt and pressing Enter does what I want - I see hello printed and then I'm given another zsh prompt. How can I get this behavior when binding the function to a key sequence?
Above is a simple example, really the function I'm trying to write is below:
my_cd() {
if [[ "$#" -ne 0 ]]; then
cd $(autojump $#)
return
fi
dir_to_cd_to=$(fasd_cd -dl | fzf --height 40% --reverse --inline-info)
# above isn't so important - dir_to_cd_to could be obtained in any way
cd "$dir_to_cd_to"
}
zle -N my_cd
bindkey -v '^Y' 'my_cd'
To display messages in a zle widget, you're supposed to use zle -M rather than echo. echo will output your message at whatever the current cursor position is which isn't especially helpful. If you really want to use echo, calling zle reset-prompt afterwards will redraw a fresh prompt. If you don't want a potential mess in your terminal, consider starting with \r to move the cursor to the beginning of the line and ending with $termcap[ce] to clear to the end of the line.

What is the bindkey equivalent to bind -x?

In this question, the inimitable Dennis Williamson mentions how to bind a keystroke to a command that will run in the background of the Bash shell, with bind -x. How can the same thing be done in Zsh?
(I couldn't find this in the zshzle man page, but it's possible that I overlooked it.)
You can use zle -M to display info underneath your command line. For example:
.who() {
zle -M "$(who)"
}
# Create a new widget `who` that calls our function `.who`.
zle -N who .who
# Bind said widget to alt-shift-W.
bindkey '^[W' who

Recover an interrupted command in zsh

In zsh, if one accidentally interrupted a command (^C), is there a quick way to recover the full interrupted command line?
For example,
PROMPT $ this is a long command ^C
PROMPT $ [cursor here]
I would like to recover "this is a long command" at the cursor position.
One solution is to
zle-line-init () {
if [[ -n $ZLE_LINE_ABORTED ]]; then
local savebuf="$BUFFER" savecur="$CURSOR"
BUFFER="$ZLE_LINE_ABORTED"
CURSOR="$#BUFFER"
zle split-undo
BUFFER="$savebuf" CURSOR="$savecur"
fi
}
zle -N zle-line-init
Then, in the new input line, undo (C-/ in emacs mode) would give the aborted line.
reference: http://www.zsh.org/mla/users/2015/msg00652.html
I've added a more detailed explanation here: https://www.topbug.net/blog/2016/10/03/restore-the-previously-canceled-command-in-zsh/
It'll be in $ZLE_LINE_ABORTED.
You can bind a widget specifically to restore it. Or create an undo event for it in zle-line-init (using zle split-undo) so that it can be restored by pressing undo.

ZSH: Behavior on Enter

I realize, when I'm in my terminal, I would expect to press Enter on empty input to make a ls or a git status when I'm on a git repos.
How can I achieve that? I mean, have a custom behavior on Empty input -> Enter in zsh?
EDIT: Thanks for the help. Here's my take with preexec...
precmd() {
echo $0;
if ["${0}" -eq ""]; then
if [ -d .git ]; then
git status
else
ls
fi;
else
$1
fi;
}
On Enter zsh calls the accept-line widget, which causes the buffer to be executed as command.
You can write your own widget in order to implement the behaviour you want and rebind Enter:
my-accept-line () {
# check if the buffer does not contain any words
if [ ${#${(z)BUFFER}} -eq 0 ]; then
# put newline so that the output does not start next
# to the prompt
echo
# check if inside git repository
if git rev-parse --git-dir > /dev/null 2>&1 ; then
# if so, execute `git status'
git status
else
# else run `ls'
ls
fi
fi
# in any case run the `accept-line' widget
zle accept-line
}
# create a widget from `my-accept-line' with the same name
zle -N my-accept-line
# rebind Enter, usually this is `^M'
bindkey '^M' my-accept-line
While it would be sufficient to run zle accept-line only in cases where there actually was a command, zsh would not put a new prompt after the output. And while it is possible to redraw the prompt with zle redisplay, this will probably overwrite the last line(s) of the output if you are using multi-line prompts. (Of course there are workarounds for that, too, but nothing as simple as just using zle accept-line.
Warning: This redfines an (the most?) essential part of your shell. While there is nothing wrong with that per se (else I would not have posted it here), it has the very real chance to make your shell unusable if my-accept-line does not run flawlessly. For example, if zle accept-line were to be missing, you could not use Enter to confirm any command (e.g. to redefine my-accept-line or to start an editor). So please, test it before putting it into your ~/.zshrc.
Also, by default accept-line is bound to Ctrl+J, too. I would recommend to leave it that way, to have an easy way to run the default accept-line.
In my .zshrc I use a combination of precmd and preexec found here:
http://zsh.sourceforge.net/Doc/Release/Functions.html#Hook-Functions
I also find that the git-prompt is super useful:
https://github.com/olivierverdier/zsh-git-prompt

Resources