Mercurial > vim-gutentags
changeset 136:286e5b3095d0
Allow restricting tag generation to files listed by custom commands
This adds a new setting, g:gutentags_file_list_command, which specifies
command(s) to use to list files for which tags should be generated,
instead of recursively examining all files within the project root.
This is useful in projects using source control to restrict tag
generation to only files tracked in the repository.
This setting is conceptually similar to CtrlP's ctrlp_user_command
option.
This implements the feature requested in
https://github.com/ludovicchabant/vim-gutentags/issues/90
author | Stephen Kent <smkent@smkent.net> |
---|---|
date | Fri, 22 Jul 2016 19:25:05 -0700 |
parents | 4c9e2de7d46a |
children | 98cec968205b |
files | autoload/gutentags.vim autoload/gutentags/cscope.vim autoload/gutentags/ctags.vim doc/gutentags.txt plat/unix/update_scopedb.sh plat/unix/update_tags.sh plat/win32/update_scopedb.cmd plat/win32/update_tags.cmd plugin/gutentags.vim |
diffstat | 9 files changed, 156 insertions(+), 12 deletions(-) [+] |
line wrap: on
line diff
--- a/autoload/gutentags.vim Fri Jul 22 19:25:01 2016 -0700 +++ b/autoload/gutentags.vim Fri Jul 22 19:25:05 2016 -0700 @@ -78,6 +78,29 @@ let s:known_projects[a:path] = l:result endfunction +function! gutentags#validate_cmd(cmd) abort + if !empty(a:cmd) && executable(split(a:cmd)[0]) + return a:cmd + endif + return "" +endfunction + +function! gutentags#get_project_file_list_cmd(path) abort + if type(g:gutentags_file_list_command) == type("") + return gutentags#validate_cmd(g:gutentags_file_list_command) + elseif type(g:gutentags_file_list_command) == type({}) + let l:markers = get(g:gutentags_file_list_command, 'markers', []) + if type(l:markers) == type({}) + for [marker, file_list_cmd] in items(l:markers) + if getftype(a:path . '/' . marker) != "" + return gutentags#validate_cmd(file_list_cmd) + endif + endfor + endif + endif + return "" +endfunction + " Finds the first directory with a project marker by walking up from the given " file path. function! gutentags#get_project_root(path) abort
--- a/autoload/gutentags/cscope.vim Fri Jul 22 19:25:01 2016 -0700 +++ b/autoload/gutentags/cscope.vim Fri Jul 22 19:25:05 2016 -0700 @@ -55,6 +55,11 @@ let l:cmd .= ' -e ' . g:gutentags_cscope_executable let l:cmd .= ' -p ' . a:proj_dir let l:cmd .= ' -f ' . a:tags_file + let l:file_list_cmd = + \ gutentags#get_project_file_list_cmd(l:proj_dir) + if !empty(l:file_list_cmd) + let l:cmd .= ' -L "' . l:file_list_cmd . '"' + endif let l:cmd .= ' ' let l:cmd .= gutentags#get_execute_cmd_suffix()
--- a/autoload/gutentags/ctags.vim Fri Jul 22 19:25:01 2016 -0700 +++ b/autoload/gutentags/ctags.vim Fri Jul 22 19:25:05 2016 -0700 @@ -79,10 +79,18 @@ let l:cur_file_path = fnamemodify(l:cur_file_path, ':.') endif let l:cmd .= ' -s "' . l:cur_file_path . '"' + else + let l:file_list_cmd = gutentags#get_project_file_list_cmd(l:actual_proj_dir) + if !empty(l:file_list_cmd) + let l:cmd .= ' -L ' . '"' . l:file_list_cmd. '"' + endif endif - " Pass the Gutentags recursive options file before the project - " options file, so that users can override --recursive. - let l:cmd .= ' -o "' . gutentags#get_res_file('ctags_recursive.options') . '"' + if empty(get(l:, 'file_list_cmd', '')) + " Pass the Gutentags recursive options file before the project + " options file, so that users can override --recursive. + " Omit --recursive if this project uses a file list command. + let l:cmd .= ' -o "' . gutentags#get_res_file('ctags_recursive.options') . '"' + endif let l:proj_options_file = a:proj_dir . '/' . \g:gutentags_ctags_options_file if filereadable(l:proj_options_file)
--- a/doc/gutentags.txt Fri Jul 22 19:25:01 2016 -0700 +++ b/doc/gutentags.txt Fri Jul 22 19:25:05 2016 -0700 @@ -417,6 +417,40 @@ See |gutentags_ctags_executable_{filetype}| for more information. + *gutentags_file_list_command* +g:gutentags_file_list_command + Specifies command(s) to use to list files for which + tags should be generated, instead of recursively + examining all files within the project root. When + invoked, file list commands will execute in the + project root directory. + + This setting is useful in projects using source + control to restrict tag generation to only files + tracked in the repository. + + This variable may be set in one of two ways. If + set as a |String|, the specified command will be used to + list files for all projects. For example: > + + let g:gutentags_file_list_command = 'find . -type f' +< + If set as a |Dictionary|, this variable should be set + as a mapping of project root markers to the desired + file list command for that root marker. (See + |gutentags_project_root| for how Gutentags uses root + markerts to locate the project.) For example: > + + let g:gutentags_file_list_command = { + \ 'markers': { + \ '.git': 'git ls-files', + \ '.hg': 'hg locate', + \ }, + \ } +< + Note: If a custom ctags executable is specified, it + must support the '-L' command line option in order to + read the list of files to be examined. ============================================================================= 5. Project Settings *gutentags-project-settings*
--- a/plat/unix/update_scopedb.sh Fri Jul 22 19:25:01 2016 -0700 +++ b/plat/unix/update_scopedb.sh Fri Jul 22 19:25:05 2016 -0700 @@ -4,8 +4,10 @@ PROG_NAME=$0 CSCOPE_EXE=cscope +CSCOPE_ARGS= DB_FILE=cscope.out PROJECT_ROOT= +FILE_LIST_CMD= ShowUsage() { echo "Usage:" @@ -14,11 +16,12 @@ echo " -e [exe=cscope]: The cscope executable to run" echo " -f [file=cscope.out]: The path to the ctags file to update" echo " -p [dir=]: The path to the project root" + echo " -L [cmd=]: The file list command to run" echo "" } -while getopts "h?e:f:p:" opt; do +while getopts "h?e:f:p:L:" opt; do case $opt in h|\?) ShowUsage @@ -33,6 +36,9 @@ p) PROJECT_ROOT=$OPTARG ;; + L) + FILE_LIST_CMD=$OPTARG + ;; esac done @@ -47,16 +53,30 @@ echo $$ > "$DB_FILE.lock" # Remove lock and temp file if script is stopped unexpectedly. -trap 'rm -f "$DB_FILE.lock" "$DB_FILE.temp"' INT QUIT TERM EXIT +trap 'rm -f "$DB_FILE.lock" "$DB_FILE.files" "$DB_FILE.temp"' INT QUIT TERM EXIT PREVIOUS_DIR=$(pwd) if [ -d "$PROJECT_ROOT" ]; then cd "$PROJECT_ROOT" fi +if [ -n "${FILE_LIST_CMD}" ]; then + if [ "${PROJECT_ROOT}" = "." ]; then + $FILE_LIST_CMD > "${DB_FILE}.files" + else + # If using a tags cache directory, use absolute paths + $FILE_LIST_CMD | while read -r l; do + echo "${PROJECT_ROOT%/}/${l}" + done > "${DB_FILE}.files" + fi + CSCOPE_ARGS="${CSCOPE_ARGS} -i ${DB_FILE}.files" +else + CSCOPE_ARGS="${CSCOPE_ARGS} -R" +fi + echo "Running cscope" -echo "$CSCOPE_EXE -R -b -k -f \"$DB_FILE.temp\"" -"$CSCOPE_EXE" -R -v -b -k -f "$DB_FILE.temp" +echo "$CSCOPE_EXE $CSCOPE_ARGS -b -k -f \"$DB_FILE.temp\"" +"$CSCOPE_EXE" $CSCOPE_ARGS -v -b -k -f "$DB_FILE.temp" if [ -d "$PROJECT_ROOT" ]; then cd "$PREVIOUS_DIR"
--- a/plat/unix/update_tags.sh Fri Jul 22 19:25:01 2016 -0700 +++ b/plat/unix/update_tags.sh Fri Jul 22 19:25:05 2016 -0700 @@ -7,6 +7,7 @@ CTAGS_ARGS= TAGS_FILE=tags PROJECT_ROOT= +FILE_LIST_CMD= UPDATED_SOURCE= PAUSE_BEFORE_EXIT=0 @@ -18,6 +19,7 @@ echo " -e [exe=ctags]: The ctags executable to run" echo " -t [file=tags]: The path to the ctags file to update" echo " -p [dir=]: The path to the project root" + echo " -L [cmd=]: The file list command to run" echo " -s [file=]: The path to the source file that needs updating" echo " -x [pattern=]: A pattern of files to exclude" echo " -o [options=]: An options file to read additional options from" @@ -26,7 +28,7 @@ } -while getopts "h?e:x:t:p:s:o:c" opt; do +while getopts "h?e:x:t:p:L:s:o:c" opt; do case $opt in h|\?) ShowUsage @@ -44,6 +46,9 @@ p) PROJECT_ROOT=$OPTARG ;; + L) + FILE_LIST_CMD=$OPTARG + ;; s) UPDATED_SOURCE=$OPTARG ;; @@ -67,7 +72,7 @@ echo $$ > "$TAGS_FILE.lock" # Remove lock and temp file if script is stopped unexpectedly. -trap 'errorcode=$?; rm -f "$TAGS_FILE.lock" "$TAGS_FILE.temp"; exit $errorcode' INT QUIT TERM EXIT +trap 'errorcode=$?; rm -f "$TAGS_FILE.lock" "$TAGS_FILE.files" "$TAGS_FILE.temp"; exit $errorcode' INT QUIT TERM EXIT INDEX_WHOLE_PROJECT=1 if [ -f "$TAGS_FILE" ]; then @@ -82,6 +87,17 @@ fi if [ $INDEX_WHOLE_PROJECT -eq 1 ]; then + if [ -n "${FILE_LIST_CMD}" ]; then + if [ "${PROJECT_ROOT}" = "." ]; then + $FILE_LIST_CMD > "${TAGS_FILE}.files" + else + # If using a tags cache directory, use absolute paths + $FILE_LIST_CMD | while read -r l; do + echo "${PROJECT_ROOT%/}/${l}" + done > "${TAGS_FILE}.files" + fi + CTAGS_ARGS="${CTAGS_ARGS} -L ${TAGS_FILE}.files" + fi echo "Running ctags on whole project" echo "$CTAGS_EXE -f \"$TAGS_FILE.temp\" $CTAGS_ARGS \"$PROJECT_ROOT\"" $CTAGS_EXE -f "$TAGS_FILE.temp" $CTAGS_ARGS "$PROJECT_ROOT"
--- a/plat/win32/update_scopedb.cmd Fri Jul 22 19:25:01 2016 -0700 +++ b/plat/win32/update_scopedb.cmd Fri Jul 22 19:25:05 2016 -0700 @@ -6,7 +6,9 @@ rem ========================================== set CSCOPE_EXE=cscope +set CSCOPE_ARGS= set DB_FILE=cscope.out +set FILE_LIST_CMD= :ParseArgs if [%1]==[] goto :DoneParseArgs @@ -25,6 +27,11 @@ shift goto :LoopParseArgs ) +if [%1]==[-L] ( + set FILE_LIST_CMD=%~2 + shift + goto :LoopParseArgs +) echo Invalid Argument: %1 goto :Usage @@ -43,13 +50,25 @@ echo locked > "%DB_FILE%.lock" echo Running cscope -"%CSCOPE_EXE%" -R -b -k -f "%DB_FILE%" +if NOT ["%FILE_LIST_CMD%"]==[""] ( + if ["%PROJECT_ROOT%"]==["."] ( + call %FILE_LIST_CMD% > %DB_FILE%.files + ) else ( + rem Potentially useful: + rem http://stackoverflow.com/questions/9749071/cmd-iterate-stdin-piped-from-another-command + %FILE_LIST_CMD% | for /F "usebackq delims=" %%F in (`findstr "."`) do @echo %PROJECT_ROOT%\%%F > %DB_FILE%.files + ) + set CSCOPE_ARGS=%CSCOPE_ARGS% -i %TAGS_FILE%.files +) ELSE ( + set CSCOPE_ARGS=%CSCOPE_ARGS% -R +) +"%CSCOPE_EXE%" %CSCOPE_ARGS% -b -k -f "%DB_FILE%" if ERRORLEVEL 1 ( echo ERROR: Cscope executable returned non-zero code. ) echo Unlocking db file -del /F "%DB_FILE%.lock" +del /F "%DB_FILE%.files" "%DB_FILE%.lock" if ERRORLEVEL 1 ( echo ERROR: Unable to remove file lock. ) @@ -70,5 +89,6 @@ echo -e [exe=cscope]: The cscope executable to run echo -f [file=scope.out]: The path to the database file to create echo -p [dir=]: The path to the project root +echo -L [cmd=]: The file list command to run echo.
--- a/plat/win32/update_tags.cmd Fri Jul 22 19:25:01 2016 -0700 +++ b/plat/win32/update_tags.cmd Fri Jul 22 19:25:05 2016 -0700 @@ -9,6 +9,7 @@ set CTAGS_ARGS= set TAGS_FILE=tags set PROJECT_ROOT= +set FILE_LIST_CMD= set UPDATED_SOURCE= set PAUSE_BEFORE_EXIT=0 set LOG_FILE= @@ -35,6 +36,11 @@ shift goto :LoopParseArgs ) +if [%1]==[-L] ( + set FILE_LIST_CMD=%~2 + shift + goto :LoopParseArgs +) if [%1]==[-s] ( set UPDATED_SOURCE=%~2 shift @@ -85,6 +91,16 @@ ) if ["%INDEX_WHOLE_PROJECT%"]==["1"] ( set CTAGS_ARGS=%CTAGS_ARGS% "%PROJECT_ROOT%" + if NOT ["%FILE_LIST_CMD%"]==[""] ( + if ["%PROJECT_ROOT%"]==["."] ( + call %FILE_LIST_CMD% > %TAGS_FILE%.files + ) else ( + rem Potentially useful: + rem http://stackoverflow.com/questions/9749071/cmd-iterate-stdin-piped-from-another-command + %FILE_LIST_CMD% | for /F "usebackq delims=" %%F in (`findstr "."`) do @echo %PROJECT_ROOT%\%%F > %TAGS_FILE%.files + ) + set CTAGS_ARGS=%CTAGS_ARGS% -L %TAGS_FILE%.files + ) ) echo Running ctags >> %LOG_FILE% @@ -105,7 +121,7 @@ :Unlock echo Unlocking tags file... >> %LOG_FILE% -del /F "%TAGS_FILE%.lock" +del /F "%TAGS_FILE.files" "%TAGS_FILE%.lock" if ERRORLEVEL 1 ( echo ERROR: Unable to remove file lock. >> %LOG_FILE% ) @@ -129,6 +145,7 @@ echo -e [exe=ctags]: The ctags executable to run echo -t [file=tags]: The path to the ctags file to update echo -p [dir=]: The path to the project root +echo -L [cmd=]: The file list command to run echo -s [file=]: The path to the source file that needs updating echo -l [log=]: The log file to output to echo -o [options=]: An options file to read additional options from
--- a/plugin/gutentags.vim Fri Jul 22 19:25:01 2016 -0700 +++ b/plugin/gutentags.vim Fri Jul 22 19:25:05 2016 -0700 @@ -44,6 +44,7 @@ let g:gutentags_generate_on_new = get(g:, 'gutentags_generate_on_new', 1) let g:gutentags_generate_on_missing = get(g:, 'gutentags_generate_on_missing', 1) let g:gutentags_generate_on_write = get(g:, 'gutentags_generate_on_write', 1) +let g:gutentags_file_list_command = get(g:, 'gutentags_file_list_command', '') if !exists('g:gutentags_cache_dir') let g:gutentags_cache_dir = ''