From c75a022a233642bde012966face566d32a484481 Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Sun, 19 Jul 2009 17:21:57 +1200 Subject: [PATCH] extend the api and move the fs operations into a plugin --- ftplugin/nerdtree_fs_menu.vim | 233 ++++++++++++++++++++++++++ plugin/NERD_tree.vim | 298 ++++++++++------------------------ 2 files changed, 317 insertions(+), 214 deletions(-) create mode 100644 ftplugin/nerdtree_fs_menu.vim diff --git a/ftplugin/nerdtree_fs_menu.vim b/ftplugin/nerdtree_fs_menu.vim new file mode 100644 index 0000000..1e81f2b --- /dev/null +++ b/ftplugin/nerdtree_fs_menu.vim @@ -0,0 +1,233 @@ +" ============================================================================ +" File: nerdtree_fs_menu.vim +" Description: plugin for the NERD Tree that provides a file system menu +" Maintainer: Martin Grenfell +" Last Change: 17 July, 2009 +" License: This program is free software. It comes without any warranty, +" to the extent permitted by applicable law. You can redistribute +" it and/or modify it under the terms of the Do What The Fuck You +" Want To Public License, Version 2, as published by Sam Hocevar. +" See http://sam.zoy.org/wtfpl/COPYING for more details. +" +" ============================================================================ +if exists("g:loaded_nerdtree_fs_menu") + finish +endif +let g:loaded_nerdtree_fs_menu = 1 + +call NERDTreeAddMenuItem('(f)ilesystem menu', 'f', 'NERDTreeShowFilesystemMenu') + +"FUNCTION: s:echo(msg){{{1 +function! s:echo(msg) + redraw + echomsg "NERDTree: " . a:msg +endfunction + +"FUNCTION: s:echoWarning(msg){{{1 +function! s:echoWarning(msg) + echohl warningmsg + call s:echo(a:msg) + echohl normal +endfunction + +"FUNCTION: s:promptToDelBuffer(bufnum, msg){{{1 +"prints out the given msg and, if the user responds by pushing 'y' then the +"buffer with the given bufnum is deleted +" +"Args: +"bufnum: the buffer that may be deleted +"msg: a message that will be echoed to the user asking them if they wish to +" del the buffer +function! s:promptToDelBuffer(bufnum, msg) + echo a:msg + if nr2char(getchar()) ==# 'y' + exec "silent bdelete! " . a:bufnum + endif +endfunction + +"FUNCTION: NERDTreeShowFilesystemMenu(){{{1 +function! NERDTreeShowFilesystemMenu() + let prompt = "NERDTree Filesystem Menu\n" . + \ "==========================================================\n". + \ "Select the desired operation: \n" . + \ " (a)dd a childnode\n". + \ " (m)ove the current node\n". + \ " (d)elete the current node\n" + if g:NERDTreePath.CopyingSupported() + let prompt = prompt . " (c)opy the current node\n\n" + else + let prompt = prompt . " \n" + endif + + echo prompt + + let choice = nr2char(getchar()) + + if choice ==? "a" + call s:create() + elseif choice ==? "m" + call s:move() + elseif choice ==? "d" + call s:delete() + elseif choice ==? "c" && g:NERDTreePath.CopyingSupported() + call s:copy() + endif +endfunction + +"FUNCTION: s:create(){{{1 +function! s:create() + let curDirNode = g:NERDTreeDirNode.GetSelected() + + let newNodeName = input("Add a childnode\n". + \ "==========================================================\n". + \ "Enter the dir/file name to be created. Dirs end with a '/'\n" . + \ "", curDirNode.path.strForGlob() . g:NERDTreePath.Slash()) + + if newNodeName ==# '' + call s:echo("Node Creation Aborted.") + return + endif + + try + let newPath = g:NERDTreePath.Create(newNodeName) + let parentNode = b:NERDTreeRoot.findNode(newPath.getPathTrunk()) + + let newTreeNode = g:NERDTreeFileNode.New(newPath) + if parentNode.isOpen || !empty(parentNode.children) + call parentNode.addChild(newTreeNode, 1) + call NERDTreeRender() + call newTreeNode.putCursorHere(1, 0) + endif + catch /^NERDTree/ + call s:echoWarning("Node Not Created.") + endtry +endfunction + +"FUNCTION: s:move(){{{1 +function! s:move() + let curNode = g:NERDTreeFileNode.GetSelected() + if curNode ==# {} + call s:echo("Put the cursor on a node first" ) + return + endif + + let newNodePath = input("Rename the current node\n" . + \ "==========================================================\n" . + \ "Enter the new path for the node: \n" . + \ "", curNode.path.strForOS(0)) + + if newNodePath ==# '' + call s:echo("Node Renaming Aborted.") + return + endif + + try + let bufnum = bufnr(curNode.path.str(0)) + + call curNode.rename(newNodePath) + call NERDTreeRender() + + "if the node is open in a buffer, ask the user if they want to + "close that buffer + if bufnum != -1 + let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" + call s:promptToDelBuffer(bufnum, prompt) + endif + + call curNode.putCursorHere(1, 0) + + redraw + catch /^NERDTree/ + call s:echoWarning("Node Not Renamed.") + endtry +endfunction + +" FUNCTION: s:delete() {{{1 +function! s:delete() + let currentNode = g:NERDTreeFileNode.GetSelected() + if currentNode ==# {} + call s:echo("Put the cursor on a node first") + return + endif + + let confirmed = 0 + + if currentNode.path.isDirectory + let choice =input("Delete the current node\n" . + \ "==========================================================\n" . + \ "STOP! To delete this entire directory, type 'yes'\n" . + \ "" . currentNode.path.strForOS(0) . ": ") + let confirmed = choice ==# 'yes' + else + echo "Delete the current node\n" . + \ "==========================================================\n". + \ "Are you sure you wish to delete the node:\n" . + \ "" . currentNode.path.strForOS(0) . " (yN):" + let choice = nr2char(getchar()) + let confirmed = choice ==# 'y' + endif + + + if confirmed + try + call currentNode.delete() + call NERDTreeRender() + + "if the node is open in a buffer, ask the user if they want to + "close that buffer + let bufnum = bufnr(currentNode.path.str(0)) + if buflisted(bufnum) + let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" + call s:promptToDelBuffer(bufnum, prompt) + endif + + redraw + catch /^NERDTree/ + call s:echoWarning("Could not remove node") + endtry + else + call s:echo("delete aborted") + endif + +endfunction + +" FUNCTION: s:copy() {{{1 +function! s:copy() + let currentNode = g:NERDTreeFileNode.GetSelected() + if currentNode ==# {} + call s:echo("Put the cursor on a file node first") + return + endif + + let newNodePath = input("Copy the current node\n" . + \ "==========================================================\n" . + \ "Enter the new path to copy the node to: \n" . + \ "", currentNode.path.str(0)) + + if newNodePath != "" + "strip trailing slash + let newNodePath = substitute(newNodePath, '\/$', '', '') + + let confirmed = 1 + if currentNode.path.copyingWillOverwrite(newNodePath) + call s:echo("Warning: copying may overwrite files! Continue? (yN)") + let choice = nr2char(getchar()) + let confirmed = choice ==# 'y' + endif + + if confirmed + try + let newNode = currentNode.copy(newNodePath) + call NERDTreeRender() + call newNode.putCursorHere(0, 0) + catch /^NERDTree/ + call s:echoWarning("Could not copy node") + endtry + endif + else + call s:echo("Copy aborted.") + endif + redraw +endfunction + +" vim: set sw=4 sts=4 et fdm=marker: diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index b2e1f1e..bfd7379 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -104,7 +104,7 @@ call s:initVariable("g:NERDTreeMapCloseChildren", "X") call s:initVariable("g:NERDTreeMapCloseDir", "x") call s:initVariable("g:NERDTreeMapDeleteBookmark", "D") call s:initVariable("g:NERDTreeMapExecute", "!") -call s:initVariable("g:NERDTreeMapFilesystemMenu", "m") +call s:initVariable("g:NERDTreeMapMenu", "m") call s:initVariable("g:NERDTreeMapHelp", "?") call s:initVariable("g:NERDTreeMapJumpFirstChild", "K") call s:initVariable("g:NERDTreeMapJumpLastChild", "J") @@ -139,11 +139,6 @@ let s:tree_wid = 2 let s:tree_markup_reg = '^[ `|]*[\-+~]' let s:tree_up_dir_line = '.. (up a dir)' -let s:os_slash = '/' -if s:running_windows - let s:os_slash = '\' -endif - "the number to add to the nerd tree buffer name to make the buf name unique let s:next_buffer_number = 1 @@ -431,6 +426,56 @@ function! s:Bookmark.Write() endfor call writefile(bookmarkStrings, g:NERDTreeBookmarksFile) endfunction +"CLASS: MenuCallback {{{2 +"============================================================ +let s:MenuCallback = {} + +"FUNCTION: MenuCallback.All() {{{3 +function! s:MenuCallback.All() + if !exists("s:menuCallbacks") + let s:menuCallbacks = [] + endif + return s:menuCallbacks +endfunction + +"FUNCTION: MenuCallback.Create(text, shortcut, callback) {{{3 +function! s:MenuCallback.Create(text, shortcut, callback) + let newCallback = {} + let newCallback = copy(self) + let newCallback.text = a:text + let newCallback.shortcut = a:shortcut + let newCallback.callback = a:callback + call add(s:MenuCallback.All(), newCallback) +endfunction + +"FUNCTION: MenuCallback.ShowMenu() {{{3 +function! s:MenuCallback.ShowMenu() + let curNode = s:TreeFileNode.GetSelected() + if curNode ==# {} + call s:echo("Put the cursor on a node first" ) + return + endif + + let prompt = "NERDTree Menu\n" . + \ "==========================================================\n" + + for i in s:MenuCallback.All() + let prompt .= i.text . "\n" + endfor + + echo prompt + + let choice = nr2char(getchar()) + + for i in s:MenuCallback.All() + if choice ==# i.shortcut + exec "call " . i.callback . "()" + return + endif + endfor +endfunction + + "CLASS: TreeFileNode {{{2 "This class is the parent of the TreeDirNode class and constitures the "'Component' part of the composite design pattern between the treenode @@ -1432,7 +1477,7 @@ function! s:Path.AbsolutePathFor(str) let toReturn = a:str if prependCWD - let toReturn = getcwd() . s:os_slash . a:str + let toReturn = getcwd() . s:Path.Slash() . a:str endif return toReturn @@ -1778,6 +1823,12 @@ function! s:Path.New(path) return newPath endfunction +"FUNCTION: Path.Slash() {{{3 +"return the slash to use for the current OS +function! s:Path.Slash() + return s:running_windows ? '\' : '/' +endfunction + "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3 " " @@ -1928,7 +1979,7 @@ function! s:Path.strForEditCmd() let cwd = tolower(getcwd()) endif - let cwd = cwd . s:os_slash + let cwd = cwd . s:Path.Slash() "return a relative path if we can if stridx(p, cwd) ==# 0 @@ -1944,14 +1995,14 @@ function! s:Path.strForEditCmd() endfunction "FUNCTION: Path.strForGlob() {{{3 function! s:Path.strForGlob() - let lead = s:os_slash + let lead = s:Path.Slash() "if we are running windows then slap a drive letter on the front if s:running_windows let lead = self.drive . '\' endif - let toReturn = lead . join(self.pathSegments, s:os_slash) + let toReturn = lead . join(self.pathSegments, s:Path.Slash()) if !s:running_windows let toReturn = escape(toReturn, s:escape_chars) @@ -1968,14 +2019,14 @@ endfunction "esc: if 1 then all the tricky chars in the returned string will be " escaped. If we are running windows then the str is double quoted instead. function! s:Path.strForOS(esc) - let lead = s:os_slash + let lead = s:Path.Slash() "if we are running windows then slap a drive letter on the front if s:running_windows let lead = self.drive . '\' endif - let toReturn = lead . join(self.pathSegments, s:os_slash) + let toReturn = lead . join(self.pathSegments, s:Path.Slash()) if a:esc if s:running_windows @@ -2082,7 +2133,7 @@ function! s:initNerdTree(name) "hack to get an absolute path if a relative path is given if dir =~ '^\.' - let dir = getcwd() . s:os_slash . dir + let dir = getcwd() . s:Path.Slash() . dir endif let dir = resolve(dir) @@ -2287,8 +2338,14 @@ function! s:unique(list) endfor return uniqlist endfunction -" SECTION: Public Functions {{{1 +" SECTION: Public API {{{1 "============================================================ +let g:NERDTreePath = s:Path +let g:NERDTreeDirNode = s:TreeDirNode +let g:NERDTreeFileNode = s:TreeFileNode +let g:NERDTreeBookmark = s:Bookmark + + "Returns the node that the cursor is currently on. " "If the cursor is not in the NERDTree window, it is temporarily put there. @@ -2328,6 +2385,14 @@ function! NERDTreeGetCurrentPath() endif endfunction +function! NERDTreeAddMenuItem(text, shortcut, callback) + call s:MenuCallback.Create(a:text, a:shortcut, a:callback) +endfunction + +function! NERDTreeRender() + call s:renderView() +endfunction + " SECTION: View Functions {{{1 "============================================================ "FUNCTION: s:centerView() {{{2 @@ -2485,7 +2550,7 @@ function! s:dumpHelp() let @h=@h."\" but leave old root open\n" let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n" let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\n" - let @h=@h."\" ". g:NERDTreeMapFilesystemMenu .": Show filesystem menu\n" + let @h=@h."\" ". g:NERDTreeMapMenu .": Show filesystem menu\n" let @h=@h."\" ". g:NERDTreeMapChdir .":change the CWD to the\n" let @h=@h."\" selected dir\n" @@ -3103,7 +3168,7 @@ function! s:bindMappings() exec "nnoremap ". g:NERDTreeMapCloseDir ." :call closeCurrentDir()" exec "nnoremap ". g:NERDTreeMapCloseChildren ." :call closeChildren()" - exec "nnoremap ". g:NERDTreeMapFilesystemMenu ." :call showFileSystemMenu()" + exec "nnoremap ". g:NERDTreeMapMenu ." :call showMenu()" exec "nnoremap ". g:NERDTreeMapJumpParent ." :call jumpToParent()" exec "nnoremap ". g:NERDTreeMapJumpNextSibling ." :call jumpToSibling(1)" @@ -3260,45 +3325,6 @@ function! s:closeTreeWindow() endif endif endfunction -" FUNCTION: s:copyNode() {{{2 -function! s:copyNode() - let currentNode = s:TreeFileNode.GetSelected() - if currentNode ==# {} - call s:echo("Put the cursor on a file node first") - return - endif - - let newNodePath = input("Copy the current node\n" . - \ "==========================================================\n" . - \ "Enter the new path to copy the node to: \n" . - \ "", currentNode.path.str(0)) - - if newNodePath != "" - "strip trailing slash - let newNodePath = substitute(newNodePath, '\/$', '', '') - - let confirmed = 1 - if currentNode.path.copyingWillOverwrite(newNodePath) - call s:echo("\nWarning: copying may overwrite files! Continue? (yN)") - let choice = nr2char(getchar()) - let confirmed = choice ==# 'y' - endif - - if confirmed - try - let newNode = currentNode.copy(newNodePath) - call s:renderView() - call newNode.putCursorHere(0, 0) - catch /^NERDTree/ - call s:echoWarning("Could not copy node") - endtry - endif - else - call s:echo("Copy aborted.") - endif - redraw -endfunction - " FUNCTION: s:deleteBookmark() {{{2 " if the cursor is on a bookmark, prompt to delete function! s:deleteBookmark() @@ -3324,57 +3350,6 @@ function! s:deleteBookmark() endfunction -" FUNCTION: s:deleteNode() {{{2 -" if the current node is a file, pops up a dialog giving the user the option -" to delete it -function! s:deleteNode() - let currentNode = s:TreeFileNode.GetSelected() - if currentNode ==# {} - call s:echo("Put the cursor on a file node first") - return - endif - - let confirmed = 0 - - if currentNode.path.isDirectory - let choice =input("Delete the current node\n" . - \ "==========================================================\n" . - \ "STOP! To delete this entire directory, type 'yes'\n" . - \ "" . currentNode.path.strForOS(0) . ": ") - let confirmed = choice ==# 'yes' - else - echo "Delete the current node\n" . - \ "==========================================================\n". - \ "Are you sure you wish to delete the node:\n" . - \ "" . currentNode.path.strForOS(0) . " (yN):" - let choice = nr2char(getchar()) - let confirmed = choice ==# 'y' - endif - - - if confirmed - try - call currentNode.delete() - call s:renderView() - - "if the node is open in a buffer, ask the user if they want to - "close that buffer - let bufnum = bufnr(currentNode.path.str(0)) - if buflisted(bufnum) - let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" - call s:promptToDelBuffer(bufnum, prompt) - endif - - redraw - catch /^NERDTree/ - call s:echoWarning("Could not remove node") - endtry - else - call s:echo("delete aborted" ) - endif - -endfunction - " FUNCTION: s:displayHelp() {{{2 " toggles the help display function! s:displayHelp() @@ -3419,40 +3394,6 @@ function! s:handleMiddleMouse() endfunction -" FUNCTION: s:insertNewNode() {{{2 -" Adds a new node to the filesystem and then into the tree -function! s:insertNewNode() - let curDirNode = s:TreeDirNode.GetSelected() - if curDirNode ==# {} - call s:echo("Put the cursor on a node first" ) - return - endif - - let newNodeName = input("Add a childnode\n". - \ "==========================================================\n". - \ "Enter the dir/file name to be created. Dirs end with a '/'\n" . - \ "", curDirNode.path.strForGlob() . s:os_slash) - - if newNodeName ==# '' - call s:echo("Node Creation Aborted.") - return - endif - - try - let newPath = s:Path.Create(newNodeName) - let parentNode = b:NERDTreeRoot.findNode(newPath.getPathTrunk()) - - let newTreeNode = s:TreeFileNode.New(newPath) - if parentNode.isOpen || !empty(parentNode.children) - call parentNode.addChild(newTreeNode, 1) - call s:renderView() - call newTreeNode.putCursorHere(1, 0) - endif - catch /^NERDTree/ - call s:echoWarning("Node Not Created.") - endtry -endfunction - " FUNCTION: s:jumpToFirstChild() {{{2 " wrapper for the jump to child method function! s:jumpToFirstChild() @@ -3655,80 +3596,9 @@ function! s:refreshCurrent() redraw call s:echo("Refreshing node. This could take a while... DONE") endfunction -" FUNCTION: s:renameCurrent() {{{2 -" allows the user to rename the current node -function! s:renameCurrent() - let curNode = s:TreeFileNode.GetSelected() - if curNode ==# {} - call s:echo("Put the cursor on a node first" ) - return - endif - - let newNodePath = input("Rename the current node\n" . - \ "==========================================================\n" . - \ "Enter the new path for the node: \n" . - \ "", curNode.path.strForOS(0)) - - if newNodePath ==# '' - call s:echo("Node Renaming Aborted.") - return - endif - - try - let bufnum = bufnr(curNode.path.str(0)) - - call curNode.rename(newNodePath) - call s:renderView() - - "if the node is open in a buffer, ask the user if they want to - "close that buffer - if bufnum != -1 - let prompt = "\nNode renamed.\n\nThe old file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? " (hidden)" : "") .". Delete this buffer? (yN)" - call s:promptToDelBuffer(bufnum, prompt) - endif - - call curNode.putCursorHere(1, 0) - - redraw - catch /^NERDTree/ - call s:echoWarning("Node Not Renamed.") - endtry -endfunction - -" FUNCTION: s:showFileSystemMenu() {{{2 -function! s:showFileSystemMenu() - let curNode = s:TreeFileNode.GetSelected() - if curNode ==# {} - call s:echo("Put the cursor on a node first" ) - return - endif - - - let prompt = "NERDTree Filesystem Menu\n" . - \ "==========================================================\n". - \ "Select the desired operation: \n" . - \ " (a)dd a childnode\n". - \ " (m)ove the current node\n". - \ " (d)elete the current node\n" - if s:Path.CopyingSupported() - let prompt = prompt . " (c)opy the current node\n\n" - else - let prompt = prompt . " \n" - endif - - echo prompt - - let choice = nr2char(getchar()) - - if choice ==? "a" - call s:insertNewNode() - elseif choice ==? "m" - call s:renameCurrent() - elseif choice ==? "d" - call s:deleteNode() - elseif choice ==? "c" && s:Path.CopyingSupported() - call s:copyNode() - endif +" FUNCTION: s:showMenu() {{{2 +function! s:showMenu() + call s:MenuCallback.ShowMenu() endfunction " FUNCTION: s:toggleIgnoreFilter() {{{2