leoliu / ggtags

Emacs frontend to GNU Global source code tagging system.
http://elpa.gnu.org
575 stars 57 forks source link

auto-update gtags when a file is newer #166

Open tromey opened 6 years ago

tromey commented 6 years ago

I used ggtags and saw this message:

File ‘block.c’ is newer than GTAGS

I have ggtags-update-on-save set to t. It seems to me that in this case, it would be nice if ggtags automatically updated the gtags files, just as if I had saved this file.

tromey commented 6 years ago

I tried this again and it seemed like this time it did auto-update. Not sure why ... ?

leoliu commented 6 years ago

It is handled byggtags-project-update-mtime-maybe. so once a stale GTAGS file is detected, ggtags flags the project as dirty and next commands will trigger an update. Not sure if this is the best strategy. I often jump to a tag and if it is the wrong location, rewind (using M-,) and jump again.

When ggtags-update-on-save is true saving buffers in ggtags-mode will always auto-update. What I describe above is for the case where files are modified outside emacs such as by git pull.

leoliu commented 6 years ago

Hi @tromey,

The message File ‘block.c’ is newer than GTAGS is confusing (incomplete) though because it doesn't say it will be taken care of. Any suggestion on how to improve it? Thanks.

Leo

tromey commented 6 years ago

Would it be possible to just remove the message and instead have the operation wait for the GTAGS file to be updated? I have been using the M-. M-, M-. sequence, but it would be nicer if this just happened automatically.

leoliu commented 6 years ago

That's how it works i.e. GTAGS etc. are kept up-to-date always.

But discovering a stale GTAGS (due to modifications outside ggtags-mode) is done after the fact i.e. after jumping to a file having a mtime newer than GTAGS mtime.

tromey commented 6 years ago

Would it be possible to notice that during the course of the M-. command and re-run global at that time? Sorry if I am being obtuse! This just strikes me as weird.

leoliu commented 6 years ago

No worries. There may be something needs improving in this area.

The GTAGS etc files become stale when source files are modified but global -u is yet to be run. Emacs doesn't know unless 1. those files are modified by it in ggtags-mode or 2. it finds a file with a more recent mtime.

Case 1 is handled when saving buffers. Case 2 is currently handled when files are opened due to jumping to a tag.

Whether global -u is run is also dependent on project size. Running global -u may take seconds for large projects.

ggtags has two updating strategies per ggtags-oversize-limit: 1. For small projects with GTAGS size less than ggtags-oversize-limit run global -u (ggtags-update-tags); 2. otherwise global --single-update ... (ggtags-update-tags-single).

The question is is there a way to keep GTAGS etc. up-to-date before or during M-. and the like. At the moment it looks unlikely unless we are ready to aggressively use global -u. What do you think?

tromey commented 5 years ago

Oops, I forgot about this.

The question is is there a way to keep GTAGS etc. up-to-date before or during M-. and the like. At the moment it looks unlikely unless we are ready to aggressively use global -u. What do you think?

What I end up doing in practice is typing M-. M-, M-. in order to force global to find the correct tag. So, I'm pretty strongly in favor of having it auto-update at the correct time, or do a second lookup if the file changed, since I normally just want to go right to the correct spot without this.

leoliu commented 5 years ago

The following patch adds a var ggtags-find-tag-retry-if-stale that controls whether to retry search. Do you have time to test it?

diff --git a/ggtags.el b/ggtags.el
index a61ae7db..07257b7e 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -260,6 +260,12 @@ (defcustom ggtags-enable-navigation-keys t
   :type 'boolean
   :group 'ggtags)

+(defcustom ggtags-find-tag-retry-if-stale nil
+  "Retry current search if match file is newer than GTAGS file."
+  :safe 'booleanp
+  :type 'boolean
+  :group 'ggtags)
+
 (defcustom ggtags-find-tag-hook nil
   "Hook run immediately after finding a tag."
   :options '(recenter reposition-window)
@@ -1869,9 +1875,6 @@ (defun ggtags-global-next-error-function ()
   (when (eq next-error-last-buffer ggtags-global-last-buffer)
     (ggtags-move-to-tag)
     (ggtags-global-save-start-marker)
-    (and (ggtags-project-update-mtime-maybe)
-         (message "File `%s' is newer than GTAGS"
-                  (file-name-nondirectory buffer-file-name)))
     (and ggtags-mode-sticky (ggtags-mode 1))
     (ignore-errors
       (ggtags-ensure-global-buffer
@@ -1889,8 +1892,22 @@ (defun ggtags-global-next-error-function ()
           (add-to-history 'ggtags-global-search-history
                           (cons id (ggtags-global-current-search))
                           ggtags-global-history-length))))
+    (when (ggtags-project-update-mtime-maybe)
+      (if ggtags-find-tag-retry-if-stale
+          (ggtags-find-tag-retry)
+        (message "File `%s' is newer than GTAGS"
+                 (file-name-nondirectory buffer-file-name))))
     (run-hooks 'ggtags-find-tag-hook)))

+(defun ggtags-find-tag-retry ()
+  (ggtags-with-temp-message "Retry current search..."
+    (when (ggtags-project-oversize-p)
+      (ggtags-update-tags-single buffer-file-name))
+    (if ggtags-navigation-mode
+        (ggtags-navigation-mode-abort)
+      (xref-pop-marker-stack))
+    (ggtags-global-rerun-search (cdar ggtags-global-search-history))))
+
 (put 'ggtags-navigation-mode-lighter 'risky-local-variable t)

 (defvar ggtags-navigation-mode-lighter