0

Short Question:

What is netrw function for up one level, how can I call it in my own function?

Context:

After installing a new ruby I had to reinstall vim to get it working again. But now netrw has some issue where if I press - to go up one level, it goes up a level and then starts visual mode such that I can't just make it go up another level without Esc every time.

I have an idea for a hacky solution, but I don't know what the function netrw uses to go up a level is, and I don't want to emulate it by searching for line .. and pressing enter, since then netrw won't remember my place I think. Incidentally I have a shortcut for opening Rex, but undoing it doesn't fix the problem. vim -u NONE then :se nocp :so $VIMRUNTIME/plugin/netrwPlugin.vim then :E will make it behave, but I don't have any clue where in my vimrc or plugins something could be going wrong. For now I think if I know the uponelevel function I could use it as a good band aid to the problem.

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

function! NetrwMapping()
    noremap <buffer> - -
endfunction

fu! OpenExplorer()
    " if &ft ==# "netrw"
    "   exe "-"
    " endif
    try
        :Rex
    catch
        :Ex
    endtry
endfu

" Like :Ex, but put curson on file you opened from.
nno - :call OpenExplorer()<CR>
4

Ignoring the context, the answer to your "short question" is that you can't (easily) call it from your own function.

You can find out what function - invokes in netrw by entering the following command in a netrw buffer:

:verbose nmap -

For me, the output contains this function call:

call <SNR>68_NetrwBrowseUpDir(1)

But what is <SNR>?

:help <SNR> and :help s: give the full details, but the gist is that when defining a function you can prepend its name with s: which means that the function can only be called from within the script it's defined. This allows you to name your functions anything you like, safe in the knowledge that it can't be accidentally (or deliberately) called from elsewhere.

The presence of <SNR> at the beginning of the function name means that the netrw "go up a directory" function was defined using this technique, and thus you cannot map to it yourself. (At least, not without some shenanigans.)

Thus, I'd argue you might be better off attempting to track down the root cause of the original issue with netrw's - mapping.

However, if you're determined to forge ahead, the answer lies in the sid item in the description of maparg()'s {dict} parameter.

Try the following:

augroup netrw_mapping
  autocmd!
  autocmd FileType netrw call NetrwMapping()
augroup END

function! NetrwMapping() abort
  let netrw_sid = maparg('s', 'n', 0, 1)['sid']

  execute 'nnoremap <buffer> - :<C-U>call <SNR>'.netrw_sid.'_NetrwBrowseUpDir(1)<CR>'
endfunction

Note that, rather than checking the SID of the - mapping, we inspect a different mapping that netrw makes. This is necessary to ensure that we don't overwrite the correct original SID with the SID of our mapping if the function is run more than once in the same buffer. There are other ways to ensure that we always use the correct SID, but this is the simplest.

  • Hm, I see thanks. I wonder if that's not going to play nicely with vim sessions, not sure if SID is maintained when reloading a vimsession, particularly if any changes have occurred in terms of plugin load order or what have you. This is going to be a hard one to find the root cause of, because it occurred after brew install vim, not after changing my vim files, so if it's in my vim files it's because something in my vimfiles relied on some quirk in my last installation of vim. Although I have updated vim in the past with no issues, and I'm still on the same version of vim. Just new ruby. – Wumbo Oct 4 at 17:16
0

Thanks Rich for giving me some direction. I couldn't get Rich's solution to work for me directly, but this little hacky bandaid will do for now until I get some time to look into this. If anyone else experienced this bug I would love to know, especially if you solved it more directly. I took the script number function from another stackexchange answer here

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


" n  -            @<Plug>NetrwBrowseUpDir<Space>
"         Last set from /usr/local/Cellar/vim/8.1.2100/share/vim/vim81/autoload/netrw.vim line 6331
    " E117: Unknown function: <SNR>1_NetrwBrowseUpDir

func! GetScriptNumber(script_name)
    " Return the <SNR> of a script.
    "
    " Args:
    "   script_name : (str) The name of a sourced script.
    "
    " Return:
    "   (int) The <SNR> of the script; if the script isn't found, -1.

    redir => scriptnames
    silent! scriptnames
    redir END

    for script in split(l:scriptnames, "\n")
        if l:script =~ a:script_name
            return str2nr(split(l:script, ":")[0])
        endif
    endfor

    return -1
endfunc

fu! GetScriptPathForNormalMapping(mapping)
    redir => result
    exe 'silent verbose nmap '. a:mapping
    redir END
    return matchstr(result, '\v[\~\/][^ ]*')
endfu

fu! UpALevel()
    call eval(
            \ printf("<SNR>%d_NetrwBrowseUpDir(1)",
            \ GetScriptNumber(g:netrw_path)))
endfu

function! NetrwMapping()
    if !exists( "g:netrw_path" )
        let g:netrw_path = GetScriptPathForNormalMapping('-')
    end
    nno <buffer> - :call UpALevel()<CR>
endfunction
  • Try the code in my new answer. It should do the same as your code, but is quite a bit simpler. Let me know if you have any problems! – Rich Oct 4 at 22:44
  • Thanks for looking again Rich. I find your solution a little circuitous (getting the script id via the mapping for s (sort) and relying on the context of being in a netrw buffer), so I'll keep using the GetScriptName function from the other answer, as it's a bit more flexible and easier for me to understand. I think getting the script number each time the function is called is a little safer as well against reloaded sessions. I guess you would like your answer to be marked correct, so I will do so. My hard coded path is the main issue with my solution. Might need system('find') to get it. – Wumbo Oct 6 at 21:20
  • Haha. Funnily enough, I think your solution is a little circuitous, but there’s no accounting for taste! ;). FWIW, it’s pretty easy to get the SID from the - instead of the s mapping (let me know if you’re interested in how), but again, I thought my current solution was simpler and more direct. Anyhow, thanks for awarding me the accepted answer even though you’re not actually using my code, and I’m glad to have been able to help point you in the right direction! – Rich Oct 6 at 21:56
  • I found a way to get the script fairly easily. It relies on context like your solution, but better than nothing. I'll put it in my answer. My problem is so unusual that no one is very likely to make use of it, but for completeness sake. – Wumbo Oct 6 at 22:10

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.