2

So I've created some helper functions to help work around netrw. They are some keybind simplifications that help with things like window/cursor management when opening files, where to open them, handing marked files, closing netrw when done etc.

I map them with the following in my vimrc:

augroup NetrwGroup
    autocmd!
    autocmd filetype netrw call NetrwMapping()
augroup END
function! NetrwMapping()
    setl bufhidden=wipe | setl modifiable
    nmap <buffer>x mf
    nmap <buffer>. cd
    nmap <buffer>C :NetrwTouch<CR>
    nmap <buffer>D :NetrwDelete<CR>
    nmap <buffer>h o
    nmap <silent> <buffer>T :call NetrwOpenInTabs()<CR>
    nmap <silent> <buffer>W :call NetrwOpenInWindows()<CR>
    nmap <silent> <buffer>B :call NetrwOpenInBuffers()<CR>
    " remap refresh to <c-r>, to free up <c-l>, so that it doesn't overwrite my window navigation mapping
    nunmap <buffer><C-r>
    nmap <buffer><C-r> <Plug>NetrwRefresh
    nmap <buffer><C-l> <C-w><Right>
endfunction

Now, what I'm trying to add is an alternate to normal v mapping. I know that normally v is mapped to <SNR>76_NetrwSplit(5)<CR>). I'd like to be able to call that in my function and then do some additional things. So the mapping would be:

nmap <buffer>v :call NetrwOpenVirtSplit()<CR>

and a simple example of my function:

function! NetrwOpenVirtSplit()
    normal v
    " some other stuff
    " move the cursor
    " close netrw etc.
endfunction

Pressing v in netrw got me this: NetrwOpenVirtSplit too recursive

So if that's an easy fix, I'd like to do that and go about my life.

Alternatively, and more complicatedly, I've looked into calling <SNR>76_NetrwSplit(5) directly, but I can't quite figure out the name spacing situation. This worked for example:

nmap <buffer>v :call <SNR>76_NetrwSplit(5)<CR>

But I know that the <SNR>76_ part depends on the script ordering and is subject to change and could break etc. I've tried to dynamically compute it via the s:SID() function mentioned in the :h <SID> help but don't know where to put it or how to use it. Plus, I can't really easily add additional functionality to that in an easy way like I do with the other mappings.

Note: regarding namespacing, all the above mappings/function definitions are in my vimrc and <SID>NetrwSplit() is in autoload/netrw

  • As a rule of thumb always use non recursive version of the map command unless you know what you are doing so here try to make your mapping nnoremap <buffer>v :call NetrwOpenVirtSplit()<CR>, and as a second rule of thumb when in a script always force normal not to follow your mapping so in your function use normal! v. I think doing so has good chances to fix your recursive call. – statox May 15 at 7:51
  • That is my understanding for nnoremap as well. However, for mapping netrw commands, only nmap seems to have any effect at all. And normal! v did stop the recursive descent, however it ended up only activating visual mode; it didn't run the normal netrw command for v unfortunately. – brianclements May 15 at 9:05
  • I'm I see where is the issue. I think you have two options: 1) you don't use v in your mapping but V and this way the mapping is not recursive anymore or 2) in NetrwOpenVirtSplit() you replace normal v by a call to NetrwSplit or to your own code which will get the file under the cursor and open it in a split. I'll try to make the second option work if I have time today :) – statox May 15 at 9:56
1

First let's understand what is the problem with your current code:

You need to use normal v in your NetrwOpenVirtSplit() function because that is how netrw opens a split. But as you have mapped v with a recursive mapping, you recursively call your function infinitely.

About calling NetrwSplit() directly from your mapping, I could be wrong but as this is a private function of the netrw plugin I don't think you can use it in a mapping defined outside of the plugin. This is because this local function feature was made to create private functions not callable by other scripts.


So we have several solutions:

The first one is my favorite: you keep everything as it is currently but you use another key in your mapping e.g V (nmap <buffer> V :call NetrwOpenVirtSplit()<CR>) this way you don't map an already used key and you don't have any recursion issues anymore.

The second one is a bit more complex but based on my tests it seems to be working: Instead of using normal v in your function, you can reimplement the split opening by yourself with execute 'vsplit ' . expand('<cfile>').

However this will not work in all situations because expand('<cfile>') will return only the filename in the netrw window and not its complete path so if you are exploring a directory which is not the current one you will open new files in the current directory (and of course they will be empty).

To fix that you can use let g:netrw_keepdir= 0 to keep the current directory synced with your netrw browsing but this may have other undesired side effects.

If you are fine with that, the solution could be like this:

let g:netrw_keepdir= 0

augroup NetrwGroup
    autocmd!
    autocmd filetype netrw call NetrwMapping()
augroup END

function! NetrwMapping()
    nnoremap <buffer> v :call NetrwOpenVirtSplit()<CR>
endfunction

function! NetrwOpenVirtSplit()
    execute 'vsplit ' . expand('<cfile>')
    norm! 5j
endfunction

As a side note I really recommend that you get rid of your NetrwGroup autocommand group and NetrwMapping() function because all you are doing here is reimplementing something Vim is already doing.

Instead you should just create the file ~/.vim/after/ftplugin/netrw.vim. In this file put only the content of your NetrwMapping() function.

Your new ftplugin file will be source when the filetype of a buffer is set to netrw (i.e. when you open Netrw) and you don't have to bloat your config with one more autocommand group.

Here is an amazing resource to help you doing this migration.

| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.