Merge branch 'api'

This commit is contained in:
marty 2009-08-19 15:57:41 +12:00
commit ec1f7e3e6e
6 changed files with 719 additions and 329 deletions

View File

@ -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

View File

@ -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* 4. Hacking the NERD tree *NERDTreeHacking*
Public functions ~ TODO: fill in when new api is complete
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 <silent> <buffer> o :call <sid>openInNewVimInstance()<cr>
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.
============================================================================== ==============================================================================
5. About *NERDTreeAbout* 5. About *NERDTreeAbout*

View File

@ -0,0 +1,40 @@
" ============================================================================
" File: nerdtree_fs_menu.vim
" Description: plugin for NERD Tree that provides an execute file menu item
" Maintainer: Martin Grenfell <martin_grenfell at msn dot com>
" 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

209
nerdtree_plugin/fs_menu.vim Normal file
View File

@ -0,0 +1,209 @@
" ============================================================================
" File: nerdtree_fs_menu.vim
" Description: plugin for the NERD Tree that provides a file system menu
" Maintainer: Martin Grenfell <martin_grenfell at msn dot com>
" 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:

View File

@ -0,0 +1,113 @@
" ============================================================================
" File: nerdtree_git_menu.vim
" Description: plugin for the NERD Tree that provides a git menu
" Maintainer: Martin Grenfell <martin_grenfell at msn dot com>
" 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

View File

