├── LICENSE ├── README.md └── ftplugin └── dirvish.vim /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rogin Farrer 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 | # 🧰 vim-dirvish-dovish 2 | 3 | > The file manipulation commands for [vim-dirvish][dirvish] that you've always wanted 4 | 5 | Have only tested on MacOS and Neovim, but it should work with Vim. 6 | 7 | ## Installation & Requirements 8 | 9 | You'll need: 10 | 11 | - [dirvish.vim][dirvish] 12 | - A CLI that provides a `trash` command, such as [trash](https://formulae.brew.sh/formula/trash) or [trash-cli](https://github.com/sindresorhus/trash-cli) 13 | 14 | Then install with your favorite package manager: 15 | 16 | ```vim 17 | Plug 'roginfarrer/vim-dirvish-dovish', {'branch': 'main'} 18 | ``` 19 | 20 | ## Mappings 21 | 22 | | Function | Default | Key | 23 | | --------------------------------------- | ------- | --------------------------------- | 24 | | Create file | `a` | `(dovish_create_file)` | 25 | | Create directory | `A` | `(dovish_create_directory)` | 26 | | Delete under cursor | `dd` | `(dovish_delete)` | 27 | | Rename under cursor | `r` | `(dovish_rename)` | 28 | | Yank under cursor (or visual selection) | `yy` | `(dovish_yank)` | 29 | | Copy file to current directory | `pp` | `(dovish_copy)` | 30 | | Move file to current directory | `PP` | `(dovish_move)` | 31 | 32 | You can unmap all of the maps above and set your own (mine are below). Add this to `ftplugin/dirvish.vim`: 33 | 34 | ```vim 35 | " unmap all default mappings 36 | let g:dirvish_dovish_map_keys = 0 37 | 38 | " unmap dirvish default 39 | unmap p 40 | 41 | " Your preferred mappings 42 | nmap i (dovish_create_file) 43 | nmap I (dovish_create_directory) 44 | nmap dd (dovish_delete) 45 | nmap r (dovish_rename) 46 | nmap yy (dovish_yank) 47 | xmap yy (dovish_yank) 48 | nmap p (dovish_copy) 49 | nmap P (dovish_move) 50 | ``` 51 | 52 | ## Customize Commands 53 | 54 | Most file operations can be customized. Below are the defaults: 55 | 56 | ```vim 57 | " Used for (dovish_yank) 58 | function! g:DovishCopyFile(target, destination) abort 59 | return 'cp ' . a:target . ' ' . a:destination 60 | endfunction 61 | 62 | " Used for (dovish_yank) 63 | function! g:DovishCopyDirectory(target, destination) abort 64 | return 'cp -r' . a:target . ' ' . a:destination 65 | endfunction 66 | 67 | " Used for (dovish_move) 68 | function! g:DovishMove(target, destination) abort 69 | return 'mv ' . a:target . ' ' . a:destination 70 | endfunction 71 | 72 | " Used for (dovish_delete) 73 | function! g:DovishDelete(target) abort 74 | return 'trash ' . a:target 75 | endfunction 76 | 77 | " Used for (dovish_rename) 78 | function! g:DovishRename(target, destination) abort 79 | return 'mv ' . a:target . ' ' . a:destination 80 | endfunction 81 | ``` 82 | 83 | [dirvish]: https://github.com/justinmk/vim-dirvish 84 | 85 | ## Credit 86 | 87 | Big shout out to [Melandel](https://github.com/Melandel) for laying the [foundation](https://github.com/Melandel/desktop/blob/c323969e4bd48dda6dbceada3a7afe8bacdda0f5/setup/my_vimrc.vim#L976-L1147) for this plugin! 88 | -------------------------------------------------------------------------------- /ftplugin/dirvish.vim: -------------------------------------------------------------------------------- 1 | " Only do this when not done yet for this buffer 2 | if exists("b:dovish_ftplugin") 3 | finish 4 | endif 5 | let b:dovish_ftplugin = 1 6 | 7 | if !exists('g:DovishCopyFile') 8 | function! g:DovishCopyFile(target, destination) abort 9 | return 'cp ' . shellescape(a:target) . ' ' . shellescape(a:destination) 10 | endfunction 11 | end 12 | 13 | if !exists('g:DovishCopyDirectory') 14 | function! g:DovishCopyDirectory(target, destination) abort 15 | return 'cp -r ' . shellescape(a:target) . ' ' . shellescape(a:destination) 16 | endfunction 17 | end 18 | 19 | if !exists('g:DovishMove') 20 | function! g:DovishMove(target, destination) abort 21 | return 'mv ' . shellescape(a:target) . ' ' . shellescape(a:destination) 22 | endfunction 23 | end 24 | 25 | if !exists('g:DovishDelete') 26 | function! g:DovishDelete(target) abort 27 | return 'trash ' . shellescape(a:target) 28 | endfunction 29 | end 30 | 31 | if !exists('g:DovishRename') 32 | function! g:DovishRename(target, destination) abort 33 | return 'mv ' . shellescape(a:target) . ' ' . shellescape(a:destination) 34 | endfunction 35 | end 36 | 37 | function! s:moveCursorTo(target) 38 | call search('\V'.escape(a:target, '\\').'\$') 39 | endfunction 40 | 41 | " https://stackoverflow.com/a/47051271 42 | function! s:getVisualSelection() 43 | if mode()=="v" 44 | let [line_start, column_start] = getpos("v")[1:2] 45 | let [line_end, column_end] = getpos(".")[1:2] 46 | else 47 | let [line_start, column_start] = getpos("'<")[1:2] 48 | let [line_end, column_end] = getpos("'>")[1:2] 49 | end 50 | if (line2byte(line_start)+column_start) > (line2byte(line_end)+column_end) 51 | let [line_start, column_start, line_end, column_end] = 52 | \ [line_end, column_end, line_start, column_start] 53 | end 54 | let lines = getline(line_start, line_end) 55 | if len(lines) == 0 56 | return '' 57 | endif 58 | let lines[-1] = lines[-1][: column_end - 1] 59 | let lines[0] = lines[0][column_start - 1:] 60 | return lines 61 | endfunction 62 | 63 | function! s:createFile() abort 64 | " Prompt for new filename 65 | let filename = input('File name: ') 66 | if trim(filename) == '' 67 | return 68 | endif 69 | " Append filename to the path of the current buffer 70 | let filepath = expand("%") . filename 71 | 72 | let output = system("touch " . shellescape(filepath)) 73 | if v:shell_error 74 | call s:logError(cmd) 75 | endif 76 | 77 | " Reload the buffer 78 | Dirvish % 79 | call s:moveCursorTo(filename) 80 | endf 81 | 82 | function! s:createDirectory() abort 83 | let dirname = input('Directory name: ') 84 | if trim(dirname) == '' 85 | return 86 | endif 87 | let dirpath = expand("%") . dirname 88 | if isdirectory(dirpath) 89 | redraw 90 | echomsg printf('"%s" already exists.', dirpath) 91 | return 92 | endif 93 | 94 | let output = system("mkdir " . shellescape(dirpath)) 95 | if v:shell_error 96 | call s:logError(output) 97 | endif 98 | 99 | " Reload the buffer 100 | Dirvish % 101 | call s:moveCursorTo(dirname . '/') 102 | endf 103 | 104 | function! s:deleteItemUnderCursor() abort 105 | " Grab the line under the cursor. Each line is a filepath 106 | let target = trim(getline('.')) 107 | " Feed the filepath to a delete command like, rm or trash 108 | let check = confirm("Delete ".target, "&Yes\n&No", 2) 109 | if check != 1 110 | echo 'Cancelled.' 111 | return 112 | endif 113 | let output = system(g:DovishDelete(target)) 114 | if v:shell_error 115 | call s:logError(output) 116 | endif 117 | 118 | " Reload the buffer 119 | Dirvish % 120 | endfunction 121 | 122 | function! s:renameItemUnderCursor() abort 123 | let target = trim(getline('.')) 124 | let filename = fnamemodify(target, ':t') 125 | let newname = input('Rename: ', filename) 126 | if empty(newname) || newname ==# filename 127 | return 128 | endif 129 | let cmd = g:DovishRename(target, expand("%") . newname) 130 | let output = system(cmd) 131 | if v:shell_error 132 | call s:logError(output) 133 | endif 134 | 135 | " Reload the buffer 136 | Dirvish % 137 | endfunction 138 | 139 | function! s:isPreviouslyYankedItemValid() abort 140 | if len(s:yanked) < 1 141 | return 0 142 | endif 143 | 144 | for target in s:yanked 145 | if target == '' 146 | return 0 147 | endif 148 | endfor 149 | 150 | return 1 151 | endfunction 152 | 153 | function! s:promptUserForRenameOrSkip(filename) abort 154 | let renameOrSkip = confirm(a:filename." already exists.", "&Rename\n&Abort", 2) 155 | if renameOrSkip != 1 156 | return '' 157 | endif 158 | return input('Rename to: ', a:filename) 159 | endfunction 160 | 161 | function! s:moveYankedItemToCurrentDirectory() abort 162 | if !s:isPreviouslyYankedItemValid() 163 | echomsg 'Select a path first!' 164 | return 165 | endif 166 | 167 | let cwd = getcwd() 168 | let destinationDir = expand("%") 169 | for i in s:yanked 170 | let item = i 171 | let filename = fnamemodify(item, ':t') 172 | let directoryName = split(fnamemodify(item, ':p:h'), '/')[-1] 173 | 174 | if isdirectory(item) 175 | if (isdirectory(destinationDir . directoryName)) 176 | let directoryName = s:promptUserForRenameOrSkip(directoryName) 177 | redraw 178 | if directoryName == '' 179 | return 180 | endif 181 | endif 182 | let cmd = g:DovishMove(item, destinationDir . directoryName) 183 | else 184 | if (!empty(glob(destinationDir . filename))) 185 | let filename = s:promptUserForRenameOrSkip(filename) 186 | redraw 187 | if filename == '' 188 | return 189 | endif 190 | endif 191 | let cmd = g:DovishMove(item, destinationDir . filename) 192 | endif 193 | 194 | let output = system(cmd) 195 | if v:shell_error 196 | call s:logError(output) 197 | endif 198 | endfor 199 | 200 | " Reload the buffer 201 | Dirvish % 202 | endfunction 203 | 204 | function! s:copyYankedItemToCurrentDirectory() abort 205 | if !s:isPreviouslyYankedItemValid() 206 | echomsg 'Select a path first!' 207 | return 208 | endif 209 | 210 | let cwd = getcwd() 211 | let destinationDir = expand("%") 212 | 213 | for i in s:yanked 214 | let item = i 215 | let filename = fnamemodify(item, ':t') 216 | let directoryName = split(fnamemodify(item, ':p:h'), '/')[-1] 217 | 218 | if isdirectory(item) 219 | if (isdirectory(destinationDir . directoryName)) 220 | let directoryName = s:promptUserForRenameOrSkip(directoryName) 221 | redraw 222 | if directoryName == '' 223 | return 224 | endif 225 | endif 226 | let cmd = g:DovishCopyDirectory(item, destinationDir . directoryName) 227 | else 228 | if (!empty(glob(destinationDir . filename))) 229 | let filename = s:promptUserForRenameOrSkip(filename) 230 | redraw 231 | if filename == '' 232 | return 233 | endif 234 | endif 235 | 236 | let cmd = g:DovishCopyFile(item, destinationDir . filename) 237 | endif 238 | 239 | let output = system(cmd) 240 | if v:shell_error 241 | call s:logError(output) 242 | endif 243 | endfor 244 | 245 | " Reload the buffer 246 | Dirvish % 247 | endfunction 248 | 249 | function! s:copyFilePathUnderCursor() abort 250 | let s:yanked = [trim(getline('.'))] 251 | echo 'Selected '.s:yanked[0] 252 | endfunction 253 | 254 | function! s:copyVisualSelection() abort 255 | let lines = s:getVisualSelection() 256 | let s:yanked = lines 257 | 258 | let msg = 'Selected:' 259 | for file in lines 260 | " Print a nicely formatted message: 261 | " 262 | " @example: 263 | " Selected: 264 | " - file/path 265 | " - another/file/path 266 | let msg = msg."\n- ".file 267 | endfor 268 | echo msg 269 | endfunction 270 | 271 | function! s:logError(error) abort 272 | " clear any current cmdline msg 273 | redraw 274 | echohl WarningMsg | echomsg a:error | echohl None 275 | endfunction 276 | 277 | nnoremap (dovish_create_file) : call createFile() 278 | nnoremap (dovish_create_directory) : call createDirectory() 279 | nnoremap (dovish_rename) : call renameItemUnderCursor() 280 | nnoremap (dovish_delete) : call deleteItemUnderCursor() 281 | nnoremap (dovish_yank) : call copyFilePathUnderCursor() 282 | xnoremap (dovish_yank) : call copyVisualSelection() 283 | nnoremap (dovish_copy) : call copyYankedItemToCurrentDirectory() 284 | nnoremap (dovish_move) : call moveYankedItemToCurrentDirectory() 285 | 286 | if !exists("g:dirvish_dovish_map_keys") 287 | let g:dirvish_dovish_map_keys = 1 288 | endif 289 | 290 | if g:dirvish_dovish_map_keys 291 | if !hasmapto('(dovish_create_file)', 'n') 292 | execute 'nmap a (dovish_create_file)' 293 | endif 294 | if !hasmapto('(dovish_create_directory)', 'n') 295 | execute 'nmap A (dovish_create_directory)' 296 | endif 297 | if !hasmapto('(dovish_delete)', 'n') 298 | execute 'nmap dd (dovish_delete)' 299 | endif 300 | if !hasmapto('(dovish_rename)', 'n') 301 | execute 'nmap r (dovish_rename)' 302 | endif 303 | if !hasmapto('(dovish_yank)', 'n') 304 | execute 'nmap yy (dovish_yank)' 305 | endif 306 | if !hasmapto('(dovish_yank)', 'v') 307 | execute 'xmap yy (dovish_yank)' 308 | endif 309 | if !hasmapto('(dovish_copy)', 'n') 310 | execute 'nmap pp (dovish_copy)' 311 | endif 312 | if !hasmapto('(dovish_move)', 'n') 313 | execute 'nmap PP (dovish_move)' 314 | endif 315 | endif 316 | --------------------------------------------------------------------------------