;; Copyright (C) 2013-2014 Free Software Foundation, Inc.
;; Author: Leo Liu <sdl.web@gmail.com>
-;; Version: 0.8.2
+;; Version: 0.8.3
;; Keywords: tools, convenience
;; Created: 2013-01-29
;; URL: https://github.com/leoliu/ggtags
(setenv "GTAGSLABEL" "ctags"))
(ggtags-with-temp-message "`gtags' in progress..."
(let ((default-directory (file-name-as-directory root))
- (args (cl-remove-if
- ;; Place --idutils first
- #'null (list (and ggtags-use-idutils "--idutils")
- (and conf "--gtagsconf")
- (and conf (ggtags-ensure-localname conf))))))
+ (args (cl-remove-if #'null
+ (list (and ggtags-use-idutils "--idutils")
+ (and conf "--gtagsconf")
+ (and conf (ggtags-ensure-localname conf))))))
(condition-case err
(apply #'ggtags-process-string "gtags" args)
(error (if (and ggtags-use-idutils
(setf (ggtags-project-dirty-p (ggtags-find-project)) nil)
(setf (ggtags-project-mtime (ggtags-find-project)) (float-time))))))
+(defun ggtags-delete-tags ()
+ "Delete file GTAGS, GRTAGS, GPATH, ID etc. generated by gtags."
+ (interactive (ignore (ggtags-check-project)))
+ (when (ggtags-current-project-root)
+ (let* ((re (concat "\\`" (regexp-opt '("GPATH" "GRTAGS" "GTAGS" "ID")) "\\'"))
+ (files (cl-remove-if-not
+ (lambda (file)
+ ;; Don't trust `directory-files'.
+ (let ((case-fold-search nil))
+ (string-match-p re (file-name-nondirectory file))))
+ (directory-files (ggtags-current-project-root) t re)))
+ (buffer "*GTags File List*"))
+ (or files (user-error "No tag files found"))
+ (with-output-to-temp-buffer buffer
+ (princ (mapconcat #'identity files "\n")))
+ (let ((win (get-buffer-window buffer)))
+ (unwind-protect
+ (progn
+ (fit-window-to-buffer win)
+ (when (yes-or-no-p "Remove GNU Global tag files? ")
+ (with-demoted-errors (mapc #'delete-file files))
+ (remhash (ggtags-current-project-root) ggtags-projects)
+ (and (overlayp ggtags-highlight-tag-overlay)
+ (delete-overlay ggtags-highlight-tag-overlay))))
+ (when (window-live-p win)
+ (quit-window t win)))))))
+
(defvar-local ggtags-completion-cache nil)
;; See global/libutil/char.c
args)))
(mapconcat #'identity (delq nil xs) " ")))
-;; takes three values: nil, t and a marker
+;; Can be three values: nil, t and a marker; t means start marker has
+;; been saved in the tag ring.
(defvar ggtags-global-start-marker nil)
-(defvar ggtags-global-exit-status 0)
-(defvar ggtags-global-match-count 0)
(defvar ggtags-tag-ring-index nil)
(defvar ggtags-global-search-history nil)
(nth 4 (assoc (ggtags-global-search-id command default-directory)
ggtags-global-search-history)))
(ggtags-navigation-mode +1)
- (setq ggtags-global-exit-status 0
- ggtags-global-match-count 0)
(ggtags-update-tags)
(ggtags-with-current-project
(with-current-buffer (with-display-buffer-no-window
(save-excursion
(beginning-of-line)
(and (looking-at re) (match-string sub))))
- (_ (warn "Invalid value for `ggtags-include-pattern':%s"
+ (_ (warn "Invalid value for `ggtags-include-pattern': %s"
ggtags-include-pattern)
nil)))
(if include (list include 'include)
(list (ggtags-read-tag 'definition current-prefix-arg)
(and current-prefix-arg 'definition)))))
- (ggtags-check-project) ; for `ggtags-current-project-root' below
+ (ggtags-check-project) ; For `ggtags-current-project-root' below.
(cond
((eq what 'include)
(ggtags-find-file name))
(defun ggtags-bookmark-jump (bmk)
(ggtags-global-rerun-search-1 (bookmark-prop-get bmk 'ggtags-search)))
-(defun ggtags-delete-tag-files ()
- "Delete the GTAGS, GRTAGS, GPATH etc. files generated by gtags."
- (interactive (ignore (ggtags-check-project)))
- (when (ggtags-current-project-root)
- (let* ((re (concat "\\`" (regexp-opt '("GPATH" "GRTAGS" "GTAGS" "ID")) "\\'"))
- (files (cl-remove-if-not
- (lambda (file)
- ;; Don't trust `directory-files'.
- (let ((case-fold-search nil))
- (string-match-p re (file-name-nondirectory file))))
- (directory-files (ggtags-current-project-root) t re)))
- (buffer "*GTags File List*"))
- (or files (user-error "No tag files found"))
- (with-output-to-temp-buffer buffer
- (princ (mapconcat #'identity files "\n")))
- (let ((win (get-buffer-window buffer)))
- (unwind-protect
- (progn
- (fit-window-to-buffer win)
- (when (yes-or-no-p "Remove GNU Global tag files? ")
- (with-demoted-errors (mapc #'delete-file files))
- (remhash (ggtags-current-project-root) ggtags-projects)
- (and (overlayp ggtags-highlight-tag-overlay)
- (delete-overlay ggtags-highlight-tag-overlay))))
- (when (window-live-p win)
- (quit-window t win)))))))
-
(defun ggtags-browse-file-as-hypertext (file line)
"Browse FILE in hypertext (HTML) form."
(interactive (if (or current-prefix-arg (not buffer-file-name))
(goto-char (marker-position m)))
(_ (error "Dead marker"))))
+(defun ggtags-global-exit-message-1 ()
+ "Get the total of matches and db file used."
+ (save-excursion
+ (goto-char (point-max))
+ (if (re-search-backward
+ "^\\w+ \\(not found\\)\\|^\\([0-9]+\\) \\w+ located" nil t)
+ (cons (or (and (match-string 1) 0)
+ (string-to-number (match-string 2)))
+ (when (re-search-forward
+ "using \\(?:\\(idutils\\)\\|'[^']*/\\(\\w+\\)'\\)"
+ (line-end-position)
+ t)
+ (or (and (match-string 1) "ID")
+ (match-string 2))))
+ (cons 0 nil))))
+
+(defvar-local ggtags-global-exit-info nil) ; (EXIT-STATUS COUNT DB)
+
(defun ggtags-global-exit-message-function (_process-status exit-status msg)
- (setq ggtags-global-exit-status exit-status)
- (pcase-let ((`(,count . ,db)
- (save-excursion
- (goto-char (point-max))
- (if (re-search-backward
- "^\\w+ \\(not found\\)\\|^\\([0-9]+\\) \\w+ located" nil t)
- (cons (or (and (match-string 1) 0)
- (string-to-number (match-string 2)))
- (when (re-search-forward
- "using \\(?:\\(idutils\\)\\|'[^']*/\\(\\w+\\)'\\)"
- (line-end-position)
- t)
- (or (and (match-string 1) "ID")
- (match-string 2))))
- (cons 0 nil)))))
- (setq ggtags-global-match-count count)
- ;; Clear the start marker in case of zero matches.
- (and (zerop count)
- (markerp ggtags-global-start-marker)
- (setq ggtags-global-start-marker nil))
- (cons (if (> exit-status 0)
- msg
- (format "found %d %s"
- count
- (funcall (if (= count 1) #'car #'cadr)
- (pcase db
- ;; ` required for 24.1 and 24.2
- (`"GTAGS" '("definition" "definitions"))
- (`"GSYMS" '("symbol" "symbols"))
- (`"GRTAGS" '("reference" "references"))
- (`"GPATH" '("file" "files"))
- (`"ID" '("identifier" "identifiers"))
- (_ '("match" "matches"))))))
- exit-status)))
+ "A function for `compilation-exit-message-function'."
+ (pcase (ggtags-global-exit-message-1)
+ (`(,count . ,db)
+ (setq ggtags-global-exit-info (list exit-status count db))
+ ;; Clear the start marker in case of zero matches.
+ (and (zerop count)
+ (markerp ggtags-global-start-marker)
+ (setq ggtags-global-start-marker nil))
+ (cons (if (> exit-status 0)
+ msg
+ (format "found %d %s" count
+ (funcall (if (= count 1) #'car #'cadr)
+ (pcase db
+ ;; ` required for 24.1 and 24.2
+ (`"GTAGS" '("definition" "definitions"))
+ (`"GSYMS" '("symbol" "symbols"))
+ (`"GRTAGS" '("reference" "references"))
+ (`"GPATH" '("file" "files"))
+ (`"ID" '("identifier" "identifiers"))
+ (_ '("match" "matches"))))))
+ exit-status))))
(defun ggtags-global-column (start)
;; START is the beginning position of source text.
(defun ggtags-abbreviate-files (start end)
(goto-char start)
- (let* ((error-re (cdr (assq ggtags-global-output-format
+ (let* ((error-re (cdr (assq (car compilation-error-regexp-alist)
ggtags-global-error-regexp-alist-alist)))
(sub (cadr error-re)))
(when (and ggtags-global-abbreviate-filename error-re)
(define-compilation-mode ggtags-global-mode "Global"
"A mode for showing outputs from gnu global."
- ;; Make it buffer local for `ggtags-abbreviate-files'.
- (make-local-variable 'ggtags-global-output-format)
- (setq-local compilation-error-regexp-alist
- (list ggtags-global-output-format))
+ ;; Note: Place `ggtags-global-output-format' as first element for
+ ;; `ggtags-abbreviate-files'.
+ (setq-local compilation-error-regexp-alist (list ggtags-global-output-format))
(pcase ggtags-auto-jump-to-match
(`history (make-local-variable 'ggtags-auto-jump-to-match-target)
(setq-local compilation-auto-jump-to-first-error
ggtags-global-history-length))))
(run-hooks 'ggtags-find-tag-hook)))
+(put 'ggtags-navigation-mode-lighter 'risky-local-variable t)
+
+(defvar ggtags-navigation-mode-lighter
+ '(" GG["
+ (:eval
+ (if (not (buffer-live-p ggtags-global-last-buffer))
+ '(:propertize "??" face error help-echo "No Global buffer")
+ (with-current-buffer ggtags-global-last-buffer
+ (pcase (or ggtags-global-exit-info '(0 0 ""))
+ (`(,exit ,count ,db)
+ `((:propertize ,(pcase db
+ (`"GTAGS" "D")
+ (`"GRTAGS" "R")
+ (`"GSYMS" "S")
+ (`"GPATH" "F")
+ (`"ID" "I"))
+ face success)
+ (:propertize
+ ,(pcase (get-text-property (line-beginning-position)
+ 'compilation-message)
+ (`nil "?")
+ ;; Assume the first match appears at line 5
+ (_ (number-to-string (- (line-number-at-pos) 4))))
+ face success)
+ "/"
+ (:propertize ,(number-to-string count) face success)
+ ,(unless (zerop exit)
+ `(":" (:propertize ,(number-to-string exit) face error)))))))))
+ "]")
+ "Ligher for `ggtags-navigation-mode'; set to nil to disable it.")
+
(define-minor-mode ggtags-navigation-mode nil
- :lighter
- (" GG[" (:eval
- (ignore-errors
- (ggtags-ensure-global-buffer
- (let ((index (when (get-text-property (line-beginning-position)
- 'compilation-message)
- ;; Assume the first match appears at line 5
- (- (line-number-at-pos) 4))))
- `((:propertize ,(if index
- (number-to-string (max index 0))
- "?") face success) "/")))))
- (:propertize (:eval (number-to-string ggtags-global-match-count))
- face success)
- (:eval
- (unless (zerop ggtags-global-exit-status)
- `(":" (:propertize ,(number-to-string ggtags-global-exit-status)
- face error))))
- "]")
+ :lighter ggtags-navigation-mode-lighter
:global t
(if ggtags-navigation-mode
(progn
(let ((m (make-sparse-keymap)))
;; Globally bound to `M-g p'.
;; (define-key m "\M-'" 'previous-error)
- (define-key m (kbd "M-DEL") 'ggtags-delete-tag-files)
+ (define-key m (kbd "M-DEL") 'ggtags-delete-tags)
(define-key m "\M-p" 'ggtags-prev-mark)
(define-key m "\M-n" 'ggtags-next-mark)
(define-key m "\M-f" 'ggtags-find-file)
'(menu-item "Browse as hypertext" ggtags-browse-file-as-hypertext
:enable (ggtags-find-project)))
(define-key menu [delete-tags]
- '(menu-item "Delete tag files" ggtags-delete-tag-files
- :enable (ggtags-find-project)))
+ '(menu-item "Delete tags" ggtags-delete-tags
+ :enable (ggtags-find-project)
+ :help "Delete file GTAGS, GRTAGS, GPATH, ID etc."))
(define-key menu [kill-buffers]
'(menu-item "Kill project file buffers" ggtags-kill-file-buffers
:enable (ggtags-find-project)))
(define-key menu [prev-mark]
'(menu-item "Previous mark" ggtags-prev-mark))
(define-key menu [sep1] menu-bar-separator)
- (define-key menu [rerun-search]
- '(menu-item "Re-run past search" ggtags-global-rerun-search))
- (define-key menu [save-to-register]
- '(menu-item "Save search to register" ggtags-save-to-register))
(define-key menu [previous-error]
'(menu-item "Previous match" previous-error))
(define-key menu [next-error]
'(menu-item "Next match" next-error))
+ (define-key menu [rerun-search]
+ '(menu-item "Re-run past search" ggtags-global-rerun-search))
+ (define-key menu [save-to-register]
+ '(menu-item "Save search to register" ggtags-save-to-register))
(define-key menu [find-file]
'(menu-item "Find files" ggtags-find-file))
(define-key menu [query-replace]
(define-key menu [grep]
'(menu-item "Grep" ggtags-grep))
(define-key menu [find-symbol]
- '(menu-item "Find other symbol" ggtags-find-other-symbol))
+ '(menu-item "Find other symbol" ggtags-find-other-symbol
+ :help "Find references without definition"))
(define-key menu [find-tag-regexp]
'(menu-item "Find tag matching regexp" ggtags-find-tag-regexp))
(define-key menu [show-definition]