-
-
Notifications
You must be signed in to change notification settings - Fork 685
Description
Steps to reproduce
Hello, I apologize in advance for this crappy bug report. But something that used to work with MacVim is no longer working. But I don't know when exactly this broke because I wasn't upgrading my MacVim regularly and had been away in Xcode for the past couple of years.
The problem is centered around a launcher script that I've been using with MacVim for over a decade. It is derived from this old Gist "by Wout Mertens and suggestions from Laurent Bihanic".
https://gist.github.com/shakefu/3780676
The intent of this script is when opening a file on the command line with MacVim, it will first check to see if that file is already open in MacVim. If so, it will bring forward the already open window instead of opening the file again in another new window (and avoid triggering the alert 'Swap file already exists').
Well, something has changed semi-recently such that this is no longer working for me. I see when I invoke my script on the command line, I see error messages in my console "Send expression failed".
For completeness, below is the full version of my (modified) script from the gist. It's been north of a decade since I've touched it. I remember at the time, the Gist didn't fully work for me out of the box, so I modified it. But I honestly forgot how everything works.
# This script is in my /usr/local/bin/gvim
#!/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=
# http://superuser.com/questions/205127/how-to-retrieve-the-absolute-path-of-an-arbitrary-file-from-the-os-x
function abspath() { pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null; }
# 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
# 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.
did_find_file=0
declare -a found_file_list
# The following code tries to handle the case where a file is already
# open in a window/tab. Instead of alerting the user the file is
# already open, we should just bring the window/tab forward.
# Get the list of VIM servers so we can query each one to find if it
# contains the file we are looking for
serverlist=`"$binary" --serverlist`
for server in $serverlist; do
# echo "server is $server"
# Lookup each file in this server to see if it contains the an already
# open window/tab we are looking for.
for a_file in "${1:+"$@"}"; do
# echo "a_file $a_file"
# We need to disambiguate between files of the same name but
# from different directories.
# MacVim seems to understand absolute files in remote-expr,
# so we just need to focus on getting the absolute path
# of the requested file.
absolute_path_of_requested_file=`abspath $a_file`
# echo $absolute_path_of_requested_file
# This remote expression found in MMAppController.m,
# (see evaluateVimExpression) will return the number of
# buffers matching our request. If we get more than 0 back,
# the server already has the file we are looking for open.
# We request one file here, so the max we get back should be 1.
ret_num=`"$binary" --servername $server --remote-expr "map([\"$absolute_path_of_requested_file\"],\"bufloaded(v:val)\")"`
# echo "ret_num $ret_num"
# The next problem is that
# absolute_path_of_requested_file=abspath $a_file
# absolute_path_of_
if [ "$ret_num" != "" ] && [ "$ret_num" -gt 0 ]; then
# The server has the window/tab we are looking for.
# This remote-send command will send some commands to
# make the window/tab come forward.
`"$binary" -g $opts --servername $server --remote-send "<C-\\><C-N>:let oldswb=&swb|let &swb=\"useopen,usetab\" | tab sb $absolute_path_of_requested_file |let &swb=oldswb|unl oldswb|cal foreground()<CR><CR>"`
did_find_file=1
# echo "found file: $a_file, adding to found_file_list"
# Because multiple files could be passed as arguments,
# we need to keep track of which files we found so we
# can later distinguish which files we need to create/open.
found_file_list[${#found_file_list[*]}]=$a_file
#found_file_list=( "${found_file_list[@]}" "$a_file" )
fi
done
done
# If multiple files were listed as arguments, there is a possibility
# there is a mixture of both types.
# So we need to determine the list of files we didn't find already open
# and open those up.
if [ "$did_find_file" == 0 ]; then
# If we found no files at all, we have a trivial case and can just
# do the standard behavior.
exec "$binary" -g $opts ${1:+"$@"}
else
declare -a new_file_list
# We will loop through all files and compare to all files we found
# to be open. If the file was not opened, we add this to our
# new_file_list so we can act on it later.
for a_file in "${1:+"$@"}"; do
# echo "a_file2 $a_file"
# Initialize flag...assume we need to open new file until we find it
need_to_create_file=1
# for-in syntax doesn't seem to work with arrays
# ...can only get first value.
# Using numeric for instead
for ((i=0; i<${#found_file_list[*]}; i++)); do
found_file=${found_file_list[$i]}
# echo "found_file2 $found_file"
if [ "$found_file" == "$a_file" ]; then
# echo "$found_file == $a_file"
# We can skip this file
need_to_create_file=0
break
fi
done
if [ $need_to_create_file == 1 ]; then
# Append the file to the list
new_file_list[${#new_file_list[*]}]=$a_file
fi
done
# If there are remaining files, pass the list to open new windows
if (( ${#new_file_list[@]} > 0 )); then
exec "$binary" -g $opts "${new_file_list[@]}"
fi
fi
else
exec "$binary" $opts ${1:+"$@"}
fi
Expected behaviour
So assuming I have some file, e.g. main.c:
- Going to Terminal.app and typing, 'gvim main.c' will open the file in MacVim.
- Then going back to Terminal.app, and doing 'gvim main.c' a second time will make MacVim the front-most app and bring the main.c window to become the active frontmost window. (It will not try to open main.c a second time and will not create a second window with main.c.)
But maybe this bug is irrelevant if there is a new/better way to accomplish what this script was doing. I actually don't care that much about this specific bug, but I just want an easy way to accomplish the exact same thing. For context, I have a habit of keeping a lot of windows open because I'm often going back and forth between files, and I forget what I have open. I also have terminals in my various source directories, so I just type 'gvim file_i_want' all the time to get to the file I want to get back to.
Version of MacVim
MacVim r182 (Vim 9.1.1887)
MacVim installation method
This was downloaded from the GitHub release a very long time ago, and use the built-in updater to update to later versions through the many years.
Environment
macOS version: 15.7.3 (24G419)
Mac: Mac mini 2018
terminal: Apple Terminal
Logs and stack traces
Vim and MacVim configuration where issue is reproducable
$ defaults read org.vim.MacVim | grep 'MM|SU'
MMAutosaveColumns = 162;
MMAutosaveRows = 55;
MMCurrentPreferencePane = General;
MMLastUsedBundleVersion = 182;
MMRenderer = 0;
MMSuppressTerminationAlert = 1;
MMTopLeftPoint = "{505, 1055}";
"NSWindow Frame SUStatusFrame" = "1187 307 400 135 0 0 1920 1055 ";
"NSWindow Frame SUUpdateAlert" = "650 511 620 392 0 0 1920 1055 ";
"NSWindow Frame SUUpdateAlertFrame" = "1078 133 566 395 0 0 1680 1028 ";
SUCheckAtStartup = 1;
SUEnableAutomaticChecks = 1;
SUHasLaunchedBefore = 1;
SULastCheckTime = "2026-01-13 21:18:39 +0000";
SUUpdateGroupIdentifier = 1092579332;
SUUpdateRelaunchingMarker = 1;
.gvimrc
" Use Vim settings, rather then Vi settings (much better!).
" This must be first, because it changes other options as a side effect.
set nocompatible
" Security options
"set exrc
"set secure
if has('gui_running')
" Sets horizontal scrollbar
set guioptions+=b
" Sets the font and height (size)
set gfn=Monaco:h12
else
endif
" command history
set history=50
filetype plugin indent on
syntax enable
set backspace=indent,eol,start
set ruler
set showcmd
" Sets tabs to 4 spaces
set tabstop=4
" Sets >> to 4 spaces
set shiftwidth=4
" will force backspace to actually visibly erase characters as it happens
set cpo-=v
" highlights search words
set hlsearch
" read/write a .viminfo file, remember 10 files, don't store more than 50 lines of registers
set viminfo='10,\"50
" Remember where you were reading from last time
au BufReadPost * if line("'\"") > 0|if line("'\"") <= line("$")|exe("norm '\"")|else|exe "norm $"|endif|endif
"
set colorcolumn=80
highlight ColorColumn ctermbg=lightgray guibg=lightgray
"set columns=80
"set lines=40
"set wrapmargin=80
" Problems with wrapping and resizing windows. Disable for now.
set nowrap
"textwidth
set tw=0
"set backup
" Get vim to highlight (),{},[] pairs as they are entered
set showmatch
" Speed up the highlight time for showmatch
set matchtime=2
" Set matchpairs will let you change the pairs to check?
" Highlight as you type the string
"set incsearch
" Case insensitive searches with smartness
" Smartcase will match anything when all lowercase, but
" if any uppercase letters appear in the search critera, it only
" does exact matching
set ignorecase
set smartcase
" smarter indenting
"set cindent
"set smartindent
" Set indenting style
" g0 will set public: private: protected: to flush left
" t0 will keep int func() when on separate lines flush left
" (0,w1 is supposed to do something with complex multiline
" expressions like when &&, ||, etc are used
" (s,m1 is supposed to flush the closing parenthesis left when
" mulitple lines are used
"set cino=g0,t0,(0,w1,(s,m1,
" Set to automatically format comments (formatoptions)
" r triggers autocomment with return.
" c autowraps comments to textwidth
"set fo=rc
" Remap TAB to keyword completion
"function! InsertTabWrapper(direction)
" let col = col('.') - 1
" if !col || getline('.')[col - 1] !~ '\k'
" return "\<tab>"
" elseif "backward" == a:direction
" return "\<c-p>"
" elseif "forward" == a:direction
" return "\<c-n>"
" else
" return "\<c-x>\<c-k>"
" endif
"endfunction
"inoremap <tab> <c-r>=InsertTabWrapper ("forward")<cr>
"inoremap <s-tab> <c-r>=InsertTabWrapper ("backward")<cr>
"inoremap <c-tab> <c-r>=InsertTabWrapper ("startkey")<cr>
" toggle tab completion
"function! ToggleTabCompletion()
" if mapcheck("\<tab>", "i") != ""
" :iunmap <tab>
" :iunmap <s-tab>
" :iunmap <c-tab>
" echo "tab completion off"
" else
" :imap <tab> <c-n>
" :imap <s-tab> <c-p>
" :imap <c-tab> <c-x><c-l>
" echo "tab completion on"
" endif
"endfunction
"map <Leader>tc :call ToggleTabCompletion()<cr>
" tell complete to look in the dictionary
"set complete-=k complete+=k
" load the dictionary according to syntax
":au BufReadPost * if exists("b:current_syntax")
":au BufReadPost *ÊÊ let &dictionary = substitute("C:\\vim\\vimfiles\\dict\\FT.dict", "FT", b:current_syntax, "")
":au BufReadPost * endif
" http://www.alexeyshmalko.com/2014/using-vim-as-c-cpp-ide/
" By default, vim assumes all .h files to be C++ files. However, I work with pure C and want filetype to be c. Since project also comes with doxygen documentation, I want to set subtype to doxygen to enable very nice doxygen highlighting.
augroup project
autocmd!
autocmd BufRead,BufNewFile *.h,*.c set filetype=c.doxygen formatoptions-=cro
augroup END
" Disable middle click (Magic Trackpad with middle tap plugin is too sensitive)
nnoremap <MiddleMouse> <Nop>
nnoremap <2-MiddleMouse> <Nop>
nnoremap <3-MiddleMouse> <Nop>
nnoremap <4-MiddleMouse> <Nop>
inoremap <MiddleMouse> <Nop>
inoremap <2-MiddleMouse> <Nop>
inoremap <3-MiddleMouse> <Nop>
inoremap <4-MiddleMouse> <Nop>
" clang-format
map <C-K> :pyf /opt/local/bin/clang-format.py<cr>
imap <C-K> <c-o>:pyf /opt/local/bin/clang-format.py<cr>
let g:ycm_global_ycm_extra_conf = "~/.vim/.ycm_extra_conf.py"
" set the runtime path to include Vundle and initialize
set rtp+=~/.vim/bundle/Vundle.vim
call vundle#begin()
" alternatively, pass a path where Vundle should install plugins
"call vundle#begin('~/some/path/here')
" let Vundle manage Vundle, required
Plugin 'gmarik/Vundle.vim'
Plugin 'Valloric/YouCompleteMe'
Plugin 'rhysd/vim-clang-format'
" All of your Plugins must be added before the following line
call vundle#end() " required
filetype plugin indent on " required
" To ignore plugin indent changes, instead use:
"filetype plugin on
"
" Brief help
" :PluginList - lists configured plugins
" :PluginInstall - installs plugins; append `!` to update or just :PluginUpdate
" :PluginSearch foo - searches for foo; append `!` to refresh local cache
" :PluginClean - confirms removal of unused plugins; append `!` to auto-approve removal
"
" see :h vundle for more details or wiki for FAQ
" Put your non-Plugin stuff after this line
How was MacVim launched?
- by launching MacVim.app in macOS (by using the Dock, Spotlight, or the
opencommand) - by running mvim/gvim in the terminal
Issue has been tested with clean configuration
- by running
mvim --clean(orgvim, supplied by MacVim distribution). You can also use the File → "New Clean Window" menu item within MacVim. - by running
vim --clean(in terminal, supplied by MacVim distribution) - by running
vim --clean(in terminal, other suppliers, e.g. /usr/bin/vim)