" -*- vim -*-
" FILE: "/home/wlee/vim/vimfiles/plugin/DirDo.vim" {{{
" LAST MODIFICATION: "Thu, 12 Jun 2003 16:05:11 -0700 (wlee)"
" VERSION: 1.2
" (C) 2002-2003 by William Lee, <wlee@sendmail.com>
" }}}
"
" PURPOSE: {{{
" - Performs Vim commands over files recursively under multiple
" directories.
"
" - This plugin is like :argdo but it works recursively under a directory (or
" multiple directories). The limitation for :argdo is that it does not glob
" the files inside the subdirectories. DirDo works in a similar manner but
" you're allowed to specify multiple directories (good for refactoring code
" and modifying files in a deep hierarchy). DirDo also globs the files in
" the subdirectories so you can make sure an operation is performed
" consistantly.
"
" REQUIREMENTS:
" - The :argdo command in Vim 6.0
"
" USAGE:
" Put this file in your ~/.vim/plugin directory.
"
" The syntax of the commands:
"
" First we need to set what directory we would like to perform this
" command on
"
" :DirDoDir [/my/directory1] [/my/directory2] [/my/directory3]
"
" or
"
" :DDD [/my/directory1] [/my/directory2] [/my/directory3]
"
" If no argument is given, then it'll display the directories that you're
" going to work on and let you edit them by separating them with commas
" (',')
"
" You can also use the following command to add a directory to the DirDoDir
" variable:
"
" :DirDoAdd [/my/dir]
"
" or
"
" :DDA [/my/dir]
"
" If you do not give an argument to DDA, it'll add the current working
" directory to the DirDoDir variable.
"
" Then we set the file glob pattern
"
" :DirDoPattern [file glob pattern1] [file glob pattern2] ...
"
" or
"
" :DDP [file glob pattern1] [file glob pattern2] ...
"
" If no argument is given, then it'll display the patterns and let you
" edit them (separate each pattern with spaces).
"
" Then, you can do:
"
" :DirDo [commands]
"
" or
"
" :DDO [commands]
"
" to executes the commands on each file that matches the glob pattern
" recursively under the directories that you have specified. The format of
" [commands] is just like what you do on :argdo. See :help argdo for
" details.
"
" If no argument is given, then it'll reuse the last command.
"
" Examples:
"
" Replace all the instaces of "Foo" to "Bar" in all the Java or C files under
" the directory /my/directory and its subdirectories, confirming each match:
"
" :DDD /my/directory (or just :DDD<CR> and type it in)
" :DDP *.java *.c (or just :DDP<CR> and type it in)
" :DDO %s/Foo/Bar/gce | update
"
" (See :h argdo for the commands after DDO.)
"
" Same scenario but replacing "Bar" with "Baz" without confirmation for each
" match (note the directory and patterns are saved):
"
" :DDO %s/Bar/Baz/ge | update
"
" (Since 1.2) There is a command called DirDoFast where you can set the
" directory, pattern, and command at once. Note that your arguments need to
" be escaped if there is an space to it, otherwise Vim will break up the
" arguments incorrectly. The syntax is:
"
" :DirDoFast [directories] [patterns] command
"
" or
"
" :DDF [directories] [patterns] command
"
" Multiple directories need to be separated by comma. Patterns need to be
" separated by spaces. Since space is used by Vim to separate the command
" arguments, ALL spaces in the directory, pattern, or command need to be
" escaped. For example, the last command using DDF would look like:
"
" :DDF /my/directory *.java\ *.c %s/Foo/Bar/gce\ |\ update
"
" If you only give 2 arguments to :DDF, you assume the directory is set to
" ".".
"
" If you only give 1 argument to :DDF, you assume the directory is set to
" "." and the pattern is set to "*".
"
" There is an option to run DirDo with less verbosity, to toggle the
" setting, run:
"
" :DirDoVerbose
"
" or
"
" :DDV
"
" You can also set the following variables in your .vimrc to set the default
" directory and pattern. This is good for pointing to the directories for
" code refactoring:
"
" let g:DirDoPattern = "..."
"
" let g:DirDoDir = "..."
"
" For example, if you want by default have the command apply on all your C,
" C++, and Java source, you can set the DirDoPattern to:
"
" let g:DirDoPattern = "*.c *.cpp *.java"
"
" If you want to apply your changes to /dir1, /dir2, and /dir3, you can do:
"
" let g:DirDoDir = "/dir1,/dir2,/dir3"
"
" CREDITS:
"
" Please mail any comment/suggestion/patch to
"
" William Lee <wl1012@yahoo.com>
"
" (c) 2002-2003. This script is under the same copyright as Vim itself.
"
" THANKS:
"
" Lijian Liu for reporting numerous bugs.
"
" HISTORY:
" 1.0 - 8/7/2002 Initial release
" 1.1 - 8/19/2002 Added DirDoAdd command to add directory
" 1.2 - 6/12/2003 Added the DirDoFast command so you can set directory,
" pattern, and command at once. Fixed bug where the unix hidden file will
" not show up in the file list. Fixed bug where ~ in the path does not work.
"
" Mappings
command! -nargs=* DDO call <SID>DirDo(<f-args>)
command! -nargs=* DirDo call <SID>DirDo(<f-args>)
command! -nargs=* DDF call <SID>DirDoFast(<f-args>)
command! -nargs=* DirDoFast call <SID>DirDoFast(<f-args>)
command! -nargs=0 DDV call <SID>DirDoVerbose()
command! -nargs=0 DirDoVerbose call <SID>DirDoVerbose()
command! -nargs=* -complete=dir DDD call <SID>DirDoDir(<f-args>)
command! -nargs=* -complete=dir DirDoDir call <SID>DirDoDir(<f-args>)
command! -nargs=* -complete=dir DDA call <SID>DirDoAdd(<f-args>)
command! -nargs=* -complete=dir DirDoAdd call <SID>DirDoAdd(<f-args>)
command! -nargs=* DDP call <SID>DirDoPattern(<f-args>)
command! -nargs=* DirDoPattern call <SID>DirDoPattern()
" Sort the import with preferences to the java.* classes
"
if !exists("g:DirDoPattern")
let g:DirDoPattern = ""
endif
if !exists("g:DirDoDir")
let g:DirDoDir = ""
endif
if !exists("g:DirDoVerbose")
let s:Verbose = 1
else
let s:Verbose = g:DirDoVerbose
endif
let s:LastCommand = ""
" Ask to enter the file
let s:AskFile = 1
" Ask to cancel the operation
let s:CancelFile = 0
" Sets the directory
fun! <SID>DirDoDir(...)
" Constructs the arguments as a comma separated list
if (a:0 != 0)
let ctr = 1
let g:DirDoDir = ""
while (ctr <= a:0)
if (g:DirDoDir != "")
let g:DirDoDir = g:DirDoDir . ','
endif
let g:DirDoDir = g:DirDoDir . a:{ctr}
let ctr = ctr + 1
endwhile
else
" Edit the directories
let g:DirDoDir = input ("Set DirDo directories (use ',' to separate multiple entries): " , g:DirDoDir)
endif
endfun
" Adds to the DirDo directory
fun! <SID>DirDoAdd(...)
" Constructs the arguments as a comma separated list
if (a:0 != 0)
let ctr = 1
let add_dir = ""
while (ctr <= a:0)
if (add_dir != "")
let add_dir = add_dir . ','
endif
let add_dir = add_dir . a:{ctr}
let ctr = ctr + 1
endwhile
if (g:DirDoDir == "")
let g:DirDoDir = add_dir
else
let g:DirDoDir = g:DirDoDir . ',' . add_dir
endif
else
" Add the current directory of the current file to the DirDo Path
let c_dir = getcwd()
echo ("Adding to DirDoDir: " . c_dir)
if (g:DirDoDir == "")
let g:DirDoDir = c_dir
else
let g:DirDoDir = g:DirDoDir . ',' . c_dir
endif
endif
endfun
" Sets the pattern
fun! <SID>DirDoPattern(...)
" Constructs the arguments as a comma separated list
if (a:0 != 0)
let ctr = 1
let g:DirDoPattern = ""
while (ctr <= a:0)
if (g:DirDoPattern != "")
let g:DirDoPattern = g:DirDoPattern . ' '
endif
let g:DirDoPattern = g:DirDoPattern . a:{ctr}
let ctr = ctr + 1
endwhile
else
" Edits the pattern
let g:DirDoPattern = input ("Set DirDo glob patterns (use ' ' to separate multiple entries): " , g:DirDoPattern)
endif
endfun
" Sets the directoris that we would like to work on
fun! <SID>DirDoVerbose()
if (s:Verbose == 1)
let s:Verbose = 0
echo "Setting DirDo verbosity to off."
else
let s:Verbose = 1
echo "Setting DirDo verbosity to on."
endif
endfun
" Trim the string with white spaces in front or at the end
fun! <SID>TrimStr(str)
let rtn = substitute(a:str, '\(^\s*\|\s$\)', '', 'g')
return rtn
endfun
" A one line command that does it all
fun! <SID>DirDoFast(...)
if (a:0 == 3)
let g:DirDoDir = a:1
let g:DirDoPattern = a:2
let cmd = a:3
elseif (a:0 == 2)
let g:DirDoDir = "."
let g:DirDoPattern = a:1
let cmd = a:2
elseif (a:0 == 1)
let g:DirDoDir = "."
let g:DirDoPattern = "*"
let cmd = a:1
else
echo "Error: Missing argument for: DirDoFast \"[dir]\" \"[patterns]\" \"[cmd]\" "
return
endif
if (cmd == "")
echo "You have to specify a command for DirDo!"
return 1
endif
echo "Directories: " . g:DirDoDir
echo "Glob Pattern: " . g:DirDoPattern
echo "Command: " . cmd
let in = confirm("Run DirDo?", "&Yes\n&No", 1)
if (in != 1)
return 0
endif
let s:MatchRegexPattern = <SID>GetRegExPattern()
echo "MatchRegexPattern is " . s:MatchRegexPattern
let currPaths = <SID>TrimStr(g:DirDoDir)
" See if currPaths has a ',' at the end, if not, we add it.
"echo "currPaths begin is " . currPaths
if (match(currPaths, ',$') == -1)
let currPaths = currPaths . ','
endif
let fileCount = 0
while (currPaths != "")
let sepIdx = stridx(currPaths, ",")
" Gets the substring exluding the newline
let currPath = strpart(currPaths, 0, sepIdx)
let currPath = <SID>TrimStr(currPath)
let currPath = fnamemodify(expand(currPath, ":p"), ":p")
if (s:Verbose == 1)
echo "Applying command recursively in root path: " . currPath . " ..."
endif
let currPaths = strpart(currPaths, sepIdx + 1, strlen(currPaths) - sepIdx - 1)
let fileCount = fileCount + <SID>DirDoHlp(currPath, cmd)
endwhile
" Reset the argument list
argl
echo "Done. Applied command on " . fileCount . " files."
endfun
" Recursively apply the commands to the list of directories in DirDoDir
fun! <SID>DirDo(...)
" Update the AskFile to true
let s:AskFile = 1
let s:CancelFile = 0
" Save current document
update
" Constructs the arguments
let ctr = 1
let cmd = ""
if (a:0 != 0)
while (ctr <= a:0)
if (cmd != "")
let cmd = cmd . ' '
endif
let cmd = cmd . a:{ctr}
let ctr = ctr + 1
endwhile
let s:LastCommand = cmd
else
let cmd = s:LastCommand
endif
call <SID>DirDoFast(g:DirDoDir, g:DirDoPattern, cmd)
endfun
" Turns the global glob pattens into a regex pattern
fun! <SID>GetRegExPattern()
" Trim the string
let matchPat = <SID>TrimStr(g:DirDoPattern)
if (matchPat == "")
" Default to all file
matchPat = '*'
endif
let matchPat = matchPat . ' '
" We would like to change the * to .* and escape the . in the glob pattern
" this will work on most cases...
let patStr = substitute(matchPat, '\.', '\\.', 'g')
let patStr = substitute(patStr, '\*', '.*', 'g')
let patStr = substitute(patStr, '\s', '$\\|', 'g')
" get rid of the extra \\| at the end
let patStr = substitute(patStr, '\\|$', '', '')
return ('\(' . patStr . '\)')
endfun
" The helper function to apply the command to a directory
fun! <SID>DirDoHlp(cpath, cmd)
"echo "Arguments " . a:cpath . " cmd is " . a:cmd
"we would like to ignore path that ends with [\/].. and [\/].
if (match(a:cpath, '\([\\/]\.$\|[\\/]\.\.$\|^\.\.$\|^\.$\)') > -1)
return 0
endif
if (!isdirectory(a:cpath) && match(a:cpath, s:MatchRegexPattern) > -1)
let i = 1
if (s:CancelFile == 1)
return 0
endif
if (s:Verbose == 1 && s:AskFile == 1)
let i = confirm("Apply command to file " . a:cpath . "?", "&Yes\nYes for &All Files\n&No\n&Cancel", 1)
endif
if (i == 4)
let s:CancelFile = 1
return 0
endif
if (i == 3)
return 0
endif
if (i == 2)
let s:AskFile = 0
endif
exe "argl " . a:cpath
exe "argdo " . a:cmd
return 1
else
if (isdirectory(a:cpath))
let files = glob(a:cpath . "/*")
let files = files . "\n"
" we would like to get the hidden files also
let hfiles = glob(a:cpath . "/.*")
let hfiles = hfiles . "\n"
let files = files . hfiles
let fileCtr = 0
"while (files != "" && files !~ '\n$')
while (files != "" && files != '\n$')
let sepIdx = stridx(files, "\n")
" Gets the substring exluding the newline
let file = strpart(files, 0, sepIdx)
let files = strpart(files, sepIdx + 1, strlen(files) - sepIdx - 1)
let fileCtr = fileCtr + <SID>DirDoHlp(file, a:cmd)
endwhile
return fileCtr
endif
endif
endfun