├── README.md └── plugin └── bufferlist.vim /README.md: -------------------------------------------------------------------------------- 1 | # VIM bufferlist 2 | 3 | This is an implementation of 4 | [EMACS bufferlist](https://github.com/roblillack/emacs-bufferlist) for 5 | [Vim](https://www.vim.org) or [Neovim](https://neovim.io). 6 | 7 | [![asciicast](https://asciinema.org/a/468786.svg)](https://asciinema.org/a/468786) 8 | 9 | ## About 10 | 11 | Upon keypress this script display a nice list of buffers on the left, which can 12 | be selected with mouse or keyboard. As soon as a buffer is selected (Return, 13 | double click) the list disappears. 14 | 15 | The selection can be cancelled with the same key that is configured to open the 16 | list or by pressing `q`. Movement key and mouse (wheel) should work as one 17 | expects. 18 | 19 | Buffers that are visible (in any window) are marked with `*`, ones that are 20 | modified are marked with `+`. 21 | 22 | To delete a buffer from the list (i.e. close the file) press `d`. 23 | 24 | ## Installation 25 | 26 | As a Vundle user, add 27 | 28 | Plugin 'roblillack/vim-bufferlist' 29 | 30 | to your `~/.vimrc` or `~/.config/nvim/init.vim`. 31 | 32 | Alternatively, put bufferlist.vim file into your `~/.vim/plugin` directory and 33 | set it up like this in your `~/.vimrc`: 34 | 35 | ## Usage 36 | 37 | Configure a hotkey to open the bufferlist: 38 | 39 | map :call BufferList() 40 | 41 | Optionally, change some of the settings, like the default and maximum width or 42 | the colors used for the sidebar: 43 | 44 | let g:BufferListWidth = 25 45 | let g:BufferListMaxWidth = 50 46 | hi BufferSelected term=reverse ctermfg=white ctermbg=red cterm=bold 47 | hi BufferNormal term=NONE ctermfg=black ctermbg=darkcyan cterm=NONE 48 | 49 | ## License 50 | 51 | Copyright(c) 2005-2022, [Robert Lillack](https://roblillack.net/) \ 52 | Redistribution in any form with or without modification permitted. 53 | -------------------------------------------------------------------------------- /plugin/bufferlist.vim: -------------------------------------------------------------------------------- 1 | "=== VIM BUFFER LIST SCRIPT 1.4 ================================================ 2 | "= Copyright(c) 2005-2022, Robert Lillack = 3 | "= Redistribution in any form with or without modification permitted. = 4 | "= = 5 | "= INFORMATION ================================================================= 6 | "= Upon keypress this script display a nice list of buffers on the left, which = 7 | "= can be selected with mouse or keyboard. As soon as a buffer is selected = 8 | "= (Return, double click) the list disappears. = 9 | "= The selection can be cancelled with the same key that is configured to open = 10 | "= the list or by pressing 'q'. Movement key and mouse (wheel) should work as = 11 | "= one expects. = 12 | "= Buffers that are visible (in any window) are marked with '*', ones that are = 13 | "= Modified are marked with '+' = 14 | "= To delete a buffer from the list (i.e. close the file) press 'd'. = 15 | "= = 16 | "= USAGE ======================================================================= 17 | "= Put this file into you ~/.vim/plugin directory and set up up like this in = 18 | "= your ~/.vimrc: = 19 | "= = 20 | "= NEEDED: = 21 | "= map :call BufferList() = 22 | "= OPTIONAL: = 23 | "= let g:BufferListWidth = 25 = 24 | "= let g:BufferListMaxWidth = 50 = 25 | "= hi BufferSelected term=reverse ctermfg=white ctermbg=red cterm=bold = 26 | "= hi BufferNormal term=NONE ctermfg=black ctermbg=darkcyan cterm=NONE = 27 | "=============================================================================== 28 | 29 | if exists('g:BufferListLoaded') 30 | finish 31 | endif 32 | let g:BufferListLoaded = 1 33 | 34 | if !exists('g:BufferListWidth') 35 | let g:BufferListWidth = 20 36 | endif 37 | 38 | if !exists('g:BufferListMaxWidth') 39 | let g:BufferListMaxWidth = 40 40 | endif 41 | 42 | " toggled the buffer list on/off 43 | function! BufferList() 44 | " if we get called and the list is open --> close it 45 | if bufexists(bufnr("__BUFFERLIST__")) 46 | exec ':' . bufnr("__BUFFERLIST__") . 'bwipeout' 47 | return 48 | endif 49 | 50 | let l:bufcount = bufnr('$') 51 | let l:displayedbufs = 0 52 | let l:activebuf = bufnr('') 53 | let l:activebufline = 0 54 | let l:buflist = '' 55 | let l:bufnumbers = '' 56 | let l:width = g:BufferListWidth 57 | 58 | " iterate through the buffers 59 | let l:i = 0 | while l:i <= l:bufcount | let l:i = l:i + 1 60 | let l:bufname = bufname(l:i) 61 | if strlen(l:bufname) 62 | \&& getbufvar(l:i, '&modifiable') 63 | \&& getbufvar(l:i, '&buflisted') 64 | 65 | " adapt width and/or buffer name 66 | if l:width < (strlen(l:bufname) + 5) 67 | if strlen(l:bufname) + 5 < g:BufferListMaxWidth 68 | let l:width = strlen(l:bufname) + 5 69 | else 70 | let l:width = g:BufferListMaxWidth 71 | let l:bufname = '...' . strpart(l:bufname, strlen(l:bufname) - g:BufferListMaxWidth + 8) 72 | endif 73 | endif 74 | 75 | if bufwinnr(l:i) != -1 76 | let l:bufname = l:bufname . '*' 77 | endif 78 | if getbufvar(l:i, '&modified') 79 | let l:bufname = l:bufname . '+' 80 | endif 81 | " count displayed buffers 82 | let l:displayedbufs = l:displayedbufs + 1 83 | " remember buffer numbers 84 | let l:bufnumbers = l:bufnumbers . l:i . ':' 85 | " remember the buffer that was active BEFORE showing the list 86 | if l:activebuf == l:i 87 | let l:activebufline = l:displayedbufs 88 | endif 89 | " fill the name with spaces --> gives a nice selection bar 90 | " use MAX width here, because the width may change inside of this 'for' loop 91 | while strlen(l:bufname) < g:BufferListMaxWidth 92 | let l:bufname = l:bufname . ' ' 93 | endwhile 94 | " add the name to the list 95 | let l:buflist = l:buflist . ' ' .l:bufname . "\n" 96 | endif 97 | endwhile 98 | 99 | " Generate a variable full of non-breaking spaces 100 | " to fill the buffer afterwards" (we need this for "full window" color :) 101 | let l:fill = "\n" 102 | let l:i = 0 | while l:i < l:width | let l:i = l:i + 1 103 | let l:fill = ' ' . l:fill 104 | endwhile 105 | 106 | " now, create the buffer & set it up 107 | exec 'silent! ' . l:width . 'vne __BUFFERLIST__' 108 | setlocal noshowcmd 109 | setlocal noswapfile 110 | setlocal buftype=nofile 111 | setlocal bufhidden=delete 112 | setlocal nobuflisted 113 | setlocal nomodifiable 114 | setlocal nowrap 115 | setlocal nonumber 116 | setlocal norelativenumber 117 | 118 | " set up syntax highlighting 119 | if has("syntax") 120 | syn clear 121 | syn match BufferNormal / .*/ 122 | syn match BufferSelected /> .*/hs=s+1 123 | hi def BufferNormal ctermfg=black ctermbg=white 124 | hi def BufferSelected ctermfg=white ctermbg=black 125 | endif 126 | 127 | " Disable highlighting spaces at the end of the line, if 128 | " https://github.com/ntpeters/vim-better-whitespace is installed 129 | if exists(":DisableWhitespace") 130 | exec 'silent! :DisableWhitespace' 131 | endif 132 | 133 | setlocal modifiable 134 | if l:displayedbufs > 0 135 | " input the buffer list, delete the trailing newline, & fill with blank lines 136 | put! =l:buflist 137 | " is there any way to NOT delete into a register? bummer... 138 | "norm Gdd$ 139 | norm GkJ 140 | while winheight(0) > line(".") 141 | put =l:fill 142 | endwhile 143 | else 144 | let l:i = 0 | while l:i < winheight(0) | let l:i = l:i + 1 145 | put! =l:fill 146 | endwhile 147 | norm 0 148 | endif 149 | setlocal nomodifiable 150 | 151 | " set up the keymap 152 | noremap :call LoadBuffer() 153 | map q :bwipeout 154 | map j :call BufferListMove("down") 155 | map k :call BufferListMove("up") 156 | map d :call BufferListDeleteBuffer() 157 | map :call BufferListMove("up") 158 | map :call BufferListMove("down") 159 | map 160 | map :call BufferListMove("mouse") 161 | map <2-LeftMouse> :call BufferListMove("mouse") 162 | \:call LoadBuffer() 163 | map j 164 | map k 165 | map h 166 | map l 167 | map 168 | map 169 | map i 170 | map a 171 | map I 172 | map A 173 | map o 174 | map O 175 | map :call BufferListMove(1) 176 | map :call BufferListMove(line("$")) 177 | 178 | " make the buffer count & the buffer numbers available 179 | " for our other functions 180 | let b:bufnumbers = l:bufnumbers 181 | let b:bufcount = l:displayedbufs 182 | 183 | " go to the correct line 184 | call BufferListMove(l:activebufline) 185 | endfunction 186 | 187 | " move the selection bar of the list: 188 | " where can be "up"/"down"/"mouse" or 189 | " a line number 190 | function! BufferListMove(where) 191 | if b:bufcount < 1 192 | return 193 | endif 194 | let l:newpos = 0 195 | if !exists('b:lastline') 196 | let b:lastline = 0 197 | endif 198 | setlocal modifiable 199 | 200 | " the mouse was pressed: remember which line 201 | " and go back to the original location for now 202 | if a:where == "mouse" 203 | let l:newpos = line(".") 204 | call BufferListGoto(b:lastline) 205 | endif 206 | 207 | " exchange the first char (>) with a space 208 | call setline(line("."), " ".strpart(getline(line(".")), 1)) 209 | 210 | " go where the user want's us to go 211 | if a:where == "up" 212 | call BufferListGoto(line(".")-1) 213 | elseif a:where == "down" 214 | call BufferListGoto(line(".")+1) 215 | elseif a:where == "mouse" 216 | call BufferListGoto(l:newpos) 217 | else 218 | call BufferListGoto(a:where) 219 | endif 220 | 221 | " and mark this line with a > 222 | call setline(line("."), ">".strpart(getline(line(".")), 1)) 223 | 224 | " remember this line, in case the mouse is clicked 225 | " (which automatically moves the cursor there) 226 | let b:lastline = line(".") 227 | 228 | setlocal nomodifiable 229 | endfunction 230 | 231 | " tries to set the cursor to a line of the buffer list 232 | function! BufferListGoto(line) 233 | if b:bufcount < 1 | return | endif 234 | if a:line < 1 235 | call cursor(1, 1) 236 | elseif a:line > b:bufcount 237 | call cursor(b:bufcount, 1) 238 | else 239 | call cursor(a:line, 1) 240 | endif 241 | endfunction 242 | 243 | " loads the selected buffer 244 | function! LoadBuffer() 245 | " get the selected buffer 246 | let l:str = BufferListGetSelectedBuffer() 247 | " kill the buffer list 248 | bwipeout 249 | " ...and switch to the buffer number 250 | exec ":b " . l:str 251 | endfunction 252 | 253 | " deletes the selected buffer 254 | function! BufferListDeleteBuffer() 255 | " get the selected buffer 256 | let l:str = BufferListGetSelectedBuffer() 257 | " kill the buffer list 258 | bwipeout 259 | " delete the selected buffer 260 | exec ":bdelete " . l:str 261 | " and reopen the list 262 | call BufferList() 263 | endfunction 264 | 265 | function! BufferListGetSelectedBuffer() 266 | " this is our string containing the buffer numbers in 267 | " the order of the list (separated by ':') 268 | let l:str = b:bufnumbers 269 | 270 | " remove all numbers BEFORE the one we want 271 | let l:i = 1 | while l:i < line(".") | let l:i = l:i + 1 272 | let l:str = strpart(l:str, stridx(l:str, ':') + 1) 273 | endwhile 274 | 275 | " and everything AFTER 276 | let l:str = strpart(l:str, 0, stridx(l:str, ':')) 277 | 278 | return l:str 279 | endfunction 280 | 281 | --------------------------------------------------------------------------------