@ -104,7 +104,7 @@ call s:initVariable("g:NERDTreeMapCloseChildren", "X")
call s:initVariable("g:NERDTreeMapCloseDir", "x") call s:initVariable("g:NERDTreeMapCloseDir", "x")
call s:initVariable("g:NERDTreeMapDeleteBookmark", "D") call s:initVariable("g:NERDTreeMapDeleteBookmark", "D")
call s:initVariable("g:NERDTreeMapExecute", "!") 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:NERDTreeMapHelp", "?")
call s:initVariable("g:NERDTreeMapJumpFirstChild", "K") call s:initVariable("g:NERDTreeMapJumpFirstChild", "K")
call s:initVariable("g:NERDTreeMapJumpLastChild", "J") call s:initVariable("g:NERDTreeMapJumpLastChild", "J")
@ -140,11 +140,6 @@ let s:tree_wid = 2
let s:tree_markup_reg = '^[ `|]*[\-+~]' let s:tree_markup_reg = '^[ `|]*[\-+~]'
let s:tree_up_dir_line = '.. (up a dir)' 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 "the number to add to the nerd tree buffer name to make the buf name unique
let s:next_buffer_number = 1 let s:next_buffer_number = 1
@ -163,6 +158,9 @@ augroup NERDTree
exec "autocmd BufWinLeave *". s:NERDTreeBufName ." call <SID>saveScreenState()" exec "autocmd BufWinLeave *". s:NERDTreeBufName ." call <SID>saveScreenState()"
"cache bookmarks when vim loads "cache bookmarks when vim loads
autocmd VimEnter * call s:Bookmark.CacheBookmarks(0) autocmd VimEnter * call s:Bookmark.CacheBookmarks(0)
"load all nerdtree plugins after vim starts
autocmd VimEnter * runtime! nerdtree_plugin/*.vim
augroup END augroup END
if g:NERDTreeHijackNetrw if g:NERDTreeHijackNetrw
@ -432,6 +430,300 @@ function! s:Bookmark.Write()
endfor endfor
call writefile(bookmarkStrings, g:NERDTreeBookmarksFile) call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
endfunction 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 <silent> <buffer> ". self.key ." :call ". self.callback ."()<cr>"
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 "CLASS: TreeFileNode {{{2
"This class is the parent of the TreeDirNode class and constitures the "This class is the parent of the TreeDirNode class and constitures the
"'Component' part of the composite design pattern between the treenode "'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.path.rename(newName)
call self.parent.removeChild(self) call self.parent.removeChild(self)
let parentPath = self.path.getPathTrunk() let parentPath = self.path.getParent()
let newParent = b:NERDTreeRoot.findNode(parentPath) let newParent = b:NERDTreeRoot.findNode(parentPath)
if newParent != {} if newParent != {}
@ -1212,7 +1504,7 @@ function! s:TreeDirNode._initChildren(silent)
"filter out the .. and . directories "filter out the .. and . directories
"Note: we must match .. AND ../ cos sometimes the globpath returns "Note: we must match .. AND ../ cos sometimes the globpath returns
"../ for path with strange chars (eg $) "../ for path with strange chars (eg $)
if i !~ '^\.\.\/\?$' && i !~ '^\.\/\?$' if i !~ '\/\.\.\/\?$' && i !~ '\/\.\/\?$'
"put the next file in a new node and attach it "put the next file in a new node and attach it
try try
@ -1329,7 +1621,10 @@ function! s:TreeDirNode.refresh()
let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*') let filesStr = globpath(dir.strForGlob(), '*') . "\n" . globpath(dir.strForGlob(), '.*')
let files = split(filesStr, "\n") let files = split(filesStr, "\n")
for i in files 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 try
"create a new path and see if it exists in this nodes children "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 let toReturn = a:str
if prependCWD if prependCWD
let toReturn = getcwd() . s:os_slash . a:str let toReturn = getcwd() . s:Path.Slash() . a:str
endif endif
return toReturn return toReturn
@ -1475,7 +1770,7 @@ endfunction
function! s:Path.changeToDir() function! s:Path.changeToDir()
let dir = self.strForCd() let dir = self.strForCd()
if self.isDirectory ==# 0 if self.isDirectory ==# 0
let dir = self.getPathTrunk().strForCd() let dir = self.getParent().strForCd()
endif endif
try try
@ -1619,13 +1914,7 @@ endfunction
function! s:Path.delete() function! s:Path.delete()
if self.isDirectory if self.isDirectory
let cmd = "" let cmd = g:NERDTreeRemoveDirCmd . self.strForOS(1)
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 success = system(cmd) let success = system(cmd)
if v:shell_error != 0 if v:shell_error != 0
@ -1702,12 +1991,6 @@ function! s:Path.getLastPathComponent(dirSlash)
return toReturn return toReturn
endfunction 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 "FUNCTION: Path.getSortOrderIndex() {{{3
"returns the index of the pattern in g:NERDTreeSortOrder that this path matches "returns the index of the pattern in g:NERDTreeSortOrder that this path matches
function! s:Path.getSortOrderIndex() function! s:Path.getSortOrderIndex()
@ -1779,6 +2062,12 @@ function! s:Path.New(path)
return newPath return newPath
endfunction 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 "FUNCTION: Path.readInfoFromDisk(fullpath) {{{3
" "
" "
@ -1878,22 +2167,9 @@ function! s:Path.str(esc)
return toReturn return toReturn
endfunction 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 "FUNCTION: Path.strForCd() {{{3
" "
" returns a string that can be used with :cd " 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() function! s:Path.strForCd()
if s:running_windows if s:running_windows
return self.strForOS(0) return self.strForOS(0)
@ -1905,9 +2181,6 @@ endfunction
" "
"Returns a string that specifies how the path should be represented as a "Returns a string that specifies how the path should be represented as a
"string "string
"
"Return:
"a string that can be used in the view to represent this path
function! s:Path.strDisplay() function! s:Path.strDisplay()
if self.cachedDisplayString ==# "" if self.cachedDisplayString ==# ""
call self.cacheDisplayString() call self.cacheDisplayString()
@ -1929,7 +2202,7 @@ function! s:Path.strForEditCmd()
let cwd = tolower(getcwd()) let cwd = tolower(getcwd())
endif endif
let cwd = cwd . s:os_slash let cwd = cwd . s:Path.Slash()
"return a relative path if we can "return a relative path if we can
if stridx(p, cwd) ==# 0 if stridx(p, cwd) ==# 0
@ -1945,14 +2218,14 @@ function! s:Path.strForEditCmd()
endfunction endfunction
"FUNCTION: Path.strForGlob() {{{3 "FUNCTION: Path.strForGlob() {{{3
function! s:Path.strForGlob() 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 we are running windows then slap a drive letter on the front
if s:running_windows if s:running_windows
let lead = self.drive . '\' let lead = self.drive . '\'
endif endif
let toReturn = lead . join(self.pathSegments, s:os_slash) let toReturn = lead . join(self.pathSegments, s:Path.Slash())
if !s:running_windows if !s:running_windows
let toReturn = escape(toReturn, s:escape_chars) 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 "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. " escaped. If we are running windows then the str is double quoted instead.
function! s:Path.strForOS(esc) 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 we are running windows then slap a drive letter on the front
if s:running_windows if s:running_windows
let lead = self.drive . '\' let lead = self.drive . '\'
endif endif
let toReturn = lead . join(self.pathSegments, s:os_slash) let toReturn = lead . join(self.pathSegments, s:Path.Slash())
if a:esc if a:esc
if s:running_windows if s:running_windows
@ -2083,7 +2356,7 @@ function! s:initNerdTree(name)
"hack to get an absolute path if a relative path is given "hack to get an absolute path if a relative path is given
if dir =~ '^\.' if dir =~ '^\.'
let dir = getcwd() . s:os_slash . dir let dir = getcwd() . s:Path.Slash() . dir
endif endif
let dir = resolve(dir) let dir = resolve(dir)
@ -2288,45 +2561,28 @@ function! s:unique(list)
endfor endfor
return uniqlist return uniqlist
endfunction endfunction
" SECTION: Public Functions {{{1 " SECTION: Public API {{{1
"============================================================ "============================================================
"Returns the node that the cursor is currently on. let g:NERDTreePath = s:Path
" let g:NERDTreeDirNode = s:TreeDirNode
"If the cursor is not in the NERDTree window, it is temporarily put there. let g:NERDTreeFileNode = s:TreeFileNode
" let g:NERDTreeBookmark = s:Bookmark
"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() function! NERDTreeAddMenuItem(options)
if winnr != s:getTreeWinNum() return s:MenuItem.Create(a:options)
call s:putCursorInTreeWin()
endif
let treenode = s:TreeFileNode.GetSelected()
if winnr != winnr()
call s:exec('wincmd w')
endif
return treenode
endfunction endfunction
"Returns the path object for the current node. function! NERDTreeAddMenuSeparator(...)
" let opts = a:0 ? a:1 : {}
"Subject to the same conditions as NERDTreeGetCurrentNode return s:MenuItem.CreateSeparator(opts)
function! NERDTreeGetCurrentPath() endfunction
let node = NERDTreeGetCurrentNode()
if node != {} function! NERDTreeAddKeyMap(options)
return node.path call s:KeyMap.Create(a:options)
else endfunction
return {}
endif function! NERDTreeRender()
call s:renderView()
endfunction endfunction
" SECTION: View Functions {{{1 " SECTION: View Functions {{{1
@ -2486,7 +2742,7 @@ function! s:dumpHelp()
let @h=@h."\" but leave old root open\n" let @h=@h."\" but leave old root open\n"
let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n" let @h=@h."\" ". g:NERDTreeMapRefresh .": refresh cursor dir\n"
let @h=@h."\" ". g:NERDTreeMapRefreshRoot .": refresh current root\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."\" ". g:NERDTreeMapChdir .":change the CWD to the\n"
let @h=@h."\" selected dir\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:NERDTreeMapToggleFiles .": files (" . (b:NERDTreeShowFiles ? "on" : "off") . ")\n"
let @h=@h."\" ". g:NERDTreeMapToggleBookmarks .": bookmarks (" . (b:NERDTreeShowBookmarks ? "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."\"\n\" ----------------------------\n"
let @h=@h."\" Other mappings~\n" let @h=@h."\" Other mappings~\n"
let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n" let @h=@h."\" ". g:NERDTreeMapQuit .": Close the NERDTree window\n"
@ -3107,7 +3372,7 @@ function! s:bindMappings()
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>" exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseDir ." :call <SID>closeCurrentDir()<cr>"
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>" exec "nnoremap <silent> <buffer> ". g:NERDTreeMapCloseChildren ." :call <SID>closeChildren()<cr>"
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapFilesystemMenu ." :call <SID>showFileSystemMenu()<cr>" exec "nnoremap <silent> <buffer> ". g:NERDTreeMapMenu ." :call <SID>showMenu()<cr>"
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>" exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpParent ." :call <SID>jumpToParent()<cr>"
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>" exec "nnoremap <silent> <buffer> ". g:NERDTreeMapJumpNextSibling ." :call <SID>jumpToSibling(1)<cr>"
@ -3123,6 +3388,9 @@ function! s:bindMappings()
exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>" exec "nnoremap <silent> <buffer> ". g:NERDTreeMapDeleteBookmark ." :call <SID>deleteBookmark()<cr>"
"bind all the user custom maps
call s:KeyMap.BindAll()
command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>') command! -buffer -nargs=1 Bookmark :call <SID>bookmarkNode('<args>')
command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>') command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 RevealBookmark :call <SID>revealBookmark('<args>')
command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>') command! -buffer -complete=customlist,s:completeBookmarks -nargs=1 OpenBookmark :call <SID>openBookmark('<args>')
@ -3264,45 +3532,6 @@ function! s:closeTreeWindow()
endif endif
endif endif
endfunction 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 " FUNCTION: s:deleteBookmark() {{{2
" if the cursor is on a bookmark, prompt to delete " if the cursor is on a bookmark, prompt to delete
function! s:deleteBookmark() function! s:deleteBookmark()
@ -3328,57 +3557,6 @@ function! s:deleteBookmark()
endfunction 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 " FUNCTION: s:displayHelp() {{{2
" toggles the help display " toggles the help display
function! s:displayHelp() function! s:displayHelp()
@ -3423,40 +3601,6 @@ function! s:handleMiddleMouse()
endfunction 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 " FUNCTION: s:jumpToFirstChild() {{{2
" wrapper for the jump to child method " wrapper for the jump to child method
function! s:jumpToFirstChild() function! s:jumpToFirstChild()
@ -3659,80 +3803,16 @@ function! s:refreshCurrent()
redraw redraw
call s:echo("Refreshing node. This could take a while... DONE") call s:echo("Refreshing node. This could take a while... DONE")
endfunction endfunction
" FUNCTION: s:renameCurrent() {{{2 " FUNCTION: s:showMenu() {{{2
" allows the user to rename the current node function! s:showMenu()
function! s:renameCurrent()
let curNode = s:TreeFileNode.GetSelected() let curNode = s:TreeFileNode.GetSelected()
if curNode ==# {} if curNode ==# {}
call s:echo("Put the cursor on a node first" ) call s:echo("Put the cursor on a node first" )
return return
endif endif
let newNodePath = input("Rename the current node\n" . let mc = s:MenuController.New(s:MenuItem.AllEnabled())
\ "==========================================================\n" . call mc.showMenu()
\ "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
endfunction endfunction
" FUNCTION: s:toggleIgnoreFilter() {{{2 " FUNCTION: s:toggleIgnoreFilter() {{{2
@ -3802,7 +3882,7 @@ function! s:upDir(keepState)
let oldRoot = b:NERDTreeRoot let oldRoot = b:NERDTreeRoot
if empty(b:NERDTreeRoot.parent) if empty(b:NERDTreeRoot.parent)
let path = b:NERDTreeRoot.path.getPathTrunk() let path = b:NERDTreeRoot.path.getParent()
let newRoot = s:TreeDirNode.New(path) let newRoot = s:TreeDirNode.New(path)
call newRoot.open() call newRoot.open()
call newRoot.transplantChild(b:NERDTreeRoot) call newRoot.transplantChild(b:NERDTreeRoot)