瀏覽代碼

Merge pull request #129 from drmikehenry/open-close-toggle

Flexibly open, close, and toggle BufExplorer windows and splits
jlanzarotta 9 月之前
父節點
當前提交
87ac3de521
共有 2 個文件被更改,包括 209 次插入45 次删除
  1. 84 4
      doc/bufexplorer.txt
  2. 125 41
      plugin/bufexplorer.vim

+ 84 - 4
doc/bufexplorer.txt

@@ -8,6 +8,7 @@ Plugin for easily exploring (or browsing) Vim|:buffers|.
 |bufexplorer-installation|   Installation
 |bufexplorer-usage|          Usage
 |bufexplorer-windowlayout|   Window Layout
+|bufexplorer-commands|       Commands
 |bufexplorer-customization|  Customization
 |bufexplorer-changelog|      Change Log
 |bufexplorer-todo|           Todo
@@ -49,6 +50,8 @@ To start exploring in a newly split horizontal window, use: >
 To start exploring in a newly split vertical window, use: >
  <Leader>bv   or   :BufExplorerVerticalSplit   or   Your custom key mapping
 
+For full information on these flexible commands, see |bufexplorer-commands|.
+
 If you would like to use something other than the default leader key - '\' -
 you may simply change the leader (see |mapleader|).
 
@@ -60,10 +63,6 @@ windows) in the newly split window.  When <Leader>be is issued, bufexplorer
 opens the bufexplorer contents in the current window and the buffer the user
 selects is opened in the current window.
 
-Note: If the current buffer is modified when bufexplorer started, the current
-      window is always split and the new bufexplorer is displayed in that new
-      window.
-
 Commands to use once exploring:
 
  <F1>          Toggle help information.
@@ -114,6 +113,74 @@ wanting to act upon. Once you have selected the buffer you would like,
 you can then either open it, close it (delete), resort the list, reverse
 the sort, quit exploring and so on...
 
+===============================================================================
+COMMANDS                                            *bufexplorer-commands*
+
+:BufExplorer [action]                               *:BufExplorer*
+                Open or close BufExplorer.
+
+`:BufExplorer` accepts an optional "action" argument as follows:
+
+  current   Open BufExplorer in the current window
+  split     Open BufExplorer in a new horizontal split
+  vsplit    Open BufExplorer in a new vertical split
+  above     Open BufExplorer in a new horizontal split above the current window
+  below     Open BufExplorer in a new horizontal split below the current window
+  left      Open BufExplorer in a new vertical split left of the current window
+  right     Open BufExplorer in a new vertical split right of the current window
+  close     Close BufExplorer
+
+If the action argument is not provided, then the value of
+|g:bufExplorerDefaultAction| will be used; by default, this variable contains
+`current` and thus `:Bufexplorer` is the same as `:BufExplorer current`.
+
+Note that this means the behavior of the default mapping <Leader>be (which maps
+to `:BufExplorer`) is determined by |g:bufExplorerDefaultAction|.
+
+The `close` action will close BufExplorer regardless of the tab page or window
+where it was left running.
+
+For actions other than `close`, the `:BufExplorer` command will switch to any
+existing BufExplorer window instead of launching a new instance of BufExplorer.
+
+The actions `above`, `below`, `left`, and `right` specify the type of split
+(horizontal or vertical) to create and where the new window should be placed
+relative to the current window.
+
+The `split` action makes a horizontal split; the position of the split is
+controlled by |g:bufExplorerSplitBelow| (true means `below`, false means
+`above`).
+
+The `vsplit` action makes a vertical split; the position of the split is
+controlled by |g:bufExplorerSplitRight| (true means `right`, false means
+`left`).
+
+Tab completion is provided.  For example, typing `:BufExplorer v` and then
+pressing the <Tab> key will complete the action argument to become `vsplit`.
+
+:ToggleBufExplorer [action]                         *:ToggleBufExplorer*
+                Toggle open/closed BufExplorer.
+
+In most ways, this command is identical to |:BufExplorer|, and the invocation
+`:ToggleBufExplorer action` is equivalent to `:BufExplorer action`; however, if
+BufExplorer is already running in the current window, `action` is converted to
+`close` before chaining to `:BufExplorer action`.  If BufExplorer is running in
+a different window and the action is not `close`, switch to that already-running
+BufExplorer window.
+
+Note that the behavior of the default mapping <Leader>bt (which maps to
+`:ToggleBufExplorer`) is determined by |g:bufExplorerDefaultAction|.
+
+:BufExplorerHorizontalSplit                         *:BufExplorerHorizontalSplit*
+                Open BufExplorer in a new horizontal split.
+
+This is equivalent to `:BufExplorer split`.
+
+:BufExplorerVerticalSplit                           *:BufExplorerVerticalSplit*
+                Open BufExplorer in a new vertical split.
+
+This is equivalent to `:BufExplorer vsplit`.
+
 ===============================================================================
 WINDOW LAYOUT                                       *bufexplorer-windowlayout*
 
