changeset 153:738ac7a89547

Merge branch 'master' into my-develop
author Mark Wu <wuhsinyee@hotmail.com>
date Fri, 16 Sep 2016 23:43:57 +0800
parents a876a0df4008 (current diff) 851aec42057d (diff)
children e5f2be2fabd0
files res/ctags.options
diffstat 11 files changed, 289 insertions(+), 125 deletions(-) [+]
line wrap: on
line diff
--- a/autoload/gutentags.vim	Mon May 16 02:09:40 2016 +0800
+++ b/autoload/gutentags.vim	Fri Sep 16 23:43:57 2016 +0800
@@ -78,14 +78,45 @@
     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
+    if g:gutentags_project_root_finder
+        return call(g:gutentags_project_root_finder, [a:path])
+    endif
+
     let l:path = gutentags#stripslash(a:path)
     let l:previous_path = ""
     let l:markers = g:gutentags_project_root[:]
     if exists('g:ctrlp_root_markers')
-        let l:markers += g:ctrlp_root_markers
+        for crm in g:ctrlp_root_markers
+            if index(l:markers, crm) < 0
+                call add(l:markers, crm)
+            endif
+        endfor
     endif
     while l:path != l:previous_path
         for root in l:markers
@@ -100,6 +131,15 @@
                                 \1)
                     call gutentags#throw("Marker found at root, aborting.")
                 endif
+                for ign in g:gutentags_exclude_project_root
+                    if l:proj_dir == ign
+                        call gutentags#trace(
+                                    \"Ignoring project root '" . l:proj_dir .
+                                    \"' because it is in the list of ignored" .
+                                    \" projects.")
+                        call gutentags#throw("Ignore project: " . l:proj_dir)
+                    endif
+                endfor
                 return l:proj_dir
             endif
         endfor
@@ -119,7 +159,7 @@
     let l:tag_path = gutentags#stripslash(a:root_dir) . '/' . a:filename
     if g:gutentags_cache_dir != ""
         " Put the tag file in the cache dir instead of inside the
-        " projet root.
+        " project root.
         let l:tag_path = g:gutentags_cache_dir . '/' .
                     \tr(l:tag_path, '\/: ', '---_')
         let l:tag_path = substitute(l:tag_path, '/\-', '/', '')
@@ -157,9 +197,11 @@
         if g:gutentags_resolve_symlinks
             let l:buf_dir = fnamemodify(resolve(expand('%:p', 1)), ':p:h')
         endif
-        let b:gutentags_root = gutentags#get_project_root(l:buf_dir)
+        if !exists('b:gutentags_root')
+            let b:gutentags_root = gutentags#get_project_root(l:buf_dir)
+        endif
         if filereadable(b:gutentags_root . '/.notags')
-            call gutentags#trace("'notags' file found... no gutentags support.")
+            call gutentags#trace("'.notags' file found... no gutentags support.")
             return
         endif
 
@@ -180,18 +222,22 @@
             call call("gutentags#".module."#init", [b:gutentags_root])
         endfor
     catch /^gutentags\:/
-        call gutentags#trace("Can't figure out what tag file to use... no gutentags support.")
+        call gutentags#trace("No gutentags support for this buffer.")
         return
     endtry
 
     " We know what tags file to manage! Now set things up.
-    call gutentags#trace("Setting gutentags for buffer '" . bufname('%'))
+    call gutentags#trace("Setting gutentags for buffer '".bufname('%')."'")
 
     " Autocommands for updating the tags on save.
+    " We need to pass the buffer number to the callback function in the rare
+    " case that the current buffer is changed by another `BufWritePost`
+    " callback. This will let us get that buffer's variables without causing
+    " errors.
     let l:bn = bufnr('%')
     execute 'augroup gutentags_buffer_' . l:bn
     execute '  autocmd!'
-    execute '  autocmd BufWritePost <buffer=' . l:bn . '> call s:write_triggered_update_tags()'
+    execute '  autocmd BufWritePost <buffer=' . l:bn . '> call s:write_triggered_update_tags(' . l:bn . ')'
     execute 'augroup end'
 
     " Miscellaneous commands.
@@ -208,10 +254,10 @@
             if g:gutentags_enabled
                 if g:gutentags_generate_on_missing && !filereadable(l:tagfile)
                     call gutentags#trace("Generating missing tags file: " . l:tagfile)
