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 - 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/nerdtree_plugin/exec_menuitem.vim b/nerdtree_plugin/exec_menuitem.vim new file mode 100644 index 0000000..b170161 --- /dev/null +++ b/nerdtree_plugin/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 diff --git a/nerdtree_plugin/fs_menu.vim b/nerdtree_plugin/fs_menu.vim new file mode 100644 index 0000000..850e818 --- /dev/null +++ b/nerdtree_plugin/fs_menu.vim @@ -0,0 +1,209 @@ +" ============================================================================ +" 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({'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({'text': '(c)copy the current node', 'shortcut': 'c', 'callback': 'NERDTreeCopyNode'}) +endif + +"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: NERDTreeAddNode(){{{1 +function! NERDTreeAddNode() + 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.getParent()) + + 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: NERDTreeMoveNode(){{{1 +function! NERDTreeMoveNode() + 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: NERDTreeDeleteNode() {{{1 +function! NERDTreeDeleteNode() + 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: NERDTreeCopyNode() {{{1 +function! NERDTreeCopyNode() + 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/nerdtree_plugin/git_menu.vim b/nerdtree_plugin/git_menu.vim new file mode 100644 index 0000000..a327c88 --- /dev/null +++ b/nerdtree_plugin/git_menu.vim @@ -0,0 +1,113 @@ +" ============================================================================ +" 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. +" +" ============================================================================ +" +" 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 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 + +function! s:GitRepoPath() + return b:NERDTreeRoot.path.str(0) . ".git" +endfunction + +function! NERDTreeGitMove() + let node = g:NERDTreeFileNode.GetSelected() + let path = node.path + let p = path.strForOS(1) + call s:promptCommand('mv ', p . ' ' . p, 'file') +endfunction + +function! NERDTreeGitAdd() + let node = g:NERDTreeFileNode.GetSelected() + let path = node.path + call s:promptCommand('add ', path.strForOS(1), 'file') +endfunction + +function! NERDTreeGitRemove() + let node = g:NERDTreeFileNode.GetSelected() + let path = node.path + call s:promptCommand('rm ', path.strForOS(1), 'file') +endfunction + +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) + 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 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 + call node.parent.refresh() + call NERDTreeRender() + else + echo output + endif + else + redraw + echo "Aborted" + endif +endfunction diff --git a/plugin/NERD_tree.vim b/plugin/NERD_tree.vim index cb24e06..6ac7b6e 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") @@ -140,11 +140,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 @@ -163,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 @@ -432,6 +430,300 @@ 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.quickhelpText = a:options['quickhelpText'] + 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() + call self._saveOptions() + + try + let self.selection = 0 + + let done = 0 + while !done + call self._redraw() + echo self._prompt() + let key = nr2char(getchar()) + let done = self._handleKeypress(key) + endwhile + + if self.selection != -1 + let m = self._current() + call m.execute() + endif + + 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' + call self._cursorDown() + elseif a:key == 'k' + call self._cursorUp() + elseif a:key == nr2char(27) "escape + 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 +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._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 + call self._setCmdheight() +endfunction + +"FUNCTION: MenuController._restoreOptions() {{{3 +function! s:MenuController._restoreOptions() + let &cmdheight = self._oldCmdheight + 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 = {} +"FUNCTION: MenuItem.All() {{{3 +function! s:MenuItem.All() + if !exists("s:menuItems") + let s:menuItems = [] + endif + return s:menuItems +endfunction + +"FUNCTION: MenuItem.AllEnabled() {{{3 +function! s:MenuItem.AllEnabled() + let toReturn = [] + for i in s:MenuItem.All() + if i.enabled() + call add(toReturn, i) + endif + endfor + return toReturn +endfunction + +"FUNCTION: MenuItem.Create(options) {{{3 +function! s:MenuItem.Create(options) + let newMenuItem = copy(self) + + 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 standard_options = { 'text': '--------------------', + \ 'shortcut': -1, + \ 'callback': -1 } + let options = extend(a:options, standard_options, "force") + + return s:MenuItem.Create(options) +endfunction + +"FUNCTION: MenuItem.enabled() {{{3 +function! s:MenuItem.enabled() + if self.isActiveCallback != -1 + return {self.isActiveCallback}() + endif + return 1 +endfunction + +"FUNCTION: MenuItem.execute() {{{3 +function! s:MenuItem.execute() + if len(self.children) + let mc = s:MenuController.New(self.children) + call mc.showMenu() + else + if self.callback != -1 + call {self.callback}() + endif + 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 @@ -959,7 +1251,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 != {} @@ -1212,7 +1504,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 @@ -1329,7 +1621,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 @@ -1433,7 +1728,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 @@ -1475,7 +1770,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 @@ -1619,13 +1914,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 @@ -1702,12 +1991,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() @@ -1779,6 +2062,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 " " @@ -1878,22 +2167,9 @@ 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 -" -"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) @@ -1905,9 +2181,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() @@ -1929,7 +2202,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 @@ -1945,14 +2218,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) @@ -1969,14 +2242,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 @@ -2083,7 +2356,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) @@ -2288,45 +2561,28 @@ function! s:unique(list) endfor return uniqlist endfunction -" SECTION: Public Functions {{{1 +" SECTION: Public API {{{1 "============================================================ -"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 g:NERDTreePath = s:Path +let g:NERDTreeDirNode = s:TreeDirNode +let g:NERDTreeFileNode = s:TreeFileNode +let g:NERDTreeBookmark = s:Bookmark - 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 +function! NERDTreeAddMenuItem(options) + return s:MenuItem.Create(a:options) 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 +function! NERDTreeAddMenuSeparator(...) + let opts = a:0 ? a:1 : {} + return s:MenuItem.CreateSeparator(opts) +endfunction + +function! NERDTreeAddKeyMap(options) + call s:KeyMap.Create(a:options) +endfunction + +function! NERDTreeRender() + call s:renderView() endfunction " SECTION: View Functions {{{1 @@ -2486,7 +2742,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" @@ -2497,6 +2753,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" @@ -3107,7 +3372,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)" @@ -3123,6 +3388,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('') @@ -3264,45 +3532,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() @@ -3328,57 +3557,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() @@ -3423,40 +3601,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() @@ -3659,80 +3803,16 @@ 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() +" 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 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 + let mc = s:MenuController.New(s:MenuItem.AllEnabled()) + call mc.showMenu() endfunction " FUNCTION: s:toggleIgnoreFilter() {{{2 @@ -3802,7 +3882,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)