16

Lately I've been drastically reducing the number of plugins I'm using so I'm looking for answers using only built-in Vim features.

I've been noticing a flaw in my workflow when I'm editing a lot of buffers: I am on a buffer and I want to switch to another one of which I don't remember the name and which isn't the alternative buffer. So using :b# is not possible and using the completion with the command :b isn't convenient neither.

To do that I was doing the following:

  • First use :buffers to get the list of open buffers
  • Remember the number of the buffer I want to use
  • Press Enter to close the list of buffers
  • Use :bX with X the number of the buffer to use.

This workflow seemed pretty heavy to me so I added the following lines to my .vimrc:

" Improve the navigation in the buffers 
nnoremap <Leader><S-b> :B<CR> 
command! B call Buffers() 

function! Buffers() 
    execute "buffers" 
    let buffer=input("Enter a buffer:") 
    execute "b" . buffer 
endfunction 

This give me a command :B (and a mapping) which calls the function :buffers wait for an input and finally calls :b followed by the input.

It works well but I'm surprised that I had to develop such a function: usually Vim has a pretty efficient default workflow so I'm wondering if there is a better way to switch to a buffer of which I don't know anything excepted that it as already been opened in my editing session.

3
  • 3
    I do :ls followed by :b# (don't need to hit return or escape first, can go directly from the buffer list in 'ls'.) (Posting as comment because it doesn't seem substantial enough to be an answer? Also wondering if I'm missing something and answering the wrong question.) Commented Aug 11, 2016 at 13:49
  • 2
    @LindaJeanne: As I said :b# is not what I'm looking for but yeah the fact that you can save a keystroke by entering directly :b without pressing enter before is interesting. (Indeed that is maybe too light to be an answer, but it was worth a comment :-) )
    – statox
    Commented Aug 11, 2016 at 13:54
  • I'm guessing that LindaJeanne meant the # character as a stand-in for a (buffer) number, rather than the literal # character.
    – 8bittree
    Commented Oct 5, 2016 at 20:43

3 Answers 3

14

I've been noticing a flaw in my workflow when I'm editing a lot of buffers: I am on a buffer and I want to switch to another one of which I don't remember the name and which isn't the alternative buffer. So using :b# is not possible and using the completion with the command :b isn't convenient neither.