-                    call s:update_tags(module, 1, 1)
+                    call s:update_tags(l:bn, module, 1, 1)
                 elseif g:gutentags_generate_on_new
                     call gutentags#trace("Generating tags file: " . l:tagfile)
-                    call s:update_tags(module, 1, 1)
+                    call s:update_tags(l:bn, module, 1, 1)
                 endif
             endif
         endif
@@ -259,17 +305,18 @@
 
 " (Re)Generate the tags file for the current buffer's file.
 function! s:manual_update_tags(bang) abort
+    let l:bn = bufnr('%')
     for module in g:gutentags_modules
-        call s:update_tags(module, a:bang, 0)
+        call s:update_tags(l:bn, module, a:bang, 0)
     endfor
     silent doautocmd User GutentagsUpdated
 endfunction
 
 " (Re)Generate the tags file for a buffer that just go saved.
-function! s:write_triggered_update_tags() abort
+function! s:write_triggered_update_tags(bufno) abort
     if g:gutentags_enabled && g:gutentags_generate_on_write
         for module in g:gutentags_modules
-            call s:update_tags(module, 0, 2)
+            call s:update_tags(a:bufno, module, 0, 2)
         endfor
     endif
     silent doautocmd User GutentagsUpdated
@@ -284,10 +331,11 @@
 "   0: if an update is already in progress, report it and abort.
 "   1: if an update is already in progress, abort silently.
 "   2: if an update is already in progress, queue another one.
-function! s:update_tags(module, write_mode, queue_mode) abort
+function! s:update_tags(bufno, module, write_mode, queue_mode) abort
     " Figure out where to save.
-    let l:tags_file = b:gutentags_files[a:module]
-    let l:proj_dir = b:gutentags_root
+    let l:buf_gutentags_files = getbufvar(a:bufno, 'gutentags_files')
+    let l:tags_file = l:buf_gutentags_files[a:module]
+    let l:proj_dir = getbufvar(a:bufno, 'gutentags_root')
 
     " Check that there's not already an update in progress.
     let l:lock_file = l:tags_file . '.lock'
--- a/autoload/gutentags/cscope.vim	Mon May 16 02:09:40 2016 +0800
+++ b/autoload/gutentags/cscope.vim	Fri Sep 16 23:43:57 2016 +0800
@@ -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	Mon May 16 02:09:40 2016 +0800
+++ b/autoload/gutentags/ctags.vim	Fri Sep 16 23:43:57 2016 +0800
@@ -2,25 +2,11 @@
 
 " Global Options {{{
 
-if !exists('g:gutentags_ctags_executable')
-    let g:gutentags_ctags_executable = 'ctags'
-endif
-
-if !exists('g:gutentags_tagfile')
-    let g:gutentags_tagfile = 'tags'
-endif
-
-if !exists('g:gutentags_auto_set_tags')
-    let g:gutentags_auto_set_tags = 1
-endif
-
-if !exists('g:gutentags_ctags_options_file')
-    let g:gutentags_ctags_options_file = '.gutctags'
-endif
-
-if !exists('g:gutentags_ctags_check_tagfile')
-    let g:gutentags_ctags_check_tagfile = 0
-endif
+let g:gutentags_ctags_executable = get(g:, 'gutentags_ctags_executable', 'ctags')
+let g:gutentags_tagfile = get(g:, 'gutentags_tagfile', 'tags')
+let g:gutentags_auto_set_tags = get(g:, 'gutentags_auto_set_tags', 1)
+let g:gutentags_ctags_options_file = get(g:, 'gutentags_ctags_options_file', '.gutctags')
+let g:gutentags_ctags_check_tagfile = get(g:, 'gutentags_ctags_check_tagfile', 0)
 
 " }}}
 
@@ -40,7 +26,7 @@
     endif
 
     " Check if the ctags executable exists.
-    if g:gutentags_enabled && executable(g:gutentags_ctags_executable) == 0
+    if g:gutentags_enabled && executable(expand(g:gutentags_ctags_executable, 1)) == 0
         let g:gutentags_enabled = 0
         echoerr "Executable '".g:gutentags_ctags_executable."' can't be found. "
                     \."Gutentags will be disabled. You can re-enable it by "
@@ -88,12 +74,23 @@
         let l:cmd .= ' -t "' . l:actual_tags_file . '"'
         let l:cmd .= ' -p "' . l:actual_proj_dir . '"'
         if a:write_mode == 0 && l:tags_file_exists
