├── LICENSE ├── README.md ├── plugin └── bufferhint.vim └── screenshot_1.png /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015-2017, Yanhui Shen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Buffer Hint 2 | 3 | bufferhint is a handly buffer switcher for Vim. 4 | 5 | With this plugin, you can browse all buffers in a popup side window. 6 | 7 | In addition, this plugin provides a "hint key" for each buffer, just like what Vimperator/Vimium/VimFx does. 8 | 9 | Therefore, you can switch among buffers quickly with a single key stroke in the best case. 10 | 11 | ## Installtion 12 | 13 | ### Manually 14 | 15 | ```bash 16 | cp bufferhint.vim ~/.vim/plugin/ 17 | ``` 18 | 19 | ### Pathogen 20 | ```bash 21 | cd ~/.vim/bundle 22 | git clone https://github.com/bsdelf/bufferhint.git 23 | ``` 24 | 25 | ### Configuration 26 | 27 | Add following two lines in `~/.vimrc`: 28 | 29 | ```vim 30 | nnoremap - :call bufferhint#Popup() 31 | nnoremap \ :call bufferhint#LoadPrevious() 32 | ``` 33 | 34 | ## Usage 35 | 36 | ### Basic usage 37 | 38 | 1. `vim foo.txt bar.txt doe.txt`. 39 | 2. Type `-` to popup bufferhint window. 40 | 3. Type `j`, `k`, `C-f`, `C-b`, `PGUP`, `PGDN` to navigate. 41 | 4. Type `ENTER` to load the buffer under cursor. 42 | 43 | ### Load buffer by hint 44 | 45 | 1. `vim foo.txt bar.txt doe.txt`. 46 | 2. Type `-` to popup bufferhint window. 47 | 3. Type `b` to load "bar.txt". 48 | 4. Type `\` to load the previous buffer namely "foo.txt". 49 | 50 | ### Switch sort mode 51 | 52 | 1. Type `-` to popup bufferhint window. 53 | 2. Type `SPACE` to switch sort mode. 54 | 55 | ### Delete buffers 56 | 57 | 1. `vim foo.txt bar.txt doe.txt`. 58 | 2. Type `-` to popup bufferhint window. 59 | 3. Type `dd` to delete the buffer under cursor. 60 | 4. Type `db` to delete a buffer by hint. 61 | 62 | ### Notes 63 | 64 | - Yellow hints means the buffers are sorted by path, green hints means the buffers are sorted by LRU. 65 | - You can also type `/` in bufferhint window and search what you want, then type `ENTER` to load it. 66 | - Apparently `j/k/d/n/N` are excluded from the hint chars. 67 | 68 | ## Functions 69 | 70 | - `bufferhint#Popup()` 71 | 72 | Toggle bufferhint window. 73 | 74 | - `bufferhint#LoadPrevious()` 75 | 76 | Load the second item in LRU. It's very useful in the scene where you want to switch back and forth between two files. 77 | 78 | - `bufferhint#Save()` 79 | 80 | Save current session to an external session file which can be restored by `vim -S` command in the future. The default session file name is "session.vim". 81 | 82 | ## Preference 83 | 84 | - `g:bufferhint_SortMode` 85 | 86 | Default sort mode. 87 | - 0: sort by path 88 | - 1: sort by LRU 89 | 90 | - `g:bufferhint_MaxWidth` 91 | 92 | Maxmium window width. 93 | 94 | - `g:bufferhint_PageStep` 95 | 96 | Page step, will affect `PGUP`, `PGDN`, `C-f`, `C-b`. 97 | 98 | - `g:bufferhint_SessionFile` 99 | 100 | The default session file name. 101 | 102 | - `g:bufferhint_KeepWindow` 103 | 104 | Set to `1` to keep window open after buffer deleting. 105 | 106 | ## Screenshot 107 | 108 | ![bufferhint](https://github.com/bsdelf/bufferhint/raw/master/screenshot_1.png) 109 | 110 | ## One More Thing 111 | 112 | If you like this plugin, please rate it [here](http://www.vim.org/scripts/script.php?script_id=5272)! 113 | -------------------------------------------------------------------------------- /plugin/bufferhint.vim: -------------------------------------------------------------------------------- 1 | "-------------------------------------------------------------------------------" 2 | " bufferhint 1.1.1 " 3 | " Copyright(c) 2016, Yanhui Shen " 4 | " * Inspired by Robert Lillack's BUFFER LIST " 5 | " * Sort mode: Path & LRU " 6 | "-------------------------------------------------------------------------------" 7 | 8 | " SCRIPT RULE: 9 | " row, col: start from 1 10 | " index, idx: start from 0 11 | 12 | ""if exists('g:bufferhintLoaded') 13 | "" finish 14 | ""endif 15 | ""let g:bufferhintLoaded = 1 16 | 17 | " 0=path, 1=lru 18 | if !exists("g:bufferhint_SortMode") 19 | let g:bufferhint_SortMode = 0 20 | endif 21 | 22 | if !exists('g:bufferhint_MaxWidth') 23 | let g:bufferhint_MaxWidth = 50 24 | endif 25 | 26 | if !exists('g:bufferhint_PageStep') 27 | let g:bufferhint_PageStep = 5 28 | endif 29 | 30 | if !exists('g:bufferhint_SessionFile') 31 | let g:bufferhint_SessionFile = 'session.vim' 32 | endif 33 | 34 | if !exists('g:bufferhint_BufferName') 35 | let g:bufferhint_BufferName = '::buffers::' 36 | endif 37 | 38 | if !exists('g:bufferhint_KeepWindow') 39 | let g:bufferhint_KeepWindow = 0 40 | endif 41 | 42 | " if window height changed, 43 | " we need to regenerate hits 44 | let s:Width = 0 45 | let s:Height = 0 46 | 47 | " shortcut keys 48 | let s:HintKeys = {} 49 | 50 | " buffer ids for differet mode 51 | let s:PathBids = [] 52 | let s:LRUBids = [] 53 | 54 | " buffer count 55 | let s:LineCount = 0 56 | 57 | " za__[path]_@~_ 58 | let s:ReservedSpace = 2+2+1+2+1 59 | 60 | " current line 61 | let s:CursorLine = line(".") 62 | 63 | " buffer's name 64 | let s:MyName = fnameescape(g:bufferhint_BufferName) 65 | 66 | " toggle buffer hint 67 | fu! bufferhint#Popup() 68 | if bufloaded(bufnr(s:MyName)) 69 | exe 'bwipeout ' . bufnr(s:MyName) 70 | return 71 | endif 72 | 73 | if g:bufferhint_MaxWidth > winwidth(0) || g:bufferhint_MaxWidth <= 0 74 | let g:bufferhint_MaxWidth = winwidth(0) 75 | endif 76 | 77 | if empty(s:HintKeys) || s:Height != winheight(0) 78 | call s:GenHintKeys() 79 | let s:Height = winheight(0) 80 | endif 81 | 82 | call s:UpdateLRU() 83 | 84 | " FIXME: 85 | " SetupWidth() is implicitly called inside GetContent() 86 | " this is not good 87 | let bufcontent = s:GetContent() 88 | if empty(bufcontent) 89 | return 90 | endif 91 | 92 | " create buffer 93 | exe 'silent! ' . s:Width . 'vne ' . s:MyName 94 | 95 | setlocal noshowcmd 96 | setlocal noswapfile 97 | setlocal buftype=nofile 98 | setlocal bufhidden=wipe 99 | setlocal nobuflisted 100 | setlocal nomodifiable 101 | setlocal nowrap 102 | setlocal nonumber 103 | setlocal filetype=bufferhint 104 | if has('patch-7.4.2210') 105 | setlocal signcolumn=no 106 | endif 107 | 108 | " syntax highlighting 109 | if has("syntax") 110 | sy clear 111 | sy match KeyHint /^../ 112 | sy match AtHint /@/ 113 | if !get(g:, 'bufferhint_CustomHighlight', '') 114 | hi clear KeyHint 115 | hi def AtHint ctermfg=red 116 | let mode = g:bufferhint_SortMode 117 | if mode == 0 118 | hi def KeyHint ctermfg=yellow 119 | elseif mode == 1 120 | hi def KeyHint ctermfg=green 121 | endif 122 | endif 123 | endif 124 | 125 | " set content 126 | setlocal modifiable 127 | put! = bufcontent 128 | setlocal nomodifiable 129 | 130 | " map keys for explorer 131 | map q :bwipeout 132 | map :call bufferhint#SwitchMode() 133 | 134 | " map keys for buffer 135 | call s:MapHintKeys() 136 | noremap :call bufferhint#LoadByCursor() 137 | map dd :call bufferhint#KillByCursor() 138 | 139 | " map keys for movement 140 | map j :call bufferhint#Scroll("down") 141 | map k :call bufferhint#Scroll("up") 142 | map gg :call bufferhint#Scroll("top") 143 | map G :call bufferhint#Scroll("bottom") 144 | map :call bufferhint#Scroll("pgup") 145 | map :call bufferhint#Scroll("pgdn") 146 | map :call bufferhint#Scroll("pgup") 147 | map :call bufferhint#Scroll("pgdn") 148 | map j 149 | map k 150 | map gg 151 | map G 152 | 153 | map 154 | map 155 | map I 156 | map A 157 | map O 158 | 159 | " NOTE: These hooks should be work only for this buffer!!! 160 | autocmd! VimResized call s:OnResized() 161 | autocmd! CursorMoved call s:OnCursorMoved() 162 | 163 | call s:ModeReady() 164 | endfu 165 | 166 | fu! s:OnResized() 167 | bwipeout 168 | call bufferhint#Popup() 169 | call s:DrawHints() 170 | endfu 171 | 172 | fu! s:OnCursorMoved() 173 | call s:DrawHints() 174 | if g:bufferhint_SortMode == 0 175 | let s:CursorLine = line(".") 176 | endif 177 | endfu 178 | 179 | fu! bufferhint#SwitchMode() 180 | let mode = g:bufferhint_SortMode 181 | let mode = (mode + 1) % 2 182 | let g:bufferhint_SortMode = mode 183 | bwipeout 184 | call bufferhint#Popup() 185 | endfu 186 | 187 | fu! s:ModeReady() 188 | let mode = g:bufferhint_SortMode 189 | if mode == 0 190 | call s:Goto(s:CursorLine) 191 | elseif mode == 1 192 | call bufferhint#Scroll("top") 193 | endif 194 | endfu 195 | 196 | fu! s:GetModeBids() 197 | let mode = g:bufferhint_SortMode 198 | if mode == 0 199 | return s:PathBids 200 | elseif mode == 1 201 | return s:LRUBids 202 | else 203 | return [] 204 | endif 205 | endfu 206 | 207 | fu! s:GetContent() 208 | let mode = g:bufferhint_SortMode 209 | if mode == 0 210 | return s:SortedByPath() 211 | elseif mode == 1 212 | return s:SortedByLRU() 213 | else 214 | let mode = 0 215 | return s:SortedByPath() 216 | endif 217 | endfu 218 | 219 | fu! s:GenHintKeys() 220 | let hint1 = "abcefhilmoprstuvwxyz" 221 | let hint2 = "abcdefghijklmnopqrstuvwxyz" 222 | 223 | let nhint1 = (strlen(hint1)*strlen(hint2) - winheight(0)) / (strlen(hint2)-1) 224 | let nhint2 = strlen(hint2) 225 | let ihint1 = nhint1 226 | let ihint2 = 0 227 | let hintkeys = {} 228 | 229 | for idx in range(0, winheight(0)-1) 230 | " build hint key and map it 231 | if idx < nhint1 232 | let hint = hint1[idx] 233 | else 234 | let hint = hint1[ihint1] . hint2[ihint2] 235 | let ihint2 = ihint2 + 1 236 | if ihint2 >= nhint2 237 | let ihint2 = 0 238 | let ihint1 = ihint1 + 1 239 | endif 240 | endif 241 | let hintkeys[hint] = idx 242 | endfor 243 | 244 | let s:HintKeys = hintkeys 245 | endfu 246 | 247 | fu! s:MapHintKeys() 248 | let hintkeys = s:HintKeys 249 | if empty(hintkeys) 250 | echo "Need HintKeys!" 251 | return 252 | endif 253 | setlocal modifiable 254 | for hint in keys(hintkeys) 255 | exe 'map ' . hint . ' :call bufferhint#LoadByHint("' . hint . '")' 256 | exe 'map d' . hint . ' :call bufferhint#KillByHint("' . hint . '")' 257 | endfor 258 | setlocal nomodifiable 259 | endfu 260 | 261 | fu! s:DrawHints() 262 | let hintkeys = s:HintKeys 263 | if empty(hintkeys) 264 | echo "Need HintKeys!" 265 | return 266 | endif 267 | setlocal modifiable 268 | for hint in keys(hintkeys) 269 | let row = line("w0") + hintkeys[hint] 270 | if row > s:LineCount 271 | continue 272 | endif 273 | if strlen(hint) < 2 274 | let hint = hint . " " 275 | endif 276 | let str = hint . strpart(getline(row), 2) 277 | call setline(row, str) 278 | endfor 279 | setlocal nomodifiable 280 | endfu 281 | 282 | fu! s:FormatPath(bid, path) 283 | let bid = a:bid 284 | let path = a:path 285 | let maxw = s:Width 286 | 287 | if strlen(path) <= 0 288 | \|| !getbufvar(bid, '&modifiable') 289 | \|| !getbufvar(bid, '&buflisted') 290 | return "" 291 | endif 292 | 293 | " adapt the length of path 294 | let len = strlen(path) + s:ReservedSpace 295 | if len > maxw 296 | let path = '...' . strpart(path, len - maxw + 3) 297 | "elseif len < maxw 298 | " if (bufwinnr(bid) == -1) 299 | " let path = path . repeat(' ', maxw - len) 300 | " endif 301 | endif 302 | 303 | let path = path . ' ' 304 | " buffer stats hint 305 | if bufwinnr(bid) != -1 306 | let path = path . '@' 307 | endif 308 | if getbufvar(bid, '&modified') 309 | let path = path . '~' 310 | endif 311 | 312 | return path 313 | endfu 314 | 315 | fu! s:IsBadTypeBuffer(bid) 316 | let badtypes = ['help', 'quickfix'] 317 | let buftype = getbufvar(a:bid, '&buftype') 318 | return index(badtypes, buftype) >= 0 319 | endfu 320 | 321 | fu! s:ListBuffer() 322 | redir => buflist 323 | silent! ls 324 | redir END 325 | let bids = [] 326 | for curline in split(buflist, '\n') 327 | if curline =~ '^\s*\d\+' 328 | let bid = str2nr(matchstr(curline, '^\s*\zs\d\+')) 329 | let bids += [bid] 330 | endif 331 | endfor 332 | return bids 333 | endfu 334 | 335 | fu! s:SortedByPath() 336 | let crntbuf = bufnr('') 337 | 338 | let content = "" 339 | let pathbids = [] 340 | 341 | " build path-bid map 342 | let pathidmap = {} 343 | let maxpath = 0 344 | for bid in s:ListBuffer() 345 | if !buflisted(bid) || s:IsBadTypeBuffer(bid) | continue | endif 346 | let path = s:RelativeFilePath(bufname(bid)) 347 | let pathidmap[path] = bid 348 | let len = strlen(path) 349 | if (len > maxpath) 350 | let maxpath = len 351 | endif 352 | endfor 353 | if empty(pathidmap) 354 | return 355 | endif 356 | 357 | call s:SetupWidth(maxpath) 358 | 359 | " iterate through the buffers 360 | let nline = 0 361 | for key in sort(keys(pathidmap)) 362 | let bid = pathidmap[key] 363 | let path = key 364 | 365 | let line = s:FormatPath(bid, path) 366 | if empty(line) | continue | endif 367 | 368 | " remember buffer numbers 369 | call add(pathbids, bid) 370 | 371 | " add newline, reserve 4 spaces for hint key 372 | let content = content . " " . line . "\n" 373 | let nline = nline + 1 374 | endfor 375 | 376 | let s:PathBids = pathbids 377 | let s:LineCount = nline 378 | 379 | return content 380 | endfu 381 | 382 | fu! s:SortedByLRU() 383 | let lrulst = s:LRUBids 384 | let content = "" 385 | 386 | let idpathmap = {} 387 | let maxpath = 0 388 | for bid in lrulst 389 | if !buflisted(bid) || s:IsBadTypeBuffer(bid) | continue | endif 390 | let path = s:RelativeFilePath(bufname(bid)) 391 | let idpathmap[bid] = path 392 | let len = strlen(path) 393 | if (len > maxpath) 394 | let maxpath = len 395 | endif 396 | endfor 397 | if empty(idpathmap) 398 | return 399 | endif 400 | 401 | call s:SetupWidth(maxpath) 402 | 403 | " iterate through buffers 404 | let nline = 0 405 | for bid in lrulst 406 | let path = idpathmap[bid] 407 | 408 | let line = s:FormatPath(bid, path) 409 | if empty(line) | continue | endif 410 | 411 | " add newline, reserve 4 spaces for hint key 412 | let content = content . " " . line . "\n" 413 | let nline = nline + 1 414 | endfor 415 | 416 | let s:LineCount = nline 417 | 418 | return content 419 | endfu 420 | 421 | fu! s:UpdateLRU() 422 | let lrulst = s:LRUBids 423 | let bids = s:ListBuffer() 424 | " initialize 425 | if empty(lrulst) 426 | for bid in bids 427 | " this may happen 428 | if buflisted(bid) && !s:IsBadTypeBuffer(bid) 429 | call add(lrulst, bid) 430 | endif 431 | endfor 432 | " simply sort by path will obey LRU 433 | "fu! s:bufcmp(bida, bidb) 434 | " let stra = bufname(a:bida) 435 | " let strb = bufname(a:bidb) 436 | " return stra < strb ? -1 : (stra > strb ? 1 : 0) 437 | "endfu 438 | "call sort(lrulst, "s:bufcmp") 439 | endif 440 | " track new buffers 441 | for bid in bids 442 | if buflisted(bid) 443 | \&& !s:IsBadTypeBuffer(bid) 444 | \&& index(lrulst, bid) < 0 445 | call insert(lrulst, bid, 0) 446 | endif 447 | endfor 448 | endfu 449 | 450 | fu! s:SetupWidth(pathlen) 451 | " decide max window width 452 | let width = a:pathlen + s:ReservedSpace 453 | if width > g:bufferhint_MaxWidth 454 | let width = g:bufferhint_MaxWidth 455 | endif 456 | let s:Width = width 457 | endfu 458 | 459 | fu! bufferhint#Scroll(where) 460 | let pagestep = g:bufferhint_PageStep 461 | if a:where == "up" 462 | call s:Goto(line(".")-1) 463 | elseif a:where == "down" 464 | call s:Goto(line(".")+1) 465 | elseif a:where == "pgup" 466 | call s:Goto(line(".")-pagestep) 467 | elseif a:where == "pgdn" 468 | call s:Goto(line(".")+pagestep) 469 | elseif a:where == "top" 470 | call s:Goto(1) 471 | elseif a:where == "bottom" 472 | call s:Goto(s:LineCount) 473 | endif 474 | endfu 475 | 476 | fu! s:Goto(line) 477 | let nline = s:LineCount 478 | if nline < 1 | return | endif 479 | 480 | let xoff = 4 481 | setlocal modifiable 482 | if a:line < 1 483 | call cursor(1, xoff) 484 | elseif a:line > nline 485 | call cursor(nline, xoff) 486 | else 487 | call cursor(a:line, xoff) 488 | endif 489 | setlocal nomodifiable 490 | endfu 491 | 492 | "-------------------------------------------- 493 | 494 | fu! bufferhint#LoadByHint(hint) 495 | let hintkeys = s:HintKeys 496 | if !has_key(hintkeys, a:hint) 497 | echo "No such key: " . a:hint 498 | return 499 | endif 500 | let idx = line("w0") + hintkeys[a:hint] - 1 501 | call s:LoadByIndex(idx) 502 | endfu 503 | 504 | fu! bufferhint#LoadByCursor() 505 | let idx = line(".") - 1 506 | call s:LoadByIndex(idx) 507 | endfu 508 | 509 | fu! bufferhint#LoadPrevious() 510 | let curmode = g:bufferhint_SortMode 511 | let g:bufferhint_SortMode = 1 512 | call s:UpdateLRU() 513 | call s:LoadByIndex(1) 514 | let g:bufferhint_SortMode = curmode 515 | endfu 516 | 517 | fu! s:LoadByIndex(idx) 518 | let bids = s:GetModeBids() 519 | if a:idx >= len(bids) || a:idx < 0 520 | echo "Out of range!" 521 | return 522 | endif 523 | let bid = bids[a:idx] 524 | 525 | " update LRU 526 | let lruidx = index(s:LRUBids, bid) 527 | call remove(s:LRUBids, lruidx) 528 | call insert(s:LRUBids, bid, 0) 529 | 530 | " load buffer 531 | if bufexists(bufnr(s:MyName)) 532 | exe 'bwipeout ' . bufnr(s:MyName) 533 | endif 534 | try 535 | exe "b " . bid 536 | catch /^Vim\%((\a\+)\)\=:E37/ 537 | let pos = stridx(v:exception, ':E') 538 | echohl ErrorMsg 539 | echo (pos >= 0)? strpart(v:exception, pos + 1) : v:exception 540 | echohl None 541 | endtry 542 | endfu 543 | 544 | "-------------------------------------------- 545 | 546 | fu! bufferhint#KillByHint(hint) 547 | let hintkeys = s:HintKeys 548 | if !has_key(hintkeys, a:hint) 549 | echo "No such key: " . a:hint 550 | return 551 | endif 552 | let idx = line("w0") + hintkeys[a:hint] - 1 553 | call s:KillByIndex(idx) 554 | endfu 555 | 556 | fu! bufferhint#KillByCursor() 557 | let idx = line(".") - 1 558 | call s:KillByIndex(idx) 559 | endfu 560 | 561 | fu! bufferhint#BufferKill(bang, buffer) 562 | let l:bufcount = bufnr('$') 563 | let l:switch = 0 " window which contains target buffer will be switched 564 | if empty(a:buffer) 565 | let l:target = bufnr('%') 566 | elseif a:buffer =~ '^\d\+$' 567 | let l:target = bufnr(str2nr(a:buffer)) 568 | else 569 | let l:target = bufnr(a:buffer) 570 | endif 571 | if l:target <= 0 572 | echohl ErrorMsg 573 | echomsg "cannot find buffer: '" . a:buffer . "'" 574 | echohl NONE 575 | return 0 576 | endif 577 | if empty(a:bang) && getbufvar(l:target, '&modified') 578 | echohl ErrorMsg 579 | echomsg "No write since last change (use :BufferKill!)" 580 | echohl NONE 581 | return 0 582 | endif 583 | if bufnr('#') > 0 " check alternative buffer 584 | let l:aid = bufnr('#') 585 | if l:aid != l:target && buflisted(l:aid) && getbufvar(l:aid, "&modifiable") 586 | let l:switch = l:aid " switch to alternative buffer 587 | endif 588 | endif 589 | if l:switch == 0 " check non-scratch buffers 590 | let l:index = l:bufcount 591 | while l:index >= 0 592 | if buflisted(l:index) && getbufvar(l:index, "&modifiable") 593 | if strlen(bufname(l:index)) > 0 && l:index != l:target 594 | let l:switch = l:index " switch to that buffer 595 | break 596 | endif 597 | endif 598 | let l:index = l:index - 1 599 | endwhile 600 | endif 601 | if l:switch == 0 " check scratch buffers 602 | let l:index = l:bufcount 603 | while l:index >= 0 604 | if buflisted(l:index) && getbufvar(l:index, "&modifiable") 605 | if l:index != l:target 606 | let l:switch = l:index " switch to a scratch 607 | break 608 | endif 609 | endif 610 | let l:index = l:index - 1 611 | endwhile 612 | endif 613 | if l:switch == 0 " check if only one scratch left 614 | if strlen(bufname(l:target)) == 0 && (!getbufvar(l:target, "&modified")) 615 | echo "This is the last scratch" 616 | return 0 617 | endif 618 | endif 619 | let l:ntabs = tabpagenr('$') 620 | let l:tabcc = tabpagenr() 621 | let l:wincc = winnr() 622 | let l:index = 1 623 | while l:index <= l:ntabs 624 | exec 'tabn '.l:index 625 | while 1 626 | let l:wid = bufwinnr(l:target) 627 | if l:wid <= 0 | break | endif 628 | exec l:wid.'wincmd w' 629 | if l:switch == 0 630 | exec 'enew!' 631 | let l:switch = bufnr('%') 632 | else 633 | exec 'buffer '.l:switch 634 | endif 635 | endwhile 636 | let l:index += 1 637 | endwhile 638 | exec 'tabn ' . l:tabcc 639 | exec l:wincc . 'wincmd w' 640 | exec 'bdelete! '.l:target 641 | return 1 642 | endfu 643 | 644 | fu! s:KillByIndex(idx) 645 | let bids = s:GetModeBids() 646 | if a:idx >= len(bids) || a:idx < 0 647 | echo "Out of range!" 648 | return 649 | endif 650 | let bid = bids[a:idx] 651 | 652 | bwipeout 653 | 654 | if getbufvar(bid, '&modified') 655 | echohl ErrorMsg 656 | echomsg "No write since last change" 657 | echohl NONE 658 | return 659 | endif 660 | 661 | " kill buffer 662 | if !g:bufferhint_KeepWindow 663 | exe "silent bdelete! " . bid 664 | else 665 | call bufferhint#BufferKill('!', bid) 666 | endif 667 | 668 | call remove(bids, a:idx) 669 | 670 | " update LRU 671 | let lruidx = index(s:LRUBids, bid) 672 | call remove(s:LRUBids, lruidx) 673 | 674 | call bufferhint#Popup() 675 | endfu 676 | 677 | "-------------------------------------------- 678 | 679 | fu! bufferhint#Save() 680 | let name = g:bufferhint_SessionFile 681 | let path = fnamemodify(getcwd(), ":p") 682 | call s:DoSaveAs(name, path) 683 | endfu 684 | 685 | fu! bufferhint#SaveAs() 686 | let name = input("Name: ") 687 | if empty(name) 688 | echo "\r" 689 | echo "Abort to save." 690 | return 691 | endif 692 | let path = input("Path: ") 693 | let path = fnamemodify(expand(path), ":p") 694 | echo "\r" 695 | call s:DoSaveAs(name, path) 696 | endfu 697 | 698 | fu! s:DoSaveAs(name, path) 699 | let ss = a:path . a:name 700 | 701 | let dir = getcwd() 702 | let files = s:GetLRUFiles() 703 | let current = bufname(bufnr("")) 704 | 705 | let content = [] 706 | call add(content, "chdir " . dir) 707 | call add(content, "args " . files) 708 | call add(content, "edit " . current) 709 | 710 | call writefile(content, ss) 711 | echo "Saved: " . ss 712 | endfu 713 | 714 | fu! s:GetLRUFiles() 715 | let files = "" 716 | let bids = s:LRUBids 717 | if empty(bids) 718 | let crntbuf = bufnr('') 719 | let bids = s:ListBuffer() 720 | endif 721 | 722 | for bid in bids 723 | if !buflisted(bid) | continue | endif 724 | let path = bufname(bid) 725 | let files .= path . " " 726 | endfor 727 | 728 | return l:files 729 | endfu 730 | 731 | "-------------------------------------------- 732 | 733 | fu! s:RelativeFilePath(bname) 734 | if !filereadable(a:bname) 735 | return "#" . a:bname . "#" 736 | endif 737 | let fullpath = fnamemodify(a:bname, ":p") 738 | let workpath = fnamemodify(getcwd(), ":p") 739 | let relpath = strpart(fullpath, strlen(matchstr(fullpath, workpath, 0))) 740 | return relpath 741 | endfu 742 | 743 | -------------------------------------------------------------------------------- /screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bsdelf/bufferhint/ab8d0a3e6f03b2f500177337201906dd3e4446b2/screenshot_1.png --------------------------------------------------------------------------------