extend the api and move the fs operations into a plugin

This commit is contained in:
Martin Grenfell 2009-07-19 17:21:57 +12:00
parent 1acf6321a5
commit c75a022a23
2 changed files with 317 additions and 214 deletions

View File

@ -0,0 +1,233 @@
" ============================================================================
" 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('(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:

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")
@ -139,11 +139,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
@ -431,6 +426,56 @@ function! s:Bookmark.Write()
endfor endfor
call writefile(bookmarkStrings, g:NERDTreeBookmarksFile) call writefile(bookmarkStrings, g:NERDTreeBookmarksFile)
endfunction 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 "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
@ -1432,7 +1477,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
@ -1778,6 +1823,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
" "
" "
@ -1928,7 +1979,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
@ -1944,14 +1995,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)
@ -1968,14 +2019,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
@ -2082,7 +2133,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)
@ -2287,8 +2338,14 @@ function! s:unique(list)
endfor endfor
return uniqlist return uniqlist
endfunction 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. "Returns the node that the cursor is currently on.
" "
"If the cursor is not in the NERDTree window, it is temporarily put there. "If the cursor is not in the NERDTree window, it is temporarily put there.
@ -2328,6 +2385,14 @@ function! NERDTreeGetCurrentPath()
endif endif
endfunction 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 " SECTION: View Functions {{{1
"============================================================ "============================================================
"FUNCTION: s:centerView() {{{2 "FUNCTION: s:centerView() {{{2
@ -2485,7 +2550,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"
@ -3103,7 +3168,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>"
@ -3260,45 +3325,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()
@ -3324,57 +3350,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()
@ -3419,40 +3394,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()
@ -3655,80 +3596,9 @@ 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() call s:MenuCallback.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
endfunction endfunction
" FUNCTION: s:toggleIgnoreFilter() {{{2 " FUNCTION: s:toggleIgnoreFilter() {{{2