On the contrary, I find tab-completion to be extremely convenient. Setting the right options to values that work for you may help a lot. These are the relevant lines from my vimrc (they work for me but they may not work for you so don't just copy them):

nnoremap ,b :buffer *
set wildmenu
set wildignore+=*.swp,*.bak
set wildignore+=*.pyc,*.class,*.sln,*.Master,*.csproj,*.csproj.user,*.cache,*.dll,*.pdb,*.min.*
set wildignore+=*/.git/**/*,*/.hg/**/*,*/.svn/**/*
set wildignore+=*/min/*,*/vendor/*,*/node_modules/*,*/bower_components/*
set wildignore+=tags,cscope.*
set wildignore+=*.tar.*
set wildignorecase
set wildmode=full

With that, the right buffer is rarely more than five or six keystrokes away:

tab-completion


This give me a command :B (and a mapping) which calls the function :buffers wait for an input and finally calls :b followed by the input.

Barry Arthur came up with a much simpler solution years ago that has become quite popular since then:

nnoremap <leader>b :ls<CR>:b<Space>

of which I proposed a slightly more versatile variant a couple years ago:

nnoremap gb :ls<CR>:b

gb


Since we are talking vimscript, here is a nice little function I wrote that "auto-populates" the command-line with the right command stub after list-like commands like :ls or :ilist. The advantage of that function over the mappings above is that I don't have to remember specific mappings. It works just like Vim, but with a little twist.

" smooth listing
cnoremap <expr> <CR> <SID>CCR()

function! s:CCR()
    if getcmdtype() == ":"
        let cmdline = getcmdline()
            if cmdline =~ '\v\C^(dli|il)' | return "\<CR>:" . cmdline[0] . "jump  " . split(cmdline, " ")[1] . "\<S-Left>\<Left>"
        elseif cmdline =~ '\v\C^(cli|lli)' | return "\<CR>:silent " . repeat(cmdline[0], 2) . "\<Space>"
        elseif cmdline =~ '\C^changes' | set nomore | return "\<CR>:sil se more|norm! g;\<S-Left>"
        elseif cmdline =~ '\C^ju' | set nomore | return "\<CR>:sil se more|norm! \<C-o>\<S-Left>"
        elseif cmdline =~ '\C^ol' | set nomore | return "\<CR>:sil se more|e #<"
        elseif cmdline =~ '\C^undol' | return "\<CR>:u "
        elseif cmdline =~ '\C^ls' | return "\<CR>:b"
        elseif cmdline =~ '/#$' | return "\<CR>:"
        else | return "\<CR>" | endif
    else | return "\<CR>" | endif
endfunction

ccr


That said, I'm a big proponent of "symbol-based navigation" over "file-based navigation". When applicable, symbol-based navigation is much faster and much more economic than file-based navigation.

The last GIF shows one mean of symbol-based navigation, by the way. The example is silly but… oh well.

3
  • I guess I haven't played enough with the wild* options, I will try to tweak it my way. About the Barry Arthur solution it is pretty close to @nobe4's answer that's interesting. Finally I'll have to take a deeper look to your CCR() function to really get it but it sees promising. Thanks for your answer.
    – statox
    Commented Aug 11, 2016 at 11:40
  • 5
    See this answer of mine. Specially the comic.
    – romainl
    Commented Aug 11, 2016 at 11:46
  • I'll definitely steal some of these!
    – statox
    Commented Aug 11, 2016 at 11:52
8

I encountered the same problem a while ago and I found a solution:

nnoremap <leader>b :buffers<CR>:buffer<space>

This will open the buffer list and without hiding it, give you the option to switch buffer.

I think that it makes sense that Vim does not have a "list and select" option here, these are two separate actions. But Vim is powerful enough to combine command together.

Think of it as the Unix philosophy: Do one thing and do it well.

:buffers lists the buffers and :buffer xxx allows you to select one.

2
  • 1
    That's a good way to lighten the function I suggested in my question, interesting!
    – statox
    Commented Aug 11, 2016 at 11:37
  • 1
    If you want to make the space at the end more visible, you can do: nnoremap <leader>b :buffers<CR>:buffer<Space>| Commented Aug 11, 2016 at 12:39
1

For a while I used nobe4's technique like this:

nnoremap <Leader>b :set nomore<Bar>:ls<Bar>:set more<CR>:b<Space>

Then I started using Tab-completion with that (as romainl suggests)

But more recently I have found the fastest method with the fewest keystrokes is to use:

{count} CTRL-^

This is not so different from :b#<Enter> but as you say, you need to know the buffer number!

So for that I run the MiniBufExplorer plugin, which displays a list of filenames across the top (like almost every other editor). I'm sure there are numerous alternatives.

Finally, since Ctrl-^ is a bit of a stretch for my fingers, and this is a very common operation, I moved it over to Ctrl-E. The implementation for that is not too complex:

nnoremap <C-E> :<C-U>call SwitchToBuffer()<CR>

function! SwitchToBuffer()
  if v:count > 0
    exec v:count . "b"
    return
  endif

  " Whatever you want to do if you didn't provide a count
  " In this case, fall back to nobe4's technique:
  call feedkeys(":ls\n:b ")
endfunction

So my current buffer switching process goes something like this:

  1. Look at MiniBufExplorer
  2. Hit 3 Ctrl-E

Sadly MBE and the script above are not Vim builtins, as you requested. When I'm on a remote machine with no config, I just use :b [partial] and Tab-completion.

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.