emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[ELPA-diffs] ELPA branch, master, updated. 0cdc945e2cdc9e1f7a72ed03639cf


From: Leo Liu
Subject: [ELPA-diffs] ELPA branch, master, updated. 0cdc945e2cdc9e1f7a72ed03639cff7edecc12ea
Date: Sun, 03 Nov 2013 02:27:42 +0000

This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "ELPA".

The branch, master has been updated
       via  0cdc945e2cdc9e1f7a72ed03639cff7edecc12ea (commit)
       via  34215cdb9a04d538ae1c444fd8c3beae0023312a (commit)
       via  4ea4c41fa79a962494b4746f48cfe59049127f41 (commit)
       via  5420bc1bf3429933b8561e3b42120db88350358f (commit)
       via  3e056f6e72097506a0f2c9c9a2fc7d619831785f (commit)
       via  36699816a77f86f36500bb6f65ca93b1d366890d (commit)
       via  08ec2a4b086bbd35e33ef40789cb49f4c3c9f184 (commit)
       via  4d091b56c99d2b3eff08fbc86c1d43fa8ad5cec3 (commit)
       via  852bb636882d2d937c12063bc10f61ae560a310d (commit)
       via  370e46a50265cdb9b22c3dd7f4cae33cc84f8d2e (commit)
       via  079b8d3ce33cb552bd2cf738b363cea6315e21d7 (commit)
       via  b6c79ca819f696cce520e0a180323117cd6db548 (commit)
       via  ef6e8dd70cbf05c60eceb80b750113d56ee887ed (commit)
       via  1722cf09fb5e5b07c4ef424e85e9d743f74e7b0b (commit)
       via  a12c2e4684f1502aec36c626302fe397ababa985 (commit)
       via  8c05dc12c76e4f597c6e7f8d171c6618805ed0e1 (commit)
       via  749a904ddcaf542ade378fe2d0c14a69d532a2fc (commit)
       via  c434009164f6f89ba976f18858fefb3736a3bc44 (commit)
       via  63140683ff041b9ff358e2d16a44981043d74378 (commit)
       via  7e700e9430a811228e238bffd7d9799cafb5cf5f (commit)
       via  37cb5238fe15fe2ebd81a7a055497bb256d054b2 (commit)
       via  4f3ef98dad8ead3f67209d002d21c85281aae573 (commit)
       via  e0167804aa650716ac5dfc6986f83421b7d7ebe6 (commit)
       via  f12a67f88bfb83f808df2912d5e1f4ba6c937141 (commit)
       via  59d870cb31b0d7fd8104c20c0125739d301f19ea (commit)
       via  342dadd67e8c24e6aeb965b3150106b3d49eedf5 (commit)
       via  93da71550138fd777fc54134050e5fcbfb4a615b (commit)
       via  4ae7a1802aa748b6b577fa3453be1b02b58084e6 (commit)
       via  1d3c4c6217b2ae50901d6db36b0ffae03589817e (commit)
       via  16dcf906edcecef13045a977bded0a7e645acbb8 (commit)
       via  ebbcaa7491202a40bdeef203f262e7e89d5da622 (commit)
       via  f9a2ff871101fa9eed1ec793f04750ace0f1e568 (commit)
       via  c3a16722d0d1d60ec1abdee35b721879db1cf1bc (commit)
      from  474e61713d53a2e66c5739c0c1c595d56ae86e7e (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 0cdc945e2cdc9e1f7a72ed03639cff7edecc12ea
Merge: 474e617 34215cd
Author: Leo Liu <address@hidden>
Date:   Sun Nov 3 10:26:34 2013 +0800

    Merge branch 'master' of github.com:leoliu/ggtags


commit 34215cdb9a04d538ae1c444fd8c3beae0023312a
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 22:31:35 2013 +0800

    Some tweaks to ggtags-kill-file-buffers and release v0.7.0

diff --git a/ggtags.el b/ggtags.el
index 2d90c15..dbe8d62 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -3,7 +3,7 @@
 ;; Copyright (C) 2013  Free Software Foundation, Inc.
 
 ;; Author: Leo Liu <address@hidden>
-;; Version: 0.6.8
+;; Version: 0.7.0
 ;; Keywords: tools, convenience
 ;; Created: 2013-01-29
 ;; URL: https://github.com/leoliu/ggtags
@@ -758,7 +758,7 @@ Global and Emacs."
   "Kill all buffers visiting files in current project."
   (interactive "p")
   (ggtags-check-project)
-  (let ((root (ggtags-current-project-root))
+  (let ((directories (cons (ggtags-current-project-root) (ggtags-get-libpath)))
         (count 0)
         (some (lambda (pred list)
                 (loop for x in list when (funcall pred x) return it))))
@@ -766,11 +766,10 @@ Global and Emacs."
       (let ((file (and (buffer-live-p buf)
                        (not (eq buf (current-buffer)))
                        (buffer-file-name buf))))
-        (when (and file (funcall some (apply-partially #'file-in-directory-p
-                                                       (file-truename file))
-                                 (cons root (ggtags-get-libpath))))
-          (and (kill-buffer buf)
-               (incf count)))))
+        (when (and file (funcall some
+                                 (apply-partially #'file-in-directory-p file)
+                                 directories))
+          (and (kill-buffer buf) (incf count)))))
     (and interactive
          (message "%d %s killed" count (if (= count 1) "buffer" "buffers")))))
 

commit 4ea4c41fa79a962494b4746f48cfe59049127f41
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 18:06:34 2013 +0800

    Fix thinko in "Support invert-match in ggtags-grep"
    
    commit id: 3e056f6e72097506a0f2c9c9a2fc7d619831785f

diff --git a/ggtags.el b/ggtags.el
index 2c1feb1..2d90c15 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -399,7 +399,7 @@ Invert the match when called with a prefix arg 
\\[universal-argument]."
                                            "Grep pattern"))
                      current-prefix-arg))
   (ggtags-find-tag 'grep (format "%s--regexp %S"
-                                 (and invert-match "--invert-match ")
+                                 (if invert-match "--invert-match " "")
                                  pattern)))
 
 (defun ggtags-idutils-query (pattern)

commit 5420bc1bf3429933b8561e3b42120db88350358f
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 17:47:02 2013 +0800

    Fix buglets with small improvements

diff --git a/ggtags.el b/ggtags.el
index 6f6ad2d..2c1feb1 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -245,6 +245,7 @@ properly update `ggtags-mode-map'."
   (or (ggtags-find-project) (error "File GTAGS not found")))
 
 (defun ggtags-ensure-project ()
+  (interactive)
   (or (ggtags-find-project)
       (when (or (yes-or-no-p "File GTAGS not found; run gtags? ")
                 (user-error "Aborted"))
@@ -463,7 +464,9 @@ Global and Emacs."
             (progn
               (fit-window-to-buffer win)
               (when (yes-or-no-p "Remove GNU Global tag files? ")
-                (mapc 'delete-file files)))
+                (mapc 'delete-file files)
+                (remhash (ggtags-current-project-root) ggtags-projects)
+                (kill-local-variable 'ggtags-project)))
           (when (window-live-p win)
             (quit-window t win)))))))
 
@@ -752,7 +755,7 @@ Global and Emacs."
   (setq-local ggtags-navigation-mode nil))
 
 (defun ggtags-kill-file-buffers (&optional interactive)
-  "Kill all buffers visiting files in the root directory."
+  "Kill all buffers visiting files in current project."
   (interactive "p")
   (ggtags-check-project)
   (let ((root (ggtags-current-project-root))
@@ -816,7 +819,10 @@ Global and Emacs."
     (define-key menu [sep2] menu-bar-separator)
     (define-key menu [delete-tags]
       '(menu-item "Delete tag files" ggtags-delete-tag-files
-                  :enable (ggtags-root-directory)))
+                  :enable (ggtags-find-project)))
+    (define-key menu [kill-buffers]
+      '(menu-item "Kill buffers visiting project files" 
ggtags-kill-file-buffers
+                  :enable (ggtags-find-project)))
     (define-key menu [pop-mark]
       '(menu-item "Pop mark" pop-tag-mark
                   :help "Pop to previous mark and destroy it"))
@@ -841,12 +847,12 @@ Global and Emacs."
       '(menu-item "Find tag matching regexp" ggtags-find-tag-regexp))
     (define-key menu [find-tag]
       '(menu-item "Find tag" ggtags-find-tag-dwim))
+    (define-key menu [update-tags]
+      '(menu-item "Update tag files" ggtags-update-tags
+                  :visible (ggtags-find-project)))
     (define-key menu [run-gtags]
-      '(menu-item (if (ggtags-root-directory) "Update tag files" "Run gtags")
-                  (lambda () (interactive)
-                    (if (ggtags-root-directory)
-                        (ggtags-update-tags)
-                      (ggtags-ensure-root-directory)))))
+      '(menu-item "Run gtags" ggtags-ensure-project
+                  :visible (not (ggtags-find-project))))
     map))
 
 (defun ggtags-mode-update-prefix-key (symbol value)

commit 3e056f6e72097506a0f2c9c9a2fc7d619831785f
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 17:17:45 2013 +0800

    Support invert-match in ggtags-grep

diff --git a/ggtags.el b/ggtags.el
index 0ab84fa..6f6ad2d 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -390,9 +390,16 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
                          prompt default)
                  nil nil (and default (substring-no-properties default)))))
 
-(defun ggtags-grep (pattern)
-  (interactive (list (ggtags-read-string "Grep pattern")))
-  (ggtags-find-tag 'grep (format "--regexp %S" pattern)))
+(defun ggtags-grep (pattern &optional invert-match)
+  "Use `global --grep' to search for lines matching PATTERN.
+Invert the match when called with a prefix arg \\[universal-argument]."
+  (interactive (list (ggtags-read-string (if current-prefix-arg
+                                             "Grep inverted pattern"
+                                           "Grep pattern"))
+                     current-prefix-arg))
+  (ggtags-find-tag 'grep (format "%s--regexp %S"
+                                 (and invert-match "--invert-match ")
+                                 pattern)))
 
 (defun ggtags-idutils-query (pattern)
   (interactive (list (ggtags-read-string "ID query pattern")))
@@ -521,7 +528,7 @@ Global and Emacs."
      (ctags-x "^\\([^ \t\n]+\\)[ \t]+\\([0-9]+\\)[ \t]+\\(\\(?:[^/\n]*/\\)?[^ 
\t\n]+\\)"
               3 2 nil nil 3 (1 font-lock-function-name-face))
      ;; src/dialog.cc:172:#undef ACTIVE_ESCAPE
-     (grep 
"^\\(.+?\\):\\([0-9]+\\):\\(?:[^0-9\n]\\|[0-9][^0-9\n]\\|[0-9][0-9].\\)"
+     (grep 
"^\\(.+?\\):\\([0-9]+\\):\\(?:$\\|[^0-9\n]\\|[0-9][^0-9\n]\\|[0-9][0-9].\\)"
            1 2 nil nil 1)
      ;; src/dialog.cc ACTIVE_ESCAPE 172 #undef ACTIVE_ESCAPE
      (cscope "^\\(.+?\\)[ \t]+\\([^ \t\n]+\\)[ 
\t]+\\([0-9]+\\).*\\(?:[^0-9\n]\\|[^0-9\n][0-9]\\|[^:\n][0-9][0-9]\\)$"

commit 36699816a77f86f36500bb6f65ca93b1d366890d
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 17:03:22 2013 +0800

    Fix last change

diff --git a/ggtags.el b/ggtags.el
index 5460fda..0ab84fa 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -272,8 +272,7 @@ properly update `ggtags-mode-map'."
                        (file-truename buffer-file-name)))
      (ggtags-process-string "global" "-u"))))
 
-;;;###autoload
-(defun ggtags-completion-table ()
+(defvar ggtags-completion-table
   (let (cache)
     (completion-table-dynamic
      (lambda (prefix)
@@ -303,7 +302,7 @@ properly update `ggtags-mode-map'."
           (cond (current-prefix-arg
                  (completing-read
                   (format (if default "Tag (default %s): " "Tag: ") default)
-                  (ggtags-completion-table) nil t nil nil default))
+                  ggtags-completion-table nil t nil nil default))
                 ((not default)
                  (user-error "No tag at point"))
                 (t (substring-no-properties default))))))
