Here's one way of doing this with no plugins:
It follows the structure of romainl's Redir.vim script and writes the output of
oldfiles (which is not the same thing as
browse oldfiles) to a temporary buffer opened with a vertical split.
It also defines a function
VisitFilesFromLineRange that allows a user to visit multiple files at once if a line range in visual line mode is currently active.
I also defined a wrapper function around
VisitFilesFromLineRange that takes an explicit range argument and rebound
gf in visual mode so that it ultimately calls
Oldfiles function, by itself, is far more useful than the new
gf binding in my opinion since it makes it easy to search through the list of old files using ordinary vim bindings.
" kill all the windows called oldfiles_scratch
" we don't need them around anymore
for win in range(1, winnr('$'))
if getwinvar(win, 'oldfiles_scratch')
execute win . 'windo close'
" redirect the stuff that normally prints to the console
" to the variable "output". Note that in "redir => output"
" output refers to the variable itself not its contents.
" If VimL were more like C, this would be written
" redir => &output
redir => output
" create a new pane thingy but vertically. seems to create it
" on the left by default which is cool.
" tag this particular window with out variable outfiles_scratch
" so we know to kill it later.
let w:oldfiles_scratch = 1
" our new window is ephemeral, give it ephemeralish properties.
" NOTE: explicitly make the buffer modifiable NOW.
" we set it to nomodifiable after trimming the line numbers.
setlocal buftype=nofile bufhidden=wipe nobuflisted noswapfile modifiable
" dump the contents of output as lines
call setline(1, split(output, "\n"))
" remove the leading numbers. Leading numbers make it harder to ``gf" the lines we want.
execute "%s/^\\d\\+: //"
" go to the top line
" make it nomodifiable
command! -nargs=0 Oldfiles silent call Oldfiles()
function! VisitFilesFromLineRange(low, hi)
" save the current buffer index
" so we can read from it later
let current_buf = bufnr("%")
" keep track of whether we spawned a new tab already or not
" there isn't really a convenient way to spawn a truly-empty
" container tab that we then fill up so it's easier to spawn
" the container "on demand" as we process our first line
let did_tabnew = 0
" walk the array of lines from the buffer
for line in getbufline(current_buf, a:low, a:hi)
" escape the line name before doing stuff to it
let eline = fnameescape(line)
" perform a horizontal split or tabedit depending on whether
" the new tab has been created or not
execute("split " . eline)
let did_tabnew = 1
execute("tabedit " . eline)
command! -nargs=+ VisitFilesFromLineRange silent call VisitFilesFromLineRange(<f-args>)
" define a function that accepts a range
" and forwards it into VisitFilesFromLineRange with the appropriate arguments
function! RangeFuncVisitFilesFromRange() range
call VisitFilesFromLineRange(a:firstline, a:lastline)
" VisitLines command accepting a range argument for convenience
command! -range VisitLines silent <line1,line2>call RangeFuncVisitFilesFromRange()
" detect visual line mode and call RangeFuncVisitFilesFromRange
" otherwise delegate to the default implementation of "gf" for visual mode
xnoremap <silent> <expr> gf (mode() ==# "V") ? ":call RangeFuncVisitFilesFromRange()\<cr>" : "gf"