10

Is there a way to get vim's gf command (or something similar) to recognize a +{cmd} argument for file-paths?

One can start vim and go directly to a search term by doing:

$ vim +/coding_effort ~/project/file

Similarly, in command mode you can do:

:e +/coding_effort ~/project/file

I often take notes and refer to other files like this:

For more info see +/coding_effort ~/project/file where there's a detailed discussion.

I'd love to be able to select +/coding_effort ~/project/file, type something like gf, and have vim do the right thing.

1
  • I don't think there is any way of doing this by default, but you could probably create a plugin which does. Commented May 17, 2015 at 13:29

4 Answers 4

6

No, the first problem is that the necessary space between command and filename is not part of the 'isfname' option, and adding it would mean file borders couldn't be recognized at all. You'd either have to always use a visual selection (of both command and filename), or implement custom parsing in a gf command overload.

Secondly, the built-in gf doesn't recognize the +{cmd} syntax, so there's really no way around implementing your own custom mapping. This is certainly possible (using functions like expand('<cfile>'), getline(), etc.), but probably takes a few lines of Vimscript function.

If you choose a different syntax (filename:+command), you can keep the original gf commands and implement the splitting in an :autocmd, similar to what the very related file:line plugin does for filespec:linenumber).

2

Here's a function which should replicate the built-in gf behaviour, and also scans for works starting with + which then gets passed to :edit

fun! GotoWithCommand()
        let l:file = expand('<cfile>')

        " Replace ~ with the home directory
        let l:file = substitute(l:file, '^\~', $HOME, '')

        " File doesn't exist; return
        if !filereadable(l:file)
                echohl ErrorMsg
                echomsg "E447: Can't find file \"" . l:file . '" in path'
                echohl None
                return 0
        endif

        " Save cursor postion
        let l:save_cursor = getpos(".")

        " Make sure we go to the previous word (B on the first character of the
        " filename goes to the previous word, B anywhere else goes to the start of
        " the filename)
        call search('\(\s\|^\)', 'b')

        " Try to find argument to be executed; these start with a `+'
        let l:commands = ''
        while 1
                " Go one WORD backwards
                normal! B

                " Not a +; stop the loop
                if getline('.')[col('.') - 1] != '+'
                        break
                endif

                let l:commands .= ' ' . expand('<cWORD>')
        endwhile

        " Restore cursor postion
        call setpos('.', l:save_cursor)

        " Now, open the new file...
        execute ':edit ' . l:commands . ' ' . l:file
endfun

nnoremap <Leader>gf :call GotoWithCommand()<CR>

The comments should explain what the function does in more detail ... I wrote this on the train the morning, and (obviously) didn't test it extensively ;-)

2

One option would be (assuming the +cmd file/path is selected) to yank the selection to a register, then insert that register in the command prompt. I did this with the following mapping:

vnoremap gsf "0y:e <c-r>0<cr>

2

This notation works (file name + trailing : + / pat /)

/usr/include/stdio.h:/int printf/  -- `file name` + `:` + `/ pat /`
/usr/include/stdio.h:+/int printf/
/usr/include/stdio.h: /int printf/
/usr/include/stdio.h: /#define\s/
/usr/include/stdio.h: /^#define\sSEEK/

with function and mapping below.

nn gf :<C-u>call Gf_search()<CR>

func! Gf_search() abort
    let word = expand('<cWORD>')
    let idx = stridx(word, ':')
    if idx != -1
        let remline = strpart(getline('.'),col('.'))
        let pat = substitute(remline, '.*[:+ ]/\([^/]*\)/.*', '\1', "")
        if pat != remline
            let pat = substitute(pat, '\\', '\\\\', 'g')
            let pat = substitute(pat, ' ', '\\ ', 'g')
            let filename = expand('<cfile>')
            exec 'e +/'.pat filename
            return
        endif
    endif
    " fallback to builtin gF
    norm! gF
endfunc

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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