@@ -876,7 +875,7 @@ Global and Emacs."
            (valid-tag (when bounds
                         (test-completion
                          (buffer-substring (car bounds) (cdr bounds))
-                         (ggtags-completion-table))))
+                         ggtags-completion-table)))
            (o ggtags-tag-overlay)
            (done-p (lambda ()
                      (and (memq o (overlays-at (car bounds)))
@@ -932,7 +931,7 @@ Global and Emacs."
             (and (not (equal he-search-string ""))
                  (ggtags-find-project)
                  (sort (all-completions he-search-string
-                                        (ggtags-completion-table))
+                                        ggtags-completion-table)
                        'string-lessp))))
     (if (null he-expand-list)
         (progn

commit 08ec2a4b086bbd35e33ef40789cb49f4c3c9f184
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 16:55:39 2013 +0800

    Build tag completion table using completion-table-dynamic
    
    which works better with large projects. Remove ggtags-cache. New
    structure ggtags-project to store Global-related project settings.

diff --git a/ggtags.el b/ggtags.el
index bb9ad90..5460fda 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -145,8 +145,6 @@ properly update `ggtags-mode-map'."
 
 (defvar ggtags-bug-url "https://github.com/leoliu/ggtags/issues";)
 
-(defvar ggtags-cache nil)         ; (ROOT TABLE DIRTY CTAGS TIMESTAMP)
-
 (defvar ggtags-current-tag-name nil)
 
 ;; Used by ggtags-global-mode
@@ -176,71 +174,16 @@ properly update `ggtags-mode-map'."
 
 (defmacro ggtags-with-ctags-maybe (&rest body)
   `(let ((process-environment
-          (if (ggtags-cache-ctags-p (ggtags-root-directory))
+          (if (and (ggtags-find-project)
+                   (ggtags-project-ctags-p (ggtags-find-project)))
               (cons "GTAGSLABEL=ctags" process-environment)
             process-environment)))
      ,@body))
 
-(defun ggtags-oversize-p ()
-  (pcase ggtags-oversize-limit
-    (`nil nil)
-    (`t t)
-    (t (when (ggtags-root-directory)
-         (> (or (nth 7 (file-attributes
-                        (expand-file-name "GTAGS" (ggtags-root-directory))))
-                0)
-            ggtags-oversize-limit)))))
-
-(defun ggtags-use-ctags-p (root)
-  "Non-nil if exuberant-ctags is used for indexing ROOT."
-  (let ((default-directory (file-name-as-directory root)))
-    ;; Check if GRTAGS contains no tags.
-    (<= (length (split-string (shell-command-to-string
-                               "gtags -d GRTAGS | head -10")
-                              "\n" t))
-        4)))
-
-(defun ggtags-get-timestamp (root)
-  "Get the timestamp (float) of file GTAGS in ROOT directory.
-Return -1 if it does not exist."
-  (let ((file (expand-file-name "GTAGS" root)))
-    (if (file-exists-p file)
-        (float-time (nth 5 (file-attributes file)))
-      -1)))
-
 (defun ggtags-get-libpath ()
   (split-string (or (getenv "GTAGSLIBPATH") "")
                 (regexp-quote path-separator) t))
 
-(defun ggtags-cache-get (key)
-  (assoc (file-truename key) ggtags-cache))
-
-(defun ggtags-cache-set (key val &optional dirty)
-  (let* ((key (file-truename key))
-         (c (ggtags-cache-get key))
-         (ctags (ggtags-use-ctags-p key)))
-    (if c
-        (setcdr c (list val dirty ctags (float-time)))
-      (push (list key val dirty ctags (float-time)) ggtags-cache))))
-
-(defun ggtags-cache-mark-dirty (key flag)
-  "Return non-nil if operation is successful."
-  (let ((cache (ggtags-cache-get key)))
-    (when cache
-      (setcar (cddr cache) flag))))
-
-(defun ggtags-cache-ctags-p (key)
-  (fourth (ggtags-cache-get key)))
-
-(defun ggtags-cache-dirty-p (key)
-  "Value is non-nil if 'global -u' is needed."
-  (third (ggtags-cache-get key)))
-
-(defun ggtags-cache-stale-p (key)
-  "Value is non-nil if tags in cache needs to be rebuilt."
-  (> (ggtags-get-timestamp key)
-     (or (fifth (ggtags-cache-get key)) 0)))
-
 (defun ggtags-process-string (program &rest args)
   (with-temp-buffer
     (let ((exit (apply #'process-file program nil t nil args))
@@ -252,21 +195,57 @@ Return -1 if it does not exist."
           (error "`%s' non-zero exit: %s" program output))
       output)))
 
-(defvar-local ggtags-root-directory nil
-  "Internal; use function `ggtags-root-directory' instead.")
+;;; Store for project settings
+
+(defvar ggtags-projects (make-hash-table :size 7 :test #'equal))
+
+(defstruct (ggtags-project (:constructor ggtags-project--make)
+                           (:copier nil)
+                           (:type vector)
+                           :named)
+  root dirty-p ctags-p oversize-p)
+
+(defun ggtags-make-project (root &optional ctags-p)
+  (check-type root string)
+  (let* ((root (file-truename (file-name-as-directory root)))
+         (ctags-p (or ctags-p
+                      (<= (length
+                           (split-string (let ((default-directory root))
+                                           (shell-command-to-string
+                                            "gtags -d GRTAGS | head -10"))
+                                         "\n" t))
+                          4)))
+         (oversize-p (pcase ggtags-oversize-limit
+                       (`nil nil)
+                       (`t t)
+                       (t (> (or (nth 7 (file-attributes
+                                         (expand-file-name "GTAGS" root)))
+                                 0)
+                             ggtags-oversize-limit)))))
+    (puthash root (ggtags-project--make
+                   :root root :ctags-p ctags-p :oversize-p oversize-p)
+             ggtags-projects)))
+
+(defvar-local ggtags-project nil)
 
 ;;;###autoload
-(defun ggtags-root-directory ()
-  (or ggtags-root-directory
-      (setq ggtags-root-directory
-            (ignore-errors (file-name-as-directory
-                            (ggtags-process-string "global" "-pr"))))))
-
-(defun ggtags-check-root-directory ()
-  (or (ggtags-root-directory) (error "File GTAGS not found")))
-
-(defun ggtags-ensure-root-directory ()
-  (or (ggtags-root-directory)
+(defun ggtags-find-project ()
+  (or ggtags-project
+      (let ((root (ignore-errors (file-name-as-directory
+                                  (ggtags-process-string "global" "-pr")))))
+        (and root (setq ggtags-project
+                        (or (gethash (file-truename root) ggtags-projects)
+                            (ggtags-make-project root)))))))
+
+(defun ggtags-current-project-root ()
+  (and (ggtags-find-project)
+       (ggtags-project-root (ggtags-find-project))))
+
+(defun ggtags-check-project ()
+  (or (ggtags-find-project) (error "File GTAGS not found")))
+
+(defun ggtags-ensure-project ()
+  (or (ggtags-find-project)
       (when (or (yes-or-no-p "File GTAGS not found; run gtags? ")
                 (user-error "Aborted"))
         (let ((root (read-directory-name "Directory: " nil nil t)))
@@ -279,9 +258,9 @@ Return -1 if it does not exist."
                       (default-directory (file-name-as-directory root)))
                   (and (apply #'ggtags-process-string
                               "gtags" (and ggtags-use-idutils '("--idutils")))
+                       (ggtags-make-project root)
                        t))
-            (ggtags-tag-names-1 root)   ; update cache
-            (message "GTAGS generated in `%s'" (ggtags-root-directory)))))))
+            (message "GTAGS generated in `%s'" root))))))
 
 (defun ggtags-update-tags (&optional single-update)
   "Update GNU Global tag database."
@@ -293,40 +272,38 @@ Return -1 if it does not exist."
                        (file-truename buffer-file-name)))
      (ggtags-process-string "global" "-u"))))
 
-(defun ggtags-tag-names-1 (root &optional from-cache)
-  (when root
-    (if (and (not from-cache) (ggtags-cache-stale-p root))
-        (let* ((default-directory (file-name-as-directory root))
-               (tags (with-demoted-errors
-                       (process-lines "global" "-c" ""))))
-          (and tags (ggtags-cache-set root tags))
-          tags)
-      (cadr (ggtags-cache-get root)))))
-
 ;;;###autoload