-            let l:full_path = expand('%:p')
-            let l:cmd .= ' -s "' . l:full_path . '"'
+            let l:cur_file_path = expand('%:p')
+            if empty(g:gutentags_cache_dir)
+                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 options file first, and then the project specific
-        " one, so that users can override the default behaviour.
-        let l:cmd .= ' -o "' . gutentags#get_res_file('ctags.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)
@@ -156,11 +153,9 @@
     let l:ftype = get(split(&filetype, '\.'), 0, '')
     let l:proj_info = gutentags#get_project_info(a:proj_dir)
     let l:type = get(l:proj_info, 'type', l:ftype)
-    if exists('g:gutentags_ctags_executable_{l:type}')
-        return g:gutentags_ctags_executable_{l:type}
-    else
-        return g:gutentags_ctags_executable
-    endif
+    let exepath = exists('g:gutentags_ctags_executable_{l:type}')
+        \ ? g:gutentags_ctags_executable_{l:type} : g:gutentags_ctags_executable
+    return expand(exepath, 1)
 endfunction
 
 function! s:process_options_file(proj_dir, path) abort
--- a/doc/gutentags.txt	Mon May 16 02:09:40 2016 +0800
+++ b/doc/gutentags.txt	Fri Sep 16 23:43:57 2016 +0800
@@ -54,12 +54,12 @@
 time you save, it will silently, in the background, update the tags for that
 file.
 
-Usually, ctags can only append tags to an existing tag file, so Gutentags
+Usually, `ctags` can only append tags to an existing tag file, so Gutentags
 removes the tags for the current file first, to make sure the tag file is
 always consistent with the source code.
 
 Also, Gutentags is clever enough to not stumble upon itself by triggering