@@ -246,6 +313,19 @@ be called to adjust BufExplorer's mappings: >
 If set, bufexplorer will bring up the selected buffer in the window specified
 by g:bufExplorerChgWin.
 
+                                                   *g:bufExplorerDefaultAction*
+Specify the default action for |:BufExplorer| and |:ToggleBufExplorer|.  For
+example: >
+  let g:bufExplorerDefaultAction = 'current'  " Open in current window.
+  let g:bufExplorerDefaultAction = 'split'    " Open in new horizontal split.
+  let g:bufExplorerDefaultAction = 'vsplit'   " Open in new vertical split.
+  let g:bufExplorerDefaultAction = 'above'    " Open in horizontal split above.
+  let g:bufExplorerDefaultAction = 'below'    " Open in horizontal split below.
+  let g:bufExplorerDefaultAction = 'left'     " Open in vertical split to left.
+  let g:bufExplorerDefaultAction = 'right'    " Open in vertical split to right.
+The default is `current` (open in current window). See |:BufExplorer| for more
+details.
+
                                                      *g:bufExplorerDefaultHelp*
 To control whether the default help is displayed or not, use: >
   let g:bufExplorerDefaultHelp=0       " Do not show default help.

+ 125 - 41
plugin/bufexplorer.vim

@@ -93,9 +93,28 @@ if v:version < 704
     finish
 endif
 
+" Command actions {{{2
+let s:actions = [
+        \ 'current',
+        \ 'close',
+        \ 'split',
+        \ 'vsplit',
+        \ 'above',
+        \ 'below',
+        \ 'left',
+        \ 'right',
+        \ ]
+
+" Command-line completion function for `s:actions`.
+function! s:ActionArgs(ArgLead, CmdLine, CursorPos)
+    return join(s:actions, "\n")
+endfunction
+
 " Create commands {{{2
-command! BufExplorer :call BufExplorer()
-command! ToggleBufExplorer :call ToggleBufExplorer()
+command! -nargs=? -complete=custom,<SID>ActionArgs
+        \ BufExplorer :call BufExplorer(<f-args>)
+command! -nargs=? -complete=custom,<SID>ActionArgs
+        \ ToggleBufExplorer :call ToggleBufExplorer(<f-args>)
 command! BufExplorerHorizontalSplit :call BufExplorerHorizontalSplit()
 command! BufExplorerVerticalSplit :call BufExplorerVerticalSplit()
 
@@ -121,7 +140,6 @@ let s:name = '[BufExplorer]'
 let s:bufExplorerBuffer = 0
 let s:running = 0
 let s:sort_by = ["number", "name", "fullpath", "mru", "extension"]
-let s:splitMode = ""
 let s:didSplit = 0
 let s:types = ["fullname", "homename", "path", "relativename", "relativepath", "shortname"]
 
@@ -528,7 +546,6 @@ function! s:Cleanup()
     endif
 
     let s:running = 0
-    let s:splitMode = ""
     let s:didSplit = 0
 
     delmarks!
@@ -559,46 +576,80 @@ endfunction
 
 " BufExplorerHorizontalSplit {{{2
 function! BufExplorerHorizontalSplit()
-    let s:splitMode = "sp"
-    execute "BufExplorer"
-    let s:splitMode = ""
+    call BufExplorer('split')
 endfunction
 
 " BufExplorerVerticalSplit {{{2
 function! BufExplorerVerticalSplit()
-    let s:splitMode = "vsp"
-    execute "BufExplorer"
-    let s:splitMode = ""
+    call BufExplorer('vsplit')
 endfunction
 
 " ToggleBufExplorer {{{2
-function! ToggleBufExplorer()
-    if exists("s:running") && s:running == 1 && bufname(winbufnr(0)) == s:name
-        call s:Close()
+" Args: `([action])`
+" Optional `action` argument must be taken from `s:actions`.  If not present,
+" `action` defaults to `g:bufExplorerDefaultAction`.
+function! ToggleBufExplorer(...)
+    if a:0 >= 1
+        let action = a:1
     else
-        call BufExplorer()
+        let action = g:bufExplorerDefaultAction
+    endif
+    if a:0 >= 2
+        echoerr 'Too many arguments'
+        return
+    endif
+
+    if index(s:actions, action) < 0
+        echoerr 'Invalid action ' . action
+        return
+    endif
+
+    if s:running && bufnr('%') == s:bufExplorerBuffer
+        let action = 'close'
     endif
+
+    call BufExplorer(action)
 endfunction
 
 " BufExplorer {{{2
-function! BufExplorer()
-    let name = s:name
+" Args: `([action])`
+" Optional `action` argument must be taken from `s:actions`.  If not present,
+" `action` defaults to `g:bufExplorerDefaultAction`.
+function! BufExplorer(...)
+    if a:0 >= 1
+        let action = a:1
+    else
+        let action = g:bufExplorerDefaultAction
+    endif
+    if a:0 >= 2
+        echoerr 'Too many arguments'
+        return
+    endif
 
-    if !has("win32")
-        " On non-Windows boxes, escape the name so that is shows up correctly.
-        let name = escape(name, "[]")
+    if index(s:actions, action) < 0
+        echoerr 'Invalid action ' . action
+        return
     endif
 
