nerdtree

A tree explorer plugin for vim.
Index Commits Files Refs
commit 4f1a205c8358c97ab9a7199fdb7e6b5e22c46db4
parent 3620029ae0c82d4ad83f2ddfe26e55235c975933
Author: Martin Grenfell <martin.grenfell@gmail.com>
Date:   Mon,  9 Jan 2012 15:59:21 +0000

add Opener class to handle opening nodes/bookmarks

Move the code to split windows and open nodes and bookmarks out into a
dedicated class. This will remove duplication and remove and centralise
a concern from the other classes.

Diffstat:
Mplugin/NERD_tree.vim | 505++++++++++++++++++++++++++++++++++++++++++-------------------------------------
1 file changed, 269 insertions(+), 236 deletions(-)
diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim
@@ -392,34 +392,18 @@ endfunction
 function! s:Bookmark.open(...)
     let opts = a:0 ? a:1 : {}
 
-    if self.path.isDirectory
-        if has_key(opts, 'split')
-            let n = s:TreeDirNode.New(self.path)
-            call n.open(opts)
-        else
-            call self.toRoot()
-        endif
+    if self.path.isDirectory && !has_key(opts, 'split')
+        call self.toRoot()
     else
-        if self.validate()
-            let n = s:TreeFileNode.New(self.path)
-            call n.open(opts)
-        endif
+        let opener = s:Opener.New(self.path, opts)
+        call opener.open(self)
     endif
 endfunction
 " FUNCTION: Bookmark.openInNewTab(options) {{{3
 " Create a new bookmark object with the given name and path object
 function! s:Bookmark.openInNewTab(options)
-    let currentTab = tabpagenr()
-    if self.path.isDirectory
-        tabnew
-        call s:initNerdTree(self.name)
-    else
-        exec "tabedit " . self.path.str({'format': 'Edit'})
-    endif
-
-    if s:has_opt(a:options, 'stayInCurrentTab')
-        exec "tabnext " . currentTab
-    endif
+    call s:deprecated('Bookmark.openInNewTab', 'is deprecated, use open() instead')
+    call self.open(a:options)
 endfunction
 " Function: Bookmark.setPath(path)   {{{3
 " makes this bookmark point to the given path
@@ -1232,210 +1216,30 @@ function! s:TreeFileNode.New(path)
 endfunction
 
 "FUNCTION: TreeFileNode.open() {{{3
-"Args:
-"A dictionary containing the following keys (all optional):
-"  'split': Specifies whether the node should be opened in new split/tab or in
-"           the previous window. Can be either 'v' or 'h' or 't' (for open in
-"           new tab)
-"  'reuse': if a window is displaying the file then jump the cursor there
-"  'keepopen': dont close the tree window
-"  'stay': open the file, but keep the cursor in the tree win
-"
 function! s:TreeFileNode.open(...)
+    echomsg self.path.str()
     let opts = a:0 ? a:1 : {}
-
-    let currentBuf = bufnr("")
-    let currentTab = tabpagenr()
-
-    if s:has_opt(opts, 'reuse') && self._putCursorInBufWin()
-        return
-    endif
-
-    if has_key(opts, 'split')
-        if opts['split'] == 'v'
-            call self._openVSplit()
-        elseif opts['split'] == 'h'
-            call self._openSplit()
-        else
-            call self._openInNewTab(opts)
-        endif
-    else
-        call self._open()
-    endif
-
-    if !s:has_opt(opts, 'keepopen')
-        call s:closeTreeIfQuitOnOpen()
-    endif
-
-    if s:has_opt(opts, 'stay')
-        call s:exec('normal ' . currentTab . 'gt')
-        call s:exec(bufwinnr(currentBuf) . 'wincmd w')
-    endif
+    let opener = s:Opener.New(self.path, opts)
+    call opener.open(self)
 endfunction
 
-"FUNCTION: TreeFileNode._open() {{{3
-function! s:TreeFileNode._open()
-
-    if b:NERDTreeType ==# "secondary"
-        exec 'edit ' . self.path.str({'format': 'Edit'})
-        return
-    endif
-
-    if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1
-        call self._openSplit()
-    else
-        try
-            if !s:isWindowUsable(winnr("#"))
-                call s:exec(s:firstUsableWindow() . "wincmd w")
-            else
-                call s:exec('wincmd p')
-            endif
-            exec ("edit " . self.path.str({'format': 'Edit'}))
-        catch /^Vim\%((\a\+)\)\=:E37/
-            call s:putCursorInTreeWin()
-            throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified."
-        catch /^Vim\%((\a\+)\)\=:/
-            echo v:exception
-        endtry
-    endif
-endfunction
 "FUNCTION: TreeFileNode.openSplit() {{{3
 "Open this node in a new window
 function! s:TreeFileNode.openSplit()
     call s:deprecated('TreeFileNode.openSplit', 'is deprecated, use .open() instead.')
     call self.open({'split': 'h'})
 endfunction
