I am currently working on some workflow and tools to be able to spend more time in a terminal and on the keyboard instead of window swapping and mouse clicking. I have a Windows laptop, but heavily use Cygwin. One of my goals is to be able to manage all aspects of a project within a terminal. Part of working in a project is navigating the folder structure and opening documents. To replace Windows Explorer, in my terminal setup I am using the NerdTree and CtrlP Vim plugins. This works great for most files, but I ran into problems trying to open .xlsx and .docx documents. This post is about how I overcame these problems with some custom vimscript and configuration for these vim plugins.

When opening one of these (non-text) files from either NerdTree or CtrlP, I would get a zip file error.

zipinfo:  cannot find either /path/to/file or /path/to/file2

After this error, the file would open in a Vim buffer displaying the raw contents of the file. Although I did not find the actual reason Vim treats these files as a zip file, I did find a resolution. I added the following 2 lines to my .vimrc file to disable autoloading of the zip plugin.

let g:loaded_zipPlugin= 1
let g:loaded_zip      = 1

With those to lines in the configuration, the zipinfo error is fixed, but the files still open in a Vim buffer. I needed to find a way to open these documents in the appropriate external program.

The solution for NerdTree is to create a custom mapping and to write a function to handle the new action key. I created a function that uses the windows cmd command to open the file using the windows default program. Then I created the mapping for <Shift-E> which will call the new function. I put the vimscript below in a file called mymappings.vim and placed it into the nerdtree_plugin directory under the NerdTree install path so that it would automatically get loaded with the plugin.

function! _openExternalProgram(options)
    try
        let path = g:NERDTreeFileNode.GetSelected().path.str({'escape': 1})
    catch
        return
    endtry

    let cmd = "cmd /c start \"\" \"`cygpath -w " . path . "`\""
    call system(cmd)

endfunction

call NERDTreeAddKeyMap({
    \ 'key': 'E',
    \ 'callback': '_openExternalProgram',
    \ 'quickhelpText': 'Open in external program',
    \ 'scope': 'Node' })

If everything loads correctly, you should see the new mapping under the ‘Custom mappings’ section of the NerdTree quickhelp view.

Now, when I need to open a file that’s not a text file from NerdTree, I can type a capital ‘E’ instead of ‘o’ to open it in the default program. This also works on directories. <Shift-E> on a directory will open that directory in Windows Explorer.

You might notice the use of cygpath in the command above. I am using Vim in Cygwin, so all the paths are Cygwin unix style paths. Since I am calling the cmd command, which is a Windows program, the path must be converted to the Windows path for the file. The -w option to cygpath does this.

The solution for CtrlP is very similar. But, instead of creating a new command, you write a custom function to override the open file action and apply logic to decide how to open it. Here is my function and configuration for CtrlP.

function! CTRLPExternalProgramOpenFunc(action, line)
    if a:action =~ '^[t]$'
        
        " Get the filename
        let filename = fnameescape(fnamemodify(a:line, ':p'))
        
        " Close CtrlP
        call ctrlp#exit()
        
        " Open the file
        let cmd = "cmd /c start \"\"  \"`cygpath -w " . filename . "`\""
        call system(cmd)
        
    else
    
        " Use CtrlP's default file opening function
        call call('ctrlp#acceptfile', [a:action, a:line])
        
    endif
endfunction

let g:ctrlp_open_func = { 'files': 'CTRLPExternalProgramOpenFunc' }

In CtrlP, <c-t> is the command to open a file in a new tab. This action is represented by value ‘t’ in the action argument of the open function. I’m hijacking this action in order to open a file using the cmd command. This is working out for me right now, but if I begin to have more need to open text documents in tabs in Vim, I’ll need to modify this function to be more strict on which files are opened with cmd and which I want to be opened directly in Vim.

I am still working on this terminal setup and my discipline to break the habit of grabbing the mouse. These improvements in Vim have gotten me a little closer. I’m interested to hear other tips and tricks for ‘life in a terminal’. If you have any, please share in the comments section.

Share this on: