fs_menu.vim 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  1. " ============================================================================
  2. " File: fs_menu.vim
  3. " Description: plugin for the NERD Tree that provides a file system menu
  4. " Maintainer: Martin Grenfell <martin.grenfell at gmail dot com>
  5. " License: This program is free software. It comes without any warranty,
  6. " to the extent permitted by applicable law. You can redistribute
  7. " it and/or modify it under the terms of the Do What The Fuck You
  8. " Want To Public License, Version 2, as published by Sam Hocevar.
  9. " See http://sam.zoy.org/wtfpl/COPYING for more details.
  10. "
  11. " ============================================================================
  12. if exists('g:loaded_nerdtree_fs_menu')
  13. finish
  14. endif
  15. let g:loaded_nerdtree_fs_menu = 1
  16. "Automatically delete the buffer after deleting or renaming a file
  17. if !exists('g:NERDTreeAutoDeleteBuffer')
  18. let g:NERDTreeAutoDeleteBuffer = 0
  19. endif
  20. call NERDTreeAddMenuItem({'text': '(a)dd a childnode', 'shortcut': 'a', 'callback': 'NERDTreeAddNode'})
  21. call NERDTreeAddMenuItem({'text': '(m)ove the current node', 'shortcut': 'm', 'callback': 'NERDTreeMoveNode'})
  22. call NERDTreeAddMenuItem({'text': '(d)elete the current node', 'shortcut': 'd', 'callback': 'NERDTreeDeleteNode'})
  23. if has('gui_mac') || has('gui_macvim') || has('mac')
  24. call NERDTreeAddMenuItem({'text': '(r)eveal in Finder the current node', 'shortcut': 'r', 'callback': 'NERDTreeRevealInFinder'})
  25. call NERDTreeAddMenuItem({'text': '(o)pen the current node with system editor', 'shortcut': 'o', 'callback': 'NERDTreeExecuteFile'})
  26. call NERDTreeAddMenuItem({'text': '(q)uicklook the current node', 'shortcut': 'q', 'callback': 'NERDTreeQuickLook'})
  27. endif
  28. if executable('xdg-open')
  29. call NERDTreeAddMenuItem({'text': '(r)eveal the current node in file manager', 'shortcut': 'r', 'callback': 'NERDTreeRevealFileLinux'})
  30. call NERDTreeAddMenuItem({'text': '(o)pen the current node with system editor', 'shortcut': 'o', 'callback': 'NERDTreeExecuteFileLinux'})
  31. endif
  32. if nerdtree#runningWindows()
  33. call NERDTreeAddMenuItem({'text': '(o)pen the current node with system editor', 'shortcut': 'o', 'callback': 'NERDTreeExecuteFileWindows'})
  34. endif
  35. if g:NERDTreePath.CopyingSupported()
  36. call NERDTreeAddMenuItem({'text': '(c)opy the current node', 'shortcut': 'c', 'callback': 'NERDTreeCopyNode'})
  37. endif
  38. call NERDTreeAddMenuItem({'text': (has('clipboard')?'copy (p)ath to clipboard':'print (p)ath to screen'), 'shortcut': 'p', 'callback': 'NERDTreeCopyPath'})
  39. if has('unix') || has('osx')
  40. call NERDTreeAddMenuItem({'text': '(l)ist the current node', 'shortcut': 'l', 'callback': 'NERDTreeListNode'})
  41. else
  42. call NERDTreeAddMenuItem({'text': '(l)ist the current node', 'shortcut': 'l', 'callback': 'NERDTreeListNodeWin32'})
  43. endif
  44. if exists('*system')
  45. call NERDTreeAddMenuItem({'text': 'Run (s)ystem command in this directory', 'shortcut':'s', 'callback': 'NERDTreeSystemCommand'})
  46. endif
  47. "FUNCTION: s:inputPrompt(action){{{1
  48. "returns the string that should be prompted to the user for the given action
  49. "
  50. "Args:
  51. "action: the action that is being performed, e.g. 'delete'
  52. function! s:inputPrompt(action)
  53. if a:action ==# 'add'
  54. let title = 'Add a childnode'
  55. let info = "Enter the dir/file name to be created. Dirs end with a '/'"
  56. let minimal = 'Add node:'
  57. elseif a:action ==# 'copy'
  58. let title = 'Copy the current node'
  59. let info = 'Enter the new path to copy the node to:'
  60. let minimal = 'Copy to:'
  61. elseif a:action ==# 'delete'
  62. let title = 'Delete the current node'
  63. let info = 'Are you sure you wish to delete the node:'
  64. let minimal = 'Delete?'
  65. elseif a:action ==# 'deleteNonEmpty'
  66. let title = 'Delete the current node'
  67. let info = "STOP! Directory is not empty! To delete, type 'yes'"
  68. let minimal = 'Delete directory?'
  69. elseif a:action ==# 'move'
  70. let title = 'Rename the current node'
  71. let info = 'Enter the new path for the node:'
  72. let minimal = 'Move to:'
  73. endif
  74. if g:NERDTreeMenuController.isMinimal()
  75. redraw! " Clear the menu
  76. return minimal . ' '
  77. else
  78. let divider = '=========================================================='
  79. return title . "\n" . divider . "\n" . info . "\n"
  80. end
  81. endfunction
  82. "FUNCTION: s:promptToDelBuffer(bufnum, msg){{{1
  83. "prints out the given msg and, if the user responds by pushing 'y' then the
  84. "buffer with the given bufnum is deleted
  85. "
  86. "Args:
  87. "bufnum: the buffer that may be deleted
  88. "msg: a message that will be echoed to the user asking them if they wish to
  89. " del the buffer
  90. function! s:promptToDelBuffer(bufnum, msg)
  91. echo a:msg
  92. if g:NERDTreeAutoDeleteBuffer || nr2char(getchar()) ==# 'y'
  93. " 1. ensure that all windows which display the just deleted filename
  94. " now display an empty buffer (so a layout is preserved).
  95. " Is not it better to close single tabs with this file only ?
  96. let s:originalTabNumber = tabpagenr()
  97. let s:originalWindowNumber = winnr()
  98. " Go to the next buffer in buffer list if at least one extra buffer is listed
  99. " Otherwise open a new empty buffer
  100. if v:version >= 800
  101. let l:listedBufferCount = len(getbufinfo({'buflisted':1}))
  102. elseif v:version >= 702
  103. let l:listedBufferCount = len(filter(range(1, bufnr('$')), 'buflisted(v:val)'))
  104. else
  105. " Ignore buffer count in this case to make sure we keep the old
  106. " behavior
  107. let l:listedBufferCount = 0
  108. endif
  109. if l:listedBufferCount > 1
  110. call nerdtree#exec('tabdo windo if winbufnr(0) ==# ' . a:bufnum . " | exec ':bnext! ' | endif", 1)
  111. else
  112. call nerdtree#exec('tabdo windo if winbufnr(0) ==# ' . a:bufnum . " | exec ':enew! ' | endif", 1)
  113. endif
  114. call nerdtree#exec('tabnext ' . s:originalTabNumber, 1)
  115. call nerdtree#exec(s:originalWindowNumber . 'wincmd w', 1)
  116. " 3. We don't need a previous buffer anymore
  117. call nerdtree#exec('bwipeout! ' . a:bufnum, 0)
  118. endif
  119. endfunction
  120. "FUNCTION: s:renameBuffer(bufNum, newNodeName, isDirectory){{{1
  121. "The buffer with the given bufNum is replaced with a new one
  122. "
  123. "Args:
  124. "bufNum: the buffer that may be deleted
  125. "newNodeName: the name given to the renamed node
  126. "isDirectory: determines how to do the create the new filenames
  127. function! s:renameBuffer(bufNum, newNodeName, isDirectory)
  128. if a:isDirectory
  129. let quotedFileName = fnameescape(a:newNodeName . '/' . fnamemodify(bufname(a:bufNum),':t'))
  130. let editStr = g:NERDTreePath.New(a:newNodeName . '/' . fnamemodify(bufname(a:bufNum),':t')).str({'format': 'Edit'})
  131. else
  132. let quotedFileName = fnameescape(a:newNodeName)
  133. let editStr = g:NERDTreePath.New(a:newNodeName).str({'format': 'Edit'})
  134. endif
  135. " 1. ensure that a new buffer is loaded
  136. call nerdtree#exec('badd ' . quotedFileName, 0)
  137. " 2. ensure that all windows which display the just deleted filename
  138. " display a buffer for a new filename.
  139. let s:originalTabNumber = tabpagenr()
  140. let s:originalWindowNumber = winnr()
  141. call nerdtree#exec('tabdo windo if winbufnr(0) ==# ' . a:bufNum . " | exec ':e! " . editStr . "' | endif", 0)
  142. call nerdtree#exec('tabnext ' . s:originalTabNumber, 1)
  143. call nerdtree#exec(s:originalWindowNumber . 'wincmd w', 1)
  144. " 3. We don't need a previous buffer anymore
  145. try
  146. call nerdtree#exec('confirm bwipeout ' . a:bufNum, 0)
  147. catch
  148. " This happens when answering Cancel if confirmation is needed. Do nothing.
  149. endtry
  150. endfunction
  151. "FUNCTION: NERDTreeAddNode(){{{1
  152. function! NERDTreeAddNode()
  153. let curDirNode = g:NERDTreeDirNode.GetSelected()
  154. let prompt = s:inputPrompt('add')
  155. let newNodeName = substitute(input(prompt, curDirNode.path.str() . nerdtree#slash(), 'file'), '\(^\s*\|\s*$\)', '', 'g')
  156. if newNodeName ==# ''
  157. call nerdtree#echo('Node Creation Aborted.')
  158. return
  159. endif
  160. try
  161. let newPath = g:NERDTreePath.Create(newNodeName)
  162. let parentNode = b:NERDTree.root.findNode(newPath.getParent())
  163. let newTreeNode = g:NERDTreeFileNode.New(newPath, b:NERDTree)
  164. " Emptying g:NERDTreeOldSortOrder forces the sort to
  165. " recalculate the cached sortKey so nodes sort correctly.
  166. let g:NERDTreeOldSortOrder = []
  167. if empty(parentNode)
  168. call b:NERDTree.root.refresh()
  169. call b:NERDTree.render()
  170. elseif parentNode.isOpen || !empty(parentNode.children)
  171. call parentNode.addChild(newTreeNode, 1)
  172. call NERDTreeRender()
  173. call newTreeNode.putCursorHere(1, 0)
  174. endif
  175. redraw!
  176. catch /^NERDTree/
  177. call nerdtree#echoWarning('Node Not Created.')
  178. endtry
  179. endfunction
  180. "FUNCTION: NERDTreeMoveNode(){{{1
  181. function! NERDTreeMoveNode()
  182. let curNode = g:NERDTreeFileNode.GetSelected()
  183. let prompt = s:inputPrompt('move')
  184. let newNodePath = input(prompt, curNode.path.str(), 'file')
  185. while filereadable(newNodePath)
  186. call nerdtree#echoWarning('This destination already exists. Try again.')
  187. let newNodePath = substitute(input(prompt, curNode.path.str(), 'file'), '\(^\s*\|\s*$\)', '', 'g')
  188. endwhile
  189. if newNodePath ==# ''
  190. call nerdtree#echo('Node Renaming Aborted.')
  191. return
  192. endif
  193. try
  194. if curNode.path.isDirectory
  195. let l:curPath = escape(curNode.path.str(),'\') . (nerdtree#runningWindows()?'\\':'/') . '.*'
  196. let l:openBuffers = filter(range(1,bufnr('$')),'bufexists(v:val) && fnamemodify(bufname(v:val),":p") =~# "'.escape(l:curPath,'\').'"')
  197. else
  198. let l:openBuffers = filter(range(1,bufnr('$')),'bufexists(v:val) && fnamemodify(bufname(v:val),":p") ==# curNode.path.str()')
  199. endif
  200. call curNode.rename(newNodePath)
  201. " Emptying g:NERDTreeOldSortOrder forces the sort to
  202. " recalculate the cached sortKey so nodes sort correctly.
  203. let g:NERDTreeOldSortOrder = []
  204. call b:NERDTree.root.refresh()
  205. call NERDTreeRender()
  206. " If the file node is open, or files under the directory node are
  207. " open, ask the user if they want to replace the file(s) with the
  208. " renamed files.
  209. if !empty(l:openBuffers)
  210. if curNode.path.isDirectory
  211. echo "\nDirectory renamed.\n\nFiles with the old directory name are open in buffers " . join(l:openBuffers, ', ') . '. Replace these buffers with the new files? (yN)'
  212. else
  213. echo "\nFile renamed.\n\nThe old file is open in buffer " . l:openBuffers[0] . '. Replace this buffer with the new file? (yN)'
  214. endif
  215. if g:NERDTreeAutoDeleteBuffer || nr2char(getchar()) ==# 'y'
  216. for bufNum in l:openBuffers
  217. call s:renameBuffer(bufNum, newNodePath, curNode.path.isDirectory)
  218. endfor
  219. endif
  220. endif
  221. call curNode.putCursorHere(1, 0)
  222. redraw!
  223. catch /^NERDTree/
  224. call nerdtree#echoWarning('Node Not Renamed.')
  225. endtry
  226. endfunction
  227. " FUNCTION: NERDTreeDeleteNode() {{{1
  228. function! NERDTreeDeleteNode()
  229. let currentNode = g:NERDTreeFileNode.GetSelected()
  230. let confirmed = 0
  231. if currentNode.path.isDirectory && ((currentNode.isOpen && currentNode.getChildCount() > 0) ||
  232. \ (len(currentNode._glob('*', 1)) > 0))
  233. let prompt = s:inputPrompt('deleteNonEmpty') . currentNode.path.str() . ': '
  234. let choice = input(prompt)
  235. let confirmed = choice ==# 'yes'
  236. else
  237. let prompt = s:inputPrompt('delete') . currentNode.path.str() . ' (yN): '
  238. echo prompt
  239. let choice = nr2char(getchar())
  240. let confirmed = choice ==# 'y'
  241. endif
  242. if confirmed
  243. try
  244. call currentNode.delete()
  245. call NERDTreeRender()
  246. "if the node is open in a buffer, ask the user if they want to
  247. "close that buffer
  248. let bufnum = bufnr('^'.currentNode.path.str().'$')
  249. if buflisted(bufnum)
  250. let prompt = "\nNode deleted.\n\nThe file is open in buffer ". bufnum . (bufwinnr(bufnum) ==# -1 ? ' (hidden)' : '') .'. Delete this buffer? (yN)'
  251. call s:promptToDelBuffer(bufnum, prompt)
  252. endif
  253. redraw!
  254. catch /^NERDTree/
  255. call nerdtree#echoWarning('Could not remove node')
  256. endtry
  257. else
  258. call nerdtree#echo('delete aborted')
  259. endif
  260. endfunction
  261. " FUNCTION: NERDTreeListNode() {{{1
  262. function! NERDTreeListNode()
  263. let treenode = g:NERDTreeFileNode.GetSelected()
  264. if !empty(treenode)
  265. let s:uname = system('uname')
  266. let stat_cmd = 'stat -c "%s" '
  267. if s:uname =~? 'Darwin'
  268. let stat_cmd = 'stat -f "%z" '
  269. endif
  270. let cmd = 'size=$(' . stat_cmd . shellescape(treenode.path.str()) . ') && ' .
  271. \ 'size_with_commas=$(echo $size | sed -e :a -e "s/\(.*[0-9]\)\([0-9]\{3\}\)/\1,\2/;ta") && ' .
  272. \ 'ls -ld ' . shellescape(treenode.path.str()) . ' | sed -e "s/ $size / $size_with_commas /"'
  273. let metadata = split(system(cmd),'\n')
  274. call nerdtree#echo(metadata[0])
  275. else
  276. call nerdtree#echo('No information available')
  277. endif
  278. endfunction
  279. " FUNCTION: NERDTreeListNodeWin32() {{{1
  280. function! NERDTreeListNodeWin32()
  281. let l:node = g:NERDTreeFileNode.GetSelected()
  282. if !empty(l:node)
  283. let l:path = l:node.path.str()
  284. call nerdtree#echo(printf('%s:%s MOD:%s BYTES:%d PERMISSIONS:%s',
  285. \ toupper(getftype(l:path)),
  286. \ fnamemodify(l:path, ':t'),
  287. \ strftime('%c', getftime(l:path)),
  288. \ getfsize(l:path),
  289. \ getfperm(l:path)))
  290. return
  291. endif
  292. call nerdtree#echo('node not recognized')
  293. endfunction
  294. " FUNCTION: NERDTreeCopyNode() {{{1
  295. function! NERDTreeCopyNode()
  296. let currentNode = g:NERDTreeFileNode.GetSelected()
  297. let prompt = s:inputPrompt('copy')
  298. let newNodePath = substitute(input(prompt, currentNode.path.str(), 'file'), '\(^\s*\|\s*$\)', '', 'g')
  299. if newNodePath !=# ''
  300. "strip trailing slash
  301. let newNodePath = substitute(newNodePath, '\/$', '', '')
  302. let confirmed = 1
  303. if currentNode.path.copyingWillOverwrite(newNodePath)
  304. call nerdtree#echo('Warning: copying may overwrite files! Continue? (yN)')
  305. let choice = nr2char(getchar())
  306. let confirmed = choice ==# 'y'
  307. endif
  308. if confirmed
  309. try
  310. let newNode = currentNode.copy(newNodePath)
  311. " Emptying g:NERDTreeOldSortOrder forces the sort to
  312. " recalculate the cached sortKey so nodes sort correctly.
  313. let g:NERDTreeOldSortOrder = []
  314. if empty(newNode)
  315. call b:NERDTree.root.refresh()
  316. call b:NERDTree.render()
  317. else
  318. call NERDTreeRender()
  319. call newNode.putCursorHere(0, 0)
  320. endif
  321. catch /^NERDTree/
  322. call nerdtree#echoWarning('Could not copy node')
  323. endtry
  324. endif
  325. else
  326. call nerdtree#echo('Copy aborted.')
  327. endif
  328. redraw!
  329. endfunction
  330. " FUNCTION: NERDTreeCopyPath() {{{1
  331. function! NERDTreeCopyPath()
  332. let l:nodePath = g:NERDTreeFileNode.GetSelected().path.str()
  333. if has('clipboard')
  334. if &clipboard ==# 'unnamedplus'
  335. let @+ = l:nodePath
  336. else
  337. let @* = l:nodePath
  338. endif
  339. call nerdtree#echo('The path [' . l:nodePath . '] was copied to your clipboard.')
  340. else
  341. call nerdtree#echo('The full path is: ' . l:nodePath)
  342. endif
  343. endfunction
  344. " FUNCTION: NERDTreeQuickLook() {{{1
  345. function! NERDTreeQuickLook()
  346. let l:node = g:NERDTreeFileNode.GetSelected()
  347. if empty(l:node)
  348. return
  349. endif
  350. call system('qlmanage -p 2>/dev/null ' . shellescape(l:node.path.str()))
  351. endfunction
  352. " FUNCTION: NERDTreeRevealInFinder() {{{1
  353. function! NERDTreeRevealInFinder()
  354. let l:node = g:NERDTreeFileNode.GetSelected()
  355. if empty(l:node)
  356. return
  357. endif
  358. call system('open -R ' . shellescape(l:node.path.str()))
  359. endfunction
  360. " FUNCTION: NERDTreeExecuteFile() {{{1
  361. function! NERDTreeExecuteFile()
  362. let l:node = g:NERDTreeFileNode.GetSelected()
  363. if empty(l:node)
  364. return
  365. endif
  366. call system('open ' . shellescape(l:node.path.str()))
  367. endfunction
  368. " FUNCTION: NERDTreeRevealFileLinux() {{{1
  369. function! NERDTreeRevealFileLinux()
  370. let l:node = g:NERDTreeFileNode.GetSelected()
  371. if empty(l:node)
  372. return
  373. endif
  374. " Handle the edge case of "/", which has no parent.
  375. if l:node.path.str() ==# '/'
  376. call system('xdg-open /')
  377. return
  378. endif
  379. if empty(l:node.parent)
  380. return
  381. endif
  382. call system('xdg-open ' . shellescape(l:node.parent.path.str()))
  383. endfunction
  384. " FUNCTION: NERDTreeExecuteFileLinux() {{{1
  385. function! NERDTreeExecuteFileLinux()
  386. let l:node = g:NERDTreeFileNode.GetSelected()
  387. if empty(l:node)
  388. return
  389. endif
  390. call system('xdg-open ' . shellescape(l:node.path.str()))
  391. endfunction
  392. " FUNCTION: NERDTreeExecuteFileWindows() {{{1
  393. function! NERDTreeExecuteFileWindows()
  394. let l:node = g:NERDTreeFileNode.GetSelected()
  395. if empty(l:node)
  396. return
  397. endif
  398. call system('cmd.exe /c start "" ' . shellescape(l:node.path.str()))
  399. endfunction
  400. " FUNCTION: NERDTreeSystemCommand() {{{1
  401. function! NERDTreeSystemCommand()
  402. let l:node = g:NERDTreeFileNode.GetSelected()
  403. if empty(l:node)
  404. return
  405. endif
  406. let l:cwd = getcwd()
  407. let l:directory = l:node.path.isDirectory ? l:node.path.str() : l:node.parent.path.str()
  408. execute 'cd '.l:directory
  409. let l:nl = nr2char(10)
  410. echo l:nl . system(input(l:directory . (nerdtree#runningWindows() ? '> ' : ' $ ')))
  411. execute 'cd '.l:cwd
  412. endfunction
  413. " vim: set sw=4 sts=4 et fdm=marker: