From c75a022a233642bde012966face566d32a484481 Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Sun, 19 Jul 2009 17:21:57 +1200 Subject: [PATCH 01/27] 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 From 41029aef24baf855f6785fdd2201db58bf2a6aa7 Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Sun, 19 Jul 2009 17:34:28 +1200 Subject: [PATCH 02/27] put the fs operations in the top level of the menu --- ftplugin/nerdtree_fs_menu.vim | 52 ++++++++++------------------------- 1 file changed, 14 insertions(+), 38 deletions(-) diff --git a/ftplugin/nerdtree_fs_menu.vim b/ftplugin/nerdtree_fs_menu.vim index 1e81f2b..956721a 100644 --- a/ftplugin/nerdtree_fs_menu.vim +++ b/ftplugin/nerdtree_fs_menu.vim @@ -15,7 +15,12 @@ if exists("g:loaded_nerdtree_fs_menu") endif let g:loaded_nerdtree_fs_menu = 1 -call NERDTreeAddMenuItem('(f)ilesystem menu', 'f', 'NERDTreeShowFilesystemMenu') +call NERDTreeAddMenuItem('(a)dd a childnode', 'a', 'NERDTreeAddNode') +call NERDTreeAddMenuItem('(m)ove the curent node', 'm', 'NERDTreeMoveNode') +call NERDTreeAddMenuItem('(d)elete the curent node', 'd', 'NERDTreeDeleteNode') +if g:NERDTreePath.CopyingSupported() + call NERDTreeAddMenuItem('(c)copy the current node', 'c', 'NERDTreeCopyNode') +endif "FUNCTION: s:echo(msg){{{1 function! s:echo(msg) @@ -45,37 +50,8 @@ function! s:promptToDelBuffer(bufnum, msg) 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() +"FUNCTION: NERDTreeAddNode(){{{1 +function! NERDTreeAddNode() let curDirNode = g:NERDTreeDirNode.GetSelected() let newNodeName = input("Add a childnode\n". @@ -103,8 +79,8 @@ function! s:create() endtry endfunction -"FUNCTION: s:move(){{{1 -function! s:move() +"FUNCTION: NERDTreeMoveNode(){{{1 +function! NERDTreeMoveNode() let curNode = g:NERDTreeFileNode.GetSelected() if curNode ==# {} call s:echo("Put the cursor on a node first" ) @@ -142,8 +118,8 @@ function! s:move() endtry endfunction -" FUNCTION: s:delete() {{{1 -function! s:delete() +" FUNCTION: NERDTreeDeleteNode() {{{1 +function! NERDTreeDeleteNode() let currentNode = g:NERDTreeFileNode.GetSelected() if currentNode ==# {} call s:echo("Put the cursor on a node first") @@ -191,8 +167,8 @@ function! s:delete() endfunction -" FUNCTION: s:copy() {{{1 -function! s:copy() +" FUNCTION: NERDTreeCopyNode() {{{1 +function! NERDTreeCopyNode() let currentNode = g:NERDTreeFileNode.GetSelected() if currentNode ==# {} call s:echo("Put the cursor on a file node first") From a7f9abe8276e6138e1e7056635030a15a1d600fc Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Mon, 20 Jul 2009 01:01:12 +1200 Subject: [PATCH 03/27] fix a bug where the ../ and ./ entries werent being filtered out --- plugin/NERD_tree.vim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index bfd7379..399f566 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -1256,7 +1256,7 @@ function! s:TreeDirNode._initChildren(silent) "filter out the .. and . directories "Note: we must match .. AND ../ cos sometimes the globpath returns "../ for path with strange chars (eg $) - if i !~ '^\.\.\/\?$' && i !~ '^\.\/\?$' + if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$' "put the next file in a new node and attach it try @@ -1373,7 +1373,10 @@ function! s:TreeDirNode.refresh() let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*') let files = split(filesStr, "\n") for i in files - if i !~ '\.\.$' && i !~ '\.$' + "filter out the .. and . directories + "Note: we must match .. AND ../ cos sometimes the globpath returns + "../ for path with strange chars (eg $) + if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$' try "create a new path and see if it exists in this nodes children From 4d77c3ae45501ede4d2df63056fd6d158058f693 Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Mon, 20 Jul 2009 01:05:21 +1200 Subject: [PATCH 04/27] add basic git plugin --- ftplugin/nerdtree_git_menu.vim | 61 ++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 ftplugin/nerdtree_git_menu.vim diff --git a/ftplugin/nerdtree_git_menu.vim b/ftplugin/nerdtree_git_menu.vim new file mode 100644 index 0000000..ed354d9 --- /dev/null +++ b/ftplugin/nerdtree_git_menu.vim @@ -0,0 +1,61 @@ +" ============================================================================ +" File: nerdtree_git_menu.vim +" Description: plugin for the NERD Tree that provides a git menu +" Maintainer: Martin Grenfell +" Last Change: 20 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_git_menu") + finish +endif +let g:loaded_nerdtree_git_menu = 1 + +call NERDTreeAddMenuItem('(g)it menu', 'g', 'NERDTreeGitMenu') + +function! NERDTreeGitMenu() + let node = g:NERDTreeFileNode.GetSelected() + + let path = node.path + let parent = path.getParent() + + let prompt = "NERDTree Git Menu\n" . + \ "==========================================================\n". + \ "Select the desired operation: \n" . + \ " (a) - git add\n". + \ " (c) - git checkout\n". + \ " (m) - git mv\n". + \ " (r) - git rm\n" + + echo prompt + + let choice = nr2char(getchar()) + + if choice ==# "a" + call s:promptCommand('git add ', path.strForOS(1), 'file') + elseif choice ==# "c" + call s:promptCommand('git checkout ', path.strForOS(1), 'file') + elseif choice ==# "m" + call s:promptCommand('git mv ', path.strForOS(1), 'file') + elseif choice ==# "r" + call s:promptCommand('git rm ', path.strForOS(1), 'file') + endif + + call node.parent.refresh() + call NERDTreeRender() +endfunction + +function! s:promptCommand(cmd_base, cmd_tail_default, complete) + let cmd_tail = input(":!" . a:cmd_base, a:cmd_tail_default, a:complete) + if cmd_tail != '' + let output = system(a:cmd_base . cmd_tail) + redraw! + if v:shell_error != 0 + echom output + endif + endif +endfunction From 51637ec6d442e300d9280980eebccaf11aec198e Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Mon, 20 Jul 2009 01:07:58 +1200 Subject: [PATCH 05/27] remove inferior rake symlink to travisjefferys awesome rakefile --- Rakefile | 18 ------------------ 1 file changed, 18 deletions(-) delete mode 100644 Rakefile diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 8befcfe..0000000 --- a/Rakefile +++ /dev/null @@ -1,18 +0,0 @@ -desc "Copy the vim/doc files into ~/.vim" -task :deploy_local do - run "cp plugin/NERD_tree.vim ~/.vim/plugin" - run "cp doc/NERD_tree.txt ~/.vim/doc" -end - - -desc "Create a zip archive for release to vim.org" -task :zip do - abort "NERD_tree.zip already exists, aborting" if File.exist?("NERD_tree.zip") - run "zip NERD_tree.zip plugin/NERD_tree.vim doc/NERD_tree.txt" -end - -def run(cmd) - puts "Executing: #{cmd}" - system cmd -end - From f0290b08ddd4719b5b0c9129d2566d317f597b9b Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Tue, 21 Jul 2009 19:23:44 +1200 Subject: [PATCH 06/27] add another api callback to determine whether menu items should appear this way we can e.g. not show the git submenu unless a git repo is actually present --- plugin/NERD_tree.vim | 72 +++++++++++++++++++++++++++++++++----------- 1 file changed, 55 insertions(+), 17 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 399f566..f4679dd 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -429,7 +429,6 @@ endfunction "CLASS: MenuCallback {{{2 "============================================================ let s:MenuCallback = {} - "FUNCTION: MenuCallback.All() {{{3 function! s:MenuCallback.All() if !exists("s:menuCallbacks") @@ -438,16 +437,58 @@ function! s:MenuCallback.All() return s:menuCallbacks endfunction -"FUNCTION: MenuCallback.Create(text, shortcut, callback) {{{3 -function! s:MenuCallback.Create(text, shortcut, callback) +"FUNCTION: MenuCallback.AllEnabledCallbacks() {{{3 +function! s:MenuCallback.AllEnabledCallbacks() + let toReturn = [] + for i in s:MenuCallback.All() + if i.enabled() + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: MenuCallback.FindByShortcut(shortcut) {{{3 +function! s:MenuCallback.FindByShortcut(shortcut) + for i in s:MenuCallback.All() + if i.shortcut ==# a:shortcut + return i + endif + endfor + return {} +endfunction + +"FUNCTION: MenuCallback.Create(options) {{{3 +function! s:MenuCallback.Create(options) let newCallback = {} let newCallback = copy(self) - let newCallback.text = a:text - let newCallback.shortcut = a:shortcut - let newCallback.callback = a:callback + + let shortcut = a:options['shortcut'] + let callback = a:options['callback'] + + + let newCallback.text = a:options['text'] + let newCallback.shortcut = a:options['shortcut'] + let newCallback.callback = a:options['callback'] + if has_key(a:options, 'check_to_enable_callback') + let newCallback.check_to_enable_callback = a:options['check_to_enable_callback'] + endif call add(s:MenuCallback.All(), newCallback) endfunction +"FUNCTION: MenuCallback.enabled() {{{3 +function! s:MenuCallback.enabled() + if has_key(self, "check_to_enable_callback") + return {self.check_to_enable_callback}() + endif + return 1 +endfunction + +"FUNCTION: MenuCallback.execute() {{{3 +function! s:MenuCallback.execute() + call {self.callback}() +endfunction + "FUNCTION: MenuCallback.ShowMenu() {{{3 function! s:MenuCallback.ShowMenu() let curNode = s:TreeFileNode.GetSelected() @@ -459,23 +500,20 @@ function! s:MenuCallback.ShowMenu() let prompt = "NERDTree Menu\n" . \ "==========================================================\n" - for i in s:MenuCallback.All() + for i in s:MenuCallback.AllEnabledCallbacks() let prompt .= i.text . "\n" endfor echo prompt - let choice = nr2char(getchar()) + let callback = s:MenuCallback.FindByShortcut(nr2char(getchar())) + if !empty(callback) && callback.enabled() + redraw + call callback.execute() + endif - 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 @@ -2388,8 +2426,8 @@ function! NERDTreeGetCurrentPath() endif endfunction -function! NERDTreeAddMenuItem(text, shortcut, callback) - call s:MenuCallback.Create(a:text, a:shortcut, a:callback) +function! NERDTreeAddMenuItem(options) + call s:MenuCallback.Create(a:options) endfunction function! NERDTreeRender() From e9d6a7209ce80d042356f2eff79203f305140d8c Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Tue, 21 Jul 2009 19:26:48 +1200 Subject: [PATCH 07/27] update the fs menu to use the new api --- ftplugin/nerdtree_fs_menu.vim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ftplugin/nerdtree_fs_menu.vim b/ftplugin/nerdtree_fs_menu.vim index 956721a..7522a0e 100644 --- a/ftplugin/nerdtree_fs_menu.vim +++ b/ftplugin/nerdtree_fs_menu.vim @@ -15,11 +15,11 @@ if exists("g:loaded_nerdtree_fs_menu") endif let g:loaded_nerdtree_fs_menu = 1 -call NERDTreeAddMenuItem('(a)dd a childnode', 'a', 'NERDTreeAddNode') -call NERDTreeAddMenuItem('(m)ove the curent node', 'm', 'NERDTreeMoveNode') -call NERDTreeAddMenuItem('(d)elete the curent node', 'd', 'NERDTreeDeleteNode') +call NERDTreeAddMenuItem({'text': '(a)dd a childnode', 'shortcut': 'a', 'callback': 'NERDTreeAddNode'}) +call NERDTreeAddMenuItem({'text': '(m)ove the curent node', 'shortcut': 'm', 'callback': 'NERDTreeMoveNode'}) +call NERDTreeAddMenuItem({'text': '(d)elete the curent node', 'shortcut': 'd', 'callback': 'NERDTreeDeleteNode'}) if g:NERDTreePath.CopyingSupported() - call NERDTreeAddMenuItem('(c)copy the current node', 'c', 'NERDTreeCopyNode') + call NERDTreeAddMenuItem({'text': '(c)copy the current node', 'shortcut': 'c', 'callback': 'NERDTreeCopyNode'}) endif "FUNCTION: s:echo(msg){{{1 From a163f327eb3359c527592cc6784ff7cacf2a580c Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Tue, 21 Jul 2009 19:27:10 +1200 Subject: [PATCH 08/27] make the git menu own harder --- ftplugin/nerdtree_git_menu.vim | 44 +++++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/ftplugin/nerdtree_git_menu.vim b/ftplugin/nerdtree_git_menu.vim index ed354d9..bc6e4bd 100644 --- a/ftplugin/nerdtree_git_menu.vim +++ b/ftplugin/nerdtree_git_menu.vim @@ -10,12 +10,30 @@ " See http://sam.zoy.org/wtfpl/COPYING for more details. " " ============================================================================ +" +" Adds a "g" submenu to the NERD tree menu. +" +" Note: this plugin assumes that the current tree root has a .git dir under +" it, and that the working tree and the .git repo are in the same place +" if exists("g:loaded_nerdtree_git_menu") finish endif let g:loaded_nerdtree_git_menu = 1 -call NERDTreeAddMenuItem('(g)it menu', 'g', 'NERDTreeGitMenu') +call NERDTreeAddMenuItem({ + \ 'text': '(g)it menu', + \ 'shortcut': 'g', + \ 'check_to_enable_callback': 'NERDTreeGitMenuEnabled', + \ 'callback': 'NERDTreeGitMenu' }) + +function! NERDTreeGitMenuEnabled() + return isdirectory(s:GitRepoPath()) +endfunction + +function! s:GitRepoPath() + return b:NERDTreeRoot.path.str(0) . ".git" +endfunction function! NERDTreeGitMenu() let node = g:NERDTreeFileNode.GetSelected() @@ -36,26 +54,34 @@ function! NERDTreeGitMenu() let choice = nr2char(getchar()) if choice ==# "a" - call s:promptCommand('git add ', path.strForOS(1), 'file') + call s:promptCommand('add ', path.strForOS(1), 'file') elseif choice ==# "c" - call s:promptCommand('git checkout ', path.strForOS(1), 'file') + call s:promptCommand('checkout ', path.strForOS(1), 'file') elseif choice ==# "m" - call s:promptCommand('git mv ', path.strForOS(1), 'file') + let p = path.strForOS(1) + call s:promptCommand('mv ', p . ' ' . p, 'file') elseif choice ==# "r" - call s:promptCommand('git rm ', path.strForOS(1), 'file') + call s:promptCommand('rm ', path.strForOS(1), 'file') endif call node.parent.refresh() call NERDTreeRender() endfunction -function! s:promptCommand(cmd_base, cmd_tail_default, complete) - let cmd_tail = input(":!" . a:cmd_base, a:cmd_tail_default, a:complete) +function! s:promptCommand(sub_command, cmd_tail_default, complete) + let extra_options = ' --git-dir=' . s:GitRepoPath() + let extra_options .= ' --work-tree=' . b:NERDTreeRoot.path.str(0) . ' ' + let base = "git" . extra_options . a:sub_command + + let cmd_tail = input(":!" . base, a:cmd_tail_default, a:complete) if cmd_tail != '' - let output = system(a:cmd_base . cmd_tail) + let output = system(base . cmd_tail) redraw! if v:shell_error != 0 - echom output + echo output endif + else + redraw + echo "Aborted" endif endfunction From a2ead3545e8f52936c38a31cc236b7e719c33d81 Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Tue, 21 Jul 2009 21:13:23 +1200 Subject: [PATCH 09/27] rename MenuCallback -> MenuItem --- plugin/NERD_tree.vim | 68 ++++++++++++++++++++++---------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index f4679dd..69d2df1 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -426,21 +426,21 @@ function! s:Bookmark.Write() endfor call writefile(bookmarkStrings, g:NERDTreeBookmarksFile) endfunction -"CLASS: MenuCallback {{{2 +"CLASS: MenuItem {{{2 "============================================================ -let s:MenuCallback = {} -"FUNCTION: MenuCallback.All() {{{3 -function! s:MenuCallback.All() - if !exists("s:menuCallbacks") - let s:menuCallbacks = [] +let s:MenuItem = {} +"FUNCTION: MenuItem.All() {{{3 +function! s:MenuItem.All() + if !exists("s:menuItems") + let s:menuItems = [] endif - return s:menuCallbacks + return s:menuItems endfunction -"FUNCTION: MenuCallback.AllEnabledCallbacks() {{{3 -function! s:MenuCallback.AllEnabledCallbacks() +"FUNCTION: MenuItem.AllEnabled() {{{3 +function! s:MenuItem.AllEnabled() let toReturn = [] - for i in s:MenuCallback.All() + for i in s:MenuItem.All() if i.enabled() call add(toReturn, i) endif @@ -448,9 +448,9 @@ function! s:MenuCallback.AllEnabledCallbacks() return toReturn endfunction -"FUNCTION: MenuCallback.FindByShortcut(shortcut) {{{3 -function! s:MenuCallback.FindByShortcut(shortcut) - for i in s:MenuCallback.All() +"FUNCTION: MenuItem.FindByShortcut(shortcut) {{{3 +function! s:MenuItem.FindByShortcut(shortcut) + for i in s:MenuItem.All() if i.shortcut ==# a:shortcut return i endif @@ -458,39 +458,39 @@ function! s:MenuCallback.FindByShortcut(shortcut) return {} endfunction -"FUNCTION: MenuCallback.Create(options) {{{3 -function! s:MenuCallback.Create(options) - let newCallback = {} - let newCallback = copy(self) +"FUNCTION: MenuItem.Create(options) {{{3 +function! s:MenuItem.Create(options) + let newMenuItem = {} + let newMenuItem = copy(self) let shortcut = a:options['shortcut'] let callback = a:options['callback'] - let newCallback.text = a:options['text'] - let newCallback.shortcut = a:options['shortcut'] - let newCallback.callback = a:options['callback'] + let newMenuItem.text = a:options['text'] + let newMenuItem.shortcut = a:options['shortcut'] + let newMenuItem.callback = a:options['callback'] if has_key(a:options, 'check_to_enable_callback') - let newCallback.check_to_enable_callback = a:options['check_to_enable_callback'] + let newMenuItem.check_to_enable_callback = a:options['check_to_enable_callback'] endif - call add(s:MenuCallback.All(), newCallback) + call add(s:MenuItem.All(), newMenuItem) endfunction -"FUNCTION: MenuCallback.enabled() {{{3 -function! s:MenuCallback.enabled() +"FUNCTION: MenuItem.enabled() {{{3 +function! s:MenuItem.enabled() if has_key(self, "check_to_enable_callback") return {self.check_to_enable_callback}() endif return 1 endfunction -"FUNCTION: MenuCallback.execute() {{{3 -function! s:MenuCallback.execute() +"FUNCTION: MenuItem.execute() {{{3 +function! s:MenuItem.execute() call {self.callback}() endfunction -"FUNCTION: MenuCallback.ShowMenu() {{{3 -function! s:MenuCallback.ShowMenu() +"FUNCTION: MenuItem.ShowMenu() {{{3 +function! s:MenuItem.ShowMenu() let curNode = s:TreeFileNode.GetSelected() if curNode ==# {} call s:echo("Put the cursor on a node first" ) @@ -500,16 +500,16 @@ function! s:MenuCallback.ShowMenu() let prompt = "NERDTree Menu\n" . \ "==========================================================\n" - for i in s:MenuCallback.AllEnabledCallbacks() + for i in s:MenuItem.AllEnabled() let prompt .= i.text . "\n" endfor echo prompt - let callback = s:MenuCallback.FindByShortcut(nr2char(getchar())) - if !empty(callback) && callback.enabled() + let menuItem = s:MenuItem.FindByShortcut(nr2char(getchar())) + if !empty(menuItem) && menuItem.enabled() redraw - call callback.execute() + call menuItem.execute() endif endfunction @@ -2427,7 +2427,7 @@ function! NERDTreeGetCurrentPath() endfunction function! NERDTreeAddMenuItem(options) - call s:MenuCallback.Create(a:options) + call s:MenuItem.Create(a:options) endfunction function! NERDTreeRender() @@ -3639,7 +3639,7 @@ function! s:refreshCurrent() endfunction " FUNCTION: s:showMenu() {{{2 function! s:showMenu() - call s:MenuCallback.ShowMenu() + call s:MenuItem.ShowMenu() endfunction " FUNCTION: s:toggleIgnoreFilter() {{{2 From 6518d1eb4c57fffb0e91506b208e9ba1a40a9e8c Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Tue, 21 Jul 2009 23:43:00 +1200 Subject: [PATCH 10/27] rename check_to_enable_callback to isActiveCallback --- ftplugin/nerdtree_git_menu.vim | 2 +- plugin/NERD_tree.vim | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ftplugin/nerdtree_git_menu.vim b/ftplugin/nerdtree_git_menu.vim index bc6e4bd..0ac637e 100644 --- a/ftplugin/nerdtree_git_menu.vim +++ b/ftplugin/nerdtree_git_menu.vim @@ -24,7 +24,7 @@ let g:loaded_nerdtree_git_menu = 1 call NERDTreeAddMenuItem({ \ 'text': '(g)it menu', \ 'shortcut': 'g', - \ 'check_to_enable_callback': 'NERDTreeGitMenuEnabled', + \ 'isActiveCallback': 'NERDTreeGitMenuEnabled', \ 'callback': 'NERDTreeGitMenu' }) function! NERDTreeGitMenuEnabled() diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 69d2df1..698b9fc 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -470,16 +470,16 @@ function! s:MenuItem.Create(options) let newMenuItem.text = a:options['text'] let newMenuItem.shortcut = a:options['shortcut'] let newMenuItem.callback = a:options['callback'] - if has_key(a:options, 'check_to_enable_callback') - let newMenuItem.check_to_enable_callback = a:options['check_to_enable_callback'] + if has_key(a:options, 'isActiveCallback') + let newMenuItem.isActiveCallback = a:options['isActiveCallback'] endif call add(s:MenuItem.All(), newMenuItem) endfunction "FUNCTION: MenuItem.enabled() {{{3 function! s:MenuItem.enabled() - if has_key(self, "check_to_enable_callback") - return {self.check_to_enable_callback}() + if has_key(self, "isActiveCallback") + return {self.isActiveCallback}() endif return 1 endfunction From bc2a628a100ebab05afdf7aa970f4428e4aca16a Mon Sep 17 00:00:00 2001 From: Martin Grenfell Date: Wed, 22 Jul 2009 00:16:46 +1200 Subject: [PATCH 11/27] move the ! keymap into a plugin --- ftplugin/nerdtree_exec_menuitem.vim | 40 +++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 ftplugin/nerdtree_exec_menuitem.vim diff --git a/ftplugin/nerdtree_exec_menuitem.vim b/ftplugin/nerdtree_exec_menuitem.vim new file mode 100644 index 0000000..b170161 --- /dev/null +++ b/ftplugin/nerdtree_exec_menuitem.vim @@ -0,0 +1,40 @@ +" ============================================================================ +" File: nerdtree_fs_menu.vim +" Description: plugin for NERD Tree that provides an execute file menu item +" Maintainer: Martin Grenfell +" Last Change: 22 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_exec_menuitem") + finish +endif +let g:loaded_nerdtree_exec_menuitem = 1 + +call NERDTreeAddMenuItem({ + \ 'text': '(!) - Execute file', + \ 'shortcut': '!', + \ 'callback': 'NERDTreeExecFile', + \ 'isActiveCallback': 'NERDTreeExecFileActive' }) + +function! NERDTreeExecFileActive() + let node = g:NERDTreeFileNode.GetSelected() + return !node.path.isDirectory && node.path.isExecutable +endfunction + +function! NERDTreeExecFile() + let treenode = g:NERDTreeFileNode.GetSelected() + echo "Complete the command to execute (add arguments etc): \n\n" + let cmd = treenode.path.strForOS(1) + let cmd = input(':!', cmd . ' ') + + if cmd != '' + exec ':!' . cmd + else + echo "Aborted" + endif +endfunction From 02b3cec8272f227756f6877b6ba2b379ef7bad0f Mon Sep 17 00:00:00 2001 From: marty Date: Fri, 31 Jul 2009 18:33:06 +1200 Subject: [PATCH 12/27] set up syntax highlighting even if highlighting rules already exist --- plugin/NERD_tree.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 698b9fc..29b7b6e 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -2275,7 +2275,7 @@ function! s:initNerdTreeInPlace(dir) call s:bindMappings() setfiletype nerdtree " syntax highlighting - if has("syntax") && exists("g:syntax_on") && !has("syntax_items") + if has("syntax") && exists("g:syntax_on") call s:setupSyntaxHighlighting() endif @@ -2522,7 +2522,7 @@ function! s:createTreeWin() call s:bindMappings() setfiletype nerdtree " syntax highlighting - if has("syntax") && exists("g:syntax_on") && !has("syntax_items") + if has("syntax") && exists("g:syntax_on") call s:setupSyntaxHighlighting() endif endfunction From a796715ac1839714cd7f16f19c2b914ebdc635e3 Mon Sep 17 00:00:00 2001 From: marty Date: Sun, 9 Aug 2009 21:29:28 +1200 Subject: [PATCH 13/27] add A mapping to maximize/restore tree window size this commit is a modified patch from Guillaume Duranceau --- doc/NERD_tree.txt | 12 ++++++++++++ plugin/NERD_tree.vim | 17 +++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/doc/NERD_tree.txt b/doc/NERD_tree.txt index a54de3a..77d5cbd 100644 --- a/doc/NERD_tree.txt +++ b/doc/NERD_tree.txt @@ -239,6 +239,7 @@ F.......Toggle whether files are displayed.......................|NERDTree-F| B.......Toggle whether the bookmark table is displayed...........|NERDTree-B| q.......Close the NERDTree window................................|NERDTree-q| +A.......Zoom (maximize/minimize) the NERDTree window.............|NERDTree-A| ?.......Toggle the display of the quick help.....................|NERDTree-?| ------------------------------------------------------------------------------ @@ -546,6 +547,14 @@ Applies to: no restrictions. Closes the NERDtree window. +------------------------------------------------------------------------------ + *NERDTree-A* +Default key: A +Map option: NERDTreeMapToggleZoom +Applies to: no restrictions. + +Maximize (zoom) and minimize the NERDtree window. + ------------------------------------------------------------------------------ *NERDTree-?* Default key: ? @@ -1002,6 +1011,8 @@ Next release: NERDTreeQuitOnOpen didnt play nicely, thanks to Curtis Harvey. - fix a bug where the script ignored directories whose name ended in a dot, thanks to Aggelos Orfanakos for the patch. + - added a mapping to maximize/restore the size of nerd tree window, thanks + to Guillaume Duranceau for the patch. See :help NERDTree-z for details. 3.1.1 - fix a bug where a non-listed no-name buffer was getting created every @@ -1094,6 +1105,7 @@ just downloaded pr0n instead. Alf Mikula Lucas S. Buchala Curtis Harvey + Guillaume Duranceau ============================================================================== 8. License *NERDTreeLicense* diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 29b7b6e..efd6701 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -128,6 +128,7 @@ call s:initVariable("g:NERDTreeMapToggleBookmarks", "B") call s:initVariable("g:NERDTreeMapToggleFiles", "F") call s:initVariable("g:NERDTreeMapToggleFilters", "f") call s:initVariable("g:NERDTreeMapToggleHidden", "I") +call s:initVariable("g:NERDTreeMapToggleZoom", "A") call s:initVariable("g:NERDTreeMapUpdir", "u") call s:initVariable("g:NERDTreeMapUpdirKeepOpen", "U") @@ -2605,6 +2606,8 @@ function! s:dumpHelp() let @h=@h."\"\n\" ----------------------------\n" let @h=@h."\" Other mappings~\n" let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n" + let @h=@h."\" ". g:NERDTreeMapToggleZoom .": Zoom (maximize-minimize)\n" + let @h=@h."\" the NERDTree window\n" let @h=@h."\" ". g:NERDTreeMapHelp .": toggle help\n" let @h=@h."\"\n\" ----------------------------\n" let @h=@h."\" Bookmark commands~\n" @@ -3201,6 +3204,7 @@ function! s:bindMappings() exec "nnoremap ". g:NERDTreeMapRefresh ." :call refreshCurrent()" exec "nnoremap ". g:NERDTreeMapHelp ." :call displayHelp()" + exec "nnoremap ". g:NERDTreeMapToggleZoom ." :call toggleZoom()" exec "nnoremap ". g:NERDTreeMapToggleHidden ." :call toggleShowHidden()" exec "nnoremap ". g:NERDTreeMapToggleFilters ." :call toggleIgnoreFilter()" exec "nnoremap ". g:NERDTreeMapToggleFiles ." :call toggleShowFiles()" @@ -3678,6 +3682,19 @@ function! s:toggleShowHidden() call s:centerView() endfunction +" FUNCTION: s:toggleZoom() {{2 +" zoom (maximize/minimize) the NERDTree window +function! s:toggleZoom() + if exists("b:NERDTreeZoomed") && b:NERDTreeZoomed + let size = exists("b:NERDTreeOldWindowSize") ? b:NERDTreeOldWindowSize : g:NERDTreeWinSize + exec "silent vertical resize ". size + let b:NERDTreeZoomed = 0 + else + exec "vertical resize" + let b:NERDTreeZoomed = 1 + endif +endfunction + "FUNCTION: s:upDir(keepState) {{{2 "moves the tree up a level " From 236d20946cd95ad22fd99b3586d6401e2e5e6b34 Mon Sep 17 00:00:00 2001 From: marty Date: Sun, 9 Aug 2009 22:38:21 +1200 Subject: [PATCH 14/27] correct a typo in the changelog --- doc/NERD_tree.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/NERD_tree.txt b/doc/NERD_tree.txt index 77d5cbd..72e08d9 100644 --- a/doc/NERD_tree.txt +++ b/doc/NERD_tree.txt @@ -1012,7 +1012,7 @@ Next release: - fix a bug where the script ignored directories whose name ended in a dot, thanks to Aggelos Orfanakos for the patch. - added a mapping to maximize/restore the size of nerd tree window, thanks - to Guillaume Duranceau for the patch. See :help NERDTree-z for details. + to Guillaume Duranceau for the patch. See :help NERDTree-A for details. 3.1.1 - fix a bug where a non-listed no-name buffer was getting created every From 65dd1137da79a5e7fd63dcdd2356ecc44b59a1ca Mon Sep 17 00:00:00 2001 From: marty Date: Tue, 11 Aug 2009 16:32:50 +1200 Subject: [PATCH 15/27] create a new dir for nerdtree plugins --- .../exec_menuitem.vim | 0 ftplugin/nerdtree_fs_menu.vim => nerdtree_plugin/fs_menu.vim | 0 ftplugin/nerdtree_git_menu.vim => nerdtree_plugin/git_menu.vim | 0 plugin/NERD_tree.vim | 3 +++ 4 files changed, 3 insertions(+) rename ftplugin/nerdtree_exec_menuitem.vim => nerdtree_plugin/exec_menuitem.vim (100%) rename ftplugin/nerdtree_fs_menu.vim => nerdtree_plugin/fs_menu.vim (100%) rename ftplugin/nerdtree_git_menu.vim => nerdtree_plugin/git_menu.vim (100%) diff --git a/ftplugin/nerdtree_exec_menuitem.vim b/nerdtree_plugin/exec_menuitem.vim similarity index 100% rename from ftplugin/nerdtree_exec_menuitem.vim rename to nerdtree_plugin/exec_menuitem.vim diff --git a/ftplugin/nerdtree_fs_menu.vim b/nerdtree_plugin/fs_menu.vim similarity index 100% rename from ftplugin/nerdtree_fs_menu.vim rename to nerdtree_plugin/fs_menu.vim diff --git a/ftplugin/nerdtree_git_menu.vim b/nerdtree_plugin/git_menu.vim similarity index 100% rename from ftplugin/nerdtree_git_menu.vim rename to nerdtree_plugin/git_menu.vim diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index efd6701..eb0a511 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -158,6 +158,9 @@ augroup NERDTree exec "autocmd BufWinLeave *". s:NERDTreeBufName ." call saveScreenState()" "cache bookmarks when vim loads autocmd VimEnter * call s:Bookmark.CacheBookmarks(0) + + "load all nerdtree plugins after vim starts + autocmd VimEnter * runtime! nerdtree_plugin/*.vim augroup END if g:NERDTreeHijackNetrw From a052a0db65460537077c7bca768e801238fa89fa Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 12 Aug 2009 00:53:16 +1200 Subject: [PATCH 16/27] add an API to add custom key maps --- plugin/NERD_tree.vim | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index eb0a511..42c620d 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -430,6 +430,37 @@ function! s:Bookmark.Write() endfor call writefile(bookmarkStrings, g:NERDTreeBookmarksFile) endfunction +"CLASS: KeyMap {{{2 +"============================================================ +let s:KeyMap = {} +"FUNCTION: KeyMap.All() {{{3 +function! s:KeyMap.All() + if !exists("s:keyMaps") + let s:keyMaps = [] + endif + return s:keyMaps +endfunction + +"FUNCTION: KeyMap.BindAll() {{{3 +function! s:KeyMap.BindAll() + for i in s:KeyMap.All() + call i.bind() + endfor +endfunction + +"FUNCTION: KeyMap.bind() {{{3 +function! s:KeyMap.bind() + exec "nnoremap ". self.key ." :call ". self.callback ."()" +endfunction + +"FUNCTION: KeyMap.Create(options) {{{3 +function! s:KeyMap.Create(options) + let newKeyMap = {} + let newKeyMap = copy(self) + let newKeyMap.key = a:options['key'] + let newKeyMap.callback = a:options['callback'] + call add(s:KeyMap.All(), newKeyMap) +endfunction "CLASS: MenuItem {{{2 "============================================================ let s:MenuItem = {} @@ -2434,6 +2465,10 @@ function! NERDTreeAddMenuItem(options) call s:MenuItem.Create(a:options) endfunction +function! NERDTreeAddKeyMap(options) + call s:KeyMap.Create(a:options) +endfunction + function! NERDTreeRender() call s:renderView() endfunction @@ -3232,6 +3267,9 @@ function! s:bindMappings() exec "nnoremap ". g:NERDTreeMapDeleteBookmark ." :call deleteBookmark()" + "bind all the user custom maps + call s:KeyMap.BindAll() + command! -buffer -nargs=1 Bookmark :call bookmarkNode('') command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call revealBookmark('') command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call openBookmark('') From 59257d7a3ac7a073fd7472e8ea09a4bdf1ecc38f Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 12 Aug 2009 00:53:57 +1200 Subject: [PATCH 17/27] remove the old api functions --- doc/NERD_tree.txt | 36 +----------------------------------- plugin/NERD_tree.vim | 40 ---------------------------------------- 2 files changed, 1 insertion(+), 75 deletions(-) diff --git a/doc/NERD_tree.txt b/doc/NERD_tree.txt index 72e08d9..fa7cf83 100644 --- a/doc/NERD_tree.txt +++ b/doc/NERD_tree.txt @@ -949,41 +949,7 @@ This option is used to change the size of the NERD tree when it is loaded. ============================================================================== 4. Hacking the NERD tree *NERDTreeHacking* -Public functions ~ - -The script provides 2 public functions for your hacking pleasure. Their -signatures are: > - function! NERDTreeGetCurrentNode() - function! NERDTreeGetCurrentPath() -< -The first returns the node object that the cursor is currently on, while the -second returns the corresponding path object. - -This is probably a good time to mention that the script implements prototype -style OO. To see the functions that each class provides you can read look at -the code. - -Use the node objects to manipulate the structure of the tree. Use the path -objects to access the files/directories the tree nodes represent. - -The NERD tree filetype ~ - -NERD tree buffers have a filetype of "nerdtree". You can use this to hack the -NERD tree via autocommands (on |FileType|) or via an ftplugin. - -For example, putting this code in ~/.vim/ftplugin/nerdtree.vim would override -the o mapping, making it open the selected node in a new gvim instance. > - - nnoremap o :call openInNewVimInstance() - function! s:openInNewVimInstance() - let p = NERDTreeGetCurrentPath() - if p != {} - silent exec "!gvim " . p.strForOS(1) . "&" - endif - endfunction -< -This way you can add new mappings or :commands or override any existing -mapping. +TODO: fill in when new api is complete ============================================================================== 5. About *NERDTreeAbout* diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 42c620d..25c6804 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -2421,46 +2421,6 @@ 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. -" -"If no NERD tree window exists for the current tab, a NERDTree.NoTreeForTab -"exception is thrown. -" -"If the cursor is not on a node then an empty dictionary {} is returned. -function! NERDTreeGetCurrentNode() - if !s:treeExistsForTab() || !s:isTreeOpen() - throw "NERDTree.NoTreeForTabError: there is no NERD tree open for the current tab" - endif - - let winnr = winnr() - if winnr != s:getTreeWinNum() - call s:putCursorInTreeWin() - endif - - let treenode = s:TreeFileNode.GetSelected() - - if winnr != winnr() - call s:exec('wincmd w') - endif - - return treenode -endfunction - -"Returns the path object for the current node. -" -"Subject to the same conditions as NERDTreeGetCurrentNode -function! NERDTreeGetCurrentPath() - let node = NERDTreeGetCurrentNode() - if node != {} - return node.path - else - return {} - endif -endfunction - function! NERDTreeAddMenuItem(options) call s:MenuItem.Create(a:options) endfunction From df3485ec648d6e6a29b940b39fcbfb4f0404bf51 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 12 Aug 2009 01:13:29 +1200 Subject: [PATCH 18/27] remove an unused function --- plugin/NERD_tree.vim | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 25c6804..ffc74d0 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -2004,16 +2004,6 @@ function! s:Path.str(esc) return toReturn endfunction -"FUNCTION: Path.strAbs() {{{3 -" -"Returns a string representing this path with all the symlinks resolved -" -"Return: -"string -function! s:Path.strAbs() - return resolve(self.str(1)) -endfunction - "FUNCTION: Path.strForCd() {{{3 " " returns a string that can be used with :cd From 1654ef820b7ec68eed896bf8d04ed6574269b2f5 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 12 Aug 2009 01:14:15 +1200 Subject: [PATCH 19/27] remove some redundant code --- plugin/NERD_tree.vim | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index ffc74d0..46cf603 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -1739,13 +1739,7 @@ endfunction function! s:Path.delete() if self.isDirectory - let cmd = "" - if s:running_windows - "if we are runnnig windows then put quotes around the pathstring - let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1) - else - let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1) - endif + let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1) let success = system(cmd) if v:shell_error != 0 From abc0cc4c40cf734c67a03823e844f801c13a7dc6 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 12 Aug 2009 01:14:49 +1200 Subject: [PATCH 20/27] remove some useless/misleading comments --- plugin/NERD_tree.vim | 6 ------ 1 file changed, 6 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 46cf603..a5a627f 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -2001,9 +2001,6 @@ endfunction "FUNCTION: Path.strForCd() {{{3 " " returns a string that can be used with :cd -" -"Return: -"a string that can be used in the view to represent this path function! s:Path.strForCd() if s:running_windows return self.strForOS(0) @@ -2015,9 +2012,6 @@ endfunction " "Returns a string that specifies how the path should be represented as a "string -" -"Return: -"a string that can be used in the view to represent this path function! s:Path.strDisplay() if self.cachedDisplayString ==# "" call self.cacheDisplayString() From 389f33ea81d99a1a5c44d18e657b17411ca11eb4 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 12 Aug 2009 01:38:16 +1200 Subject: [PATCH 21/27] add quickhelpText to s:KeyMap and display it in quickhelp --- plugin/NERD_tree.vim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index a5a627f..79306b8 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -458,6 +458,7 @@ function! s:KeyMap.Create(options) let newKeyMap = {} let newKeyMap = copy(self) let newKeyMap.key = a:options['key'] + let newKeyMap.quickhelpText = a:options['quickhelpText'] let newKeyMap.callback = a:options['callback'] call add(s:KeyMap.All(), newKeyMap) endfunction @@ -2579,6 +2580,15 @@ function! s:dumpHelp() let @h=@h."\" ". g:NERDTreeMapToggleFiles .": files (" . (b:NERDTreeShowFiles ? "on" : "off") . ")\n" let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (b:NERDTreeShowBookmarks ? "on" : "off") . ")\n" + "add quickhelp entries for each custom key map + if len(s:KeyMap.All()) + let @h=@h."\"\n\" ----------------------------\n" + let @h=@h."\" Custom mappings~\n" + for i in s:KeyMap.All() + let @h=@h."\" ". i.key .": ". i.quickhelpText ."\n" + endfor + endif + let @h=@h."\"\n\" ----------------------------\n" let @h=@h."\" Other mappings~\n" let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n" From fc3cb766953f6d1b1ccf57fc9e467f1f3b688f53 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 12 Aug 2009 01:49:48 +1200 Subject: [PATCH 22/27] remove a redundant function --- plugin/NERD_tree.vim | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 79306b8..63f7eff 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -1077,7 +1077,7 @@ function! s:TreeFileNode.rename(newName) call self.path.rename(newName) call self.parent.removeChild(self) - let parentPath = self.path.getPathTrunk() + let parentPath = self.path.getParent() let newParent = b:NERDTreeRoot.findNode(parentPath) if newParent != {} @@ -1596,7 +1596,7 @@ endfunction function! s:Path.changeToDir() let dir = self.strForCd() if self.isDirectory ==# 0 - let dir = self.getPathTrunk().strForCd() + let dir = self.getParent().strForCd() endif try @@ -1817,12 +1817,6 @@ function! s:Path.getLastPathComponent(dirSlash) return toReturn endfunction -"FUNCTION: Path.getPathTrunk() {{{3 -"Gets the path without the last segment on the end. -function! s:Path.getPathTrunk() - return s:Path.New(self.strTrunk()) -endfunction - "FUNCTION: Path.getSortOrderIndex() {{{3 "returns the index of the pattern in g:NERDTreeSortOrder that this path matches function! s:Path.getSortOrderIndex() @@ -3702,7 +3696,7 @@ function! s:upDir(keepState) let oldRoot = b:NERDTreeRoot if empty(b:NERDTreeRoot.parent) - let path = b:NERDTreeRoot.path.getPathTrunk() + let path = b:NERDTreeRoot.path.getParent() let newRoot = s:TreeDirNode.New(path) call newRoot.open() call newRoot.transplantChild(b:NERDTreeRoot) From 31c0ec6d633966362b14feb5b58d06a5dbf869e1 Mon Sep 17 00:00:00 2001 From: marty Date: Tue, 18 Aug 2009 09:44:49 +1200 Subject: [PATCH 23/27] add basic menu implementation --- plugin/NERD_tree.vim | 200 +++++++++++++++++++++++++++++++++---------- 1 file changed, 157 insertions(+), 43 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index 63f7eff..bec48fa 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -462,6 +462,120 @@ function! s:KeyMap.Create(options) let newKeyMap.callback = a:options['callback'] call add(s:KeyMap.All(), newKeyMap) endfunction +"CLASS: MenuController {{{2 +"============================================================ +let s:MenuController = {} +"FUNCTION: MenuController.New(menuItems) {{{3 +function! s:MenuController.New(menuItems) + let newMenuController = copy(self) + let newMenuController.menuItems = a:menuItems + return newMenuController +endfunction + +"FUNCTION: MenuController.showMenu() {{{3 +function! s:MenuController.showMenu() + let curNode = s:TreeFileNode.GetSelected() + if curNode ==# {} + call s:echo("Put the cursor on a node first" ) + return + endif + + call self._saveOptions() + let self.selection = 0 + + let key = '' + while key != "\r" + let prompt = '' + let prompt .= "NERDTree Menu. Use j/k/enter and the shortcuts indicated\n" + let prompt .= "==========================================================\n" + + for i in range(0, len(self.menuItems)-1) + if self.selection == i + let prompt .= "> " + else + let prompt .= " " + endif + + let prompt .= self.menuItems[i].text . "\n" + endfor + + if has("gui_running") + redraw! + else + redraw + endif + + echo prompt + + let key = nr2char(getchar()) + if self._handleKeypress(key) == -1 + call self._restoreOptions() + return + endif + endwhile + + call self._restoreOptions() + + let chosen = self.menuItems[self.selection] + call chosen.execute() + +endfunction + +"FUNCTION: MenuController._handleKeypress(key) {{{3 +function! s:MenuController._handleKeypress(key) + if a:key == 'j' + if self.selection < len(self.menuItems)-1 + let self.selection += 1 + else + let self.selection = 0 + endif + elseif a:key == 'k' + if self.selection > 0 + let self.selection -= 1 + else + let self.selection = len(self.menuItems)-1 + endif + elseif a:key == nr2char(27) "escape + return -1 + else + let index = self._nextIndexFor(a:key) + if index != -1 + let self.selection = index + endif + endif +endfunction + +"FUNCTION: MenuController._nextIndexFor(shortcut) {{{3 +function! s:MenuController._nextIndexFor(shortcut) + for i in range(self.selection+1, len(self.menuItems)-1) + if self.menuItems[i].shortcut == a:shortcut + return i + endif + endfor + + for i in range(0, self.selection) + if self.menuItems[i].shortcut == a:shortcut + return i + endif + endfor + + return -1 +endfunction + +"FUNCTION: MenuController._saveOptions() {{{3 +function! s:MenuController._saveOptions() + let self._oldLazyredraw = &lazyredraw + let self._oldCmdheight = &cmdheight + set lazyredraw + let &cmdheight = len(self.menuItems) + 2 + has("gui_running") +endfunction + +"FUNCTION: MenuController._restoreOptions() {{{3 +function! s:MenuController._restoreOptions() + let &cmdheight = self._oldCmdheight + let &lazyredraw = self._oldLazyredraw +endfunction + "CLASS: MenuItem {{{2 "============================================================ let s:MenuItem = {} @@ -484,37 +598,49 @@ function! s:MenuItem.AllEnabled() return toReturn endfunction -"FUNCTION: MenuItem.FindByShortcut(shortcut) {{{3 -function! s:MenuItem.FindByShortcut(shortcut) - for i in s:MenuItem.All() - if i.shortcut ==# a:shortcut - return i - endif - endfor - return {} -endfunction - "FUNCTION: MenuItem.Create(options) {{{3 function! s:MenuItem.Create(options) - let newMenuItem = {} let newMenuItem = copy(self) - let shortcut = a:options['shortcut'] - let callback = a:options['callback'] - - let newMenuItem.text = a:options['text'] let newMenuItem.shortcut = a:options['shortcut'] let newMenuItem.callback = a:options['callback'] + let newMenuItem.children = [] + + let newMenuItem.isActiveCallback = -1 if has_key(a:options, 'isActiveCallback') let newMenuItem.isActiveCallback = a:options['isActiveCallback'] endif + + if has_key(a:options, 'parent') + call add(a:options['parent'].children, newMenuItem) + else + call add(s:MenuItem.All(), newMenuItem) + endif + + return newMenuItem +endfunction + +"FUNCTION: MenuItem.CreateSeparator(options) {{{3 +function! s:MenuItem.CreateSeparator(options) + let newMenuItem = copy(self) + let newMenuItem.text = "--------------------" + + let newMenuItem.shortcut = -1 + let newMenuItem.callback = -1 + let newMenuItem.children = [] + + let newMenuItem.isActiveCallback = -1 + if has_key(a:options, 'isActiveCallback') + let newMenuItem.isActiveCallback = a:options['isActiveCallback'] + endif + call add(s:MenuItem.All(), newMenuItem) endfunction "FUNCTION: MenuItem.enabled() {{{3 function! s:MenuItem.enabled() - if has_key(self, "isActiveCallback") + if self.isActiveCallback != -1 return {self.isActiveCallback}() endif return 1 @@ -522,32 +648,14 @@ endfunction "FUNCTION: MenuItem.execute() {{{3 function! s:MenuItem.execute() - call {self.callback}() -endfunction - -"FUNCTION: MenuItem.ShowMenu() {{{3 -function! s:MenuItem.ShowMenu() - let curNode = s:TreeFileNode.GetSelected() - if curNode ==# {} - call s:echo("Put the cursor on a node first" ) - return + if len(self.children) + let mc = s:MenuController.New(self.children) + call mc.showMenu() + else + if self.callback != -1 + call {self.callback}() + endif endif - - let prompt = "NERDTree Menu\n" . - \ "==========================================================\n" - - for i in s:MenuItem.AllEnabled() - let prompt .= i.text . "\n" - endfor - - echo prompt - - let menuItem = s:MenuItem.FindByShortcut(nr2char(getchar())) - if !empty(menuItem) && menuItem.enabled() - redraw - call menuItem.execute() - endif - endfunction "CLASS: TreeFileNode {{{2 @@ -2395,7 +2503,12 @@ let g:NERDTreeFileNode = s:TreeFileNode let g:NERDTreeBookmark = s:Bookmark function! NERDTreeAddMenuItem(options) - call s:MenuItem.Create(a:options) + return s:MenuItem.Create(a:options) +endfunction + +function! NERDTreeAddMenuSeparator(...) + let opts = a:0 ? a:1 : {} + return s:MenuItem.CreateSeparator(opts) endfunction function! NERDTreeAddKeyMap(options) @@ -3626,7 +3739,8 @@ function! s:refreshCurrent() endfunction " FUNCTION: s:showMenu() {{{2 function! s:showMenu() - call s:MenuItem.ShowMenu() + let mc = s:MenuController.New(s:MenuItem.AllEnabled()) + call mc.showMenu() endfunction " FUNCTION: s:toggleIgnoreFilter() {{{2 From e93bf0632b114107f2d2f3d50e6b1823c7402cd7 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 19 Aug 2009 02:05:02 +1200 Subject: [PATCH 24/27] make the menu system more awesome --- plugin/NERD_tree.vim | 179 +++++++++++++++++++++++++++++++------------ 1 file changed, 129 insertions(+), 50 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index bec48fa..c6d62c9 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -474,75 +474,91 @@ endfunction "FUNCTION: MenuController.showMenu() {{{3 function! s:MenuController.showMenu() - let curNode = s:TreeFileNode.GetSelected() - if curNode ==# {} - call s:echo("Put the cursor on a node first" ) - return - endif - call self._saveOptions() - let self.selection = 0 - let key = '' - while key != "\r" - let prompt = '' - let prompt .= "NERDTree Menu. Use j/k/enter and the shortcuts indicated\n" - let prompt .= "==========================================================\n" + try + let self.selection = 0 - for i in range(0, len(self.menuItems)-1) - if self.selection == i - let prompt .= "> " - else - let prompt .= " " - endif + let done = 0 + while !done + call self._redraw() + echo self._prompt() + let key = nr2char(getchar()) + let done = self._handleKeypress(key) + endwhile - let prompt .= self.menuItems[i].text . "\n" - endfor - - if has("gui_running") - redraw! - else - redraw + if self.selection != -1 + let m = self._current() + call m.execute() endif - echo prompt - - let key = nr2char(getchar()) - if self._handleKeypress(key) == -1 - call self._restoreOptions() - return - endif - endwhile - - call self._restoreOptions() - - let chosen = self.menuItems[self.selection] - call chosen.execute() + finally + call self._restoreOptions() + endtry endfunction +"FUNCTION: MenuController._prompt() {{{3 +function! s:MenuController._prompt() + let toReturn = '' + let toReturn .= "NERDTree Menu. Use j/k/enter and the shortcuts indicated\n" + let toReturn .= "==========================================================\n" + + for i in range(0, len(self.menuItems)-1) + if self.selection == i + let toReturn .= "> " + else + let toReturn .= " " + endif + + let toReturn .= self.menuItems[i].text . "\n" + endfor + + return toReturn +endfunction + +"FUNCTION: MenuController._current(key) {{{3 +function! s:MenuController._current() + return self.menuItems[self.selection] +endfunction + "FUNCTION: MenuController._handleKeypress(key) {{{3 +"changes the selection (if appropriate) and returns 1 if the user has made +"their choice, 0 otherwise function! s:MenuController._handleKeypress(key) if a:key == 'j' - if self.selection < len(self.menuItems)-1 - let self.selection += 1 - else - let self.selection = 0 - endif + call self._cursorDown() elseif a:key == 'k' - if self.selection > 0 - let self.selection -= 1 - else - let self.selection = len(self.menuItems)-1 - endif + call self._cursorUp() elseif a:key == nr2char(27) "escape - return -1 + let self.selection = -1 + return 1 + elseif a:key == "\r" || a:key == "\n" "enter and ctrl-j + return 1 else let index = self._nextIndexFor(a:key) if index != -1 let self.selection = index + if len(self._allIndexesFor(a:key)) == 1 + return 1 + endif endif endif + + return 0 +endfunction + +"FUNCTION: MenuController._allIndexesFor(shortcut) {{{3 +function! s:MenuController._allIndexesFor(shortcut) + let toReturn = [] + + for i in range(0, len(self.menuItems)-1) + if self.menuItems[i].shortcut == a:shortcut + call add(toReturn, i) + endif + endfor + + return toReturn endfunction "FUNCTION: MenuController._nextIndexFor(shortcut) {{{3 @@ -562,12 +578,30 @@ function! s:MenuController._nextIndexFor(shortcut) return -1 endfunction +"FUNCTION: MenuController._redraw() {{{3 +function! s:MenuController._redraw() + if has("gui_running") + redraw! + else + redraw + endif +endfunction + +"FUNCTION: MenuController._setCmdheight() {{{3 +function! s:MenuController._setCmdheight() + if has("gui_running") + let &cmdheight = len(self.menuItems) + 3 + else + let &cmdheight = len(self.menuItems) + 2 + endif +endfunction + "FUNCTION: MenuController._saveOptions() {{{3 function! s:MenuController._saveOptions() let self._oldLazyredraw = &lazyredraw let self._oldCmdheight = &cmdheight set lazyredraw - let &cmdheight = len(self.menuItems) + 2 + has("gui_running") + call self._setCmdheight() endfunction "FUNCTION: MenuController._restoreOptions() {{{3 @@ -576,6 +610,40 @@ function! s:MenuController._restoreOptions() let &lazyredraw = self._oldLazyredraw endfunction +"FUNCTION: MenuController._cursorDown() {{{3 +"move the cursor to the next menu item, skipping separators +function! s:MenuController._cursorDown() + let done = 0 + while !done + if self.selection < len(self.menuItems)-1 + let self.selection += 1 + else + let self.selection = 0 + endif + + if !self._current().isSeparator() + let done = 1 + endif + endwhile +endfunction + +"FUNCTION: MenuController._cursorUp() {{{3 +"move the cursor to the previous menu item, skipping separators +function! s:MenuController._cursorUp() + let done = 0 + while !done + if self.selection > 0 + let self.selection -= 1 + else + let self.selection = len(self.menuItems)-1 + endif + + if !self._current().isSeparator() + let done = 1 + endif + endwhile +endfunction + "CLASS: MenuItem {{{2 "============================================================ let s:MenuItem = {} @@ -658,6 +726,11 @@ function! s:MenuItem.execute() endif endfunction +"FUNCTION: MenuItem.isSeparator() {{{3 +function! s:MenuItem.isSeparator() + return self.callback == -1 +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 @@ -3739,6 +3812,12 @@ function! s:refreshCurrent() endfunction " FUNCTION: s:showMenu() {{{2 function! s:showMenu() + let curNode = s:TreeFileNode.GetSelected() + if curNode ==# {} + call s:echo("Put the cursor on a node first" ) + return + endif + let mc = s:MenuController.New(s:MenuItem.AllEnabled()) call mc.showMenu() endfunction From 891f0ed3a27eb4c8a9b27bc2a22bb5861855dcb1 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 19 Aug 2009 02:05:36 +1200 Subject: [PATCH 25/27] make the git menu work and put it under a submenu --- nerdtree_plugin/git_menu.vim | 82 ++++++++++++++++++++++++------------ 1 file changed, 54 insertions(+), 28 deletions(-) diff --git a/nerdtree_plugin/git_menu.vim b/nerdtree_plugin/git_menu.vim index 0ac637e..a327c88 100644 --- a/nerdtree_plugin/git_menu.vim +++ b/nerdtree_plugin/git_menu.vim @@ -21,12 +21,41 @@ if exists("g:loaded_nerdtree_git_menu") endif let g:loaded_nerdtree_git_menu = 1 -call NERDTreeAddMenuItem({ +call NERDTreeAddMenuSeparator({'isActiveCallback': 'NERDTreeGitMenuEnabled'}) +let s:menu = NERDTreeAddMenuItem({ \ 'text': '(g)it menu', \ 'shortcut': 'g', \ 'isActiveCallback': 'NERDTreeGitMenuEnabled', \ 'callback': 'NERDTreeGitMenu' }) +call NERDTreeAddMenuItem({ + \ 'text': 'git (a)dd', + \ 'shortcut': 'a', + \ 'isActiveCallback': 'NERDTreeGitMenuEnabled', + \ 'callback': 'NERDTreeGitAdd', + \ 'parent': s:menu }) + +call NERDTreeAddMenuItem({ + \ 'text': 'git (c)heckout', + \ 'shortcut': 'c', + \ 'isActiveCallback': 'NERDTreeGitMenuEnabled', + \ 'callback': 'NERDTreeGitCheckout', + \ 'parent': s:menu }) + +call NERDTreeAddMenuItem({ + \ 'text': 'git (m)v', + \ 'shortcut': 'm', + \ 'isActiveCallback': 'NERDTreeGitMenuEnabled', + \ 'callback': 'NERDTreeGitMove', + \ 'parent': s:menu }) + +call NERDTreeAddMenuItem({ + \ 'text': 'git (r)m', + \ 'shortcut': 'r', + \ 'isActiveCallback': 'NERDTreeGitMenuEnabled', + \ 'callback': 'NERDTreeGitRemove', + \ 'parent': s:menu }) + function! NERDTreeGitMenuEnabled() return isdirectory(s:GitRepoPath()) endfunction @@ -35,37 +64,29 @@ function! s:GitRepoPath() return b:NERDTreeRoot.path.str(0) . ".git" endfunction -function! NERDTreeGitMenu() +function! NERDTreeGitMove() let node = g:NERDTreeFileNode.GetSelected() - let path = node.path - let parent = path.getParent() + let p = path.strForOS(1) + call s:promptCommand('mv ', p . ' ' . p, 'file') +endfunction - let prompt = "NERDTree Git Menu\n" . - \ "==========================================================\n". - \ "Select the desired operation: \n" . - \ " (a) - git add\n". - \ " (c) - git checkout\n". - \ " (m) - git mv\n". - \ " (r) - git rm\n" +function! NERDTreeGitAdd() + let node = g:NERDTreeFileNode.GetSelected() + let path = node.path + call s:promptCommand('add ', path.strForOS(1), 'file') +endfunction - echo prompt +function! NERDTreeGitRemove() + let node = g:NERDTreeFileNode.GetSelected() + let path = node.path + call s:promptCommand('rm ', path.strForOS(1), 'file') +endfunction - let choice = nr2char(getchar()) - - if choice ==# "a" - call s:promptCommand('add ', path.strForOS(1), 'file') - elseif choice ==# "c" - call s:promptCommand('checkout ', path.strForOS(1), 'file') - elseif choice ==# "m" - let p = path.strForOS(1) - call s:promptCommand('mv ', p . ' ' . p, 'file') - elseif choice ==# "r" - call s:promptCommand('rm ', path.strForOS(1), 'file') - endif - - call node.parent.refresh() - call NERDTreeRender() +function! NERDTreeGitCheckout() + let node = g:NERDTreeFileNode.GetSelected() + let path = node.path + call s:promptCommand('checkout ', path.strForOS(1), 'file') endfunction function! s:promptCommand(sub_command, cmd_tail_default, complete) @@ -73,11 +94,16 @@ function! s:promptCommand(sub_command, cmd_tail_default, complete) let extra_options .= ' --work-tree=' . b:NERDTreeRoot.path.str(0) . ' ' let base = "git" . extra_options . a:sub_command + let node = g:NERDTreeFileNode.GetSelected() + let cmd_tail = input(":!" . base, a:cmd_tail_default, a:complete) if cmd_tail != '' let output = system(base . cmd_tail) redraw! - if v:shell_error != 0 + if v:shell_error == 0 + call node.parent.refresh() + call NERDTreeRender() + else echo output endif else From eef8a7b280ad8a98b7b7554945807e114a6458c8 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 19 Aug 2009 02:06:11 +1200 Subject: [PATCH 26/27] bugfix in fs_menu plugin --- nerdtree_plugin/fs_menu.vim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nerdtree_plugin/fs_menu.vim b/nerdtree_plugin/fs_menu.vim index 7522a0e..850e818 100644 --- a/nerdtree_plugin/fs_menu.vim +++ b/nerdtree_plugin/fs_menu.vim @@ -66,7 +66,7 @@ function! NERDTreeAddNode() try let newPath = g:NERDTreePath.Create(newNodeName) - let parentNode = b:NERDTreeRoot.findNode(newPath.getPathTrunk()) + let parentNode = b:NERDTreeRoot.findNode(newPath.getParent()) let newTreeNode = g:NERDTreeFileNode.New(newPath) if parentNode.isOpen || !empty(parentNode.children) From 8535a906beddbdab5458a5a42ba55e0fc0d6c599 Mon Sep 17 00:00:00 2001 From: marty Date: Wed, 19 Aug 2009 15:44:53 +1200 Subject: [PATCH 27/27] refactor MenuItem.CreateSeparator() --- plugin/NERD_tree.vim | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index c6d62c9..6ac7b6e 100644 --- a/plugin/NERD_tree.vim +++ b/plugin/NERD_tree.vim @@ -691,19 +691,12 @@ endfunction "FUNCTION: MenuItem.CreateSeparator(options) {{{3 function! s:MenuItem.CreateSeparator(options) - let newMenuItem = copy(self) - let newMenuItem.text = "--------------------" + let standard_options = { 'text': '--------------------', + \ 'shortcut': -1, + \ 'callback': -1 } + let options = extend(a:options, standard_options, "force") - let newMenuItem.shortcut = -1 - let newMenuItem.callback = -1 - let newMenuItem.children = [] - - let newMenuItem.isActiveCallback = -1 - if has_key(a:options, 'isActiveCallback') - let newMenuItem.isActiveCallback = a:options['isActiveCallback'] - endif - - call add(s:MenuItem.All(), newMenuItem) + return s:MenuItem.Create(options) endfunction "FUNCTION: MenuItem.enabled() {{{3