changeset 252:56dc6f8e5472

Use tag-relative paths in ctags when needed. This is necessary when the tags file isn't generated at the project root.
author Ludovic Chabant <ludovic@chabant.com>
date Sat, 26 Oct 2019 01:17:19 -0700
parents e61d20280c6c
children ec292bfbd633
files autoload/gutentags/ctags.vim plat/unix/update_tags.sh plat/win32/update_tags.cmd
diffstat 3 files changed, 50 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/autoload/gutentags/ctags.vim	Fri Oct 25 23:52:12 2019 -0700
+++ b/autoload/gutentags/ctags.vim	Sat Oct 26 01:17:19 2019 -0700
@@ -78,9 +78,9 @@
     let l:write_mode = a:gen_opts['write_mode']
 
     let l:tags_file_exists = filereadable(a:tags_file)
-    let l:tags_file_relative = fnamemodify(a:tags_file, ':.')
-    let l:tags_file_is_local = len(l:tags_file_relative) < len(a:tags_file)
 
+    " If the tags file exists, we may want to do a sanity check to prevent
+    " weird errors that are hard to troubleshoot.
     if l:tags_file_exists && g:gutentags_ctags_check_tagfile
         let l:first_lines = readfile(a:tags_file, '', 1)
         if len(l:first_lines) == 0 || stridx(l:first_lines[0], '!_TAG_') != 0
@@ -92,6 +92,17 @@
         endif
     endif
 
+    " Get a tags file path relative to the current directory, which 
+    " happens to be the project root in this case.
+    " Since the given tags file path is absolute, and since Vim won't
+    " change the path if it is not inside the current directory, we
+    " know that the tags file is "local" (i.e. inside the project)
+    " if the path was shortened (an absolute path will always be
+    " longer than a true relative path).
+    let l:tags_file_relative = fnamemodify(a:tags_file, ':.')
+    let l:tags_file_is_local = len(l:tags_file_relative) < len(a:tags_file)
+    let l:use_tag_relative_opt = 0
+
     if empty(g:gutentags_cache_dir) && l:tags_file_is_local
         " If we don't use the cache directory, we can pass relative paths
         " around.
@@ -99,10 +110,27 @@
         " Note that if we don't do this and pass a full path for the project
         " root, some `ctags` implementations like Exhuberant Ctags can get
         " confused if the paths have spaces -- but not if you're *in* the root 
-        " directory, for some reason... (which we are, our caller in
-        " `autoload/gutentags.vim` changed it).
+        " directory, for some reason... (which will be the case, we're running
+        " the jobs from the project root).
         let l:actual_proj_dir = '.'
         let l:actual_tags_file = l:tags_file_relative
+
+        let l:tags_file_dir = fnamemodify(l:actual_tags_file, ':h')
+        if l:tags_file_dir != '.'
+            " Ok so now the tags file is stored in a subdirectory of the 
+            " project root, instead of at the root. This happens if, say,
+            " someone set `gutentags_ctags_tagfile` to `.git/tags`, which
+            " seems to be fairly popular.
+            "
+            " By default, `ctags` writes paths relative to the current 
+            " directory (the project root) but in this case we need it to
+            " be relative to the tags file (e.g. adding `../` in front of
+            " everything if the tags file is `.git/tags`).
+            "
+            " Thankfully most `ctags` implementations support an option
+            " just for this.
+            let l:use_tag_relative_opt = 1
+        endif
     else
         " else: the tags file goes in a cache directory, so we need to specify
         " all the paths absolutely for `ctags` to do its job correctly.
@@ -163,6 +191,9 @@
     for exc in g:gutentags_ctags_exclude
         let l:cmd += ['-x', '"' . exc . '"']
     endfor
+    if l:use_tag_relative_opt
+        let l:cmd += ['-r']
+    endif
     if g:gutentags_pause_after_update
         let l:cmd += ['-c']
     endif
--- a/plat/unix/update_tags.sh	Fri Oct 25 23:52:12 2019 -0700
+++ b/plat/unix/update_tags.sh	Sat Oct 26 01:17:19 2019 -0700
@@ -24,6 +24,7 @@
     echo "    -t [file=tags]: The path to the ctags file to update"
     echo "    -p [dir=]:      The path to the project root"
     echo "    -l [file=]:     The path to a log file"
+    echo "    -r              Use tag-relative paths"
     echo "    -L [cmd=]:      The file list command to run"
     echo "    -A:             Specifies that the file list command returns "
     echo "                    absolute paths"
@@ -37,7 +38,7 @@
 }
 
 
-while getopts "h?e:x:t:p:l:L:s:o:O:P:cA" opt; do
+while getopts "h?e:x:t:p:l:L:s:o:O:P:rcA" opt; do
     case $opt in
         h|\?)
             ShowUsage
@@ -73,6 +74,9 @@
         o)
             CTAGS_ARGS="$CTAGS_ARGS --options=$OPTARG"
             ;;
+        r)
+            CTAGS_ARGS="$CTAGS_ARGS --tag-relative=yes"
+            ;;
         O)
             CTAGS_ARGS="$CTAGS_ARGS $OPTARG"
             ;;
--- a/plat/win32/update_tags.cmd	Fri Oct 25 23:52:12 2019 -0700
+++ b/plat/win32/update_tags.cmd	Sat Oct 26 01:17:19 2019 -0700
@@ -66,6 +66,11 @@
     shift
     goto :LoopParseArgs
 )
+if [%1]==[-r] (
+    set CTAGS_ARGS=%CTAGS_ARGS% --tag-relative=yes
+    shift
+    goto :LoopParseArgs
+)
 if [%1]==[-O] (
     set CTAGS_ARGS=%CTAGS_ARGS% %~2
     shift
@@ -180,12 +185,16 @@
 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 [log=]:      The log file to output to
+echo    -r              Use tag-relative paths
 echo    -L [cmd=]:      The file list command to run
 echo    -A:             Specifies that the file list command returns
 echo                    absolute paths
 echo    -s [file=]:     The path to the source file that needs updating
-echo    -l [log=]:      The log file to output to
+echo    -x [pattern=]:  A pattern of files to exclude
 echo    -o [options=]:  An options file to read additional options from
+echo    -O [params=]:   Parameters to pass to ctags
+echo    -P [cmd=]:      Post process command to run on the tags file
 echo    -c:             Ask for confirmation before exiting
 echo.