leoliu / ggtags

Emacs frontend to GNU Global source code tagging system.

Home Page:http://elpa.gnu.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Update gtags.files automatically when ggtags updates

hisnawi opened this issue · comments

I work on a large source tree from which very limited directories need to be indexed. I have an alias that puts the files of these directories in gtags.files. Is there a way to execute this alias/command every time before ggtags updates? Is there a hook or method I could use for that?

If not, is it possible to add this enhancement?

there may be a way. ggtags-update-tags calls ggtags-update-tags-finish at the end so you can put an advice on ggtags-update-tags-finish to do just that.

But what I am trying to do is to put in the files (before) we update the tags, not after.

I see. do a :before advice on ggtags-update-tags, roughly:

(define-advice my-update-gtags.files (:before (&rest _))
  (let ((dir (ggtags-current-project-root)))
    ;; Check this is my project that needs updating gtags.files
    (when (and dir (string-prefix-p "/my/project/" dir))
      UPDATE_GTAGS_FILES)))

Let me know if this works.

Thanks for the help leoliu, assuming I have a shell alias "gfiles" that executes while in ggtags root directory. Where do I put this command in the function you wrote above? Apologies I am not a lisp expert :/

For example, something like this:

(define-advice my-update-gtags.files (:before (&rest _))
  (let ((dir (ggtags-current-project-root)))
    ;; Check this is my project that needs updating gtags.files
    (when (and dir (string-prefix-p "/my/project/" dir))
      (shell-command "my_cmd -flag1 -flag2"))))

If the shell command is short, you can just replace "my_cmd -flag1 -flag2" above. If it is long, put it in a shell script and place it in PATH or use absolute path so instead of (shell-command "my_cmd ...") use (shell-command "/paht/to/my_cmd ...").

What does this do?

(string-prefix-p "/my/project/" dir)

Do I need to replace /my/project with something?

Basically you need a way to check it is the project that needs updating gtags.file. The following is more robust:

(file-in-directory-p "MY_PROJECT_ROOT" dir)

Replace MY_PROJECT_ROOT with your project path.

Well, I have multiple trees with different names. I want the code to be general and not hardcoded to certain path. So how does ggtags root gets decided ? Can I trust that as long as I have gtags.conf file in the root that it will find it and execute my alias there?

(define-advice my-update-gtags.files (:before (&rest _))
  (let* ((dir (ggtags-current-project-root))
         (cmd (and dir (expand-file-name "update_files.sh" dir))))
    (and cmd (process-file-shell-command cmd))))

The above assume that the shell script is called update_files.sh and placed at project root.

I am not sure what I am doing wrong. So I have this in my init file:

(define-advice my-update-gtags.files (:before (&rest _))
  (let* ((dir (ggtags-current-project-root))
  (message "%s" dir)
         (cmd (and dir (expand-file-name "update_gfiles.sh" dir))))
    (and cmd (process-file-shell-command cmd))))

    (advice-add 'ggtags-update-tags-finish :before #'my-update-gtags.files)

But I dont see the file gtags.files getting generated.
Although from a terminal executing the shell script ./update_gfiles.sh generates the file.
I also tried with:

(advice-add 'my-update-gtags.files :before #'ggtags-update-tags-finish)

Which also didnt work.

Hi @hisnawi,

My bad. My code is wrong. Adopt the following instead:

(define-advice ggtags-update-tags (:before (&rest _) update-files)
  (let* ((dir (ggtags-current-project-root))
         (cmd (and dir (expand-file-name "update_files.sh" dir))))
    (and cmd (process-file-shell-command cmd))))

The above will attach an :before advice to ggtags-update-tags.

Still not executing the shell script.

Does ggtags consider the git project being the root?

I have my project setup this way?

~/project . -> which has the scrip and the GTAGS...etc and gtags.conf and gtags.files
and then I have:
~/project/gitrepo -> which includes the .git file and .gitignore and the rest of the project files.

If ggtags goes up to the gitrepo directory then stop then it wont find the shellscript to execute it.

The project root is what global -pr returns.

This version should give you better error on what's wrong. also make sure the script is executable.