-    " Make sure there is only one explorer open at a time.
-    if s:running == 1
-        " Go to the open buffer.
-        if has("gui")
-            execute "drop" name
-        endif
+    if action == 'close'
+        call s:Close()
+        return
+    endif
 
+    let [tabNbr, winNbr] = s:FindBufExplorer()
+    if tabNbr > 0
+        execute 'keepjumps ' . tabNbr . 'tabnext'
+        execute 'keepjumps ' . winNbr . 'wincmd w'
         return
     endif
 
+    let name = s:name
+
+    if !has("win32")
+        " On non-Windows boxes, escape the name so that is shows up correctly.
+        let name = escape(name, "[]")
+    endif
+
     let s:tabIdAtLaunch = s:MRUEnsureTabId(tabpagenr())
     let s:windowAtLaunch = winnr()
 
@@ -610,24 +661,27 @@ function! BufExplorer()
     call s:MRUGarbageCollectBufs()
     call s:MRUGarbageCollectTabs()
 
-    " We may have to split the current window.
-    if s:splitMode != ""
-        " Save off the original settings.
-        let [_splitbelow, _splitright] = [&splitbelow, &splitright]
+    " `{ action: [splitMode, botRight] }`.
+    let actionMap = {
+            \ 'split'   : ['split', g:bufExplorerSplitBelow],
+            \ 'vsplit'  : ['vsplit', g:bufExplorerSplitRight],
+            \ 'above'   : ['split', 0],
+            \ 'below'   : ['split', 1],
+            \ 'left'    : ['vsplit', 0],
+            \ 'right'   : ['vsplit', 1],
+            \ 'current' : ['', 0],
+            \}
+    let [splitMode, botRight] = actionMap[action]
 
-        " Set the setting to ours.
-        let [&splitbelow, &splitright] = [g:bufExplorerSplitBelow, g:bufExplorerSplitRight]
-        let _size = (s:splitMode == "sp") ? g:bufExplorerSplitHorzSize : g:bufExplorerSplitVertSize
-
-        " Split the window either horizontally or vertically.
-        if _size <= 0
-            execute 'keepalt ' . s:splitMode
-        else
-            execute 'keepalt ' . _size . s:splitMode
+    " We may have to split the current window.
+    if splitMode != ''
+        let size = splitMode == 'split' ? g:bufExplorerSplitHorzSize : g:bufExplorerSplitVertSize
+        let cmd = 'keepalt ' . (botRight ? 'botright ' : 'topleft ')
+        if size > 0
+            let cmd .= size
         endif
-
-        " Restore the original settings.
-        let [&splitbelow, &splitright] = [_splitbelow, _splitright]
+        let cmd .= splitMode
+        execute cmd
 
         " Remember that a split was triggered
         let s:didSplit = 1
@@ -1352,6 +1406,18 @@ endfunction
 
 " Close {{{2
 function! s:Close()
+    let [tabNbr, winNbr] = s:FindBufExplorer()
+    if tabNbr == 0
+        return
+    endif
+    let [curTabNbr, curWinNbr] = [tabpagenr(), winnr()]
+    if [tabNbr, winNbr] != [curTabNbr, curWinNbr]
+        " User has switched away from the original BufExplorer window.
+        " It's unclear how to do better than simply wiping out the
+        " BufExplorer buffer.
+        execute 'bwipeout ' . s:bufExplorerBuffer
+        return
+    endif
     " Get only the listed buffers associated with the current tab (up to 2).
     let listed = s:MRUListedBuffersForTab(s:tabIdAtLaunch, 2)
 
@@ -1377,6 +1443,23 @@ function! s:Close()
     echo
 endfunction
 
+" FindBufExplorer {{{2
+" Return `[tabNbr, winNbr]`; both numbers will be zero if not found.
+function! s:FindBufExplorer()
+    let result = [0, 0]
+    if s:running
+        let numTabs = tabpagenr('$')
+        for tabNbr in range(1, numTabs)
+            let winNbr = index(tabpagebuflist(tabNbr), s:bufExplorerBuffer) + 1
+            if winNbr > 0
+                let result = [tabNbr, winNbr]
+                break
+            endif
+        endfor
+    endif
+    return result
+endfunction
+
 " ToggleShowTerminal {{{2
 function! s:ToggleShowTerminal()
     let g:bufExplorerShowTerminal = !g:bufExplorerShowTerminal
@@ -1677,6 +1760,7 @@ endfunction
 
 " Default values {{{2
 call s:Set("g:bufExplorerDisableDefaultKeyMapping", 0)  " Do not disable default key mappings.
+call s:Set("g:bufExplorerDefaultAction", 'current')     " Default action for `:BufExplorer` with no args.
 call s:Set("g:bufExplorerDefaultHelp", 1)               " Show default help?
 call s:Set("g:bufExplorerDetailedHelp", 0)              " Show detailed help?
 call s:Set("g:bufExplorerFindActive", 1)                " When selecting an active buffer, take you to the window where it is active?