Open in vim from zsh on `filename`, maybe preexec hook

zsh

SHORT STORY:

the following works…

function default_open_in_vim () {
    filename=$(echo -e "$1" | tr -d '[:space:]')
    if [[ -f $filename ]]; then
        vim $filename
    fi
}

autoload -Uz  add-zsh-hook
add-zsh-hook preexec default_open_in_vim

…but I get the following when I close vim:

zsh: permission denied: src/findPathBFS.js

LONG VERSION:

I find myself doing the same thing over and over again:

I type CTRL-t for fzf find file under current directory. Then I press enter and fzf inserts this filename on the cursor position. This is great. sometimes I'm grepping on this file, or catting or git-checkouting or whatever. may times though I'm opening in vim and didn't start by vim CTRL-t. What I must do now is CTRL-a to go to start of line in my terminal and then type vim <​cr>.

The problem is many times I don't even notice that I didn't already type this vim before CTRL-t… you can see the problem.

My zsh with AUTO_CD will cd into a directory if I type a directory name without prepending it with cd, I want it to do something similar: If I type a filename without prepending it with anything just open it in vim.

Preexec hook works but don't like the error message.

It would be nice if I could cancel the command but it doesn't seem that zsh allows me to cancel command from preexec hook (maybe it does and I don't know about it or how to do it?)

I have tried using command_not_found_handler documented in man zshmisc but doesn't help when "the command" is a valid filename.

Best Answer

This does work:

command_not_found_handler () {
    if [[ $# -eq 1 && -f $1 ]]; then
      vim "$1"
    else
      exit 127
    fi
}

I find it rather dangerous though, you're likely to execute something by mistake. For example, if you try to edit a file in the current directory that has the same name as a command in the path, the command in the path is executed. This is not a problem with command_not_found_handler, it's a problem with your requirement. Another problem is if you type something like foo/bar, it means editing the file if the file is not executable, and executing it if it is executable.

If you want files in the current directory to take precedence over commands in the PATH, you could use the DEBUG trap or overload the accept-line editing command. That wouldn't solve the second problem though.

I recommend a different user interface. Don't mess with command execution. Instead of pressing Return to edit, press a different key, and configure that key to insert vim in front of the line. This has some nice bonuses, including retaining the call to vim in the shell history.

zle -N edit-file
edit-file () {
  BUFFER="vim $BUFFER"
  zle accept-line "$@"
}
Related Question