(define-advice ggtags-update-tags (:before (&rest _) update-files)
  (let* ((dir (ggtags-current-project-root))
         (cmd (and dir (expand-file-name "update_files.sh" dir))))
    (when (and cmd (file-executable-p cmd))
      (with-temp-buffer
        (or (zerop (process-file-shell-command cmd nil t))
            (error "%s" (buffer-string)))))))

Ok. so the error is in the find command.

Basically, I have the update_files.sh constructed this way:

#!/bin/bash
find gitrepo/somedirectory gitrepo/anotherdirectory -type f -print >gtags.files

So I get an error that gitrepo/somedirectory is not found.

I am guessing that your code doesn't execute the shell script in the "~/project" directory, but from wherever ggtags-update-tags is executed. Is that correct?

As I mentioned before, I need the shell script to not include full paths since I have multiple repos with different project names.

Also the gtags.files file has to be in the project root, that is why the shell script has to be executed in the root as well.

Edit: I confirmed that if I executed the ggtags-update-tags from the root then it works. Is it possible to make the code more robust and execute the shell from the root (without ending up changing to root directory) after we are done updating tags?

Try this.

(define-advice ggtags-update-tags (:before (&rest _) update-files)
  (let* ((dir (ggtags-current-project-root))
         (cmd (and dir (expand-file-name "update_files.sh" dir))))
    (when (and cmd (file-executable-p cmd))
      (ggtags-with-current-project
        (with-temp-buffer
          (or (zerop (process-file-shell-command cmd nil t))
              (error "%s" (buffer-string))))))))

Unfortunately, I am still getting the

gitrepo/somedirectory is not found.

furthermore, evaluating ggtags-current-project-root comes up to nil

In previous version, envvar GTAGSROOT will be set and point to the project root directory, so you can use it in your script.

The following version will always execute update_files.sh from the project root directory.

(define-advice ggtags-update-tags (:before (&rest _) update-files)
  (let* ((dir (ggtags-current-project-root))
         (cmd (and dir (expand-file-name "update_files.sh" dir))))
    (when (and cmd (file-executable-p cmd))
      (with-temp-buffer
        (let ((default-directory dir))
          (or (zerop (process-file-shell-command cmd nil t))
              (error "%s" (buffer-string))))))))

Brilliant! This worked!
Thank you so much for helping me with this although it was not a bug nor an enhancement request.

One last thing Leo.
I am using this code when both create and update ggtags. I am sure it can be optimized to eliminate duplicate code, but not sure how.
Could you help with this for completeness?


  (define-advice ggtags-create-tags (:before (&rest _) update-gfiles)
    (let* ((dir (ggtags-current-project-root))
           (cmd (and dir (expand-file-name "update_gfiles.sh" dir))))
      (when (and cmd (file-executable-p cmd))
        (with-temp-buffer
          (let ((default-directory dir))
            (or (zerop (process-file-shell-command cmd nil t))
                (error "%s" (buffer-string))))))))
  (define-advice ggtags-update-tags (:before (&rest _) update-gfiles)
    (let* ((dir (ggtags-current-project-root))
           (cmd (and dir (expand-file-name "update_gfiles.sh" dir))))
      (when (and cmd (file-executable-p cmd))
        (with-temp-buffer
          (let ((default-directory dir))
            (or (zerop (process-file-shell-command cmd nil t))
                (error "%s" (buffer-string))))))))

Maybe something along these lines (untested):

(defun update-project-gtags-files (dir)
  "Run shell script `update_gfiles.sh' (must be executable) in DIR."
  (let ((cmd (and dir (expand-file-name "update_gfiles.sh" dir))))
    (when (and cmd (file-executable-p cmd))
      (with-temp-buffer
        (let ((default-directory dir))
          (or (zerop (process-file-shell-command cmd nil t))
              (error "%s" (buffer-string))))))))

(define-advice ggtags-update-tags (:before (&rest _) update-gfiles)
  (update-project-gtags-files (ggtags-current-project-root)))

(define-advice ggtags-create-tags (:before (root) update-gfiles)
  (update-project-gtags-files root))

Worked perfectly...thanks!