├── LICENSE ├── README.md └── ftplugin └── nerdtree.vim /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Phil Runninger 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nerdtree-visual-selection 2 | This plugin requires [NERDTree](https://github.com/preservim/nerdtree) also to be installed. **nerdtree-visual-selection** defines key mappings that will work on nodes contained in a Visual selection in **NERDTree**. 3 | 4 | ## Installation 5 | 6 | Use your favorite plugin manager to install this plugin. [vim-pathogen](https://github.com/tpope/vim-pathogen), [Vundle.vim](https://github.com/VundleVim/Vundle.vim), [vim-plug](https://github.com/junegunn/vim-plug), [neobundle.vim](https://github.com/Shougo/neobundle.vim), and [dein.vim](https://github.com/Shougo/dein.vim) are some of the more popular ones. A lengthy discussion of these and other managers can be found on [vi.stackexchange.com](https://vi.stackexchange.com/questions/388/what-is-the-difference-between-the-vim-plugin-managers). 7 | 8 | If you have no favorite, or want to manage your plugins without 3rd-party dependencies, I recommend using Vim 8 packages, as described in Greg Hurrell's excellent Youtube video: [Vim screencast #75: Plugin managers](https://www.youtube.com/watch?v=X2_R3uxDN6g) 9 | 10 | ## Known Issue 11 | 12 | There are two kinds of NERDTree: 13 | * **TabTree** - Opened with `:NERDTree`, `:NERDTreeFocus`, `:NERDTreeFind`, or `:NERDTreeToggle` 14 | * **WindowTree** - Opened with `vim .` or `:e .`, among others. 15 | 16 | This plugin does not work with **WindowTree** type trees, because the first file opened by it causes the NERDTree to close, and the other files in the selection to fail to open. These commands are disabled, and an error message is printed, when trying to use them in the wrong type NERDTree. 17 | 18 | ## Configuration 19 | 20 | By default, all operations ask to be confirmed with a `Yes/No/All/Cancel` prompt. `All` is a `Yes` answer, while `Cancel` is a `No`, for the remainder of the selection. Confirmation for the different operations can be turned off by setting the following aptly-named variables to `0` in your `.vimrc`. 21 | 22 | * `g:nerdtree_vis_confirm_open` 23 | * `g:nerdtree_vis_confirm_delete` 24 | * `g:nerdtree_vis_confirm_copy` 25 | * `g:nerdtree_vis_confirm_move` 26 | * `g:nerdtree_vis_confirm_append_arglist` 27 | * `g:nerdtree_vis_confirm_set_arglist` 28 | 29 | A [mark](http://vimdoc.sourceforge.net/htmldoc/motion.html#mark-motions) is used to make your NERDTree's `Jump` mappings work while keeping your selection. By default the mark is on the `n` key, if you already use this key for a mark inside NERDTree you can change it via `g:nerdtree_vis_jumpmark` 30 | 31 | ## Mappings 32 | 33 | Where applicable, those key mappings match up with NERDTree settings. If not defined in your `.vimrc`, their default values are used. The mappings are as follows: 34 | 35 | NERDTree variable | default | Purpose 36 | ---|---|--- 37 | NERDTreeMapActivateNode | o | Open selected files. 38 | NERDTreeMapOpenSplit | i | Open selected files in horizontal splits. 39 | NERDTreeMapOpenVSplit | s | Open selected files in vertical splits. 40 | NERDTreeMapOpenInTab | t | Open selected files in tabs. 41 | *n/a* | d | Delete selected files from disk. If open in Vim, they remain open. 42 | *n/a* | m | Move the selected files to another directory. If open in Vim, the buffer still points to its old location. 43 | *n/a* | c | Copy selected files to another directory. 44 | *n/a* | a | Append selected files to the arglist. If a directory is selected, it is ignored. Only files will be added. 45 | *n/a* | A | Set the arglist to only selected file. This overwrites the previeous arglist. If a directory is selected, it is ignored. Only files will be added. 46 | NERDTreeMapJumpRoot | P | Jump to the tree root. 47 | NERDTreeMapJumpParent | p | Jump to the parent node of the cursor node. 48 | NERDTreeMapJumpFirstChild | K | Jump to the first child of the cursor node's parent. 49 | NERDTreeMapJumpLastChild | J | Jump to the last child of the cursor node's parent. 50 | NERDTreeMapJumpPrevSibling | c-k | Jump to the previous sibling of the cursor node. 51 | NERDTreeMapJumpNextSibling | c-j | Jump to the next sibling of the cursor node. 52 | -------------------------------------------------------------------------------- /ftplugin/nerdtree.vim: -------------------------------------------------------------------------------- 1 | let g:nerdtree_vis_confirm_open = get(g:, 'nerdtree_vis_confirm_open', 1) 2 | let g:nerdtree_vis_confirm_delete = get(g:, 'nerdtree_vis_confirm_delete', 1) 3 | let g:nerdtree_vis_confirm_move = get(g:, 'nerdtree_vis_confirm_move', 1) 4 | let g:nerdtree_vis_confirm_copy = get(g:, 'nerdtree_vis_confirm_copy', 1) 5 | let g:nerdtree_vis_confirm_append_arglist = get(g:, 'nerdtree_vis_confirm_append_arglist', 1) 6 | let g:nerdtree_vis_confirm_set_arglist = get(g:, 'nerdtree_vis_confirm_set_arglist', 1) 7 | 8 | execute "vnoremap " . g:NERDTreeMapActivateNode . " :call ProcessSelection('Opening', '', function('NERDTree_Open', ['p']), '', 1, ".g:nerdtree_vis_confirm_open.")" 9 | execute "vnoremap " . g:NERDTreeMapOpenSplit . " :call ProcessSelection('Opening', '', function('NERDTree_Open', ['h']), '', 1, ".g:nerdtree_vis_confirm_open.")" 10 | execute "vnoremap " . g:NERDTreeMapOpenVSplit . " :call ProcessSelection('Opening', '', function('NERDTree_Open', ['v']), '', 1, ".g:nerdtree_vis_confirm_open.")" 11 | execute "vnoremap " . g:NERDTreeMapOpenInTab . " :call ProcessSelection('Opening', '', function('NERDTree_Open', ['t']), '', 1, ".g:nerdtree_vis_confirm_open.")" 12 | execute "vnoremap d :call ProcessSelection('Deleting', '', function('NERDTree_Delete'), '', 0, ".g:nerdtree_vis_confirm_delete.")" 13 | execute "vnoremap m :call ProcessSelection('Moving', function('PRE_MoveOrCopy'), function('NERDTree_MoveOrCopy', ['Moving']), function('POST_MoveOrCopy'), 0, ".g:nerdtree_vis_confirm_move.")" 14 | execute "vnoremap c :call ProcessSelection('Copying', function('PRE_MoveOrCopy'), function('NERDTree_MoveOrCopy', ['Copying']), function('POST_MoveOrCopy'), 0, ".g:nerdtree_vis_confirm_copy.")" 15 | execute "vnoremap a :call ProcessSelection('Appending to arglist', '', function('NERDTree_AppendToArglist'), '', 0, ".g:nerdtree_vis_confirm_append_arglist.")" 16 | execute "vnoremap A :call ProcessSelection('Setting arglist', function('NERDTree_DeleteArglist'), function('NERDTree_AppendToArglist'), '', 0, ".g:nerdtree_vis_confirm_set_arglist.")" 17 | 18 | " -------------------------------------------------------------------------------- 19 | " Jump Support 20 | let g:nerdtree_vis_jumpmark = "n" 21 | 22 | function! s:NERDTree_VisRemap(key) 23 | return "vnoremap " .eval(a:key) ." :call g:NERDTreeKeyMap.Invoke(" .a:key .")m" .g:nerdtree_vis_jumpmark ."gv'" .g:nerdtree_vis_jumpmark 24 | endfunction 25 | 26 | execute s:NERDTree_VisRemap( "g:NERDTreeMapJumpNextSibling" ) 27 | execute s:NERDTree_VisRemap( "g:NERDTreeMapJumpPrevSibling" ) 28 | execute s:NERDTree_VisRemap( "g:NERDTreeMapJumpFirstChild" ) 29 | execute s:NERDTree_VisRemap( "g:NERDTreeMapJumpLastChild" ) 30 | execute s:NERDTree_VisRemap( "g:NERDTreeMapJumpParent" ) 31 | execute s:NERDTree_VisRemap( "g:NERDTreeMapJumpRoot" ) 32 | 33 | " -------------------------------------------------------------------------------- 34 | if exists("g:nerdtree_visual_selection") 35 | finish 36 | endif 37 | let g:nerdtree_visual_selection = 1 38 | 39 | " -------------------------------------------------------------------------------- 40 | " Delete 41 | function! NERDTree_Delete(node) 42 | call a:node.delete() 43 | endfunction 44 | 45 | " -------------------------------------------------------------------------------- 46 | " Open 47 | function! NERDTree_Open(target, node) 48 | if !empty(a:node) && !a:node.path.isDirectory 49 | silent call a:node.open({'where':a:target,'stay':1,'keepopen':1}) 50 | endif 51 | endfunction 52 | 53 | " -------------------------------------------------------------------------------- 54 | " Move or copy 55 | function! PRE_MoveOrCopy() 56 | let node = g:NERDTreeFileNode.GetSelected() 57 | if !exists('s:destination') 58 | let s:destination = node.path.str() 59 | if !node.path.isDirectory 60 | let s:destination = fnamemodify(s:destination, ':p:h') 61 | endif 62 | let s:destination = input('Destination directory: ', s:destination, 'dir') 63 | if s:destination == '' 64 | unlet! s:destination 65 | return 0 66 | endif 67 | let s:destination .= (s:destination =~# nerdtree#slash().'$' ? '' : nerdtree#slash()) 68 | if !isdirectory(s:destination) 69 | call mkdir(s:destination, 'p') 70 | endif 71 | endif 72 | return 1 73 | endfunction 74 | 75 | function! NERDTree_MoveOrCopy(operation, node) 76 | let l:destination = s:destination . fnamemodify(a:node.path.str(), ':t') 77 | if a:operation == 'Moving' 78 | call a:node.rename(l:destination) 79 | else 80 | call a:node.copy(l:destination) 81 | endif 82 | endfunction 83 | 84 | function! POST_MoveOrCopy() 85 | unlet! s:destination 86 | endfunction 87 | 88 | " -------------------------------------------------------------------------------- 89 | " Append to Arglist 90 | function! NERDTree_DeleteArglist() 91 | execute "argdelete *" 92 | return 1 93 | endfunction 94 | 95 | function! NERDTree_AppendToArglist(node) 96 | let l:file = a:node.path.str() 97 | if filereadable(l:file) 98 | execute "argadd " . l:file 99 | return 1 100 | else 101 | call nerdtree#echo("File not found: Make sure the file exists and is readable.") 102 | return 0 103 | endif 104 | 105 | return 0 106 | endfunction 107 | 108 | " -------------------------------------------------------------------------------- 109 | " Main Processor 110 | function! s:ProcessSelection(action, setup, callback, cleanup, closeWhenDone, confirmEachNode) range 111 | if b:NERDTree.isWinTree() 112 | call nerdtree#echo("Command is unavailable. Open NERDTree with :NERDTree, :NERDTreeToggle, or :NERDTreeFocus instead.") 113 | return 114 | endif 115 | 116 | if type(a:setup) == v:t_func 117 | if !a:setup() 118 | return 119 | endif 120 | endif 121 | 122 | let l:response = 0 123 | let curLine = a:firstline 124 | while curLine <= a:lastline 125 | call cursor(curLine, 1) 126 | let node = g:NERDTreeFileNode.GetSelected() 127 | if empty(node) 128 | let curLine += 1 129 | continue 130 | endif 131 | call nerdtree#echo(a:action . " " . node.path.str() . " (" . (curLine - a:firstline + 1) . " of " . (a:lastline - a:firstline + 1) . ")...") 132 | if a:confirmEachNode && l:response < 3 133 | let l:response = confirm("Are you sure? ", "&Yes\n&No\n&All\n&Cancel") 134 | if l:response == 0 " Make Escape behave like Cancel 135 | let l:response = 4 136 | endif 137 | endif 138 | if !a:confirmEachNode || l:response == 1 || l:response == 3 139 | call a:callback(node) 140 | endif 141 | let curLine += 1 142 | endwhile 143 | 144 | if type(a:cleanup) == v:t_func 145 | call a:cleanup() 146 | endif 147 | 148 | let g:NERDTreeOldSortOrder = [] 149 | call b:NERDTree.root.refresh() 150 | call NERDTreeRender() 151 | 152 | if g:NERDTreeQuitOnOpen && a:closeWhenDone 153 | NERDTreeClose 154 | endif 155 | 156 | call nerdtree#echo("") 157 | endfunction 158 | --------------------------------------------------------------------------------