Przeglądaj źródła

Merge pull request #124 from drmikehenry/buffer-removal

Buffer removal enhancements
jlanzarotta 9 miesięcy temu
rodzic
commit
b02c535055
1 zmienionych plików z 94 dodań i 37 usunięć
  1. 94 37
      plugin/bufexplorer.vim

+ 94 - 37
plugin/bufexplorer.vim

@@ -607,7 +607,7 @@ function! BufExplorer()
     " Forget any cached MRU ordering from previous invocations.
     unlet! s:mruOrder
 
-    silent let s:raw_buffer_listing = s:GetBufferInfo(0)
+    let s:raw_buffer_listing = s:GetBufferInfo(0)
 
     call s:MRUGarbageCollectBufs()
     call s:MRUGarbageCollectTabs()
@@ -683,6 +683,12 @@ function! s:DisplayBufferList()
     setlocal nomodifiable
 endfunction
 
+" RedisplayBufferList {{{2
+function! s:RedisplayBufferList()
+    call s:RebuildBufferList()
+    call s:UpdateHelpStatus()
+endfunction
+
 " MapKeys {{{2
 function! s:MapKeys()
     if exists("b:displayMode") && b:displayMode == "winmanager"
@@ -855,6 +861,10 @@ endfunction
 
 " CalculateBufferDetails {{{2
 " Calculate `buf`-related details.
+" Only these fields of `buf` must be defined on entry:
+" - `._bufnr`
+" - `.attributes`
+" - `.line`
 function! s:CalculateBufferDetails(buf)
     let buf = a:buf
     let name = bufname(buf._bufnr)
@@ -897,18 +907,41 @@ function! s:CalculateBufferDetails(buf)
 endfunction
 
 " GetBufferInfo {{{2
-function! s:GetBufferInfo(bufnr)
+" Return dictionary `{ bufNbr : buf }`.
+" - If `onlyBufNbr > 0`, dictionary will contain at most that buffer.
+" On return, only these fields are set for each `buf`:
+" - `._bufnr`
+" - `.attributes`
+" - `.line`
+" Other fields will be populated by `s:CalculateBufferDetails()`.
+function! s:GetBufferInfo(onlyBufNbr)
     redir => bufoutput
 
-    " Show all buffers including the unlisted ones. [!] tells Vim to show the
-    " unlisted ones.
-    buffers!
+    " Below, `:silent buffers` allows capturing the output via `:redir` but
+    " prevents display to the user.
+
+    if a:onlyBufNbr > 0 && buflisted(a:onlyBufNbr)
+        " We care only about the listed buffer `a:onlyBufNbr`, so no need to
+        " enumerate unlisted buffers.
+        silent buffers
+    else
+        " Use `!` to show all buffers including the unlisted ones.
+        silent buffers!
+    endif
     redir END
 
-    if a:bufnr > 0
+    if a:onlyBufNbr > 0
         " Since we are only interested in this specified buffer remove the
         " other buffers listed.
-        let bufoutput = substitute(bufoutput."\n", '^.*\n\(\s*'.a:bufnr.'\>.\{-}\)\n.*', '\1', '')
+        " Use a very-magic pattern starting with a newline and a run of zero or
+        " more spaces/tabs:
+        let onlyLinePattern = '\v\n\s*'
+        " Continue with the buffer number followed by a non-digit character
+        " (which will be a buffer attribute character such as `u` or ` `).
+        let onlyLinePattern .= a:onlyBufNbr . '\D'
+        " Finish with a run of zero or more non-newline characters plus newline:
+        let onlyLinePattern .= '[^\n]*\n'
+        let bufoutput = matchstr("\n" . bufoutput . "\n", onlyLinePattern)
     endif
 
     let all = {}
@@ -1150,6 +1183,11 @@ function! s:SelectBuffer(...)
 endfunction
 
 " RemoveBuffer {{{2
+" Valid `mode` values:
+" - "delete"
+" - "force_delete"
+" - "wipe"
+" - "force_wipe"
 function! s:RemoveBuffer(mode)
     " Are we on a line with a file name?
     if line('.') < s:firstBufferLine
@@ -1157,27 +1195,32 @@ function! s:RemoveBuffer(mode)
     endif
 
     let mode = a:mode
+    let forced = mode =~# '^force_'
 
     " These commands are to temporarily suspend the activity of winmanager.
     if exists("b:displayMode") && b:displayMode == "winmanager"
         call WinManagerSuspendAUs()
     end
 
-    let _bufNbr = str2nr(getline('.'))
+    let bufNbr = str2nr(getline('.'))
+    let buf = s:raw_buffer_listing[bufNbr]
 
-    if getbufvar(_bufNbr, '&modified') == 1
+    if !forced && (buf.isterminal || getbufvar(bufNbr, '&modified'))
+        if buf.isterminal
+            let msg = "Buffer " . bufNbr . " is a terminal"
+        else
+            let msg = "No write since last change for buffer " . bufNbr
+        endif
         " Calling confirm() requires Vim built with dialog option.
         if !has("dialog_con") && !has("dialog_gui")
-            call s:Error("Sorry, no write since last change for buffer "._bufNbr.", unable to delete")
+            call s:Error(msg . "; cannot remove without 'force'")
             return
         endif
 