-"FUNCTION: TreeFileNode._openSplit() {{{3
-"Open this node in a new window
-function! s:TreeFileNode._openSplit()
-
-    if b:NERDTreeType ==# "secondary"
-        exec "split " . self.path.str({'format': 'Edit'})
-        return
-    endif
-
-    " Save the user's settings for splitbelow and splitright
-    let savesplitbelow=&splitbelow
-    let savesplitright=&splitright
-
-    " 'there' will be set to a command to move from the split window
-    " back to the explorer window
-    "
-    " 'back' will be set to a command to move from the explorer window
-    " back to the newly split window
-    "
-    " 'right' and 'below' will be set to the settings needed for
-    " splitbelow and splitright IF the explorer is the only window.
-    "
-    let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l"
-    let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h"
-    let right= g:NERDTreeWinPos ==# "left"
-    let below=0
-
-    " Attempt to go to adjacent window
-    call s:exec(back)
-
-    let onlyOneWin = (winnr("$") ==# 1)
-
-    " If no adjacent window, set splitright and splitbelow appropriately
-    if onlyOneWin
-        let &splitright=right
-        let &splitbelow=below
-    else
-        " found adjacent window - invert split direction
-        let &splitright=!right
-        let &splitbelow=!below
-    endif
-
-    let splitMode = onlyOneWin ? "vertical" : ""
-
-    " Open the new window
-    try
-        exec(splitMode." sp " . self.path.str({'format': 'Edit'}))
-    catch /^Vim\%((\a\+)\)\=:E37/
-        call s:putCursorInTreeWin()
-        throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self.path.str() ." is already open and modified."
-    catch /^Vim\%((\a\+)\)\=:/
-        "do nothing
-    endtry
-
-    "resize the tree window if no other window was open before
-    if onlyOneWin
-        let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
-        call s:exec(there)
-        exec("silent ". splitMode ." resize ". size)
-        call s:exec('wincmd p')
-    endif
-
-    " Restore splitmode settings
-    let &splitbelow=savesplitbelow
-    let &splitright=savesplitright
-endfunction
 "FUNCTION: TreeFileNode.openVSplit() {{{3
 "Open this node in a new vertical window
 function! s:TreeFileNode.openVSplit()
     call s:deprecated('TreeFileNode.openVSplit', 'is deprecated, use .open() instead.')
     call self.open({'split': 'v'})
 endfunction
-"FUNCTION: TreeFileNode._openVSplit() {{{3
-"Open this node in a new vertical window
-function! s:TreeFileNode._openVSplit()
-    if b:NERDTreeType ==# "secondary"
-        exec "vnew " . self.path.str({'format': 'Edit'})
-        return
-    endif
-
-    let winwidth = winwidth(".")
-    if winnr("$")==#1
-        let winwidth = g:NERDTreeWinSize
-    endif
-
-    call s:exec("wincmd p")
-    exec "vnew " . self.path.str({'format': 'Edit'})
-
-    "resize the nerd tree back to the original size
-    call s:putCursorInTreeWin()
-    exec("silent vertical resize ". winwidth)
-    call s:exec('wincmd p')
-endfunction
 "FUNCTION: TreeFileNode.openInNewTab(options) {{{3
 function! s:TreeFileNode.openInNewTab(options)
     echomsg 'TreeFileNode.openInNewTab is deprecated'
     call self.open(extend({'split': 't'}, a:options))
 endfunction
-"FUNCTION: TreeFileNode._openInNewTab(options) {{{3
-function! s:TreeFileNode._openInNewTab(options)
-    let currentTab = tabpagenr()
-    exec "tabedit " . self.path.str({'format': 'Edit'})
-
-    if s:has_opt(a:options, 'stayInCurrentTab')
-        exec "tabnext " . currentTab
-    endif
-endfunction
-"FUNCTION: TreeFileNode._putCursorInBufWin(){{{3
-"put the cursor in the first window we find for this file
-"
-"return 1 if we were successful
-function! s:TreeFileNode._putCursorInBufWin(...)
-    "check the current tab for the window
-    let winnr = bufwinnr('^' . self.path.str() . '$')
-    if winnr != -1
-        call s:exec(winnr . "wincmd w")
-        return 1
-    else
-        "check other tabs
-        let tabnr = self.path.tabnr()
-        if tabnr
-            call s:exec('normal! ' . tabnr . 'gt')
-            let winnr = bufwinnr('^' . self.path.str() . '$')
-            call s:exec(winnr . "wincmd w")
-            return 1
-        endif
-    endif
-    return 0
-endfunction
-
-
 "FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3
 "Places the cursor on the line number this node is rendered on
 "
@@ -1870,21 +1674,9 @@ unlet s:TreeDirNode.open
 function! s:TreeDirNode.open(...)
     let opts = a:0 ? a:1 : {}
 
-    if has_key(opts, 'split') && opts['split'] == 't'
-        let currentBuf = bufnr("")
-        let currentTab = tabpagenr()
-
-        call self._openInNewTab()
-
-        if s:has_opt(opts, 'stay')
-            call s:exec('normal ' . currentTab . 'gt')
-            call s:exec(bufwinnr(currentBuf) . 'wincmd w')
-        endif
-
-        if !s:has_opt(opts, 'keepopen')
-            call s:closeTreeIfQuitOnOpen()
-        endif
-
+    if has_key(opts, 'split') && !empty(opts['split'])
+        let opener = s:Opener.New(self.path, opts)
+        call opener.open(self)
     else
         let self.isOpen = 1
         if self.children ==# []
@@ -1899,23 +1691,14 @@ endfunction
 " opens an explorer window for this node in the previous window (could be a
 " nerd tree or a netrw)
 function! s:TreeDirNode.openExplorer()
-    let oldwin = winnr()
-    call s:exec('wincmd p')
-    if oldwin ==# winnr() || (&modified && s:bufInWindows(winbufnr(winnr())) < 2)
-        call s:exec('wincmd p')
-        call self.openSplit()
-    else
-        exec ("silent edit " . self.path.str({'format': 'Edit'}))
-    endif
+    call self.open({'split': 'p'})
 endfunction
 "FUNCTION: TreeDirNode.openInNewTab(options) {{{3
-unlet s:TreeDirNode.openInNewTab
 function! s:TreeDirNode.openInNewTab(options)
     call s:deprecated('TreeDirNode.openInNewTab', 'is deprecated, use open() instead')
     call self.open({'split': 't'})
 endfunction
 "FUNCTION: TreeDirNode._openInNewTab() {{{3
-unlet s:TreeDirNode._openInNewTab
 function! s:TreeDirNode._openInNewTab()
     tabnew
     call s:initNerdTree(self.path.str())
@@ -2086,6 +1869,243 @@ function! s:TreeDirNode.transplantChild(newNode)
     endfor
 endfunction
 "============================================================
+"CLASS: Opener {{{2
+"============================================================
+let s:Opener = {}
+"FUNCTION: Opener._gotoTargetWin() {{{3
+function! s:Opener._gotoTargetWin()
+    if b:NERDTreeType ==# "secondary"
+        if self._split == 'v'
+            vsplit
+        elseif self._split == 'h'
+            split
+        elseif self._split == 't'
+            tabnew
+        endif
+    else
+
+        if self._split == 'v'
+            call self._newVSplit()
+        elseif self._split == 'h'
+            call self._newSplit()
+        elseif self._split == 't'
+            tabnew
+        elseif self._split == 'p'
+            call self._previousWindow()
+        endif
+    endif
+endfunction
+
+"FUNCTION: Opener.New(path, opts) {{{3
+"Args:
+"
+"a:path: The path object that is to be opened.
+"
+"a:opts:
+"
+"A dictionary containing the following keys (all optional):
+"  'split': Specifies whether the node should be opened in new split/tab or in
+"           the previous window. Can be either 'v' or 'h' or 't' (for open in
+"           new tab)
+"  'reuse': if a window is displaying the file then jump the cursor there
+"  'keepopen': dont close the tree window
+"  'stay': open the file, but keep the cursor in the tree win
+function! s:Opener.New(path, opts)
+    let newObj = copy(self)
+
+    let newObj._path = a:path
+    let newObj._stay = s:has_opt(a:opts, 'stay')
+    let newObj._reuse = s:has_opt(a:opts, 'reuse')
+    let newObj._keepopen = s:has_opt(a:opts, 'keepopen')
+    let newObj._split = has_key(a:opts, 'split') ? a:opts['split'] : ''
+    call newObj._saveCursorPos()
+
+    return newObj
+endfunction
+
+"FUNCTION: Opener._newSplit() {{{3
+function! s:Opener._newSplit()
+    " Save the user's settings for splitbelow and splitright
+    let savesplitbelow=&splitbelow
+    let savesplitright=&splitright
+
+    " 'there' will be set to a command to move from the split window
+    " back to the explorer window
+    "
+    " 'back' will be set to a command to move from the explorer window
+    " back to the newly split window
+    "
+    " 'right' and 'below' will be set to the settings needed for
+    " splitbelow and splitright IF the explorer is the only window.
+    "
+    let there= g:NERDTreeWinPos ==# "left" ? "wincmd h" : "wincmd l"
+    let back = g:NERDTreeWinPos ==# "left" ? "wincmd l" : "wincmd h"
+    let right= g:NERDTreeWinPos ==# "left"
+    let below=0
+
+    " Attempt to go to adjacent window
+    call s:exec(back)
+
+    let onlyOneWin = (winnr("$") ==# 1)
+
+    " If no adjacent window, set splitright and splitbelow appropriately
+    if onlyOneWin
+        let &splitright=right
+        let &splitbelow=below
+    else
+        " found adjacent window - invert split direction
+        let &splitright=!right
+        let &splitbelow=!below
+    endif
+
+    let splitMode = onlyOneWin ? "vertical" : ""
+
+    " Open the new window
+    try
+        exec(splitMode." sp ")
+    catch /^Vim\%((\a\+)\)\=:E37/
+        call s:putCursorInTreeWin()
+        throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self._path.str() ." is already open and modified."
+    catch /^Vim\%((\a\+)\)\=:/
+        "do nothing
+    endtry
+
+    "resize the tree window if no other window was open before
+    if onlyOneWin
+        let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize
+        call s:exec(there)
+        exec("silent ". splitMode ." resize ". size)
+        call s:exec('wincmd p')
+    endif
+
+    " Restore splitmode settings
+    let &splitbelow=savesplitbelow
+    let &splitright=savesplitright
+endfunction
+
+"FUNCTION: Opener._newVSplit() {{{3
+function! s:Opener._newVSplit()
+    let winwidth = winwidth(".")
+    if winnr("$")==#1
+        let winwidth = g:NERDTreeWinSize
+    endif
+
+    call s:exec("wincmd p")
+    vnew
+
+    "resize the nerd tree back to the original size
+    call s:putCursorInTreeWin()
+    exec("silent vertical resize ". winwidth)
+    call s:exec('wincmd p')
+endfunction
+
+"FUNCTION: Opener.open(target) {{{3
+function! s:Opener.open(target)
+    if self._path.isDirectory
+        call self._openDirectory(a:target)
+    else
+        call self._openFile()
+    endif
+endfunction
+
+"FUNCTION: Opener._openFile() {{{3
+function! s:Opener._openFile()
+    if self._reuse && self._reuseWindow()
+        return
+    endif
+
+    if b:NERDTreeType ==# "secondary"
+        call self._gotoTargetWin()
+        call self._path.edit()
+    else
+        call self._gotoTargetWin()
+        call self._path.edit()
+
+        if self._keepopen
+            call s:closeTreeIfQuitOnOpen()
+        endif
+
+        if self._stay
+            call self._restoreCursorPos()
+        endif
+    endif
+endfunction
+
+"FUNCTION: Opener._openDirectory(node) {{{3
+function! s:Opener._openDirectory(node)
+    if b:NERDTreeType ==# "secondary"
+        call self._gotoTargetWin()
+        call s:initNerdTreeInPlace(a:node.path.str())
+    else
+        call self._gotoTargetWin()
+        if empty(self._split)
+            call a:node.makeRoot()
+            call s:renderView()
+            call a:node.putCursorHere(0, 0)
+        elseif self._split == 't'
+            call s:initNerdTree(a:node.path.str())
+        else
+            call s:initNerdTreeInPlace(a:node.path.str())
+        endif
+    endif
+endfunction
+
+"FUNCTION: Opener._previousWindow() {{{3
+function! s:Opener._previousWindow()
+    if !s:isWindowUsable(winnr("#")) && s:firstUsableWindow() ==# -1
+        call self._newSplit()
+    else
+        try
+            if !s:isWindowUsable(winnr("#"))
+                call s:exec(s:firstUsableWindow() . "wincmd w")
+            else
+                call s:exec('wincmd p')
+            endif
+            exec ("edit " . self._path.str({'format': 'Edit'}))
+        catch /^Vim\%((\a\+)\)\=:E37/
+            call s:putCursorInTreeWin()
+            throw "NERDTree.FileAlreadyOpenAndModifiedError: ". self._path.str() ." is already open and modified."
+        catch /^Vim\%((\a\+)\)\=:/
+            echo v:exception
+        endtry
+    endif
+endfunction
+
+"FUNCTION: Opener._restoreCursorPos(){{{3
+function! s:Opener._restoreCursorPos()
+    call s:exec('normal ' . self._tabnr . 'gt')
+    call s:exec(bufwinnr(self._bufnr) . 'wincmd w')
+endfunction
+
+"FUNCTION: Opener._reuseWindow(){{{3
+"put the cursor in the first window we find for this file
+"
+"return 1 if we were successful
+function! s:Opener._reuseWindow()
+    "check the current tab for the window
+    let winnr = bufwinnr('^' . self._path.str() . '$')
+    if winnr != -1
+        call s:exec(winnr . "wincmd w")
+        return 1
+    else
+        "check other tabs
+        let tabnr = self._path.tabnr()
+        if tabnr
+            call s:exec('normal! ' . tabnr . 'gt')
+            let winnr = bufwinnr('^' . self._path.str() . '$')
+            call s:exec(winnr . "wincmd w")
+            return 1
+        endif
+    endif
+    return 0
+endfunction
+
+"FUNCTION: Opener._saveCursorPos(){{{3
+function! s:Opener._saveCursorPos()
+    let self._bufnr = bufnr("")
+    let self._tabnr = tabpagenr()
+endfunction
+
 "CLASS: Path {{{2
 "============================================================
 let s:Path = {}
@@ -2317,6 +2337,10 @@ function! s:Path.displayString()
 
     return self.cachedDisplayString
 endfunction
+"FUNCTION: Path.edit() {{{3
+function! s:Path.edit()
+    exec "edit " . self.str({'format': 'Edit'})
+endfunction
 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
 "
 "If running windows, cache the drive letter for this path
@@ -3803,16 +3827,22 @@ function! s:activateAll()
     endif
 endfunction
 
-"FUNCTION: s:activateNode() {{{2
+"FUNCTION: s:activateDirNode() {{{2
 "handle the user activating a tree node
-function! s:activateNode(node)
+function! s:activateDirNode(node)
     call a:node.activate({'reuse': 1})
 endfunction
 
+"FUNCTION: s:activateFileNode() {{{2
+"handle the user activating a tree node
+function! s:activateFileNode(node)
+    call a:node.activate({'reuse': 1, 'split': 'p'})
+endfunction
+
 "FUNCTION: s:activateBookmark() {{{2
 "handle the user activating a bookmark
 function! s:activateBookmark(bm)
-    call a:bm.activate()
+    call a:bm.activate(!a:bm.path.isDirectory ? {'split': 'p'} : {})
 endfunction
 
 "FUNCTION: s:bindMappings() {{{2
@@ -3821,12 +3851,15 @@ function! s:bindMappings()
 
     call NERDTreeAddKeyMap({ 'key': 'middlerelease', 'scope': "all", 'callback': s."handleMiddleMouse" })
     call NERDTreeAddKeyMap({ 'key': 'leftrelease', 'scope': "all", 'callback': s."handleLeftClick" })
-    call NERDTreeAddKeyMap({ 'key': '2-leftmouse', 'scope': "Node", 'callback': s."activateNode" })
+    call NERDTreeAddKeyMap({ 'key': '2-leftmouse', 'scope': "DirNode", 'callback': s."activateDirNode" })
+    call NERDTreeAddKeyMap({ 'key': '2-leftmouse', 'scope': "FileNode", 'callback': s."activateFileNode" })
     call NERDTreeAddKeyMap({ 'key': '2-leftmouse', 'scope': "Bookmark", 'callback': s."activateBookmark" })
     call NERDTreeAddKeyMap({ 'key': '2-leftmouse', 'scope': "all", 'callback': s."activateAll" })
 
 
     call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "Node", 'callback': s."activateNode" })
+    call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "DirNode", 'callback': s."activateDirNode" })
+    call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "FileNode", 'callback': s."activateFileNode" })
     call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "Bookmark", 'callback': s."activateBookmark" })
     call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "all", 'callback': s."activateAll" })
     exec "nnoremap <silent> <buffer> <cr> :call <SID>KeyMap_Invoke('". g:NERDTreeMapActivateNode ."')<cr>"
@@ -4108,7 +4141,7 @@ function! s:openBookmark(name)
     if targetNode.path.isDirectory
         call targetNode.openExplorer()
     else
-        call targetNode.open()
+        call targetNode.open({'split': 'p'})
     endif
 endfunction
 
@@ -4148,7 +4181,7 @@ endfunction
 
 "FUNCTION: s:previewNodeCurrent(node) {{{2
 function! s:previewNodeCurrent(node)
-    call a:node.open({'stay': 1})
+    call a:node.open({'stay': 1, 'split': 'p'})
 endfunction
 
 "FUNCTION: s:previewNodeHSplit(node) {{{2