-multiple ctags processes if you save files to fast, or your project is really
+multiple ctags processes if you save files too fast, or your project is really
 big.
 
 There are some similar Vim plugins out there ("vim-tags", "vim-autotag",
@@ -76,8 +76,8 @@
   the tag file, otherwise you will still "see" tags for deleted or renamed
   classes and functions.
 * Automatically create the tag file: you open something from a freshly forked
-  project, it should start indexing it automatically, just in Sublime Text or
-  Visual Studio or any other IDE.
+  project, it should start indexing it automatically, just like in Sublime Text 
+  or Visual Studio or any other IDE.
 
 
 =============================================================================
@@ -239,9 +239,46 @@
                         file named after |gutentags_tagfile| will be created at
                         the project root.
                         Defaults to `[]` (an empty |List|).
-                        A list of default markers will always be appended to
-                        the user-defined ones: ['.git', '.hg', '.bzr',
-                        '_darcs'].
+                        A list of default markers will be appended to the
+                        user-defined ones unless
+                        |gutentags_add_default_project_roots| is set to 0.
+                        
+                                            *gutentags_add_default_project_roots*
+g:gutentags_add_default_project_roots
+                        Defines whether Gutentags should always define some
+                        default project roots (see |gutentags_project_root|).
+                        This can be useful to prevent unnecessary disk access
+                        when Gutentags searches for a project root.
+                        The default markers are:
+                        `['.git', '.hg', '.bzr', '_darcs', '_darcs', '_FOSSIL_', '.fslckout']`
+
+                                                *gutentags_exclude_project_root*
+g:gutentags_exclude_project_root
+                        A list of project roots to generally ignore. If a file
+                        is opened inside one of those projects, Gutentags
+                        won't be activated. This is similar to placing
+                        a `.notags` file in the root of those projects, but
+                        can be useful when you don't want to, or can't, place
+                        such a file there.
+                        Defaults to `['/usr/local']`, which is the folder where
+                        Homebrew is known to create a Git directory by default
+                        on MacOS.
+
+                                                *gutentags_project_root_finder*
+g:gutentags_project_root_finder
+                        When a buffer is loaded, Gutentags uses a default
+                        (internal) implementation to find that file's
+                        project's root directory, using settings like
+                        |g:gutentags_project_root|. When you specify
+                        |g:gutentags_project_root_finder|, you can tell
+                        Gutentags to use a custom implementation, such as
+                        `vim-projectroot`. The value of this setting must be
+                        the name of a function that takes a single string
+                        argument (the path to the current buffer's file) and
+                        returns a string value (the project's root directory).
+                        Defaults to `''`.
+                        Note: when set, the called implementation will most 
+                        likely ignore |g:gutentags_project_root|.
 
                                                 *gutentags_exclude*
 g:gutentags_exclude
@@ -332,9 +369,11 @@
 g:gutentags_enabled_user_func
                         When set to a non-empty string, it is expected to be
                         the name of a function that will be called when a file
-                        is open in a project. The function gets passed the
+                        is opened in a project. The function gets passed the
                         path of the file and if it returns 0, Gutentags won't
                         be enabled for that file.
+                        You can use this also to manually set `b:gutentags_root`
+                        (see |gutentags_project_root|).
                         Defaults to "".
 
                                             *gutentags_define_advanced_commands*
@@ -378,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 files',
+                                 \ },
+                             \ }
+<
+                        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	Mon May 16 02:09:40 2016 +0800
+++ b/plat/unix/update_scopedb.sh	Fri Sep 16 23:43:57 2016 +0800
@@ -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	Mon May 16 02:09:40 2016 +0800
+++ b/plat/unix/update_tags.sh	Fri Sep 16 23:43:57 2016 +0800
@@ -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,19 +72,32 @@
 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
     if [ "$UPDATED_SOURCE" != "" ]; then
         echo "Removing references to: $UPDATED_SOURCE"
-        echo "grep -v \"$UPDATED_SOURCE\" \"$TAGS_FILE\" > \"$TAGS_FILE.temp\""
-        grep -v "$UPDATED_SOURCE" "$TAGS_FILE" > "$TAGS_FILE.temp"
+        tab="	"
+        cmd="grep --text -Ev '^[^$tab]+$tab$UPDATED_SOURCE$tab' '$TAGS_FILE' > '$TAGS_FILE.temp'"
+        echo "$cmd"
+        eval "$cmd" || true
         INDEX_WHOLE_PROJECT=0
     fi
 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	Mon May 16 02:09:40 2016 +0800
+++ b/plat/win32/update_scopedb.cmd	Fri Sep 16 23:43:57 2016 +0800
@@ -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	Mon May 16 02:09:40 2016 +0800
+++ b/plat/win32/update_tags.cmd	Fri Sep 16 23:43:57 2016 +0800
@@ -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
@@ -77,7 +83,7 @@
 if exist "%TAGS_FILE%" (
     if not ["%UPDATED_SOURCE%"]==[""] (
         echo Removing references to: %UPDATED_SOURCE% >> %LOG_FILE%
-        echo type "%TAGS_FILE%" ^| findstr /V /C:"%UPDATED_SOURCE%" ^> "%TAGS_FILE%.temp" >> %LOG_FILE%
+        echo findstr /V /C:"%UPDATED_SOURCE%" "%TAGS_FILE%" ^> "%TAGS_FILE%.temp" >> %LOG_FILE%
         findstr /V /C:"%UPDATED_SOURCE%" "%TAGS_FILE%" > "%TAGS_FILE%.temp"
         set CTAGS_ARGS=%CTAGS_ARGS% --append "%UPDATED_SOURCE%"
         set INDEX_WHOLE_PROJECT=0
@@ -85,6 +91,20 @@
 )
 if ["%INDEX_WHOLE_PROJECT%"]==["1"] (
     set CTAGS_ARGS=%CTAGS_ARGS% "%PROJECT_ROOT%"
+    if not ["%FILE_LIST_CMD%"]==[""] (
+        echo Running custom file lister >> %LOG_FILE%
+        if ["%PROJECT_ROOT%"]==["."] (
+            echo call %FILE_LIST_CMD% ^> %TAGS_FILE%.files >> %LOG_FILE%
+            call %FILE_LIST_CMD% > %TAGS_FILE%.files
+        ) else (
+            rem Potentially useful:
+            rem http://stackoverflow.com/questions/9749071/cmd-iterate-stdin-piped-from-another-command
+            echo call %FILE_LIST_CMD% -- with loop for prepending project root >> %LOG_FILE%
+            type NUL > %TAGS_FILE%.files
+            for /F "usebackq delims=" %%F in (`%FILE_LIST_CMD%`) do @echo %PROJECT_ROOT%\%%F >> %TAGS_FILE%.files
+        )
+        set CTAGS_ARGS=%CTAGS_ARGS% -L %TAGS_FILE%.files
+    )
 )
 
 echo Running ctags >> %LOG_FILE%
@@ -105,7 +125,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 +149,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	Mon May 16 02:09:40 2016 +0800
+++ b/plugin/gutentags.vim	Fri Sep 16 23:43:57 2016 +0800
@@ -9,9 +9,7 @@
     finish
 endif
 
-if !exists('g:gutentags_debug')
-    let g:gutentags_debug = 0
-endif
+let g:gutentags_debug = get(g:, 'gutentags_debug', 0)
 
 if (exists('g:loaded_gutentags') || &cp) && !g:gutentags_debug
     finish
@@ -21,64 +19,32 @@
 endif
 let g:loaded_gutentags = 1
 
-if !exists('g:gutentags_trace')
-    let g:gutentags_trace = 0
-endif
-
-if !exists('g:gutentags_fake')
-    let g:gutentags_fake = 0
-endif
-
-if !exists('g:gutentags_background_update')
-    let g:gutentags_background_update = 1
-endif
-
-if !exists('g:gutentags_pause_after_update')
-    let g:gutentags_pause_after_update = 0
-endif
+let g:gutentags_trace = get(g:, 'gutentags_trace', 0)
+let g:gutentags_fake = get(g:, 'gutentags_fake', 0)
+let g:gutentags_background_update = get(g:, 'gutentags_background_update', 1)
+let g:gutentags_pause_after_update = get(g:, 'gutentags_pause_after_update', 0)
+let g:gutentags_enabled = get(g:, 'gutentags_enabled', 1)
+let g:gutentags_enabled_user_func = get(g:, 'gutentags_enabled_user_func', '')
+let g:gutentags_modules = get(g:, 'gutentags_modules', ['ctags'])
 
-if !exists('g:gutentags_enabled')
-    let g:gutentags_enabled = 1
+let g:gutentags_add_default_project_roots = get(g:, 'gutentags_add_default_project_roots', 1)
+let g:gutentags_project_root = get(g:, 'gutentags_project_root', [])
+if g:gutentags_add_default_project_roots
+    let g:gutentags_project_root += ['.git', '.hg', '.svn', '.bzr', '_darcs', '_FOSSIL_', '.fslckout']
 endif
-
-if !exists('g:gutentags_enabled_user_func')
-    let g:gutentags_enabled_user_func = ''
-endif
+let g:gutentags_project_root_finder = ''
 
-if !exists('g:gutentags_modules')
-    let g:gutentags_modules = ['ctags']
-endif
-
-if !exists('g:gutentags_project_root')
-    let g:gutentags_project_root = []
-endif
-let g:gutentags_project_root += ['.git', '.hg', '.svn', '.bzr', '_darcs', '_FOSSIL_', '.fslckout']
-
-if !exists('g:gutentags_project_info')
-    let g:gutentags_project_info = []
-endif
+let g:gutentags_project_info = get(g:, 'gutentags_project_info', [])
 call add(g:gutentags_project_info, {'type': 'python', 'file': 'setup.py'})
 call add(g:gutentags_project_info, {'type': 'ruby', 'file': 'Gemfile'})
 
-if !exists('g:gutentags_exclude')
-    let g:gutentags_exclude = []
-endif
-
-if !exists('g:gutentags_resolve_symlinks')
-    let g:gutentags_resolve_symlinks = 0
-endif
-
-if !exists('g:gutentags_generate_on_new')
-    let g:gutentags_generate_on_new = 1
-endif
-
-if !exists('g:gutentags_generate_on_missing')
-    let g:gutentags_generate_on_missing = 1
-endif
-
-if !exists('g:gutentags_generate_on_write')
-    let g:gutentags_generate_on_write = 1
-endif
+let g:gutentags_exclude = get(g:, 'gutentags_exclude', [])
+let g:gutentags_exclude_project_root = get(g:, 'gutentags_exclude_project_root', ['/usr/local'])
+let g:gutentags_resolve_symlinks = get(g:, 'gutentags_resolve_symlinks', 0)
+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 = ''
@@ -89,9 +55,7 @@
     let g:gutentags_cache_dir = fnamemodify(g:gutentags_cache_dir, ':s?[/\\]$??')
 endif
 
-if !exists('g:gutentags_define_advanced_commands')
-    let g:gutentags_define_advanced_commands = 0
-endif
+let g:gutentags_define_advanced_commands = get(g:, 'gutentags_define_advanced_commands', 0)
 
 if g:gutentags_cache_dir != '' && !isdirectory(g:gutentags_cache_dir)
     call mkdir(g:gutentags_cache_dir, 'p')
--- a/res/ctags.options	Mon May 16 02:09:40 2016 +0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
---recurse=yes
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/res/ctags_recursive.options	Fri Sep 16 23:43:57 2016 +0800
@@ -0,0 +1,2 @@
+--recurse=yes
+