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.
This commit is contained in:
Martin Grenfell
2012-01-09 15:59:21 +00:00
parent 3620029ae0
commit 4f1a205c83

View File

@@ -392,34 +392,18 @@ endfunction
function! s:Bookmark.open(...) function! s:Bookmark.open(...)
let opts = a:0 ? a:1 : {} let opts = a:0 ? a:1 : {}
if self.path.isDirectory if self.path.isDirectory && !has_key(opts, 'split')
if has_key(opts, 'split') call self.toRoot()
let n = s:TreeDirNode.New(self.path)
call n.open(opts)
else
call self.toRoot()
endif
else else
if self.validate() let opener = s:Opener.New(self.path, opts)
let n = s:TreeFileNode.New(self.path) call opener.open(self)
call n.open(opts)
endif
endif endif
endfunction endfunction
" FUNCTION: Bookmark.openInNewTab(options) {{{3 " FUNCTION: Bookmark.openInNewTab(options) {{{3
" Create a new bookmark object with the given name and path object " Create a new bookmark object with the given name and path object
function! s:Bookmark.openInNewTab(options) function! s:Bookmark.openInNewTab(options)
let currentTab = tabpagenr() call s:deprecated('Bookmark.openInNewTab', 'is deprecated, use open() instead')
if self.path.isDirectory call self.open(a:options)
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
endfunction endfunction
" Function: Bookmark.setPath(path) {{{3 " Function: Bookmark.setPath(path) {{{3
" makes this bookmark point to the given path " makes this bookmark point to the given path
@@ -1232,210 +1216,30 @@ function! s:TreeFileNode.New(path)
endfunction endfunction
"FUNCTION: TreeFileNode.open() {{{3 "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(...) function! s:TreeFileNode.open(...)
echomsg self.path.str()
let opts = a:0 ? a:1 : {} let opts = a:0 ? a:1 : {}
let opener = s:Opener.New(self.path, opts)
let currentBuf = bufnr("") call opener.open(self)
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
endfunction 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 "FUNCTION: TreeFileNode.openSplit() {{{3
"Open this node in a new window "Open this node in a new window
function! s:TreeFileNode.openSplit() function! s:TreeFileNode.openSplit()
call s:deprecated('TreeFileNode.openSplit', 'is deprecated, use .open() instead.') call s:deprecated('TreeFileNode.openSplit', 'is deprecated, use .open() instead.')
call self.open({'split': 'h'}) call self.open({'split': 'h'})
endfunction 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 "FUNCTION: TreeFileNode.openVSplit() {{{3
"Open this node in a new vertical window "Open this node in a new vertical window
function! s:TreeFileNode.openVSplit() function! s:TreeFileNode.openVSplit()
call s:deprecated('TreeFileNode.openVSplit', 'is deprecated, use .open() instead.') call s:deprecated('TreeFileNode.openVSplit', 'is deprecated, use .open() instead.')
call self.open({'split': 'v'}) call self.open({'split': 'v'})
endfunction 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: TreeFileNode.openInNewTab(options) {{{3
function! s:TreeFileNode.openInNewTab(options) function! s:TreeFileNode.openInNewTab(options)
echomsg 'TreeFileNode.openInNewTab is deprecated' echomsg 'TreeFileNode.openInNewTab is deprecated'
call self.open(extend({'split': 't'}, a:options)) call self.open(extend({'split': 't'}, a:options))
endfunction 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 "FUNCTION: TreeFileNode.putCursorHere(isJump, recurseUpward){{{3
"Places the cursor on the line number this node is rendered on "Places the cursor on the line number this node is rendered on
" "
@@ -1870,21 +1674,9 @@ unlet s:TreeDirNode.open
function! s:TreeDirNode.open(...) function! s:TreeDirNode.open(...)
let opts = a:0 ? a:1 : {} let opts = a:0 ? a:1 : {}
if has_key(opts, 'split') && opts['split'] == 't' if has_key(opts, 'split') && !empty(opts['split'])
let currentBuf = bufnr("") let opener = s:Opener.New(self.path, opts)
let currentTab = tabpagenr() call opener.open(self)
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
else else
let self.isOpen = 1 let self.isOpen = 1
if self.children ==# [] if self.children ==# []
@@ -1899,23 +1691,14 @@ endfunction
" opens an explorer window for this node in the previous window (could be a " opens an explorer window for this node in the previous window (could be a
" nerd tree or a netrw) " nerd tree or a netrw)
function! s:TreeDirNode.openExplorer() function! s:TreeDirNode.openExplorer()
let oldwin = winnr() call self.open({'split': 'p'})
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
endfunction endfunction
"FUNCTION: TreeDirNode.openInNewTab(options) {{{3 "FUNCTION: TreeDirNode.openInNewTab(options) {{{3
unlet s:TreeDirNode.openInNewTab
function! s:TreeDirNode.openInNewTab(options) function! s:TreeDirNode.openInNewTab(options)
call s:deprecated('TreeDirNode.openInNewTab', 'is deprecated, use open() instead') call s:deprecated('TreeDirNode.openInNewTab', 'is deprecated, use open() instead')
call self.open({'split': 't'}) call self.open({'split': 't'})
endfunction endfunction
"FUNCTION: TreeDirNode._openInNewTab() {{{3 "FUNCTION: TreeDirNode._openInNewTab() {{{3
unlet s:TreeDirNode._openInNewTab
function! s:TreeDirNode._openInNewTab() function! s:TreeDirNode._openInNewTab()
tabnew tabnew
call s:initNerdTree(self.path.str()) call s:initNerdTree(self.path.str())
@@ -2086,6 +1869,243 @@ function! s:TreeDirNode.transplantChild(newNode)
endfor endfor
endfunction 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 "CLASS: Path {{{2
"============================================================ "============================================================
let s:Path = {} let s:Path = {}
@@ -2317,6 +2337,10 @@ function! s:Path.displayString()
return self.cachedDisplayString return self.cachedDisplayString
endfunction endfunction
"FUNCTION: Path.edit() {{{3
function! s:Path.edit()
exec "edit " . self.str({'format': 'Edit'})
endfunction
"FUNCTION: Path.extractDriveLetter(fullpath) {{{3 "FUNCTION: Path.extractDriveLetter(fullpath) {{{3
" "
"If running windows, cache the drive letter for this path "If running windows, cache the drive letter for this path
@@ -3803,16 +3827,22 @@ function! s:activateAll()
endif endif
endfunction endfunction
"FUNCTION: s:activateNode() {{{2 "FUNCTION: s:activateDirNode() {{{2
"handle the user activating a tree node "handle the user activating a tree node
function! s:activateNode(node) function! s:activateDirNode(node)
call a:node.activate({'reuse': 1}) call a:node.activate({'reuse': 1})
endfunction 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 "FUNCTION: s:activateBookmark() {{{2
"handle the user activating a bookmark "handle the user activating a bookmark
function! s:activateBookmark(bm) function! s:activateBookmark(bm)
call a:bm.activate() call a:bm.activate(!a:bm.path.isDirectory ? {'split': 'p'} : {})
endfunction endfunction
"FUNCTION: s:bindMappings() {{{2 "FUNCTION: s:bindMappings() {{{2
@@ -3821,12 +3851,15 @@ function! s:bindMappings()
call NERDTreeAddKeyMap({ 'key': 'middlerelease', 'scope': "all", 'callback': s."handleMiddleMouse" }) call NERDTreeAddKeyMap({ 'key': 'middlerelease', 'scope': "all", 'callback': s."handleMiddleMouse" })
call NERDTreeAddKeyMap({ 'key': 'leftrelease', 'scope': "all", 'callback': s."handleLeftClick" }) 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': "Bookmark", 'callback': s."activateBookmark" })
call NERDTreeAddKeyMap({ 'key': '2-leftmouse', 'scope': "all", 'callback': s."activateAll" }) 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': "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': "Bookmark", 'callback': s."activateBookmark" })
call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "all", 'callback': s."activateAll" }) call NERDTreeAddKeyMap({ 'key': g:NERDTreeMapActivateNode, 'scope': "all", 'callback': s."activateAll" })
exec "nnoremap <silent> <buffer> <cr> :call <SID>KeyMap_Invoke('". g:NERDTreeMapActivateNode ."')<cr>" exec "nnoremap <silent> <buffer> <cr> :call <SID>KeyMap_Invoke('". g:NERDTreeMapActivateNode ."')<cr>"
@@ -4108,7 +4141,7 @@ function! s:openBookmark(name)
if targetNode.path.isDirectory if targetNode.path.isDirectory
call targetNode.openExplorer() call targetNode.openExplorer()
else else
call targetNode.open() call targetNode.open({'split': 'p'})
endif endif
endfunction endfunction
@@ -4148,7 +4181,7 @@ endfunction
"FUNCTION: s:previewNodeCurrent(node) {{{2 "FUNCTION: s:previewNodeCurrent(node) {{{2
function! s:previewNodeCurrent(node) function! s:previewNodeCurrent(node)
call a:node.open({'stay': 1}) call a:node.open({'stay': 1, 'split': 'p'})
endfunction endfunction
"FUNCTION: s:previewNodeHSplit(node) {{{2 "FUNCTION: s:previewNodeHSplit(node) {{{2