├── .gitignore ├── README └── cdargs.vim /.gitignore: -------------------------------------------------------------------------------- 1 | # Metadata 2 | .DS_Store 3 | .*.swp 4 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | cdargs.vim - Directory bookmarking based on CDargs 2 | 3 | Reads your cdargs bookmark file ("~/.cdargs") and provides a couple commands: 4 | Cdb - Change directory to bookmark 5 | Eb - Edit file in bookmark directory 6 | Tb - Similar to Eb but edit the file in a new tab 7 | Cdadd - Add the current directory 8 | 9 | CDargs: http://www.skamphausen.de/cgi-bin/ska/CDargs 10 | 11 | To install copy cdargs.vim to ~/.vim/plugins/ 12 | -------------------------------------------------------------------------------- /cdargs.vim: -------------------------------------------------------------------------------- 1 | " cdargs.vim - Directory bookmarking based on CDargs 2 | " Author: Chris Gaffney 3 | " GetLatestVimScripts: 2466 1 :AutoInstall: cdargs.vim 4 | " URL: http://github.com/gaffneyc/vim-cdargs 5 | " VimURL: http://www.vim.org/scripts/script.php?script_id=2466 6 | " CDargs: http://www.skamphausen.de/cgi-bin/ska/CDargs 7 | 8 | " Commands 9 | " Cdb - Change directory to bookmark 10 | " Eb - Edit file in bookmark directory 11 | " Tb - Similar to Eb but edit the file in a new tab 12 | 13 | " History 14 | " 1.0 15 | " Initial Release 16 | " 17 | " 1.1 18 | " Added Tb command 19 | " Refactored command execution 20 | " 21 | " 1.2 22 | " Fix bookmarks that have a space in their path 23 | " Better error handling if a subpath cannot be found 24 | " 25 | " 1.3 26 | " Added Cdadd for adding a bookmark for the current directory 27 | 28 | " Exit quickly when: 29 | " - this plugin was already loaded (or disabled) 30 | " - when 'compatible' is set 31 | if &cp || (exists('g:loaded_cdargs') && g:loaded_cdargs) 32 | finish 33 | endif 34 | let g:loaded_cdargs = 1 35 | 36 | " What file to read 37 | let s:cdargs_file = $HOME . '/.cdargs' 38 | let g:cdargs_current_directory = '' 39 | 40 | " Bookmark caching 41 | let s:cached_bookmarks = {} 42 | let s:cdargs_file_mod_time = 0 43 | 44 | function! s:error(str) 45 | echohl ErrorMsg 46 | echomsg a:str 47 | echohl None 48 | let v:errmsg = a:str 49 | endfunction 50 | 51 | " Might be a good idea to check if .cdargs exists and is readable. 52 | " Raise an error if isn't. An empty bookmarks file should still be an 53 | " empty dictionary. 54 | " 55 | " NOTES: 56 | " filereadable(file) => Is file readable 57 | function! s:bookmarks() 58 | " Check to see if the bookmarks definition file has been updated 59 | if s:cdargs_file_mod_time == getftime(s:cdargs_file) 60 | return s:cached_bookmarks 61 | endif 62 | 63 | let l:bookmarks = {} 64 | 65 | " Parse the bookmark definition file 66 | for line in readfile(s:cdargs_file) 67 | let l:idx = stridx(line, " ") 68 | 69 | let l:bookmark = strpart(line, 0, l:idx) 70 | let l:path = strpart(line, l:idx + 1) 71 | 72 | let l:bookmarks[l:bookmark] = s:trim_trailing_slash(l:path) 73 | endfor 74 | 75 | " Update the cache and it's modification time 76 | let s:cached_bookmarks = l:bookmarks 77 | let s:cdargs_file_mod_time = getftime(s:cdargs_file) 78 | 79 | return l:bookmarks 80 | endfunction 81 | 82 | " Match a bookmark then match any directory under that bookmark 83 | " 84 | " 1. Complete on the bookmark 85 | " 2. Once we have a matched bookmark then matched directories / files 86 | " based on the associated path 87 | " 88 | " With bookmarks: [ "midstate", "phantom", "phoenix" ] 89 | " Bookmark completion: 90 | " "" => [ "midstate/", "phantom/", "phoenix/" ] 91 | " "ph" => [ "phantom/", "phoenix/" ] 92 | " "phoenix" => [ "phoenix/" ] 93 | " Path completion: 94 | " "phoenix/" => [ "phoenix/app/", "phoenix/test/", ... ] 95 | " "phoenix/app" => [ "phoenix/app/" ] 96 | " "phoenix/app/" => [ "phoenix/app/controllers/", "phoenix/app/models/" ] 97 | function! s:bookmark_completion(argument, show_files) 98 | let [ l:bookmark, l:subpath ] = s:parse_bookmark_and_path(a:argument) 99 | 100 | let l:bookmarks = s:bookmarks() 101 | 102 | if has_key(l:bookmarks, l:bookmark) 103 | " Path completion 104 | let l:matched = [] 105 | let l:path = get(l:bookmarks, l:bookmark) 106 | let l:files = split(globpath(l:path, l:subpath . '*'), "\n") 107 | 108 | for file in l:files 109 | " Filter out files if necessary 110 | if !a:show_files && !isdirectory(file) 111 | continue 112 | endif 113 | 114 | " Cut the path down to just that after the bookmark 115 | let trimmed = s:trim_leading_slash(file[strlen(l:path):]) 116 | 117 | " Ignore any files that don't matched the given subpath 118 | if strlen(l:subpath) && match(trimmed, '^' . l:subpath) == -1 119 | continue 120 | end 121 | 122 | " Directories are designated by a trailing slash 123 | if isdirectory(file) 124 | let trimmed = s:ensure_trailing_slash(trimmed) 125 | endif 126 | 127 | call add(l:matched, l:bookmark . '/' . trimmed) 128 | endfor 129 | 130 | return l:matched 131 | else 132 | " Bookmark completion 133 | let l:matched = [] 134 | 135 | for bookmark in keys(l:bookmarks) 136 | if match(bookmark, "^" . a:argument) >= 0 137 | call add(l:matched, bookmark . '/') 138 | endif 139 | endfor 140 | 141 | return matched 142 | endif 143 | endfunction 144 | 145 | function! s:trim_leading_slash(path) 146 | return a:path[0] == '/' ? a:path[1:] : a:path 147 | endfunction 148 | 149 | function! s:trim_trailing_slash(path) 150 | return a:path[-1:] == '/' ? a:path[:-2] : a:path 151 | endfunction 152 | 153 | function! s:ensure_trailing_slash(path) 154 | return a:path[-1:] == '/' ? a:path : a:path . '/' 155 | endfunction 156 | 157 | " Custom list completion that shows only directories 158 | function! s:directory_completion(ArgLead, CmdLine, CursorPos) 159 | return s:bookmark_completion(a:ArgLead, 0) 160 | endfunction 161 | 162 | " Custom list completion that shows files and directories 163 | function! s:file_completion(ArgLead, CmdLine, CursorPos) 164 | return s:bookmark_completion(a:ArgLead, 1) 165 | endfunction 166 | 167 | " Custom completion for the name of the current directory 168 | function! s:cwd_completion(ArgLead, CmdLine, CursorPos) 169 | let l:cwd = s:trim_trailing_slash(getcwd()) 170 | let l:idx = strridx(l:cwd, "/") 171 | 172 | return strpart(l:cwd, l:idx + 1) 173 | endfunction 174 | 175 | " ... 176 | " 177 | " "" => [ "", ""] 178 | " "/" => [ "", "" ] 179 | " "/abc" => [ "", "" ] 180 | " "ph" => [ "ph", "" ] 181 | " "ph/" => [ "ph", "" ] 182 | " "ph/app" => [ "ph", "app" ] 183 | " "ph/app/" => [ "ph", "app/" ] 184 | " "ph/app/models" => [ "ph", "app/models" ] 185 | function! s:parse_bookmark_and_path(raw) 186 | let l:idx = stridx(a:raw, '/') 187 | 188 | " No valid slash so always return [ bookmark, '' ] 189 | if l:idx < 0 190 | return [ a:raw, ''] 191 | elseif l:idx == 0 192 | return [ '', '' ] 193 | endif 194 | 195 | " Valid slash 196 | let l:bookmark = a:raw[:l:idx - 1] 197 | let l:path = a:raw[l:idx + 1:] 198 | 199 | return [ l:bookmark , l:path ] 200 | endfunction 201 | 202 | function! s:path_for(raw) 203 | let [ l:bookmark, l:subpath ] = s:parse_bookmark_and_path(a:raw) 204 | let l:path = get(s:bookmarks(), l:bookmark, '') 205 | 206 | if strlen(l:path) 207 | return s:ensure_trailing_slash(l:path) . l:subpath 208 | else 209 | call s:error('Could not find bookmark: ' . l:bookmark) 210 | return '' 211 | end 212 | endfunction 213 | 214 | " Add current directory to bookmarks 215 | function! s:add(bookmark) 216 | if stridx(a:bookmark, " ") >= 0 217 | call s:error("Bookmark must be a single word") 218 | return '' 219 | end 220 | 221 | let l:path = get(s:bookmarks(), a:bookmark, '') 222 | let l:cwd = getcwd() 223 | 224 | if strlen(l:path) 225 | call s:error("Bookmark already exists: " . a:bookmark) 226 | elseif !strlen(l:cwd) 227 | " Happens when the current directory has been deleted 228 | call s:error("Unable to determine current directory") 229 | else 230 | echo system("cdargs -a ':" . a:bookmark . ":" . l:cwd . "'") 231 | end 232 | endfunction 233 | 234 | " Execute the vim command with the bookmark's path 235 | function! s:execute(command, raw) 236 | " Escape spaces in the path 237 | let l:path = substitute(s:path_for(a:raw), " ", '\\ ', "g") 238 | 239 | if a:command == 'cd' 240 | let g:cdargs_current_directory = split(a:raw, '/')[0] 241 | endif 242 | 243 | if strlen(l:path) 244 | try 245 | execute a:command . ' ' . l:path 246 | catch 247 | call s:error('Unable to find path: ' . l:path) 248 | endtry 249 | end 250 | endfunction 251 | 252 | function! CdargsCurrentDirectory() 253 | return g:cdargs_current_directory 254 | endfunction 255 | 256 | " Change working directory to bookmark or it's subpath 257 | command! -nargs=1 -complete=customlist,s:directory_completion Cdb call s:execute('cd', ) 258 | 259 | " Edit file under a bookmark's path 260 | command! -nargs=1 -complete=customlist,s:file_completion Eb call s:execute('edit', ) 261 | 262 | " Edit file under a bookmark's path in a new tab 263 | command! -nargs=1 -complete=customlist,s:file_completion Tb call s:execute('tabedit', ) 264 | 265 | " Add current directory to bookmarks 266 | command! -nargs=1 -complete=custom,s:cwd_completion Cdadd call s:add() 267 | --------------------------------------------------------------------------------