-(defun ggtags-tag-names (&optional from-cache)
-  "Get a list of tag names."
-  (let ((root (ggtags-root-directory)))
-    (when (and root
-               (not (ggtags-oversize-p))
-               (not from-cache)
-               (ggtags-cache-dirty-p root))
-      (ggtags-update-tags))
-    (apply 'append (mapcar (lambda (r)
-                             (ggtags-tag-names-1 r from-cache))
-                           (cons root (ggtags-get-libpath))))))
+(defun ggtags-completion-table ()
+  (let (cache)
+    (completion-table-dynamic
+     (lambda (prefix)
+       (when (ggtags-find-project)
+         (when (and (ggtags-project-dirty-p (ggtags-find-project))
+                    (not (ggtags-project-oversize-p (ggtags-find-project))))
+           (ggtags-update-tags)
+           (setf (ggtags-project-dirty-p (ggtags-find-project)) nil))
+         (unless (equal prefix (car cache))
+           (setq cache
+                 (cons prefix
+                       (ggtags-with-ctags-maybe
+                        (split-string
+                         (apply #'ggtags-process-string
+                                "global"
+                                (if completion-ignore-case
+                                    (list "--ignore-case" "-Tc" prefix)
+                                  (list "-Tc" prefix)))
+                         "\n" t))))))
+       (cdr cache)))))
 
 (defun ggtags-read-tag ()
-  (ggtags-ensure-root-directory)
+  (ggtags-ensure-project)
   (let ((default (thing-at-point 'symbol))
         (completing-read-function ggtags-completing-read-function))
     (setq ggtags-current-tag-name
           (cond (current-prefix-arg
                  (completing-read
                   (format (if default "Tag (default %s): " "Tag: ") default)
-                  ;; XXX: build tag names more lazily such as using
-                  ;; `completion-table-dynamic'.
-                  (ggtags-tag-names) nil t nil nil default))
+                  (ggtags-completion-table) nil t nil nil default))
                 ((not default)
                  (user-error "No tag at point"))
                 (t (substring-no-properties default))))))
@@ -359,7 +336,7 @@ Return -1 if it does not exist."
     (setq ggtags-global-start-marker t)))
 
 (defun ggtags-global-start (command &optional root)
-  (let* ((default-directory (or root (ggtags-root-directory)))
+  (let* ((default-directory (or root (ggtags-current-project-root)))
          (split-window-preferred-function ggtags-split-window-function))
     (setq ggtags-global-start-marker (point-marker))
     (ggtags-navigation-mode +1)
@@ -374,7 +351,7 @@ Return -1 if it does not exist."
       (compile-goto-error))))
 
 (defun ggtags-find-tag (cmd name)
-  (ggtags-check-root-directory)
+  (ggtags-check-project)
   (ggtags-global-start (ggtags-global-build-command cmd name)))
 
 ;;;###autoload
@@ -384,7 +361,7 @@ If point is at a definition tag, find references, and vice 
versa.
 With a prefix arg (non-nil DEFINITION) always find defintions."
   (interactive (list (ggtags-read-tag) current-prefix-arg))
   (if (or definition
-          (ggtags-cache-ctags-p (ggtags-root-directory))
+          (ggtags-current-project-root)
           (not buffer-file-name))
       (ggtags-find-tag 'definition name)
     (ggtags-find-tag (format "--from-here=%d:%s"
@@ -404,7 +381,7 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
 
 (defun ggtags-read-string (prompt)
   "Like `read-string' but handle default automatically."
-  (ggtags-ensure-root-directory)
+  (ggtags-ensure-project)
   (let ((prompt (if (string-match ": *\\'" prompt)
                     (substring prompt 0 (match-beginning 0))
                   prompt))
@@ -429,8 +406,8 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
    (list (ggtags-read-string "POSIX regexp")
          (if current-prefix-arg
              (read-directory-name "Directory: " nil nil t)
-           (ggtags-root-directory))))
-  (ggtags-check-root-directory)
+           (ggtags-current-project-root))))
+  (ggtags-check-project)
   (let ((root (file-name-as-directory directory))
         (cmd (ggtags-global-build-command
               nil nil "-l" "--regexp" (prin1-to-string regexp))))
@@ -440,8 +417,8 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
   "Query replace FROM with TO on files in the Global buffer.
 If not in navigation mode, do a grep on FROM first.
 
-Note: the regexp FROM must be supported both by Global and
-Emacs."
+Note: the regular expression FROM must be supported by both
+Global and Emacs."
   (interactive (query-replace-read-args "Query replace (regexp)" t t))
   (unless (bound-and-true-p ggtags-navigation-mode)
     (let ((ggtags-auto-jump-to-first-match nil))
@@ -466,8 +443,8 @@ Emacs."
 (defun ggtags-delete-tag-files ()
   "Delete the tag files generated by gtags."
   (interactive)
-  (when (ggtags-root-directory)
-    (let ((files (directory-files (ggtags-root-directory) t
+  (when (ggtags-current-project-root)
+    (let ((files (directory-files (ggtags-current-project-root) t
                                   (regexp-opt '("GPATH" "GRTAGS" "GTAGS" 
"ID"))))
           (buffer "*GTags File List*"))
       (or files (user-error "No tag files found"))
@@ -771,8 +748,8 @@ Emacs."
 (defun ggtags-kill-file-buffers (&optional interactive)
   "Kill all buffers visiting files in the root directory."
   (interactive "p")
-  (ggtags-check-root-directory)
-  (let ((root (ggtags-root-directory))
+  (ggtags-check-project)
+  (let ((root (ggtags-current-project-root))
         (count 0)
         (some (lambda (pred list)
                 (loop for x in list when (funcall pred x) return it))))
@@ -789,12 +766,12 @@ Emacs."
          (message "%d %s killed" count (if (= count 1) "buffer" "buffers")))))
 
 (defun ggtags-after-save-function ()
-  (let ((root (with-demoted-errors (ggtags-root-directory))))
-    (when root
-      (ggtags-cache-mark-dirty root t)
-      ;; When oversize update on a per-save basis.
-      (when (and buffer-file-name (ggtags-oversize-p))
-        (ggtags-update-tags 'single-update)))))
+  (when (ggtags-find-project)
+    (setf (ggtags-project-dirty-p (ggtags-find-project)) t)
+    ;; When oversize update on a per-save basis.
+    (when (and buffer-file-name
+               (ggtags-project-oversize-p (ggtags-find-project)))
+      (ggtags-update-tags 'single-update))))
 
 (defvar ggtags-tag-overlay nil)
 (defvar ggtags-highlight-tag-timer nil)
@@ -897,8 +874,9 @@ Emacs."
       (overlay-put ggtags-tag-overlay 'ggtags t))
     (let* ((bounds (bounds-of-thing-at-point 'symbol))
            (valid-tag (when bounds
-                        (member (buffer-substring (car bounds) (cdr bounds))
-                                (ggtags-tag-names (ggtags-oversize-p)))))
+                        (test-completion
+                         (buffer-substring (car bounds) (cdr bounds))
+                         (ggtags-completion-table))))
            (o ggtags-tag-overlay)
            (done-p (lambda ()
                      (and (memq o (overlays-at (car bounds)))
@@ -952,9 +930,9 @@ Emacs."
                       (point))
       (setq he-expand-list
             (and (not (equal he-search-string ""))
-                 (with-demoted-errors (ggtags-root-directory))
+                 (ggtags-find-project)
                  (sort (all-completions he-search-string
-                                        (ggtags-tag-names))
+                                        (ggtags-completion-table))
                        'string-lessp))))
     (if (null he-expand-list)
         (progn

commit 4d091b56c99d2b3eff08fbc86c1d43fa8ad5cec3
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 10:58:02 2013 +0800

    Re-write ggtags-query-replace to be more IO-efficient
    
    by limiting to matched files. Also re-use tags-query-replace to
    provide an interruptible UI.

diff --git a/ggtags.el b/ggtags.el
index 301a54a..bb9ad90 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -436,32 +436,32 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
               nil nil "-l" "--regexp" (prin1-to-string regexp))))
     (ggtags-global-start cmd root)))
 
-(defun ggtags-query-replace (from to &optional delimited directory)
-  "Query replace FROM with TO on all files in DIRECTORY."
-  (interactive
-   (append (query-replace-read-args "Query replace (regexp)" t t)
-           (list (read-directory-name "In directory: " nil nil t))))
-  (let ((default-directory (file-name-as-directory directory)))
-    (ggtags-check-root-directory)
-    (dolist (file (process-lines "global" "-P" "-l" "."))
-      (let ((file (expand-file-name file directory)))
-        (when (file-exists-p file)
-          (let* ((message-log-max nil)
-                 (visited (get-file-buffer file))
-                 (buffer (or visited
-                             (with-demoted-errors
-                               (find-file-noselect file)))))
-            (when buffer
-              (set-buffer buffer)
-              (if (save-excursion
-                    (goto-char (point))
-                    (re-search-forward from nil t))
-                  (progn
-                    (switch-to-buffer (current-buffer))
-                    (perform-replace from to t t delimited
-                                     nil multi-query-replace-map))
-                (message "Nothing to do for `%s'" file)
-                (or visited (kill-buffer))))))))))
+(defun ggtags-query-replace (from to &optional delimited)
+  "Query replace FROM with TO on files in the Global buffer.
+If not in navigation mode, do a grep on FROM first.
+
+Note: the regexp FROM must be supported both by Global and
+Emacs."
+  (interactive (query-replace-read-args "Query replace (regexp)" t t))
+  (unless (bound-and-true-p ggtags-navigation-mode)
+    (let ((ggtags-auto-jump-to-first-match nil))
+      (ggtags-grep from)))
+  (let ((file-form
+         '(let ((files))
+            (ggtags-ensure-global-buffer
+              (with-temp-message "Waiting for Grep to finish..."
+                (while (get-buffer-process (current-buffer))
+                  (sit-for 0.2)))
+              (goto-char (point-min))
+              (while (ignore-errors (compilation-next-file 1) t)
+                (let ((m (get-text-property (point) 'compilation-message)))
+                  (push (expand-file-name
+                         (caar (compilation--loc->file-struct
+                                (compilation--message->loc m))))
+                        files))))
+            (ggtags-navigation-mode -1)
+            (nreverse files))))
+    (tags-query-replace from to delimited file-form)))
 
 (defun ggtags-delete-tag-files ()
   "Delete the tag files generated by gtags."

commit 852bb636882d2d937c12063bc10f61ae560a310d
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 08:08:38 2013 +0800

    Actually fix #17 (evil of copy&paste code)

diff --git a/ggtags.el b/ggtags.el
index 1673f56..301a54a 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -177,8 +177,8 @@ properly update `ggtags-mode-map'."
 (defmacro ggtags-with-ctags-maybe (&rest body)
   `(let ((process-environment
           (if (ggtags-cache-ctags-p (ggtags-root-directory))
-              (cons "GTAGSLABEL=ctags" process-environment))
-          process-environment))
+              (cons "GTAGSLABEL=ctags" process-environment)
+            process-environment)))
      ,@body))
 
 (defun ggtags-oversize-p ()

commit 370e46a50265cdb9b22c3dd7f4cae33cc84f8d2e
Author: Leo Liu <address@hidden>
Date:   Sat Nov 2 06:40:13 2013 +0800

    Fix #17 due to a typo

diff --git a/ggtags.el b/ggtags.el
index 50e4bc1..1673f56 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -268,14 +268,14 @@ Return -1 if it does not exist."
 (defun ggtags-ensure-root-directory ()
   (or (ggtags-root-directory)
       (when (or (yes-or-no-p "File GTAGS not found; run gtags? ")
-                (error "Aborted"))
+                (user-error "Aborted"))
         (let ((root (read-directory-name "Directory: " nil nil t)))
-          (and (zerop (length root)) (error "No directory chosen"))
+          (and (zerop (length root)) (user-error "No directory chosen"))
           (when (let ((process-environment
                        (if (and (not (getenv "GTAGSLABEL"))
                                 (yes-or-no-p "Use `ctags' backend? "))
-                           (cons "GTAGSLABEL=ctags" process-environment))
-                       process-environment)
+                           (cons "GTAGSLABEL=ctags" process-environment)
+                         process-environment))
                       (default-directory (file-name-as-directory root)))
                   (and (apply #'ggtags-process-string
                               "gtags" (and ggtags-use-idutils '("--idutils")))

commit 079b8d3ce33cb552bd2cf738b363cea6315e21d7
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 18:36:51 2013 +0800

    Fix ggtags-navigation-mode-abort to pop mark conditionally

diff --git a/ggtags.el b/ggtags.el
index 7952064..50e4bc1 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -349,13 +349,14 @@ Return -1 if it does not exist."
                     args)))
     (mapconcat 'identity (delq nil xs) " ")))
 
+;; takes three values: nil, t and a marker
 (defvar ggtags-global-start-marker nil)
 
 (defun ggtags-global-save-start-marker ()
   (when (markerp ggtags-global-start-marker)
     (eval-and-compile (require 'etags))
     (ring-insert find-tag-marker-ring ggtags-global-start-marker)
-    (setq ggtags-global-start-marker nil)))
+    (setq ggtags-global-start-marker t)))
 
 (defun ggtags-global-start (command &optional root)
   (let* ((default-directory (or root (ggtags-root-directory)))
@@ -711,10 +712,13 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
 (defun ggtags-navigation-mode-abort ()
   (interactive)
   (ggtags-navigation-mode -1)
-  (ggtags-navigation-mode-cleanup nil 0)
   ;; Run after (ggtags-navigation-mode -1) or
   ;; ggtags-global-start-marker might not have been saved.
-  (pop-tag-mark))
+  (when (and (not (markerp ggtags-global-start-marker))
+             ggtags-global-start-marker)
+    (setq ggtags-global-start-marker nil)
+    (pop-tag-mark))
+  (ggtags-navigation-mode-cleanup nil 0))
 
 (defun ggtags-navigation-next-file (n)
   (interactive "p")

commit b6c79ca819f696cce520e0a180323117cd6db548
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 18:26:12 2013 +0800

    Simplify ggtags-find-tag-regexp and robustify PATTERN args

diff --git a/ggtags.el b/ggtags.el
index 41459b1..7952064 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -415,31 +415,24 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
 
 (defun ggtags-grep (pattern)
   (interactive (list (ggtags-read-string "Grep pattern")))
-  (ggtags-find-tag 'grep pattern))
+  (ggtags-find-tag 'grep (format "--regexp %S" pattern)))
 
 (defun ggtags-idutils-query (pattern)
   (interactive (list (ggtags-read-string "ID query pattern")))
-  (ggtags-find-tag 'idutils pattern))
+  (ggtags-find-tag 'idutils (format "--regexp %S" pattern)))
 
 ;; NOTE: Coloured output in grep requested: http://goo.gl/Y9IcX
-(defun ggtags-find-tag-regexp (regexp file-or-directory)
-  "List all tags matching REGEXP in FILE-OR-DIRECTORY."
+(defun ggtags-find-tag-regexp (regexp directory)
+  "List tags matching REGEXP in DIRECTORY (default to project root)."
   (interactive
    (list (ggtags-read-string "POSIX regexp")
          (if current-prefix-arg
-             (read-file-name "File or directory: " nil buffer-file-name t)
+             (read-directory-name "Directory: " nil nil t)
            (ggtags-root-directory))))
   (ggtags-check-root-directory)
-  (let* ((root (if (file-directory-p file-or-directory)
-                   (file-name-as-directory file-or-directory)
-                 (file-name-directory file-or-directory)))
-         (cmd (ggtags-global-build-command
-               nil nil
-               (format "-e %S" regexp)
-               (if (file-directory-p file-or-directory)
-                   "-l ."
-                 (concat "-f " (shell-quote-argument
-                                (file-name-nondirectory 
file-or-directory)))))))
+  (let ((root (file-name-as-directory directory))
+        (cmd (ggtags-global-build-command
+              nil nil "-l" "--regexp" (prin1-to-string regexp))))
     (ggtags-global-start cmd root)))
 
 (defun ggtags-query-replace (from to &optional delimited directory)

commit ef6e8dd70cbf05c60eceb80b750113d56ee887ed
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 12:09:42 2013 +0800

    New macro ggtags-with-ctags-maybe to setup GTAGSLABEL

diff --git a/ggtags.el b/ggtags.el
index d0a464a..41459b1 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -174,6 +174,13 @@ properly update `ggtags-mode-map'."
          (error "No global buffer found"))
      (with-current-buffer compilation-last-buffer ,@body)))
 
+(defmacro ggtags-with-ctags-maybe (&rest body)
+  `(let ((process-environment
+          (if (ggtags-cache-ctags-p (ggtags-root-directory))
+              (cons "GTAGSLABEL=ctags" process-environment))
+          process-environment))
+     ,@body))
+
 (defun ggtags-oversize-p ()
   (pcase ggtags-oversize-limit
     (`nil nil)
@@ -279,15 +286,12 @@ Return -1 if it does not exist."
 (defun ggtags-update-tags (&optional single-update)
   "Update GNU Global tag database."
   (interactive)
-  (let ((process-environment
-         (if (ggtags-cache-ctags-p (ggtags-root-directory))
-             (cons "GTAGSLABEL=ctags" process-environment))
-         process-environment))
-    (if single-update
-        (when buffer-file-name
-          (process-file "global" nil 0 nil "--single-update"
-                        (file-truename buffer-file-name)))
-      (ggtags-process-string "global" "-u"))))
+  (ggtags-with-ctags-maybe
+   (if single-update
+       (when buffer-file-name
+         (process-file "global" nil 0 nil "--single-update"
+                       (file-truename buffer-file-name)))
+     (ggtags-process-string "global" "-u"))))
 
 (defun ggtags-tag-names-1 (root &optional from-cache)
   (when root
@@ -354,11 +358,12 @@ Return -1 if it does not exist."
     (setq ggtags-global-start-marker nil)))
 
 (defun ggtags-global-start (command &optional root)
-  (let ((default-directory (or root (ggtags-root-directory)))
-        (split-window-preferred-function ggtags-split-window-function))
+  (let* ((default-directory (or root (ggtags-root-directory)))
+         (split-window-preferred-function ggtags-split-window-function))
     (setq ggtags-global-start-marker (point-marker))
     (ggtags-navigation-mode +1)
-    (compilation-start command 'ggtags-global-mode)))
+    (ggtags-with-ctags-maybe
+     (compilation-start command 'ggtags-global-mode))))
 
 (defun ggtags-find-tag-resume ()
   (interactive)
@@ -429,7 +434,8 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
                    (file-name-as-directory file-or-directory)
                  (file-name-directory file-or-directory)))
          (cmd (ggtags-global-build-command
-               nil nil "-e" regexp
+               nil nil
+               (format "-e %S" regexp)
                (if (file-directory-p file-or-directory)
                    "-l ."
                  (concat "-f " (shell-quote-argument
@@ -711,9 +717,11 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
 
 (defun ggtags-navigation-mode-abort ()
   (interactive)
-  (pop-tag-mark)
   (ggtags-navigation-mode -1)
-  (ggtags-navigation-mode-cleanup nil 0))
+  (ggtags-navigation-mode-cleanup nil 0)
+  ;; Run after (ggtags-navigation-mode -1) or
+  ;; ggtags-global-start-marker might not have been saved.
+  (pop-tag-mark))
 
 (defun ggtags-navigation-next-file (n)
   (interactive "p")
@@ -925,7 +933,8 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
     (let ((file (file-truename buffer-file-name)))
       (with-temp-buffer
         (when (with-demoted-errors
-                (zerop (process-file "global" nil t nil "-f" file)))
+                (zerop (ggtags-with-ctags-maybe
+                        (process-file "global" nil t nil "-x" "-f" file))))
           (goto-char (point-min))
           (loop while (re-search-forward
                        "^\\([^ \t]+\\)[ \t]+\\([0-9]+\\)" nil t)

commit 1722cf09fb5e5b07c4ef424e85e9d743f74e7b0b
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 11:14:18 2013 +0800

    Fix "Don't save point to the tag marker ring if zero matches"
    
    of commit "7e700e9430a811228e238bffd7d9799cafb5cf5f".

diff --git a/ggtags.el b/ggtags.el
index 3720caf..d0a464a 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -524,6 +524,8 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
                  (if (re-search-backward "^\\([0-9]+\\) \\w+ located" nil t)
                      (string-to-number (match-string 1))
                    0))))
+    ;; Clear the start marker in case of zero matches.
+    (and (zerop count) (setq ggtags-global-start-marker nil))
     (cons (if (> exit-status 0)
               msg
             (format "found %d %s" count (if (= count 1) "match" "matches")))
@@ -689,8 +691,6 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
       (goto-char orig))))
 
 (defun ggtags-navigation-mode-cleanup (&optional buf time)
-  ;; Clear the marker in case of zero matches.
-  (setq ggtags-global-start-marker nil)
   (let ((buf (or buf compilation-last-buffer)))
     (and (buffer-live-p buf)
          (with-current-buffer buf
@@ -752,6 +752,9 @@ With a prefix arg (non-nil DEFINITION) always find 
defintions."
         (add-hook 'next-error-hook 'ggtags-move-to-tag)
         (add-hook 'next-error-hook 'ggtags-global-save-start-marker)
         (add-hook 'minibuffer-setup-hook 'ggtags-minibuffer-setup-function))
+    ;; Call `ggtags-global-save-start-marker' in case of exiting from
+    ;; `ggtags-handle-single-match' for single match.
+    (ggtags-global-save-start-marker)
     (remove-hook 'next-error-hook 'ggtags-global-save-start-marker)
     (remove-hook 'next-error-hook 'ggtags-move-to-tag)
     (remove-hook 'minibuffer-setup-hook 'ggtags-minibuffer-setup-function)))

commit a12c2e4684f1502aec36c626302fe397ababa985
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 09:23:12 2013 +0800

    Doc fix

diff --git a/README.rst b/README.rst
index f9a0b90..49fdb33 100644
--- a/README.rst
+++ b/README.rst
@@ -69,17 +69,21 @@ point is underlined if it is a valid (definition) tag.
 i.e. if point is at a definition tag find references and vice versa.
 ``M-]`` finds references.
 
-If multiple matches are found, navigation mode is entered. In this
-mode, ``M-n`` and ``M-p`` moves to next and previous match, ``M-}``
-and ``M-{`` to next and previous file respectively. ``M-o`` toggles
-between full and abbreviated displays of file names in the auxiliary
-popup window. When you locate the right match, press RET to finish
-which hides the auxiliary window and exits navigation mode. You can
-resume the search using ``M-,``. To abort the search press ``M-*``.
+If multiple matches are found, navigation mode is entered, the
+mode-line lighter changed, and a navigation menu-bar entry
+presented. In this mode, ``M-n`` and ``M-p`` moves to next and
+previous match, ``M-}`` and ``M-{`` to next and previous file
+respectively. ``M-o`` toggles between full and abbreviated displays of
+file names in the auxiliary popup window. When you locate the right
+match, press RET to finish which hides the auxiliary window and exits
+navigation mode. You can resume the search using ``M-,``. To abort the
+search press ``M-*``.
 
 Normally after a few searches a dozen buffers are created visiting
 files tracked by GNU Global. ``C-c M-k`` helps clean them up.
 
+Check the menu-bar entry ``Ggtags`` for other useful commands.
+
 Development
 ~~~~~~~~~~~
 
diff --git a/ggtags.el b/ggtags.el
index 90c6c5b..3720caf 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -36,16 +36,20 @@
 ;; point, i.e. if point is at a definition tag find references and
 ;; vice versa. `M-]' finds references.
 ;;
-;; If multiple matches are found, navigation mode is entered. In this
-;; mode, `M-n' and `M-p' moves to next and previous match, `M-}' and
-;; `M-{' to next and previous file respectively. `M-o' toggles between
-;; full and abbreviated displays of file names in the auxiliary popup
-;; window. When you locate the right match, press RET to finish which
-;; hides the auxiliary window and exits navigation mode. You can
-;; resume the search using `M-,'. To abort the search press `M-*'.
+;; If multiple matches are found, navigation mode is entered, the
+;; mode-line lighter changed, and a navigation menu-bar entry
+;; presented. In this mode, `M-n' and `M-p' moves to next and previous
+;; match, `M-}' and `M-{' to next and previous file respectively.
+;; `M-o' toggles between full and abbreviated displays of file names
+;; in the auxiliary popup window. When you locate the right match,
+;; press RET to finish which hides the auxiliary window and exits
+;; navigation mode. You can resume the search using `M-,'. To abort
+;; the search press `M-*'.
 ;;
 ;; Normally after a few searches a dozen buffers are created visiting
 ;; files tracked by GNU Global. `C-c M-k' helps clean them up.
+;;
+;; Check the menu-bar entry `Ggtags' for other useful commands.
 
 ;;; Code:
 
@@ -368,12 +372,12 @@ Return -1 if it does not exist."
   (ggtags-global-start (ggtags-global-build-command cmd name)))
 
 ;;;###autoload
-(defun ggtags-find-tag-dwim (name &optional force)
+(defun ggtags-find-tag-dwim (name &optional definition)
   "Find definitions or references of tag NAME by context.
 If point is at a definition tag, find references, and vice versa.
-With a prefix arg (non-nil FORCE) always find defintions."
+With a prefix arg (non-nil DEFINITION) always find defintions."
   (interactive (list (ggtags-read-tag) current-prefix-arg))
-  (if (or force
+  (if (or definition
           (ggtags-cache-ctags-p (ggtags-root-directory))
           (not buffer-file-name))
       (ggtags-find-tag 'definition name)
@@ -388,11 +392,12 @@ With a prefix arg (non-nil FORCE) always find defintions."
   (ggtags-find-tag 'reference name))
 
 (defun ggtags-find-other-symbol (name)
-  ;; Other than definition or reference
+  "Find tag NAME wchi is a reference without a definition."
   (interactive (list (ggtags-read-tag)))
   (ggtags-find-tag 'symbol name))
 
 (defun ggtags-read-string (prompt)
+  "Like `read-string' but handle default automatically."
   (ggtags-ensure-root-directory)
   (let ((prompt (if (string-match ": *\\'" prompt)
                     (substring prompt 0 (match-beginning 0))
@@ -821,6 +826,9 @@ With a prefix arg (non-nil FORCE) always find defintions."
     (define-key menu [delete-tags]
       '(menu-item "Delete tag files" ggtags-delete-tag-files
                   :enable (ggtags-root-directory)))
+    (define-key menu [pop-mark]
+      '(menu-item "Pop mark" pop-tag-mark
+                  :help "Pop to previous mark and destroy it"))
     (define-key menu [next-mark]
       '(menu-item "Next mark" ggtags-next-mark))
     (define-key menu [prev-mark]

commit 8c05dc12c76e4f597c6e7f8d171c6618805ed0e1
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 08:25:01 2013 +0800

    Make it possible to force finding definitions

diff --git a/ggtags.el b/ggtags.el
index 73db74e..90c6c5b 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -368,11 +368,13 @@ Return -1 if it does not exist."
   (ggtags-global-start (ggtags-global-build-command cmd name)))
 
 ;;;###autoload
-(defun ggtags-find-tag-dwim (name)
+(defun ggtags-find-tag-dwim (name &optional force)
   "Find definitions or references of tag NAME by context.
-If point is at a definition tag, find references, and vice versa."
-  (interactive (list (ggtags-read-tag)))
-  (if (or (ggtags-cache-ctags-p (ggtags-root-directory))
+If point is at a definition tag, find references, and vice versa.
+With a prefix arg (non-nil FORCE) always find defintions."
+  (interactive (list (ggtags-read-tag) current-prefix-arg))
+  (if (or force
+          (ggtags-cache-ctags-p (ggtags-root-directory))
           (not buffer-file-name))
       (ggtags-find-tag 'definition name)
     (ggtags-find-tag (format "--from-here=%d:%s"

commit 749a904ddcaf542ade378fe2d0c14a69d532a2fc
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 08:14:14 2013 +0800

    New variable ggtags-global-ignore-case

diff --git a/ggtags.el b/ggtags.el
index 6e17b6b..73db74e 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -119,6 +119,12 @@ If an integer abbreviate only names longer than that 
number."
                  (const cscope))
   :group 'ggtags)
 
+(defcustom ggtags-global-ignore-case nil
+  "Non-nil if Global should ignore case."
+  :safe 'booleanp
+  :type 'boolean
+  :group 'ggtags)
+
 (defcustom ggtags-mode-prefix-key "\C-c"
   "Key binding used for `ggtags-mode-prefix-map'.
 Users should change the value using `customize-variable' to
@@ -321,6 +327,7 @@ Return -1 if it does not exist."
   ;; CMD can be definition, reference, symbol, grep, idutils
   (let ((xs (append (list "global" "-v"
                           (format "--result=%s" ggtags-global-output-format)
+                          (and ggtags-global-ignore-case "--ignore-case")
                           (and ggtags-global-has-color "--color")
                           (and ggtags-global-has-path-style
                                "--path-style=shorter")

commit c434009164f6f89ba976f18858fefb3736a3bc44
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 02:10:18 2013 +0800

    New helper function ggtags-read-string and use it

diff --git a/ggtags.el b/ggtags.el
index a88c6a2..6e17b6b 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -383,19 +383,30 @@ If point is at a definition tag, find references, and 
vice versa."
   (interactive (list (ggtags-read-tag)))
   (ggtags-find-tag 'symbol name))
 
+(defun ggtags-read-string (prompt)
+  (ggtags-ensure-root-directory)
+  (let ((prompt (if (string-match ": *\\'" prompt)
+                    (substring prompt 0 (match-beginning 0))
+                  prompt))
+        (default (thing-at-point 'symbol)))
+    (read-string (format (if default "%s (default `%s'): "
+                           "%s: ")
+                         prompt default)
+                 nil nil (and default (substring-no-properties default)))))
+
 (defun ggtags-grep (pattern)
-  (interactive (list (read-string "Grep pattern: ")))
+  (interactive (list (ggtags-read-string "Grep pattern")))
   (ggtags-find-tag 'grep pattern))
 
 (defun ggtags-idutils-query (pattern)
-  (interactive (list (read-string "ID query pattern: ")))
+  (interactive (list (ggtags-read-string "ID query pattern")))
   (ggtags-find-tag 'idutils pattern))
 
 ;; NOTE: Coloured output in grep requested: http://goo.gl/Y9IcX
 (defun ggtags-find-tag-regexp (regexp file-or-directory)
   "List all tags matching REGEXP in FILE-OR-DIRECTORY."
   (interactive
-   (list (read-string "POSIX regexp: ")
+   (list (ggtags-read-string "POSIX regexp")
          (if current-prefix-arg
              (read-file-name "File or directory: " nil buffer-file-name t)
            (ggtags-root-directory))))

commit 63140683ff041b9ff358e2d16a44981043d74378
Author: Leo Liu <address@hidden>
Date:   Fri Nov 1 00:16:53 2013 +0800

    More refactoring to add more commands

diff --git a/README.rst b/README.rst
index 697cc1d..f9a0b90 100644
--- a/README.rst
+++ b/README.rst
@@ -67,8 +67,7 @@ point is underlined if it is a valid (definition) tag.
 
 ``M-.`` finds definitions or references according to the tag at point,
 i.e. if point is at a definition tag find references and vice versa.
-``C-u M-.`` is verbose and will ask you the name - with completion
-- and the type of tag to search.
+``M-]`` finds references.
 
 If multiple matches are found, navigation mode is entered. In this
 mode, ``M-n`` and ``M-p`` moves to next and previous match, ``M-}``
diff --git a/ggtags.el b/ggtags.el
index d25e13d..a88c6a2 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -34,8 +34,7 @@
 ;;
 ;; `M-.' finds definition or references according to the context at
 ;; point, i.e. if point is at a definition tag find references and
-;; vice versa. `C-u M-.' is verbose and will ask you the name - with
-;; completion - and the type of tag to search.
+;; vice versa. `M-]' finds references.
 ;;
 ;; If multiple matches are found, navigation mode is entered. In this
 ;; mode, `M-n' and `M-p' moves to next and previous match, `M-}' and
@@ -106,6 +105,11 @@ If an integer abbreviate only names longer than that 
number."
   :type 'function
   :group 'ggtags)
 
+(defcustom ggtags-use-idutils (and (executable-find "mkid") t)
+  "Non-nil to also generate the idutils DB."
+  :type 'boolean
+  :group 'ggtags)
+
 (defcustom ggtags-global-output-format 'grep
   "The output format for the 'global' command."
   :type '(choice (const path)
@@ -256,7 +260,9 @@ Return -1 if it does not exist."
                            (cons "GTAGSLABEL=ctags" process-environment))
                        process-environment)
                       (default-directory (file-name-as-directory root)))
-                  (and (ggtags-process-string "gtags") t))
+                  (and (apply #'ggtags-process-string
+                              "gtags" (and ggtags-use-idutils '("--idutils")))
+                       t))
             (ggtags-tag-names-1 root)   ; update cache
             (message "GTAGS generated in `%s'" (ggtags-root-directory)))))))
 
@@ -296,20 +302,20 @@ Return -1 if it does not exist."
                              (ggtags-tag-names-1 r from-cache))
                            (cons root (ggtags-get-libpath))))))
 
-(defun ggtags-read-tag (quick)
+(defun ggtags-read-tag ()
   (ggtags-ensure-root-directory)
   (let ((default (thing-at-point 'symbol))
         (completing-read-function ggtags-completing-read-function))
     (setq ggtags-current-tag-name
-          (if quick (if default
-                        (substring-no-properties default)
-                      (user-error "No tag at point"))
-            (completing-read
-             (format (if default "Tag (default %s): " "Tag: ") default)
-             ;; XXX: build tag names more lazily such as using
-             ;; `completion-table-dynamic'.
-             (ggtags-tag-names)
-             nil t nil nil default)))))
+          (cond (current-prefix-arg
+                 (completing-read
+                  (format (if default "Tag (default %s): " "Tag: ") default)
+                  ;; XXX: build tag names more lazily such as using
+                  ;; `completion-table-dynamic'.
+                  (ggtags-tag-names) nil t nil nil default))
+                ((not default)
+                 (user-error "No tag at point"))
+                (t (substring-no-properties default))))))
 
 (defun ggtags-global-build-command (cmd &rest args)
   ;; CMD can be definition, reference, symbol, grep, idutils
@@ -350,37 +356,40 @@ Return -1 if it does not exist."
     (let ((split-window-preferred-function ggtags-split-window-function))
       (compile-goto-error))))
 
-;;;###autoload
-(defun ggtags-find-tag (name &optional verbose)
-  "Find definitions or references to tag NAME by context.
-If point is at a definition tag, find references, and vice versa.
-When called with prefix, ask the name and kind of tag."
-  (interactive (list (ggtags-read-tag (not current-prefix-arg))
-                     current-prefix-arg))
+(defun ggtags-find-tag (cmd name)
   (ggtags-check-root-directory)
-  (when (equal name "")
-    (user-error "No tag"))
-  (let ((help-char ??)
-        (help-form "\
-d: definition          (-d)
-r: reference           (-r)
-s: symbol              (-s)
-?: show this help\n")
-        (cmd (cond (verbose
-                    (format "-%c" (read-char-choice
-                                   "Tag type? (d/r/s/?) " '(?d ?r ?s))))
-                   ((ggtags-cache-ctags-p (ggtags-root-directory))
-                    "-d")
-                   ((not buffer-file-name)
-                    (ggtags-find-tag name t)
-                    nil)
-                   (t (format "--from-here=%d:%s"
-                              (line-number-at-pos)
-                              (shell-quote-argument
-                               (expand-file-name
-                                (file-truename buffer-file-name))))))))
-    (when cmd
-      (ggtags-global-start (ggtags-global-build-command cmd name)))))
+  (ggtags-global-start (ggtags-global-build-command cmd name)))
+
+;;;###autoload
+(defun ggtags-find-tag-dwim (name)
+  "Find definitions or references of tag NAME by context.
+If point is at a definition tag, find references, and vice versa."
+  (interactive (list (ggtags-read-tag)))
+  (if (or (ggtags-cache-ctags-p (ggtags-root-directory))
+          (not buffer-file-name))
+      (ggtags-find-tag 'definition name)
+    (ggtags-find-tag (format "--from-here=%d:%s"
+                             (line-number-at-pos)
+                             (shell-quote-argument
+                              (file-truename buffer-file-name)))
+                     name)))
+
+(defun ggtags-find-reference (name)
+  (interactive (list (ggtags-read-tag)))
+  (ggtags-find-tag 'reference name))
+
+(defun ggtags-find-other-symbol (name)
+  ;; Other than definition or reference
+  (interactive (list (ggtags-read-tag)))
+  (ggtags-find-tag 'symbol name))
+
+(defun ggtags-grep (pattern)
+  (interactive (list (read-string "Grep pattern: ")))
+  (ggtags-find-tag 'grep pattern))
+
+(defun ggtags-idutils-query (pattern)
+  (interactive (list (read-string "ID query pattern: ")))
+  (ggtags-find-tag 'idutils pattern))
 
 ;; NOTE: Coloured output in grep requested: http://goo.gl/Y9IcX
 (defun ggtags-find-tag-regexp (regexp file-or-directory)
@@ -762,6 +771,9 @@ s: symbol              (-s)
     (define-key m (kbd "M-DEL") 'ggtags-delete-tag-files)
     (define-key m "\M-p" 'ggtags-prev-mark)
     (define-key m "\M-n" 'ggtags-next-mark)
+    (define-key m "\M-s" 'ggtags-find-other-symbol)
+    (define-key m "\M-g" 'ggtags-grep)
+    (define-key m "\M-i" 'ggtags-idutils-query)
     (define-key m "\M-k" 'ggtags-kill-file-buffers)
     (define-key m (kbd "M-%") 'ggtags-query-replace)
     m))
@@ -769,7 +781,8 @@ s: symbol              (-s)
 (defvar ggtags-mode-map
   (let ((map (make-sparse-keymap))
         (menu (make-sparse-keymap "Ggtags")))
-    (define-key map "\M-." 'ggtags-find-tag)
+    (define-key map "\M-." 'ggtags-find-tag-dwim)
+    (define-key map (kbd "M-]") 'ggtags-find-reference)
     (define-key map (kbd "C-M-.") 'ggtags-find-tag-regexp)
     (define-key map ggtags-mode-prefix-key ggtags-mode-prefix-map)
     ;; Menu items
@@ -795,12 +808,20 @@ s: symbol              (-s)
     (define-key menu [sep1] menu-bar-separator)
     (define-key menu [query-replace]
       '(menu-item "Query replace" ggtags-query-replace))
-    (define-key menu [find-tag-regexp]
-      '(menu-item "Find tag matching regexp" ggtags-find-tag-regexp))
+    (define-key menu [idutils]
+      '(menu-item "Query idutils DB" ggtags-idutils-query))
+    (define-key menu [grep]
+      '(menu-item "Use grep" ggtags-grep))
+    (define-key menu [find-symbol]
+      '(menu-item "Find other symbol" ggtags-find-other-symbol))
+    (define-key menu [find-reference]
+      '(menu-item "Find reference" ggtags-find-reference))
     (define-key menu [find-tag-resume]
       '(menu-item "Resume find tag" tags-loop-continue))
+    (define-key menu [find-tag-regexp]
+      '(menu-item "Find tag matching regexp" ggtags-find-tag-regexp))
     (define-key menu [find-tag]
-      '(menu-item "Find tag" ggtags-find-tag))
+      '(menu-item "Find tag" ggtags-find-tag-dwim))
     (define-key menu [run-gtags]
       '(menu-item (if (ggtags-root-directory) "Update tag files" "Run gtags")
                   (lambda () (interactive)

commit 7e700e9430a811228e238bffd7d9799cafb5cf5f
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 23:07:39 2013 +0800

    Don't save point to the tag marker ring if zero matches
    
    Thus running it in next-error-hook.

diff --git a/ggtags.el b/ggtags.el
index b9f6a53..d25e13d 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -328,15 +328,20 @@ Return -1 if it does not exist."
                     args)))
     (mapconcat 'identity (delq nil xs) " ")))
 
+(defvar ggtags-global-start-marker nil)
+
+(defun ggtags-global-save-start-marker ()
+  (when (markerp ggtags-global-start-marker)
+    (eval-and-compile (require 'etags))
+    (ring-insert find-tag-marker-ring ggtags-global-start-marker)
+    (setq ggtags-global-start-marker nil)))
+
 (defun ggtags-global-start (command &optional root)
   (let ((default-directory (or root (ggtags-root-directory)))
         (split-window-preferred-function ggtags-split-window-function))
-    (prog1 (compilation-start command 'ggtags-global-mode)
-      (eval-and-compile (require 'etags))
-      (ring-insert find-tag-marker-ring (point-marker))
-      (setq tags-loop-scan t
-            tags-loop-operate '(ggtags-find-tag-resume))
-      (ggtags-navigation-mode +1))))
+    (setq ggtags-global-start-marker (point-marker))
+    (ggtags-navigation-mode +1)
+    (compilation-start command 'ggtags-global-mode)))
 
 (defun ggtags-find-tag-resume ()
   (interactive)
@@ -650,6 +655,8 @@ s: symbol              (-s)
       (goto-char orig))))
 
 (defun ggtags-navigation-mode-cleanup (&optional buf time)
+  ;; Clear the marker in case of zero matches.
+  (setq ggtags-global-start-marker nil)
   (let ((buf (or buf compilation-last-buffer)))
     (and (buffer-live-p buf)
          (with-current-buffer buf
@@ -664,6 +671,8 @@ s: symbol              (-s)
   (interactive)
   (ggtags-navigation-mode -1)
   (setq ggtags-current-mark nil)
+  (setq tags-loop-scan t
+        tags-loop-operate '(ggtags-find-tag-resume))
   (ggtags-navigation-mode-cleanup))
 
 (defun ggtags-navigation-mode-abort ()
@@ -707,7 +716,9 @@ s: symbol              (-s)
   (if ggtags-navigation-mode
       (progn
         (add-hook 'next-error-hook 'ggtags-move-to-tag)
+        (add-hook 'next-error-hook 'ggtags-global-save-start-marker)
         (add-hook 'minibuffer-setup-hook 'ggtags-minibuffer-setup-function))
+    (remove-hook 'next-error-hook 'ggtags-global-save-start-marker)
     (remove-hook 'next-error-hook 'ggtags-move-to-tag)
     (remove-hook 'minibuffer-setup-hook 'ggtags-minibuffer-setup-function)))
 

commit 37cb5238fe15fe2ebd81a7a055497bb256d054b2
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 22:04:21 2013 +0800

    Remove FMT arg to ggtags-global-build-command
    
    it must be globally set to work properly.

diff --git a/ggtags.el b/ggtags.el
index 6da083a..b9f6a53 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -311,23 +311,21 @@ Return -1 if it does not exist."
              (ggtags-tag-names)
              nil t nil nil default)))))
 
-(defun ggtags-global-build-command (fmt cmd &rest args)
-  ;; FMT if nil takes the value of ggtags-global-output-format
+(defun ggtags-global-build-command (cmd &rest args)
   ;; CMD can be definition, reference, symbol, grep, idutils
-  (let* ((fmt (format "--result=%s"
-                      (or fmt ggtags-global-output-format)))
-         (xs (append (list "global" "-v" fmt
-                           (and ggtags-global-has-color "--color")
-                           (and ggtags-global-has-path-style
-                                "--path-style=shorter")
-                           (pcase cmd
-                             ((pred stringp) cmd)
-                             (`definition "-d")
-                             (`reference "-r")
-                             (`symbol "-s")
-                             (`grep "--grep")
-                             (`idutils "--idutils")))
-                     args)))
+  (let ((xs (append (list "global" "-v"
+                          (format "--result=%s" ggtags-global-output-format)
+                          (and ggtags-global-has-color "--color")
+                          (and ggtags-global-has-path-style
+                               "--path-style=shorter")
+                          (pcase cmd
+                            ((pred stringp) cmd)
+                            (`definition "-d")
+                            (`reference "-r")
+                            (`symbol "-s")
+                            (`grep "--grep")
+                            (`idutils "--idutils")))
+                    args)))
     (mapconcat 'identity (delq nil xs) " ")))
 
 (defun ggtags-global-start (command &optional root)
@@ -377,7 +375,7 @@ s: symbol              (-s)
                                (expand-file-name
                                 (file-truename buffer-file-name))))))))
     (when cmd
-      (ggtags-global-start (ggtags-global-build-command nil cmd name)))))
+      (ggtags-global-start (ggtags-global-build-command cmd name)))))
 
 ;; NOTE: Coloured output in grep requested: http://goo.gl/Y9IcX
 (defun ggtags-find-tag-regexp (regexp file-or-directory)

commit 4f3ef98dad8ead3f67209d002d21c85281aae573
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 21:49:40 2013 +0800

    Refactor out ggtags-global-start for re-use
    
    Rename ggtags-global-options to ggtags-global-build-command.
    Rename ggtags-list-tags to ggtags-find-tag-regexp.

diff --git a/ggtags.el b/ggtags.el
index 981e7f0..6da083a 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -238,7 +238,8 @@ Return -1 if it does not exist."
 (defun ggtags-root-directory ()
   (or ggtags-root-directory
       (setq ggtags-root-directory
-            (ignore-errors (ggtags-process-string "global" "-pr")))))
+            (ignore-errors (file-name-as-directory
+                            (ggtags-process-string "global" "-pr"))))))
 
 (defun ggtags-check-root-directory ()
   (or (ggtags-root-directory) (error "File GTAGS not found")))
@@ -297,10 +298,12 @@ Return -1 if it does not exist."
 
 (defun ggtags-read-tag (quick)
   (ggtags-ensure-root-directory)
-  (let ((default (substring-no-properties (or (thing-at-point 'symbol) "")))
+  (let ((default (thing-at-point 'symbol))
         (completing-read-function ggtags-completing-read-function))
     (setq ggtags-current-tag-name
-          (if quick (or default (user-error "No tag at point"))
+          (if quick (if default
+                        (substring-no-properties default)
+                      (user-error "No tag at point"))
             (completing-read
              (format (if default "Tag (default %s): " "Tag: ") default)
              ;; XXX: build tag names more lazily such as using
@@ -308,11 +311,41 @@ Return -1 if it does not exist."
              (ggtags-tag-names)
              nil t nil nil default)))))
 
-(defun ggtags-global-options ()
-  (concat "-v --result="
-          (symbol-name ggtags-global-output-format)
-          (and ggtags-global-has-color " --color")
-          (and ggtags-global-has-path-style " --path-style=shorter")))
+(defun ggtags-global-build-command (fmt cmd &rest args)
+  ;; FMT if nil takes the value of ggtags-global-output-format
+  ;; CMD can be definition, reference, symbol, grep, idutils
+  (let* ((fmt (format "--result=%s"
+                      (or fmt ggtags-global-output-format)))
+         (xs (append (list "global" "-v" fmt
+                           (and ggtags-global-has-color "--color")
+                           (and ggtags-global-has-path-style
+                                "--path-style=shorter")
+                           (pcase cmd
+                             ((pred stringp) cmd)
+                             (`definition "-d")
+                             (`reference "-r")
+                             (`symbol "-s")
+                             (`grep "--grep")
+                             (`idutils "--idutils")))
+                     args)))
+    (mapconcat 'identity (delq nil xs) " ")))
+
+(defun ggtags-global-start (command &optional root)
+  (let ((default-directory (or root (ggtags-root-directory)))
+        (split-window-preferred-function ggtags-split-window-function))
+    (prog1 (compilation-start command 'ggtags-global-mode)
+      (eval-and-compile (require 'etags))
+      (ring-insert find-tag-marker-ring (point-marker))
+      (setq tags-loop-scan t
+            tags-loop-operate '(ggtags-find-tag-resume))
+      (ggtags-navigation-mode +1))))
+
+(defun ggtags-find-tag-resume ()
+  (interactive)
+  (ggtags-ensure-global-buffer
+    (ggtags-navigation-mode +1)
+    (let ((split-window-preferred-function ggtags-split-window-function))
+      (compile-goto-error))))
 
 ;;;###autoload
 (defun ggtags-find-tag (name &optional verbose)
@@ -322,76 +355,49 @@ When called with prefix, ask the name and kind of tag."
   (interactive (list (ggtags-read-tag (not current-prefix-arg))
                      current-prefix-arg))
   (ggtags-check-root-directory)
-  (let ((split-window-preferred-function ggtags-split-window-function)
-        (default-directory (ggtags-root-directory))
-        (help-char ??)
+  (when (equal name "")
+    (user-error "No tag"))
+  (let ((help-char ??)
         (help-form "\
-d: definitions          (-d)
-r: references           (-r)
-s: symbols              (-s)
-?: show this help\n"))
-    (compilation-start
-     (cond
-      (verbose
-       (format "global %s -%s \"%s\""
-               (ggtags-global-options)
-               (char-to-string
-                (read-char-choice "Tag type? (d/r/s/?) " '(?d ?r ?s)))
-               name))
-      ((ggtags-cache-ctags-p (ggtags-root-directory))
-       (format "global %s -d %s" (ggtags-global-options) name))
-      ((not buffer-file-name)
-       (ggtags-find-tag name t))
-      (t
-       (format "global %s --from-here=%d:%s \"%s\""
-               (ggtags-global-options)
-               (line-number-at-pos)
-               (shell-quote-argument
-                (expand-file-name (file-truename buffer-file-name)))
-               name)))
-     'ggtags-global-mode))
-  (eval-and-compile (require 'etags))
-  (ring-insert find-tag-marker-ring (point-marker))
-  (setq tags-loop-scan t
-        tags-loop-operate '(ggtags-find-tag-resume))
-  (ggtags-navigation-mode +1))
-
-(defun ggtags-find-tag-resume ()
-  (interactive)
-  (ggtags-ensure-global-buffer
-    (ggtags-navigation-mode +1)
-    (let ((split-window-preferred-function ggtags-split-window-function))
-      (compile-goto-error))))
+d: definition          (-d)
+r: reference           (-r)
+s: symbol              (-s)
+?: show this help\n")
+        (cmd (cond (verbose
+                    (format "-%c" (read-char-choice
+                                   "Tag type? (d/r/s/?) " '(?d ?r ?s))))
+                   ((ggtags-cache-ctags-p (ggtags-root-directory))
+                    "-d")
+                   ((not buffer-file-name)
+                    (ggtags-find-tag name t)
+                    nil)
+                   (t (format "--from-here=%d:%s"
+                              (line-number-at-pos)
+                              (shell-quote-argument
+                               (expand-file-name
+                                (file-truename buffer-file-name))))))))
+    (when cmd
+      (ggtags-global-start (ggtags-global-build-command nil cmd name)))))
 
 ;; NOTE: Coloured output in grep requested: http://goo.gl/Y9IcX
-(defun ggtags-list-tags (regexp file-or-directory)
+(defun ggtags-find-tag-regexp (regexp file-or-directory)
   "List all tags matching REGEXP in FILE-OR-DIRECTORY."
-  (interactive (list (read-string "POSIX regexp: ")
-                     (read-file-name "Directory: "
-                                     (if current-prefix-arg
-                                         (ggtags-root-directory)
-                                       default-directory)
-                                     buffer-file-name t)))
-  (let ((split-window-preferred-function ggtags-split-window-function)
-        (default-directory (if (file-directory-p file-or-directory)
-                               (file-name-as-directory file-or-directory)
-                             (file-name-directory file-or-directory))))
-    (ggtags-check-root-directory)
-    (eval-and-compile (require 'etags))
-    (ggtags-navigation-mode +1)
-    (ring-insert find-tag-marker-ring (point-marker))
-    (with-current-buffer
-        (compilation-start (format "global %s -e %s %s"
-                                   (ggtags-global-options)
-                                   regexp
-                                   (if (file-directory-p file-or-directory)
-                                       "-l ."
-                                     (concat "-f " (shell-quote-argument
-                                                    (file-name-nondirectory
-                                                     file-or-directory)))))
-                           'ggtags-global-mode)
-      (setq-local compilation-auto-jump-to-first-error nil)
-      (remove-hook 'compilation-finish-functions 'ggtags-handle-single-match 
t))))
+  (interactive
+   (list (read-string "POSIX regexp: ")
+         (if current-prefix-arg
+             (read-file-name "File or directory: " nil buffer-file-name t)
+           (ggtags-root-directory))))
+  (ggtags-check-root-directory)
+  (let* ((root (if (file-directory-p file-or-directory)
+                   (file-name-as-directory file-or-directory)
+                 (file-name-directory file-or-directory)))
+         (cmd (ggtags-global-build-command
+               nil nil "-e" regexp
+               (if (file-directory-p file-or-directory)
+                   "-l ."
+                 (concat "-f " (shell-quote-argument
+                                (file-name-nondirectory 
file-or-directory)))))))
+    (ggtags-global-start cmd root)))
 
 (defun ggtags-query-replace (from to &optional delimited directory)
   "Query replace FROM with TO on all files in DIRECTORY."
@@ -748,7 +754,6 @@ s: symbols              (-s)
     (define-key m "\M-p" 'ggtags-prev-mark)
     (define-key m "\M-n" 'ggtags-next-mark)
     (define-key m "\M-k" 'ggtags-kill-file-buffers)
-    (define-key m "\M-l" 'ggtags-list-tags)
     (define-key m (kbd "M-%") 'ggtags-query-replace)
     m))
 
@@ -756,6 +761,7 @@ s: symbols              (-s)
   (let ((map (make-sparse-keymap))
         (menu (make-sparse-keymap "Ggtags")))
     (define-key map "\M-." 'ggtags-find-tag)
+    (define-key map (kbd "C-M-.") 'ggtags-find-tag-regexp)
     (define-key map ggtags-mode-prefix-key ggtags-mode-prefix-map)
     ;; Menu items
     (define-key map [menu-bar ggtags] (cons "Ggtags" menu))
@@ -780,8 +786,8 @@ s: symbols              (-s)
     (define-key menu [sep1] menu-bar-separator)
     (define-key menu [query-replace]
       '(menu-item "Query replace" ggtags-query-replace))
-    (define-key menu [list-tags]
-      '(menu-item "List tags" ggtags-list-tags))
+    (define-key menu [find-tag-regexp]
+      '(menu-item "Find tag matching regexp" ggtags-find-tag-regexp))
     (define-key menu [find-tag-resume]
       '(menu-item "Resume find tag" tags-loop-continue))
     (define-key menu [find-tag]

commit e0167804aa650716ac5dfc6986f83421b7d7ebe6
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 19:36:33 2013 +0800

    New function ggtags-process-string and use it

diff --git a/ggtags.el b/ggtags.el
index d089f34..981e7f0 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -66,10 +66,7 @@
 
 (eval-and-compile
   (or (fboundp 'user-error)
-      (defalias 'user-error 'error))
-  ;; File newcomment.el is preloaded since 24.3.
-  (or (fboundp 'comment-string-strip)
-      (autoload 'comment-string-strip "newcomment")))
+      (defalias 'user-error 'error)))
 
 (defgroup ggtags nil
   "GNU Global source code tagging system."
@@ -145,14 +142,14 @@ properly update `ggtags-mode-map'."
 ;; http://thread.gmane.org/gmane.comp.gnu.global.bugs/1518
 (defvar ggtags-global-has-path-style    ; introduced in global 6.2.8
   (with-demoted-errors                  ; in case `global' not found
-    (zerop (call-process "global" nil nil nil
+    (zerop (process-file "global" nil nil nil
                          "--path-style" "shorter" "--help")))
   "Non-nil if `global' supports --path-style switch.")
 
 ;; http://thread.gmane.org/gmane.comp.gnu.global.bugs/1542
 (defvar ggtags-global-has-color         ; introduced in global 6.2.9
   (with-demoted-errors
-    (zerop (call-process "global" nil nil nil "--color" "--help"))))
+    (zerop (process-file "global" nil nil nil "--color" "--help"))))
 
 (defmacro ggtags-ensure-global-buffer (&rest body)
   (declare (indent 0))
@@ -223,6 +220,17 @@ Return -1 if it does not exist."
   (> (ggtags-get-timestamp key)
      (or (fifth (ggtags-cache-get key)) 0)))
 
+(defun ggtags-process-string (program &rest args)
+  (with-temp-buffer
+    (let ((exit (apply #'process-file program nil t nil args))
+          (output (progn
+                    (goto-char (point-max))
+                    (skip-chars-backward " \t\n")
+                    (buffer-substring (point-min) (point)))))
+      (or (zerop exit)
+          (error "`%s' non-zero exit: %s" program output))
+      output)))
+
 (defvar-local ggtags-root-directory nil
   "Internal; use function `ggtags-root-directory' instead.")
 
@@ -230,10 +238,7 @@ Return -1 if it does not exist."
 (defun ggtags-root-directory ()
   (or ggtags-root-directory
       (setq ggtags-root-directory
-            (with-temp-buffer
-              (when (zerop (call-process "global" nil (list t nil) nil "-pr"))
-                (file-name-as-directory
-                 (comment-string-strip (buffer-string) t t)))))))
+            (ignore-errors (ggtags-process-string "global" "-pr")))))
 
 (defun ggtags-check-root-directory ()
   (or (ggtags-root-directory) (error "File GTAGS not found")))
@@ -244,22 +249,17 @@ Return -1 if it does not exist."
                 (error "Aborted"))
         (let ((root (read-directory-name "Directory: " nil nil t)))
           (and (zerop (length root)) (error "No directory chosen"))
-          (when (with-temp-buffer
-                  (let ((process-environment
-                         (if (and (not (getenv "GTAGSLABEL"))
-                                  (yes-or-no-p "Use `ctags' backend? "))
-                             (cons "GTAGSLABEL=ctags" process-environment))
-                         process-environment)
-                        (default-directory
-                          (file-name-as-directory root)))
-                    (or (zerop (call-process "gtags" nil t))
-                        (error "%s" (comment-string-strip
-                                     (buffer-string) t t)))))
+          (when (let ((process-environment
+                       (if (and (not (getenv "GTAGSLABEL"))
+                                (yes-or-no-p "Use `ctags' backend? "))
+                           (cons "GTAGSLABEL=ctags" process-environment))
+                       process-environment)
+                      (default-directory (file-name-as-directory root)))
+                  (and (ggtags-process-string "gtags") t))
             (ggtags-tag-names-1 root)   ; update cache
-            (message "File GTAGS generated in `%s'"
-                     (ggtags-root-directory)))))))
+            (message "GTAGS generated in `%s'" (ggtags-root-directory)))))))
 
-(defun ggtags-update-tags (&optional single-update noerror)
+(defun ggtags-update-tags (&optional single-update)
   "Update GNU Global tag database."
   (interactive)
   (let ((process-environment
@@ -268,15 +268,9 @@ Return -1 if it does not exist."
          process-environment))
     (if single-update
         (when buffer-file-name
-          (or (zerop (call-process "global" nil 0 nil "--single-update"
-                                   (file-truename buffer-file-name)))
-              (funcall (if noerror 'message 'error)
-                       "Failed to run global --single-update")))
-      (with-temp-buffer
-        (or (zerop (call-process "global" nil t nil "-u"))
-            (funcall (if noerror 'message 'error)
-                     "Process global exited abnormally: %s"
-                     (buffer-string)))))))
+          (process-file "global" nil 0 nil "--single-update"
+                        (file-truename buffer-file-name)))
+      (ggtags-process-string "global" "-u"))))
 
 (defun ggtags-tag-names-1 (root &optional from-cache)
   (when root
@@ -743,7 +737,7 @@ s: symbols              (-s)
       (ggtags-cache-mark-dirty root t)
       ;; When oversize update on a per-save basis.
       (when (and buffer-file-name (ggtags-oversize-p))
-        (ggtags-update-tags t t)))))
+        (ggtags-update-tags 'single-update)))))
 
 (defvar ggtags-tag-overlay nil)
 (defvar ggtags-highlight-tag-timer nil)
@@ -864,7 +858,7 @@ s: symbols              (-s)
     (let ((file (file-truename buffer-file-name)))
       (with-temp-buffer
         (when (with-demoted-errors
-                (zerop (call-process "global" nil t nil "-f" file)))
+                (zerop (process-file "global" nil t nil "-f" file)))
           (goto-char (point-min))
           (loop while (re-search-forward
                        "^\\([^ \t]+\\)[ \t]+\\([0-9]+\\)" nil t)

commit f12a67f88bfb83f808df2912d5e1f4ba6c937141
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 17:42:57 2013 +0800

    New navigation commands to move to last/first error

diff --git a/ggtags.el b/ggtags.el
index 89d548b..d089f34 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -600,6 +600,8 @@ s: symbols              (-s)
     (define-key map "\M-p" 'previous-error)
     (define-key map "\M-}" 'ggtags-navigation-next-file)
     (define-key map "\M-{" 'ggtags-navigation-previous-file)
+    (define-key map "\M->" 'ggtags-navigation-last-error)
+    (define-key map "\M-<" 'ggtags-navigation-first-error)
     (define-key map "\M-o" 'ggtags-navigation-visible-mode)
     (define-key map [return] 'ggtags-navigation-mode-done)
     (define-key map "\r" 'ggtags-navigation-mode-done)
@@ -623,6 +625,10 @@ s: symbols              (-s)
       '(menu-item "Finish navigation" ggtags-navigation-mode-done))
     (define-key menu [abort]
       '(menu-item "Abort" ggtags-navigation-mode-abort))
+    (define-key menu [last-error]
+      '(menu-item "Last error" ggtags-navigation-last-error))
+    (define-key menu [fist-error]
+      '(menu-item "Fist error" ggtags-navigation-first-error))
     (define-key menu [previous-file]
       '(menu-item "Previous file" ggtags-navigation-previous-file))
     (define-key menu [next-file]
@@ -678,6 +684,20 @@ s: symbols              (-s)
   (interactive "p")
   (ggtags-navigation-next-file (- n)))
 
+(defun ggtags-navigation-first-error ()
+  (interactive)
+  (ggtags-ensure-global-buffer
+    (goto-char (point-min))
+    (compilation-next-error 1)
+    (compile-goto-error)))
+
+(defun ggtags-navigation-last-error ()
+  (interactive)
+  (ggtags-ensure-global-buffer
+    (goto-char (point-max))
+    (compilation-previous-error 1)
+    (compile-goto-error)))
+
 (defun ggtags-navigation-visible-mode (&optional arg)
   (interactive (list (or current-prefix-arg 'toggle)))
   (ggtags-ensure-global-buffer

commit 59d870cb31b0d7fd8104c20c0125739d301f19ea
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 15:44:11 2013 +0800

    Fix buglets

diff --git a/ggtags.el b/ggtags.el
index 6371e0d..89d548b 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -90,7 +90,8 @@ If nil, use Emacs default."
   :group 'ggtags)
 
 (defcustom ggtags-global-abbreviate-filename 35
-  "Non-nil to display file names abbreviated such as '/u/b/env'."
+  "Non-nil to display file names abbreviated e.g. \"/u/b/env\".
+If an integer abbreviate only names longer than that number."
   :type '(choice (const :tag "No" nil)
                  (const :tag "Always" t)
                  integer)
@@ -302,7 +303,7 @@ Return -1 if it does not exist."
 
 (defun ggtags-read-tag (quick)
   (ggtags-ensure-root-directory)
-  (let ((default (thing-at-point 'symbol))
+  (let ((default (substring-no-properties (or (thing-at-point 'symbol) "")))
         (completing-read-function ggtags-completing-read-function))
     (setq ggtags-current-tag-name
           (if quick (or default (user-error "No tag at point"))
@@ -451,8 +452,8 @@ s: symbols              (-s)
 (defun ggtags-next-mark (&optional arg)
   "Move to the next (newer) mark in the tag marker ring."
   (interactive)
-  (or (> (ring-length find-tag-marker-ring) 1)
-      (user-error "No %s mark" (if arg "previous" "next")))
+  (and (zerop (ring-length find-tag-marker-ring))
+       (user-error "No %s mark" (if arg "previous" "next")))
   (let ((mark (or (and ggtags-current-mark
                        ;; Note `ring-previous' gets newer item.
                        (funcall (if arg #'ring-next #'ring-previous)
@@ -610,7 +611,6 @@ s: symbols              (-s)
 (defvar ggtags-navigation-mode-map
   (let ((map (make-sparse-keymap))
         (menu (make-sparse-keymap "GG-Navigation")))
-    (set-keymap-parent map ggtags-navigation-map)
     ;; Menu items: (info "(elisp)Extended Menu Items")
     (define-key map [menu-bar ggtags-navigation] (cons "GG-Navigation" menu))
     ;; Ordered backwards

commit 342dadd67e8c24e6aeb965b3150106b3d49eedf5
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 09:31:56 2013 +0800

    Tweak ggtags-mode-prefix-map to reduce conflicts
    
    since C-c <char> are popular key bindings by emacs users.

diff --git a/README.rst b/README.rst
index fdce0a3..697cc1d 100644
--- a/README.rst
+++ b/README.rst
@@ -79,7 +79,7 @@ which hides the auxiliary window and exits navigation mode. 
You can
 resume the search using ``M-,``. To abort the search press ``M-*``.
 
 Normally after a few searches a dozen buffers are created visiting
-files tracked by GNU Global. ``C-c k`` helps clean them up.
+files tracked by GNU Global. ``C-c M-k`` helps clean them up.
 
 Development
 ~~~~~~~~~~~
diff --git a/ggtags.el b/ggtags.el
index 1f30d4f..6371e0d 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -46,7 +46,7 @@
 ;; resume the search using `M-,'. To abort the search press `M-*'.
 ;;
 ;; Normally after a few searches a dozen buffers are created visiting
-;; files tracked by GNU Global. `C-c k' helps clean them up.
+;; files tracked by GNU Global. `C-c M-k' helps clean them up.
 
 ;;; Code:
 
@@ -730,12 +730,12 @@ s: symbols              (-s)
 
 (defvar ggtags-mode-prefix-map
   (let ((m (make-sparse-keymap)))
-    (define-key m "p" 'ggtags-prev-mark)
-    (define-key m "n" 'ggtags-next-mark)
-    (define-key m "k" 'ggtags-kill-file-buffers)
-    (define-key m (kbd "DEL") 'ggtags-delete-tag-files)
-    (define-key m "l" 'ggtags-list-tags)
-    (define-key m "q" 'ggtags-query-replace)
+    (define-key m (kbd "M-DEL") 'ggtags-delete-tag-files)
+    (define-key m "\M-p" 'ggtags-prev-mark)
+    (define-key m "\M-n" 'ggtags-next-mark)
+    (define-key m "\M-k" 'ggtags-kill-file-buffers)
+    (define-key m "\M-l" 'ggtags-list-tags)
+    (define-key m (kbd "M-%") 'ggtags-query-replace)
     m))
 
 (defvar ggtags-mode-map

commit 93da71550138fd777fc54134050e5fcbfb4a615b
Author: Leo Liu <address@hidden>
Date:   Thu Oct 31 08:41:03 2013 +0800

    Document how to make ggtags work with the ctags backend

diff --git a/README.rst b/README.rst
index 4306a0e..fdce0a3 100644
--- a/README.rst
+++ b/README.rst
@@ -50,7 +50,13 @@ Enable ``ggtags-mode`` for C/C++/Java modes::
 More languages/modes are supported if `GNU Global
 <http://www.gnu.org/software/global>`_ is compiled with
 ``--with-exuberant-ctags`` to support `exuberant ctags
-<http://ctags.sourceforge.net/>`_.
+<http://ctags.sourceforge.net/>`_. Also set the environment variable
+``GTAGSCONF`` to the correct location of ``gtags.conf``. For example::
+
+  export GTAGSCONF=/usr/local/share/gtags/gtags.conf
+
+See ``plugin-factory/README`` in GNU Global source for further
+information.
 
 Tutorial
 ~~~~~~~~

commit 4ae7a1802aa748b6b577fa3453be1b02b58084e6
Author: Leo Liu <address@hidden>
Date:   Wed Oct 30 16:02:14 2013 +0800

    Add menu-bar entries and release v0.6.8

diff --git a/ggtags.el b/ggtags.el
index 8949e97..1f30d4f 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -3,7 +3,7 @@
 ;; Copyright (C) 2013  Free Software Foundation, Inc.
 
 ;; Author: Leo Liu <address@hidden>
-;; Version: 0.6.7
+;; Version: 0.6.8
 ;; Keywords: tools, convenience
 ;; Created: 2013-01-29
 ;; URL: https://github.com/leoliu/ggtags
@@ -131,6 +131,8 @@ properly update `ggtags-mode-map'."
   :type 'function
   :group 'ggtags)
 
+(defvar ggtags-bug-url "https://github.com/leoliu/ggtags/issues";)
+
 (defvar ggtags-cache nil)         ; (ROOT TABLE DIRTY CTAGS TIMESTAMP)
 
 (defvar ggtags-current-tag-name nil)
@@ -256,6 +258,25 @@ Return -1 if it does not exist."
             (message "File GTAGS generated in `%s'"
                      (ggtags-root-directory)))))))
 
+(defun ggtags-update-tags (&optional single-update noerror)
+  "Update GNU Global tag database."
+  (interactive)
+  (let ((process-environment
+         (if (ggtags-cache-ctags-p (ggtags-root-directory))
+             (cons "GTAGSLABEL=ctags" process-environment))
+         process-environment))
+    (if single-update
+        (when buffer-file-name
+          (or (zerop (call-process "global" nil 0 nil "--single-update"
+                                   (file-truename buffer-file-name)))
+              (funcall (if noerror 'message 'error)
+                       "Failed to run global --single-update")))
+      (with-temp-buffer
+        (or (zerop (call-process "global" nil t nil "-u"))
+            (funcall (if noerror 'message 'error)
+                     "Process global exited abnormally: %s"
+                     (buffer-string)))))))
+
 (defun ggtags-tag-names-1 (root &optional from-cache)
   (when root
     (if (and (not from-cache) (ggtags-cache-stale-p root))
@@ -274,13 +295,7 @@ Return -1 if it does not exist."
                (not (ggtags-oversize-p))
                (not from-cache)
                (ggtags-cache-dirty-p root))
-      (if (zerop (let ((process-environment
-                        (if (ggtags-cache-ctags-p root)
-                            (cons "GTAGSLABEL=ctags" process-environment))
-                        process-environment))
-                   (call-process "global" nil nil nil "-u")))
-          (ggtags-cache-mark-dirty root nil)
-        (message "ggtags: error running 'global -u'")))
+      (ggtags-update-tags))
     (apply 'append (mapcar (lambda (r)
                              (ggtags-tag-names-1 r from-cache))
                            (cons root (ggtags-get-libpath))))))
@@ -575,7 +590,10 @@ s: symbols              (-s)
   (add-hook 'compilation-finish-functions 'ggtags-handle-single-match nil t)
   (define-key ggtags-global-mode-map "o" 'visible-mode))
 
-(defvar ggtags-navigation-mode-map
+;; NOTE: Need this to avoid putting menu items in
+;; `emulation-mode-map-alists', which creates double entries. See
+;; http://i.imgur.com/VJJTzVc.png
+(defvar ggtags-navigation-map
   (let ((map (make-sparse-keymap)))
     (define-key map "\M-n" 'next-error)
     (define-key map "\M-p" 'previous-error)
@@ -589,6 +607,32 @@ s: symbols              (-s)
     (define-key map [remap ggtags-find-tag] 'undefined)
     map))
 
+(defvar ggtags-navigation-mode-map
+  (let ((map (make-sparse-keymap))
+        (menu (make-sparse-keymap "GG-Navigation")))
+    (set-keymap-parent map ggtags-navigation-map)
+    ;; Menu items: (info "(elisp)Extended Menu Items")
+    (define-key map [menu-bar ggtags-navigation] (cons "GG-Navigation" menu))
+    ;; Ordered backwards
+    (define-key menu [visible-mode]
+      '(menu-item "Visible mode" ggtags-navigation-visible-mode
+                  :button (:toggle . (ignore-errors
+                                       (ggtags-ensure-global-buffer
+                                         visible-mode)))))
+    (define-key menu [done]
+      '(menu-item "Finish navigation" ggtags-navigation-mode-done))
+    (define-key menu [abort]
+      '(menu-item "Abort" ggtags-navigation-mode-abort))
+    (define-key menu [previous-file]
+      '(menu-item "Previous file" ggtags-navigation-previous-file))
+    (define-key menu [next-file]
+      '(menu-item "Next file" ggtags-navigation-next-file))
+    (define-key menu [previous]
+      '(menu-item "Previous match" previous-error))
+    (define-key menu [next]
+      '(menu-item "Next match" next-error))
+    map))
+
 (defun ggtags-move-to-tag (&optional name)
   "Move to NAME tag in current line."
   (let ((orig (point))
@@ -679,14 +723,7 @@ s: symbols              (-s)
       (ggtags-cache-mark-dirty root t)
       ;; When oversize update on a per-save basis.
       (when (and buffer-file-name (ggtags-oversize-p))
-        (with-demoted-errors
-          (let ((process-environment
-                 (if (ggtags-cache-ctags-p root)
-                     (cons "GTAGSLABEL=ctags" process-environment))
-                 process-environment))
-            (call-process "global" nil 0 nil
-                          "--single-update"
-                          (file-truename buffer-file-name))))))))
+        (ggtags-update-tags t t)))))
 
 (defvar ggtags-tag-overlay nil)
 (defvar ggtags-highlight-tag-timer nil)
@@ -702,9 +739,45 @@ s: symbols              (-s)
     m))
 
 (defvar ggtags-mode-map
-  (let ((map (make-sparse-keymap)))
+  (let ((map (make-sparse-keymap))
+        (menu (make-sparse-keymap "Ggtags")))
     (define-key map "\M-." 'ggtags-find-tag)
     (define-key map ggtags-mode-prefix-key ggtags-mode-prefix-map)
+    ;; Menu items
+    (define-key map [menu-bar ggtags] (cons "Ggtags" menu))
+    ;; Ordered backwards
+    (define-key menu [report-bugs]
+      `(menu-item "Report bugs"
+                  (lambda () (interactive)
+                    (browse-url ggtags-bug-url)
+                    (message "Please visit %s" ggtags-bug-url))
+                  :help ,(format "Visit %s" ggtags-bug-url)))
+    (define-key menu [custom-ggtags]
+      '(menu-item "Customize Ggtags"
+                  (lambda () (interactive) (customize-group 'ggtags))))
+    (define-key menu [sep2] menu-bar-separator)
+    (define-key menu [delete-tags]
+      '(menu-item "Delete tag files" ggtags-delete-tag-files
+                  :enable (ggtags-root-directory)))
+    (define-key menu [next-mark]
+      '(menu-item "Next mark" ggtags-next-mark))
+    (define-key menu [prev-mark]
+      '(menu-item "Previous mark" ggtags-prev-mark))
+    (define-key menu [sep1] menu-bar-separator)
+    (define-key menu [query-replace]
+      '(menu-item "Query replace" ggtags-query-replace))
+    (define-key menu [list-tags]
+      '(menu-item "List tags" ggtags-list-tags))
+    (define-key menu [find-tag-resume]
+      '(menu-item "Resume find tag" tags-loop-continue))
+    (define-key menu [find-tag]
+      '(menu-item "Find tag" ggtags-find-tag))
+    (define-key menu [run-gtags]
+      '(menu-item (if (ggtags-root-directory) "Update tag files" "Run gtags")
+                  (lambda () (interactive)
+                    (if (ggtags-root-directory)
+                        (ggtags-update-tags)
+                      (ggtags-ensure-root-directory)))))
     map))
 
 (defun ggtags-mode-update-prefix-key (symbol value)
@@ -815,7 +888,7 @@ s: symbols              (-s)
 ;; Higher priority for `ggtags-navigation-mode' to avoid being
 ;; hijacked by modes such as `view-mode'.
 (defvar ggtags-mode-map-alist
-  `((ggtags-navigation-mode . ,ggtags-navigation-mode-map)))
+  `((ggtags-navigation-mode . ,ggtags-navigation-map)))
 
 (add-to-list 'emulation-mode-map-alists 'ggtags-mode-map-alist)
 

commit 1d3c4c6217b2ae50901d6db36b0ffae03589817e
Author: Leo Liu <address@hidden>
Date:   Wed Oct 30 14:18:35 2013 +0800

    Customise ggtags-mode-prefix-key should just work

diff --git a/ggtags.el b/ggtags.el
index 6f88770..8949e97 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -53,9 +53,6 @@
 (eval-when-compile (require 'cl))
 (require 'compile)
 
-(if (not (fboundp 'comment-string-strip))
-    (autoload 'comment-string-strip "newcomment"))
-
 (eval-when-compile
   (unless (fboundp 'setq-local)
     (defmacro setq-local (var val)
@@ -68,8 +65,11 @@
             (list 'make-variable-buffer-local (list 'quote var))))))
 
 (eval-and-compile
-  (unless (fboundp 'user-error)
-    (defalias 'user-error 'error)))
+  (or (fboundp 'user-error)
+      (defalias 'user-error 'error))
+  ;; File newcomment.el is preloaded since 24.3.
+  (or (fboundp 'comment-string-strip)
+      (autoload 'comment-string-strip "newcomment")))
 
 (defgroup ggtags nil
   "GNU Global source code tagging system."
@@ -118,7 +118,11 @@ If nil, use Emacs default."
   :group 'ggtags)
 
 (defcustom ggtags-mode-prefix-key "\C-c"
-  "Key binding used for `ggtags-mode-prefix-map'."
+  "Key binding used for `ggtags-mode-prefix-map'.
+Users should change the value using `customize-variable' to
+properly update `ggtags-mode-map'."
+  ;; Set later or initialisation will fail.
+  ;; :set 'ggtags-mode-update-prefix-key
   :type 'key-sequence
   :group 'ggtags)
 
@@ -443,7 +447,8 @@ s: symbols              (-s)
                     (ring-insert find-tag-marker-ring (point-marker))))))
     (setq ggtags-current-mark mark)
     (let ((i (- (ring-length find-tag-marker-ring)
-                (ring-member find-tag-marker-ring ggtags-current-mark))))
+                (ring-member find-tag-marker-ring ggtags-current-mark)))
+          (message-log-max nil))
       (message "%d%s marker" i (pcase i
                                  (1 "st")
                                  (2 "nd")
@@ -702,6 +707,17 @@ s: symbols              (-s)
     (define-key map ggtags-mode-prefix-key ggtags-mode-prefix-map)
     map))
 
+(defun ggtags-mode-update-prefix-key (symbol value)
+  (let ((old (and (boundp symbol) (symbol-value symbol))))
+    (and old (define-key ggtags-mode-map old nil)))
+  (when value
+    (define-key ggtags-mode-map value ggtags-mode-prefix-map))
+  (set-default symbol value))
+
+;; Set here to avoid initialisation problem for
+;; `ggtags-mode-prefix-key'.
+(put 'ggtags-mode-prefix-key 'custom-set #'ggtags-mode-update-prefix-key)
+
 ;;;###autoload
 (define-minor-mode ggtags-mode nil
   :lighter (:eval (if ggtags-navigation-mode "" " GG"))

commit 16dcf906edcecef13045a977bded0a7e645acbb8
Author: Leo Liu <address@hidden>
Date:   Tue Oct 29 23:25:55 2013 +0800

    Fix #10: Move forward/backward in the navigation history
    
    Consolidate miscellaneous commands into
    ggtags-mode-prefix-key (customisable).

diff --git a/README.rst b/README.rst
index e79e912..4306a0e 100644
--- a/README.rst
+++ b/README.rst
@@ -1,7 +1,7 @@
 =========================
  Use GNU Global in Emacs
 =========================
- 
+
 A package for working with `GNU Global
 <http://www.gnu.org/software/global>`_ source tagging system in Emacs.
 
@@ -17,6 +17,7 @@ Features
 #. Highlight valid tag at point
 #. Built on top of ``compile.el`` (asynchonrous and other nice
    features)
+#. Support `exuberant ctags <http://ctags.sourceforge.net/>`_ backend.
 #. Support all  output formats  of ``global``:  ``grep``, ``ctags-x``,
    ``cscope`` etc.
 #. Abbreviated display of file names
@@ -46,6 +47,11 @@ Enable ``ggtags-mode`` for C/C++/Java modes::
                 (when (derived-mode-p 'c-mode 'c++-mode 'java-mode)
                   (ggtags-mode 1))))
 
+More languages/modes are supported if `GNU Global
+<http://www.gnu.org/software/global>`_ is compiled with
+``--with-exuberant-ctags`` to support `exuberant ctags
+<http://ctags.sourceforge.net/>`_.
+
 Tutorial
 ~~~~~~~~
 
@@ -67,7 +73,7 @@ which hides the auxiliary window and exits navigation mode. 
You can
 resume the search using ``M-,``. To abort the search press ``M-*``.
 
 Normally after a few searches a dozen buffers are created visiting
-files tracked by GNU Global. ``C-c M-k`` helps clean them up.
+files tracked by GNU Global. ``C-c k`` helps clean them up.
 
 Development
 ~~~~~~~~~~~
@@ -79,3 +85,10 @@ Bugs
 ~~~~
 
 https://github.com/leoliu/ggtags/issues
+
+known problems
+++++++++++++++
+
+When there is only one match a window is still popped up to be closed
+immediately due to a `bug <http://debbugs.gnu.org/13594>`_ in
+``compile.el``.
diff --git a/ggtags.el b/ggtags.el
index 711e04d..6f88770 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -46,7 +46,7 @@
 ;; resume the search using `M-,'. To abort the search press `M-*'.
 ;;
 ;; Normally after a few searches a dozen buffers are created visiting
-;; files tracked by GNU Global. `C-c M-k' helps clean them up.
+;; files tracked by GNU Global. `C-c k' helps clean them up.
 
 ;;; Code:
 
@@ -117,12 +117,17 @@ If nil, use Emacs default."
                  (const cscope))
   :group 'ggtags)
 
+(defcustom ggtags-mode-prefix-key "\C-c"
+  "Key binding used for `ggtags-mode-prefix-map'."
+  :type 'key-sequence
+  :group 'ggtags)
+
 (defcustom ggtags-completing-read-function completing-read-function
   "Ggtags specific `completing-read-function' (which see)."
   :type 'function
   :group 'ggtags)
 
-(defvar ggtags-cache nil)               ; (ROOT TABLE DIRTY CTAGS TIMESTAMP)
+(defvar ggtags-cache nil)         ; (ROOT TABLE DIRTY CTAGS TIMESTAMP)
 
 (defvar ggtags-current-tag-name nil)
 
@@ -231,7 +236,7 @@ Return -1 if it does not exist."
       (when (or (yes-or-no-p "File GTAGS not found; run gtags? ")
                 (error "Aborted"))
         (let ((root (read-directory-name "Directory: " nil nil t)))
-          (and (= (length root) 0) (error "No directory chosen"))
+          (and (zerop (length root)) (error "No directory chosen"))
           (when (with-temp-buffer
                   (let ((process-environment
                          (if (and (not (getenv "GTAGSLABEL"))
@@ -425,22 +430,30 @@ s: symbols              (-s)
 (defvar ggtags-current-mark nil)
 
 (defun ggtags-next-mark (&optional arg)
-  "Move to the next mark in the tag marker ring."
+  "Move to the next (newer) mark in the tag marker ring."
   (interactive)
   (or (> (ring-length find-tag-marker-ring) 1)
       (user-error "No %s mark" (if arg "previous" "next")))
   (let ((mark (or (and ggtags-current-mark
-                       (marker-buffer ggtags-current-mark)
-                       (funcall (if arg #'ring-previous #'ring-next)
+                       ;; Note `ring-previous' gets newer item.
+                       (funcall (if arg #'ring-next #'ring-previous)
                                 find-tag-marker-ring ggtags-current-mark))
-                  (progn
-                    (ring-insert find-tag-marker-ring (point-marker))
-                    (ring-ref find-tag-marker-ring 0)))))
+                  (prog1
+                      (ring-ref find-tag-marker-ring (if arg 0 -1))
+                    (ring-insert find-tag-marker-ring (point-marker))))))
+    (setq ggtags-current-mark mark)
+    (let ((i (- (ring-length find-tag-marker-ring)
+                (ring-member find-tag-marker-ring ggtags-current-mark))))
+      (message "%d%s marker" i (pcase i
+                                 (1 "st")
+                                 (2 "nd")
+                                 (3 "rd")
+                                 (_ "th"))))
     (switch-to-buffer (marker-buffer mark))
-    (goto-char mark)
-    (setq ggtags-current-mark mark)))
+    (goto-char mark)))
 
 (defun ggtags-prev-mark ()
+  "Move to the previous (older) mark in the tag marker ring."
   (interactive)
   (ggtags-next-mark 'previous))
 
@@ -597,6 +610,7 @@ s: symbols              (-s)
 (defun ggtags-navigation-mode-done ()
   (interactive)
   (ggtags-navigation-mode -1)
+  (setq ggtags-current-mark nil)
   (ggtags-navigation-mode-cleanup))
 
 (defun ggtags-navigation-mode-abort ()
@@ -672,10 +686,20 @@ s: symbols              (-s)
 (defvar ggtags-tag-overlay nil)
 (defvar ggtags-highlight-tag-timer nil)
 
+(defvar ggtags-mode-prefix-map
+  (let ((m (make-sparse-keymap)))
+    (define-key m "p" 'ggtags-prev-mark)
+    (define-key m "n" 'ggtags-next-mark)
+    (define-key m "k" 'ggtags-kill-file-buffers)
+    (define-key m (kbd "DEL") 'ggtags-delete-tag-files)
+    (define-key m "l" 'ggtags-list-tags)
+    (define-key m "q" 'ggtags-query-replace)
+    m))
+
 (defvar ggtags-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "\M-." 'ggtags-find-tag)
-    (define-key map "\C-c\M-k" 'ggtags-kill-file-buffers)
+    (define-key map ggtags-mode-prefix-key ggtags-mode-prefix-map)
     map))
 
 ;;;###autoload

commit ebbcaa7491202a40bdeef203f262e7e89d5da622
Author: Leo Liu <address@hidden>
Date:   Tue Oct 29 20:46:46 2013 +0800

    Work seamlessly with exuberant-ctags

diff --git a/ggtags.el b/ggtags.el
index 0e35d17..711e04d 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -122,7 +122,7 @@ If nil, use Emacs default."
   :type 'function
   :group 'ggtags)
 
-(defvar ggtags-cache nil)               ; (ROOT TABLE DIRTY TIMESTAMP)
+(defvar ggtags-cache nil)               ; (ROOT TABLE DIRTY CTAGS TIMESTAMP)
 
 (defvar ggtags-current-tag-name nil)
 
@@ -161,6 +161,15 @@ If nil, use Emacs default."
                 0)
             ggtags-oversize-limit)))))
 
+(defun ggtags-use-ctags-p (root)
+  "Non-nil if exuberant-ctags is used for indexing ROOT."
+  (let ((default-directory (file-name-as-directory root)))
+    ;; Check if GRTAGS contains no tags.
+    (<= (length (split-string (shell-command-to-string
+                               "gtags -d GRTAGS | head -10")
+                              "\n" t))
+        4)))
+
 (defun ggtags-get-timestamp (root)
   "Get the timestamp (float) of file GTAGS in ROOT directory.
 Return -1 if it does not exist."
@@ -174,13 +183,15 @@ Return -1 if it does not exist."
                 (regexp-quote path-separator) t))
 
 (defun ggtags-cache-get (key)
-  (assoc key ggtags-cache))
+  (assoc (file-truename key) ggtags-cache))
 
 (defun ggtags-cache-set (key val &optional dirty)
-  (let ((c (ggtags-cache-get key)))
+  (let* ((key (file-truename key))
+         (c (ggtags-cache-get key))
+         (ctags (ggtags-use-ctags-p key)))
     (if c
-        (setcdr c (list val dirty (float-time)))
-      (push (list key val dirty (float-time)) ggtags-cache))))
+        (setcdr c (list val dirty ctags (float-time)))
+      (push (list key val dirty ctags (float-time)) ggtags-cache))))
 
 (defun ggtags-cache-mark-dirty (key flag)
   "Return non-nil if operation is successful."
@@ -188,6 +199,9 @@ Return -1 if it does not exist."
     (when cache
       (setcar (cddr cache) flag))))
 
+(defun ggtags-cache-ctags-p (key)
+  (fourth (ggtags-cache-get key)))
+
 (defun ggtags-cache-dirty-p (key)
   "Value is non-nil if 'global -u' is needed."
   (third (ggtags-cache-get key)))
@@ -195,7 +209,7 @@ Return -1 if it does not exist."
 (defun ggtags-cache-stale-p (key)
   "Value is non-nil if tags in cache needs to be rebuilt."
   (> (ggtags-get-timestamp key)
-     (or (fourth (ggtags-cache-get key)) 0)))
+     (or (fifth (ggtags-cache-get key)) 0)))
 
 (defvar-local ggtags-root-directory nil
   "Internal; use function `ggtags-root-directory' instead.")
@@ -219,11 +233,17 @@ Return -1 if it does not exist."
         (let ((root (read-directory-name "Directory: " nil nil t)))
           (and (= (length root) 0) (error "No directory chosen"))
           (when (with-temp-buffer
-                  (let ((default-directory
+                  (let ((process-environment
+                         (if (and (not (getenv "GTAGSLABEL"))
+                                  (yes-or-no-p "Use `ctags' backend? "))
+                             (cons "GTAGSLABEL=ctags" process-environment))
+                         process-environment)
+                        (default-directory
                           (file-name-as-directory root)))
                     (or (zerop (call-process "gtags" nil t))
                         (error "%s" (comment-string-strip
                                      (buffer-string) t t)))))
+            (ggtags-tag-names-1 root)   ; update cache
             (message "File GTAGS generated in `%s'"
                      (ggtags-root-directory)))))))
 
@@ -245,7 +265,11 @@ Return -1 if it does not exist."
                (not (ggtags-oversize-p))
                (not from-cache)
                (ggtags-cache-dirty-p root))
-      (if (zerop (call-process "global" nil nil nil "-u"))
+      (if (zerop (let ((process-environment
+                        (if (ggtags-cache-ctags-p root)
+                            (cons "GTAGSLABEL=ctags" process-environment))
+                        process-environment))
+                   (call-process "global" nil nil nil "-u")))
           (ggtags-cache-mark-dirty root nil)
         (message "ggtags: error running 'global -u'")))
     (apply 'append (mapcar (lambda (r)
@@ -288,18 +312,24 @@ r: references           (-r)
 s: symbols              (-s)
 ?: show this help\n"))
     (compilation-start
-     (if (or verbose (not buffer-file-name))
-         (format "global %s -%s \"%s\""
-                 (ggtags-global-options)
-                 (char-to-string
-                  (read-char-choice "Tag type? (d/r/s/?) " '(?d ?r ?s)))
-                 name)
+     (cond
+      (verbose
+       (format "global %s -%s \"%s\""
+               (ggtags-global-options)
+               (char-to-string
+                (read-char-choice "Tag type? (d/r/s/?) " '(?d ?r ?s)))
+               name))
+      ((ggtags-cache-ctags-p (ggtags-root-directory))
+       (format "global %s -d %s" (ggtags-global-options) name))
+      ((not buffer-file-name)
+       (ggtags-find-tag name t))
+      (t
        (format "global %s --from-here=%d:%s \"%s\""
                (ggtags-global-options)
                (line-number-at-pos)
                (shell-quote-argument
                 (expand-file-name (file-truename buffer-file-name)))
-               name))
+               name)))
      'ggtags-global-mode))
   (eval-and-compile (require 'etags))
   (ring-insert find-tag-marker-ring (point-marker))
@@ -631,9 +661,13 @@ s: symbols              (-s)
       ;; When oversize update on a per-save basis.
       (when (and buffer-file-name (ggtags-oversize-p))
         (with-demoted-errors
-          (call-process "global" nil 0 nil
-                        "--single-update"
-                        (file-truename buffer-file-name)))))))
+          (let ((process-environment
+                 (if (ggtags-cache-ctags-p root)
+                     (cons "GTAGSLABEL=ctags" process-environment))
+                 process-environment))
+            (call-process "global" nil 0 nil
+                          "--single-update"
+                          (file-truename buffer-file-name))))))))
 
 (defvar ggtags-tag-overlay nil)
 (defvar ggtags-highlight-tag-timer nil)

commit f9a2ff871101fa9eed1ec793f04750ace0f1e568
Author: Leo Liu <address@hidden>
Date:   Mon Sep 30 12:29:43 2013 +0800

    Fix #13: set tags-loop-scan and tags-loop-operate instead

diff --git a/ggtags.el b/ggtags.el
index 3f77656..0e35d17 100644
--- a/ggtags.el
+++ b/ggtags.el
@@ -1,4 +1,4 @@
-;;; ggtags.el --- GNU Global source code tagging system -*- lexical-binding: 
t; -*-
+;;; ggtags.el --- GNU Global source code tagging system  -*- lexical-binding: 
t; -*-
 
 ;; Copyright (C) 2013  Free Software Foundation, Inc.
 
@@ -303,6 +303,8 @@ s: symbols              (-s)
      'ggtags-global-mode))
   (eval-and-compile (require 'etags))
   (ring-insert find-tag-marker-ring (point-marker))
+  (setq tags-loop-scan t
+        tags-loop-operate '(ggtags-find-tag-resume))
   (ggtags-navigation-mode +1))
 
 (defun ggtags-find-tag-resume ()
@@ -639,7 +641,6 @@ s: symbols              (-s)
 (defvar ggtags-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "\M-." 'ggtags-find-tag)
-    (define-key map "\M-," 'ggtags-find-tag-resume)
     (define-key map "\C-c\M-k" 'ggtags-kill-file-buffers)
     map))
 

commit c3a16722d0d1d60ec1abdee35b721879db1cf1bc
Author: Leo Liu <address@hidden>
Date:   Thu Sep 5 10:04:11 2013 +0800

    Doc fix
    
    Thanks to Ting-Yu Lin for the pointer.

diff --git a/README.rst b/README.rst
index 57ec40c..e79e912 100644
--- a/README.rst
+++ b/README.rst
@@ -24,11 +24,9 @@ Features
 Why GNU Global
 ~~~~~~~~~~~~~~
 
-The opengrok project composed a feature comparison table between a few
-tools. The `page
-<http://hub.opensolaris.org/bin/view/Project+opengrok>`_ was taken
-offline after 2013-03-24 but `here <http://i.imgur.com/IQCPQ0j.png>`_
-is a backup.
+The opengrok project composed a feature comparison `table
+<https://github.com/OpenGrok/OpenGrok/wiki/Comparison-with-Similar-Tools>`_
+between a few tools.
 
 Screenshot
 ~~~~~~~~~~
@@ -74,8 +72,8 @@ files tracked by GNU Global. ``C-c M-k`` helps clean them up.
 Development
 ~~~~~~~~~~~
 
-The goal is to make working with GNU Global in Emacs as effortless and
-intuitively as possible.
+The goal is to make working with GNU Global in Emacs as effortlessly
+and intuitively as possible.
 
 Bugs
 ~~~~

-----------------------------------------------------------------------

Summary of changes:
 packages/ggtags/README.rst |   56 +++-
 packages/ggtags/ggtags.el  |  738 +++++++++++++++++++++++++++++---------------
 2 files changed, 519 insertions(+), 275 deletions(-)


hooks/post-receive
-- 
ELPA



reply via email to

[Prev in Thread] Current Thread [Next in Thread]