Improving mvim for MacVim

MacVim comes with a clever script called mvim which allows you to open files in MacVim via the command line (much like mate for TextMate). This is a wonderful tool, but does possess some idiosyncrasies. It creates a new window (rather than a new tab) when you invoke it and it also won’t take stdin as input unless you pass a dash1.

To fix this I modified the script to check for an existing macvim process, and if it exists attach tabs to it.2 Additionally, it checks for args and if none are present (or just -) the script invokes stdin. I am not much of a bash hacker though, so there are a few caveats…

  • –remote-tab-silent doesn’t seem to work with listening on stdin, so the script always pops it out into a new window.
  • You (apparently) can’t pass extra parameters when using –remote-tab-silent, so tricks like mvim /etc/hosts +5 to have it open with the cursor on line 5 will not work.

These are both irritating, but I feel the advantages outweigh the disadvantages. Hopefully someone can contribute fixes to these problems!

#!/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.  Some mediocre hacking by Paul Kehrer Sep 30 2009).
# 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*|rg*) gui=true ;; esac
 
# Restricted mode
case "$name" in r*) opts="$opts -Z";; esac
 
# vimdiff and view
case "$name" in
	*vimdiff)
		opts="$opts -dO"
		;;
	*view)
		opts="$opts -R"
		;;
esac
 
tabs=true
stdinoption=false
 
#let's see if we need to read from stdin
#script currently assumes NO PARAMETERS
if [ -z "$1" ] || [[ $1 == \- ]]; then
	stdinoption=true
fi
 
# 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.
	if $stdinoption; then
		exec "$binary" -g - 
	else 
		if $tabs && [ `ps x|grep 'MacOS/MacVim'|grep -v grep|wc -l` = "1" ] && [[ `$binary --serverlist` = "VIM" ]]; then
			#when using remote tabs you can't pass things like +5.
			exec "$binary" -g $opts --remote-tab-silent ${1:+"$@"}
		else
			exec "$binary" -g $opts ${1:+"$@"} 
		fi
	fi
else
	exec "$binary" $opts ${1:+"$@"}
fi
  1. This is the standard vim way, but feels irritatingly inelegant to me.
  2. The script does a ps x to check for the existence of a macvim process. This is done before the serverlist check because the script will output ugly errors to stdout if there is no macvim process.
  1. Thank you for this script.

    I’m not sure if this is bad practice, but I wanted to silence the output for the mvim script, so I appended 2> /dev/null to all the exec statements.

    For example:

    exec “$binary” -g $opts ${1:+”$@”} 2> /dev/null

    Not sure if it works in all circumstances (though I don’t see why not) .. but it’s enough for my uses.

  2. Here’s my version of that first loop that is more complete. I don’t know if all the strange bash characters will work. You can always use open -a MacVim .

    if [ -z "$VIM_APP_DIR" ]; then
      myDir="`dirname "$0"`"
      declare -a apps=("/Applications" "$HOME/Applications" "$myDir/../Applications")
      utils="Utilities"
      declare -a vims=("MacVim" "vim")
      declare -a all=( "${apps[@]}" "${apps[@]/%//$utils}" )
      declare -a more
      for i in "${vims[@]}"; do
        more=( "${more[@]}" "${all[@]/%//$i}" )
      done
      for i in "${all[@]}" "${more[@]}"; do
        if [ -x "$i/MacVim.app" ]; then
          VIM_APP_DIR="$i"
          break
        fi
      done
    fi
    
  3. Hey,

    not sure this would be the right place to ask but it seems like you have a better understanding of this script than I do..

    Whenever invoking MacVim thru the terminal via the mvim command, I get a errors detected while processing menu.vim. MacVim starts but i have to press enter a couple of times to clear the errors and be able to use the terminal again.. Would you have any idea how to stop these ?

    Thanks,

    Nicolas

  4. Is there any reason you wouldn’t want to just do an: alias mvim=”open -a MacVim” in your .profile?
    Just tried it out and it works great for me!

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">