-        let answer = confirm('No write since last change for buffer '._bufNbr.'. Delete anyway?', "&Yes\n&No", 2)
+        let answer = confirm(msg . "; Remove anyway?", "&Yes\n&No", 2)
 
-        if a:mode == "delete" && answer == 1
-            let mode = "force_delete"
-        elseif a:mode == "wipe" && answer == 1
-            let mode = "force_wipe"
+        if answer == 1
+            let mode = 'force_' . mode
         else
             return
         endif
@@ -1185,7 +1228,7 @@ function! s:RemoveBuffer(mode)
     endif
 
     " Okay, everything is good, delete or wipe the buffer.
-    call s:DeleteBuffer(_bufNbr, mode)
+    call s:DeleteBuffer(bufNbr, mode)
 
     " Reactivate winmanager autocommand activity.
     if exists("b:displayMode") && b:displayMode == "winmanager"
@@ -1195,30 +1238,50 @@ function! s:RemoveBuffer(mode)
 endfunction
 
 " DeleteBuffer {{{2
-function! s:DeleteBuffer(buf, mode)
+" Valid `mode` values:
+" - "delete"
+" - "force_delete"
+" - "wipe"
+" - "force_wipe"
+function! s:DeleteBuffer(bufNbr, mode)
     " This routine assumes that the buffer to be removed is on the current line.
+    if a:mode =~# 'delete$' && bufexists(a:bufNbr) && !buflisted(a:bufNbr)
+        call s:Error('Buffer ' . a:bufNbr
+                \ . ' is unlisted; must `wipe` to remove')
+        return
+    endif
     try
         " Wipe/Delete buffer from Vim.
         if a:mode == "wipe"
-            execute "silent bwipe" a:buf
+            execute "silent bwipe" a:bufNbr
         elseif a:mode == "force_wipe"
-            execute "silent bwipe!" a:buf
+            execute "silent bwipe!" a:bufNbr
         elseif a:mode == "force_delete"
-            execute "silent bdelete!" a:buf
+            execute "silent bdelete!" a:bufNbr
         else
-            execute "silent bdelete" a:buf
+            execute "silent bdelete" a:bufNbr
         endif
+    catch
+        call s:Error(v:exception)
+    endtry
 
+    if bufexists(a:bufNbr)
+        " Buffer is still present.  We may have failed to wipe it, or it may
+        " have changed attributes (as `:bd` only makes a buffer unlisted).
+        " Regather information on this buffer, update the buffer list, and
+        " redisplay.
+        let info = s:GetBufferInfo(a:bufNbr)
+        let s:raw_buffer_listing[a:bufNbr] = info[a:bufNbr]
+        call s:RedisplayBufferList()
+    else
         " Delete the buffer from the list on screen.
         setlocal modifiable
         normal! "_dd
         setlocal nomodifiable
 
         " Delete the buffer from the raw buffer list.
-        unlet s:raw_buffer_listing[a:buf]
-    catch
-        call s:Error(v:exception)
-    endtry
+        unlet s:raw_buffer_listing[a:bufNbr]
+    endif
 endfunction
 
 " Close {{{2
@@ -1251,22 +1314,19 @@ endfunction
 " ToggleShowTerminal {{{2
 function! s:ToggleShowTerminal()
     let g:bufExplorerShowTerminal = !g:bufExplorerShowTerminal
-    call s:RebuildBufferList()
-    call s:UpdateHelpStatus()
+    call s:RedisplayBufferList()
 endfunction
 
 " ToggleSplitOutPathName {{{2
 function! s:ToggleSplitOutPathName()
     let g:bufExplorerSplitOutPathName = !g:bufExplorerSplitOutPathName
-    call s:RebuildBufferList()
-    call s:UpdateHelpStatus()
+    call s:RedisplayBufferList()
 endfunction
 
 " ToggleShowRelativePath {{{2
 function! s:ToggleShowRelativePath()
     let g:bufExplorerShowRelativePath = !g:bufExplorerShowRelativePath
-    call s:RebuildBufferList()
-    call s:UpdateHelpStatus()
+    call s:RedisplayBufferList()
 endfunction
 
 " ToggleShowTabBuffer {{{2
@@ -1275,22 +1335,19 @@ function! s:ToggleShowTabBuffer()
     " `g:bufExplorerShowTabBuffer`.
     unlet! s:mruOrder
     let g:bufExplorerShowTabBuffer = !g:bufExplorerShowTabBuffer
-    call s:RebuildBufferList()
-    call s:UpdateHelpStatus()
+    call s:RedisplayBufferList()
 endfunction
 
 " ToggleOnlyOneTab {{{2
 function! s:ToggleOnlyOneTab()
     let g:bufExplorerOnlyOneTab = !g:bufExplorerOnlyOneTab
-    call s:RebuildBufferList()
-    call s:UpdateHelpStatus()
+    call s:RedisplayBufferList()
 endfunction
 
 " ToggleShowUnlisted {{{2
 function! s:ToggleShowUnlisted()
     let g:bufExplorerShowUnlisted = !g:bufExplorerShowUnlisted
-    let num_bufs = s:RebuildBufferList()
-    call s:UpdateHelpStatus()
+    call s:RedisplayBufferList()
 endfunction
 
 " ToggleFindActive {{{2