my-emacs-d/elpa/helm-gtags-20160917.2238/helm-gtags.el

1328 lines
51 KiB
EmacsLisp

;;; helm-gtags.el --- GNU GLOBAL helm interface -*- lexical-binding: t; -*-
;; Copyright (C) 2016 by Syohei YOSHIDA
;; Author: Syohei YOSHIDA <syohex@gmail.com>
;; URL: https://github.com/syohex/emacs-helm-gtags
;; Package-Version: 20160917.2238
;; Version: 1.5.6
;; Package-Requires: ((emacs "24.4") (helm "2.0"))
;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with this program. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;; `helm-gtags.el' is a `helm' interface of GNU Global.
;; `helm-gtags.el' is not compatible `anything-gtags.el', but `helm-gtags.el'
;; is designed for fast search.
;;
;; To use this package, add these lines to your init.el or .emacs file:
;;
;; ;; Enable helm-gtags-mode
;; (add-hook 'c-mode-hook 'helm-gtags-mode)
;; (add-hook 'c++-mode-hook 'helm-gtags-mode)
;; (add-hook 'asm-mode-hook 'helm-gtags-mode)
;;
;; ;; Set key bindings
;; (eval-after-load "helm-gtags"
;; '(progn
;; (define-key helm-gtags-mode-map (kbd "M-t") 'helm-gtags-find-tag)
;; (define-key helm-gtags-mode-map (kbd "M-r") 'helm-gtags-find-rtag)
;; (define-key helm-gtags-mode-map (kbd "M-s") 'helm-gtags-find-symbol)
;; (define-key helm-gtags-mode-map (kbd "M-g M-p") 'helm-gtags-parse-file)
;; (define-key helm-gtags-mode-map (kbd "C-c <") 'helm-gtags-previous-history)
;; (define-key helm-gtags-mode-map (kbd "C-c >") 'helm-gtags-next-history)
;; (define-key helm-gtags-mode-map (kbd "M-,") 'helm-gtags-pop-stack)))
;;
;;; Code:
(require 'cl-lib)
(require 'helm)
(require 'helm-files)
(require 'which-func)
(require 'pulse)
(require 'subr-x)
(declare-function helm-comp-read "helm-mode")
(declare-function cygwin-convert-file-name-from-windows "cygw32.c")
(declare-function cygwin-convert-file-name-to-windows "cygw32.c")
(defgroup helm-gtags nil
"GNU GLOBAL for helm."
:group 'helm)
(defcustom helm-gtags-path-style 'root
"Style of file path"
:type '(choice (const :tag "Root of the current project" root)
(const :tag "Relative from the current directory" relative)
(const :tag "Absolute Path" absolute)))
(defcustom helm-gtags-ignore-case nil
"Ignore case in each search."
:type 'boolean)
(defcustom helm-gtags-cygwin-use-global-w32-port t
"Use the GNU global win32 port in Cygwin."
:type 'boolean)
(defcustom helm-gtags-read-only nil
"Gtags read only mode."
:type 'boolean)
(defcustom helm-gtags-auto-update nil
"*If non-nil, tag files are updated whenever a file is saved."
:type 'boolean)
(defcustom helm-gtags-pulse-at-cursor t
"If non-nil, pulse at point after jumping"
:type 'boolean)
(defcustom helm-gtags-cache-select-result nil
"*If non-nil, results of helm-gtags-select and helm-gtags-select-path are cached."
:type 'boolean)
(defcustom helm-gtags-cache-max-result-size (* 10 1024 1024) ;10M
"Max size(bytes) to cache for each select result."
:type 'integer)
(defcustom helm-gtags-update-interval-second 60
"Tags are updated in `after-save-hook' if this seconds is passed from last update.
Always update if value of this variable is nil."
:type '(choice (integer :tag "Update interval seconds")
(boolean :tag "Update every time" nil)))
(defcustom helm-gtags-highlight-candidate t
"Highlight candidate or not"
:type 'boolean)
(defcustom helm-gtags-use-input-at-cursor nil
"Use input at cursor"
:type 'boolean)
(defcustom helm-gtags-prefix-key "\C-c"
"If non-nil, it is used for the prefix key of gtags-xxx command."
:type 'string)
(defcustom helm-gtags-suggested-key-mapping nil
"If non-nil, suggested key mapping is enabled."
:type 'boolean)
(defcustom helm-gtags-preselect nil
"If non-nil, preselect current file and line."
:type 'boolean)
(defcustom helm-gtags-display-style nil
"Style of display result."
:type '(choice (const :tag "Show in detail" detail)
(const :tag "Normal style" nil)))
(defcustom helm-gtags-fuzzy-match nil
"Enable fuzzy match"
:type 'boolean)
(defcustom helm-gtags-maximum-candidates (if helm-gtags-fuzzy-match 100 9999)
"Maximum number of helm candidates"
:type 'integer)
(defcustom helm-gtags-direct-helm-completing nil
"Use helm mode directly."
:type 'boolean)
(defface helm-gtags-file
'((t :inherit font-lock-keyword-face))
"Face for line numbers in the error list.")
(defface helm-gtags-lineno
'((t :inherit font-lock-doc-face))
"Face for line numbers in the error list.")
(defface helm-gtags-match
'((t :inherit helm-match))
"Face for word matched against tagname")
(defvar helm-gtags--tag-location nil)
(defvar helm-gtags--last-update-time 0)
(defvar helm-gtags--completing-history nil)
(defvar helm-gtags--context-stack (make-hash-table :test 'equal))
(defvar helm-gtags--result-cache (make-hash-table :test 'equal))
(defvar helm-gtags--saved-context nil)
(defvar helm-gtags--use-otherwin nil)
(defvar helm-gtags--local-directory nil)
(defvar helm-gtags--parsed-file nil)
(defvar helm-gtags--current-position nil)
(defvar helm-gtags--real-tag-location nil)
(defvar helm-gtags--last-input nil)
(defvar helm-gtags--query nil)
(defvar helm-gtags--last-default-directory nil)
(defconst helm-gtags--buffer "*helm gtags*")
(defconst helm-gtags--include-regexp
"\\`\\s-*#\\(?:include\\|import\\)\\s-*[\"<]\\(?:[./]*\\)?\\(.*?\\)[\">]")
(defmacro helm-declare-obsolete-variable (old new version)
`(progn
(defvaralias ,old ,new)
(make-obsolete-variable ,old ,new ,version)))
(helm-declare-obsolete-variable
'helm-c-gtags-path-style 'helm-gtags-path-style "0.8")
(helm-declare-obsolete-variable
'helm-c-gtags-ignore-case 'helm-gtags-ignore-case "0.8")
(helm-declare-obsolete-variable
'helm-c-gtags-read-only 'helm-gtags-read-only "0.8")
;; completsion function for completing-read.
(defun helm-gtags--completing-gtags (string predicate code)
(helm-gtags--complete 'tag string predicate code))
(defun helm-gtags--completing-pattern (string predicate code)
(helm-gtags--complete 'pattern string predicate code))
(defun helm-gtags--completing-grtags (string predicate code)
(helm-gtags--complete 'rtag string predicate code))
(defun helm-gtags--completing-gsyms (string predicate code)
(helm-gtags--complete 'symbol string predicate code))
(defun helm-gtags--completing-files (string predicate code)
(helm-gtags--complete 'find-file string predicate code))
(defconst helm-gtags-comp-func-alist
'((tag . helm-gtags--completing-gtags)
(pattern . helm-gtags--completing-pattern)
(rtag . helm-gtags--completing-grtags)
(symbol . helm-gtags--completing-gsyms)
(find-file . helm-gtags--completing-files)))
(defconst helm-gtags--search-option-alist
'((pattern . "-g")
(rtag . "-r")
(symbol . "-s")
(find-file . "-Poa")))
(defsubst helm-gtags--windows-p ()
(memq system-type '(windows-nt ms-dos)))
(defun helm-gtags--remove-carrige-returns ()
(when (helm-gtags--windows-p)
(save-excursion
(goto-char (point-min))
(while (re-search-forward "\xd" nil t)
(replace-match "")))))
;; Work around for GNU global Windows issue
(defsubst helm-gtags--use-abs-path-p (gtagslibpath)
(and (helm-gtags--windows-p) gtagslibpath))
(defun helm-gtags--construct-options (type completion)
(let ((find-file-p (eq type 'find-file))
(gtagslibpath (getenv "GTAGSLIBPATH"))
options)
(unless find-file-p
(push "--result=grep" options))
(when completion
(push "-c" options))
(helm-aif (assoc-default type helm-gtags--search-option-alist)
(push it options))
(when (or (eq helm-gtags-path-style 'absolute)
(helm-gtags--use-abs-path-p gtagslibpath))
(push "-a" options))
(when helm-gtags-ignore-case
(push "-i" options))
(when (and current-prefix-arg (not find-file-p))
(push "-l" options))
(when gtagslibpath
(push "-T" options))
options))
(defun helm-gtags--complete (type string predicate code)
(let* ((options (helm-gtags--construct-options type t))
(args (reverse (cons string options)))
candidates)
(with-temp-buffer
(apply #'process-file "global" nil t nil args)
(goto-char (point-min))
(while (re-search-forward "^\\(.+\\)$" nil t)
(push (match-string-no-properties 1) candidates)))
(if (not code)
(try-completion string candidates predicate)
(all-completions string candidates predicate))))
(defun helm-gtags--token-at-point (type)
(if (not (eq type 'find-file))
(thing-at-point 'symbol)
(let ((line (helm-current-line-contents)))
(when (string-match helm-gtags--include-regexp line)
(match-string-no-properties 1 line)))))
(defconst helm-gtags--prompt-alist
'((tag . "Find Definition: ")
(pattern . "Find Pattern: ")
(rtag . "Find Reference: ")
(symbol . "Find Symbol: ")
(find-file . "Find File: ")))
(defun helm-gtags--read-tagname (type &optional default-tagname)
(let ((tagname (helm-gtags--token-at-point type))
(prompt (assoc-default type helm-gtags--prompt-alist))
(comp-func (assoc-default type helm-gtags-comp-func-alist)))
(if (and tagname helm-gtags-use-input-at-cursor)
tagname
(when (and (not tagname) default-tagname)
(setq tagname default-tagname))
(when tagname
(setq prompt (format "%s(default \"%s\") " prompt tagname)))
(let ((completion-ignore-case helm-gtags-ignore-case)
(completing-read-function 'completing-read-default))
(if (and helm-gtags-direct-helm-completing (memq type '(tag rtag symbol find-file)))
(helm-comp-read prompt comp-func
:history 'helm-gtags--completing-history
:exec-when-only-one t
:default tagname)
(completing-read prompt comp-func nil nil nil
'helm-gtags--completing-history tagname))))))
(defun helm-gtags--path-libpath-p (tagroot)
(helm-aif (getenv "GTAGSLIBPATH")
(cl-loop for path in (parse-colon-path it)
for libpath = (file-name-as-directory (expand-file-name path))
thereis (string= tagroot libpath))))
(defsubst helm-gtags--convert-cygwin-windows-file-name-p ()
(and (eq system-type 'cygwin) helm-gtags-cygwin-use-global-w32-port))
(defun helm-gtags--tag-directory ()
(with-temp-buffer
(helm-aif (getenv "GTAGSROOT")
it
(unless (zerop (process-file "global" nil t nil "-p"))
(error "GTAGS not found"))
(goto-char (point-min))
(when (looking-at "^\\([^\r\n]+\\)")
(let ((tag-path (match-string-no-properties 1)))
(file-name-as-directory
(if (helm-gtags--convert-cygwin-windows-file-name-p)
(cygwin-convert-file-name-from-windows tag-path)
tag-path)))))))
(defun helm-gtags--find-tag-directory ()
(setq helm-gtags--real-tag-location nil)
(let ((tagroot (helm-gtags--tag-directory)))
(if (and (helm-gtags--path-libpath-p tagroot) helm-gtags--tag-location)
(progn
(setq helm-gtags--real-tag-location tagroot)
helm-gtags--tag-location)
(setq helm-gtags--tag-location tagroot))))
(defun helm-gtags--base-directory ()
(let ((dir (or helm-gtags--last-default-directory
helm-gtags--local-directory
(cl-case helm-gtags-path-style
(root (or helm-gtags--real-tag-location
helm-gtags--tag-location))
(otherwise default-directory))))
(remote (file-remote-p default-directory)))
(if (and remote (not (file-remote-p dir)))
(concat remote dir)
dir)))
(defsubst helm-gtags--new-context-info (index stack)
(list :index index :stack stack))
(defun helm-gtags--put-context-stack (tag-location index stack)
(puthash tag-location (helm-gtags--new-context-info index stack)
helm-gtags--context-stack))
(defsubst helm-gtags--current-context ()
(let ((file (buffer-file-name (current-buffer))))
(list :file file :position (point) :readonly buffer-file-read-only)))
(defsubst helm-gtags--save-current-context ()
(setq helm-gtags--saved-context (helm-gtags--current-context)))
(defun helm-gtags--open-file (file readonly)
(if readonly
(find-file-read-only file)
(find-file file)))
(defun helm-gtags--open-file-other-window (file readonly)
(setq helm-gtags--use-otherwin nil)
(if readonly
(find-file-read-only-other-window file)
(find-file-other-window file)))
(defun helm-gtags--get-context-info ()
(let* ((tag-location (helm-gtags--find-tag-directory))
(context-info (gethash tag-location helm-gtags--context-stack))
(context-stack (plist-get context-info :stack)))
(if (null context-stack)
(error "Context stack is empty(TAG at %s)" tag-location)
context-info)))
(defun helm-gtags--get-or-create-context-info ()
(or (gethash helm-gtags--tag-location helm-gtags--context-stack)
(helm-gtags--new-context-info -1 nil)))
;;;###autoload
(defun helm-gtags-clear-all-cache ()
(interactive)
(clrhash helm-gtags--result-cache))
;;;###autoload
(defun helm-gtags-clear-cache ()
(interactive)
(helm-gtags--find-tag-directory)
(let* ((tag-location (or helm-gtags--real-tag-location
helm-gtags--tag-location))
(gtags-path (concat tag-location "GTAGS"))
(gpath-path (concat tag-location "GPATH")))
(remhash gtags-path helm-gtags--result-cache)
(remhash gpath-path helm-gtags--result-cache)))
(defun helm-gtags--move-to-context (context)
(let ((file (plist-get context :file))
(curpoint (plist-get context :position))
(readonly (plist-get context :readonly)))
(helm-gtags--open-file file readonly)
(goto-char curpoint)
(recenter)))
;;;###autoload
(defun helm-gtags-next-history ()
"Jump to next position on context stack"
(interactive)
(let* ((context-info (helm-gtags--get-context-info))
(current-index (plist-get context-info :index))
(context-stack (plist-get context-info :stack))
context)
(when (<= current-index -1)
(error "This context is latest in context stack"))
(setf (nth current-index context-stack) (helm-gtags--current-context))
(cl-decf current-index)
(if (= current-index -1)
(setq context helm-gtags--current-position
helm-gtags--current-position nil)
(setq context (nth current-index context-stack)))
(helm-gtags--put-context-stack helm-gtags--tag-location
current-index context-stack)
(helm-gtags--move-to-context context)))
;;;###autoload
(defun helm-gtags-previous-history ()
"Jump to previous position on context stack"
(interactive)
(let* ((context-info (helm-gtags--get-context-info))
(current-index (plist-get context-info :index))
(context-stack (plist-get context-info :stack))
(context-length (length context-stack)))
(cl-incf current-index)
(when (>= current-index context-length)
(error "This context is last in context stack"))
(if (= current-index 0)
(setq helm-gtags--current-position (helm-gtags--current-context))
(setf (nth (- current-index 1) context-stack) (helm-gtags--current-context)))
(let ((prev-context (nth current-index context-stack)))
(helm-gtags--move-to-context prev-context))
(helm-gtags--put-context-stack helm-gtags--tag-location
current-index context-stack)))
(defun helm-gtags--get-result-cache (file)
(helm-gtags--find-tag-directory)
(let* ((file-path (concat (or helm-gtags--real-tag-location
helm-gtags--tag-location)
file))
(file-mtime (nth 5 (file-attributes file-path)))
(hash-value (gethash file-path helm-gtags--result-cache))
(cached-file-mtime (nth 0 hash-value)))
(if (and cached-file-mtime (equal cached-file-mtime file-mtime))
(nth 1 hash-value)
nil)))
(defun helm-gtags--put-result-cache (file cache)
(helm-gtags--find-tag-directory)
(let* ((file-path (concat (or helm-gtags--real-tag-location
helm-gtags--tag-location)
file))
(file-mtime (nth 5 (file-attributes file-path)))
(hash-value (list file-mtime cache)))
(puthash file-path hash-value helm-gtags--result-cache)))
(defun helm-gtags--referer-function (file ref-line)
(let ((is-opened (cl-loop with path = (concat default-directory file)
for buf in (buffer-list)
when (string= (buffer-file-name buf) path)
return it))
retval)
(with-current-buffer (find-file-noselect file)
(goto-char (point-min))
(forward-line (1- ref-line))
(unless (zerop (current-indentation))
(setq retval (which-function)))
(unless is-opened
(kill-buffer (current-buffer)))
retval)))
(defun helm-gtags--show-detail ()
(goto-char (point-min))
(while (not (eobp))
(let ((line (helm-current-line-contents)))
(let* ((file-and-line (helm-gtags--extract-file-and-line line))
(file (car file-and-line))
(ref-line (cdr file-and-line))
(ref-func (helm-gtags--referer-function file ref-line)))
(when ref-func
(search-forward ":" nil nil 2)
(insert " " ref-func "|"))
(forward-line 1)))))
(defun helm-gtags--print-path-in-gtagslibpath (args)
(let ((libpath (getenv "GTAGSLIBPATH")))
(when libpath
(dolist (path (parse-colon-path libpath))
(let ((default-directory (file-name-as-directory path)))
(apply #'process-file "global" nil t nil "-Poa" args))))))
(defun helm-gtags--exec-global-command (type input &optional detail)
(let ((args (helm-gtags--construct-command type input)))
(helm-gtags--find-tag-directory)
(helm-gtags--save-current-context)
(let ((buf-coding buffer-file-coding-system))
(with-current-buffer (helm-candidate-buffer 'global)
(let ((default-directory (helm-gtags--base-directory))
(input (car (last args)))
(coding-system-for-read buf-coding)
(coding-system-for-write buf-coding))
(unless (zerop (apply #'process-file "global" nil '(t nil) nil args))
(error (format "%s: not found" input)))
;; --path options does not support searching under GTAGSLIBPATH
(when (eq type 'find-file)
(helm-gtags--print-path-in-gtagslibpath args))
(helm-gtags--remove-carrige-returns)
(when detail
(helm-gtags--show-detail)))))))
(defun helm-gtags--construct-command (type &optional in)
(setq helm-gtags--local-directory nil)
(let ((dir (helm-attr 'helm-gtags-base-directory (helm-get-current-source))))
(when (and dir (not (eq type 'find-file)))
(setq helm-gtags--local-directory dir)))
(let ((input (or in helm-gtags--query))
(options (helm-gtags--construct-options type nil)))
(when (string-empty-p input)
(error "Input is empty!!"))
(setq helm-gtags--last-input input)
(reverse (cons input options))))
(defun helm-gtags--tags-init (&optional input)
(helm-gtags--exec-global-command 'tag input))
(defun helm-gtags--pattern-init (&optional input)
(helm-gtags--exec-global-command 'pattern input helm-gtags-display-style))
(defun helm-gtags--rtags-init (&optional input)
(helm-gtags--exec-global-command 'rtag input helm-gtags-display-style))
(defun helm-gtags--gsyms-init ()
(helm-gtags--exec-global-command 'symbol nil helm-gtags-display-style))
(defun helm-gtags--files-init ()
(helm-gtags--exec-global-command 'find-file nil))
(defun helm-gtags--real-file-name ()
(let ((buffile (buffer-file-name)))
(unless buffile
(error "This buffer is not related to file."))
(if (file-remote-p buffile)
(tramp-file-name-localname (tramp-dissect-file-name buffile))
(file-truename buffile))))
(defun helm-gtags--find-tag-from-here-init ()
(helm-gtags--find-tag-directory)
(helm-gtags--save-current-context)
(let ((token (helm-gtags--token-at-point 'from-here)))
(unless token
(error "Cursor is not on symbol."))
(let* ((filename (helm-gtags--real-file-name))
(from-here-opt (format "--from-here=%d:%s"
(line-number-at-pos)
(if (helm-gtags--convert-cygwin-windows-file-name-p)
(cygwin-convert-file-name-to-windows filename)
filename))))
(setq helm-gtags--last-input token)
(with-current-buffer (helm-candidate-buffer 'global)
(let* ((default-directory (helm-gtags--base-directory))
(status (process-file "global" nil '(t nil) nil
"--result=grep" from-here-opt token)))
(helm-gtags--remove-carrige-returns)
(unless (zerop status)
(cond ((= status 1)
(error "Error: %s%s" (buffer-string) filename))
((= status 3)
(error "Error: %s" (buffer-string)))
(t (error "%s: not found" token)))))))))
(defun helm-gtags--parse-file-init ()
(with-current-buffer (helm-candidate-buffer 'global)
(unless (zerop (process-file "global" nil t nil
"--result=cscope" "-f" helm-gtags--parsed-file))
(error "Failed: 'global --result=cscope -f %s" helm-gtags--parsed-file))
(helm-gtags--remove-carrige-returns)))
(defun helm-gtags--push-context (context)
(let* ((context-info (helm-gtags--get-or-create-context-info))
(current-index (plist-get context-info :index))
(context-stack (plist-get context-info :stack)))
(unless (= current-index -1)
(setq context-stack (nthcdr (1+ current-index) context-stack)))
(setq helm-gtags--current-position nil)
(push context context-stack)
(helm-gtags--put-context-stack helm-gtags--tag-location -1 context-stack)))
(defsubst helm-gtags--select-find-file-func ()
(if helm-gtags--use-otherwin
#'helm-gtags--open-file-other-window
#'helm-gtags--open-file))
(defun helm-gtags--do-open-file (open-func file line)
(funcall open-func file helm-gtags-read-only)
(goto-char (point-min))
(forward-line (1- line))
(back-to-indentation)
(recenter)
(helm-gtags--push-context helm-gtags--saved-context)
(when helm-gtags-pulse-at-cursor
(pulse-momentary-highlight-one-line (point))))
(defun helm-gtags--find-line-number (cand)
(if (string-match "\\s-+\\([1-9][0-9]+\\)\\s-+" cand)
(string-to-number (match-string-no-properties 1 cand))
(error "Can't find line number in %s" cand)))
(defun helm-gtags--parse-file-action (cand)
(let ((line (helm-gtags--find-line-number cand))
(open-func (helm-gtags--select-find-file-func)))
(helm-gtags--do-open-file open-func helm-gtags--parsed-file line)))
(defsubst helm-gtags--has-drive-letter-p (path)
(string-match-p "\\`[a-zA-Z]:" path))
(defun helm-gtags--extract-file-and-line (cand)
(if (and (helm-gtags--windows-p) (helm-gtags--has-drive-letter-p cand))
(when (string-match "\\(\\`[a-zA-Z]:[^:]+\\):\\([^:]+\\)" cand)
(cons (match-string-no-properties 1 cand)
(string-to-number (match-string-no-properties 2 cand))))
(let ((elems (split-string cand ":")))
(cons (cl-first elems) (string-to-number (cl-second elems))))))
(defun helm-gtags--action-openfile (cand)
(let* ((file-and-line (helm-gtags--extract-file-and-line cand))
(filename (car file-and-line))
(line (cdr file-and-line))
(open-func (helm-gtags--select-find-file-func))
(default-directory (helm-gtags--base-directory)))
(helm-gtags--do-open-file open-func filename line)))
(defun helm-gtags--action-openfile-other-window (cand)
(let ((helm-gtags--use-otherwin t))
(helm-gtags--action-openfile cand)))
(defun helm-gtags--file-content-at-pos (file pos)
(with-current-buffer (find-file-noselect file)
(save-excursion
(goto-char pos)
(format "%s:%d:%s:%s"
file (line-number-at-pos)
(helm-aif (which-function) (format "[%s]" it) "")
(helm-current-line-contents)))))
(defun helm-gtags--files-candidate-transformer (file)
(if (eq helm-gtags-path-style 'absolute)
file
(let ((removed-regexp (concat "\\`" helm-gtags--tag-location)))
(replace-regexp-in-string removed-regexp "" file))))
(defun helm-gtags--show-stack-init ()
(cl-loop with context-stack = (plist-get (helm-gtags--get-context-info) :stack)
with stack-length = (length context-stack)
for context in (reverse context-stack)
for file = (plist-get context :file)
for pos = (plist-get context :position)
for index = (1- stack-length) then (1- index)
for line = (helm-gtags--file-content-at-pos file pos)
for cand = (helm-gtags--files-candidate-transformer line)
collect (cons cand (propertize cand 'index index))))
(defun helm-gtags--persistent-action (cand)
(let* ((file-and-line (helm-gtags--extract-file-and-line cand))
(filename (car file-and-line))
(line (cdr file-and-line))
(default-directory (helm-gtags--base-directory)))
(when (eq helm-gtags-path-style 'relative)
(setq helm-gtags--last-default-directory default-directory))
(find-file filename)
(goto-char (point-min))
(forward-line (1- line))
(helm-highlight-current-line)))
(defvar helm-gtags--find-file-action
(helm-make-actions
"Open file" #'helm-gtags--action-openfile
"Open file other window" #'helm-gtags--action-openfile-other-window))
(defvar helm-source-gtags-tags
(helm-build-in-buffer-source "Jump to definitions"
:init 'helm-gtags--tags-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display 'helm-gtags--candidate-transformer
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--find-file-action))
(defvar helm-source-gtags-pattern
(helm-build-in-buffer-source "Find pattern"
:init 'helm-gtags--pattern-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display 'helm-gtags--candidate-transformer
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--find-file-action))
(defvar helm-source-gtags-rtags
(helm-build-in-buffer-source "Jump to references"
:init 'helm-gtags--rtags-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display 'helm-gtags--candidate-transformer
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--find-file-action))
(defvar helm-source-gtags-gsyms
(helm-build-in-buffer-source "Jump to symbols"
:init 'helm-gtags--gsyms-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display 'helm-gtags--candidate-transformer
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--find-file-action))
(defun helm-gtags--highlight-candidate (candidate)
(let ((regexp (concat "\\_<" helm-gtags--last-input "\\_>"))
(limit (1- (length candidate)))
(last-pos 0)
(case-fold-search nil))
(while (and (< last-pos limit)
(string-match regexp candidate last-pos))
(put-text-property (match-beginning 0) (match-end 0)
'face 'helm-gtags-match
candidate)
(setq last-pos (1+ (match-end 0))))
candidate))
(defun helm-gtags--transformer-regexp (candidate)
(if (and (helm-gtags--windows-p) (helm-gtags--has-drive-letter-p candidate))
"\\`\\([a-zA-Z]:[^:]+\\):\\([^:]+\\):\\(.*\\)"
"\\`\\([^:]+\\):\\([^:]+\\):\\(.*\\)"))
(defun helm-gtags--candidate-transformer (candidate)
(if (not helm-gtags-highlight-candidate)
candidate
(let ((regexp (helm-gtags--transformer-regexp candidate)))
(when (string-match regexp candidate)
(format "%s:%s:%s"
(propertize (match-string 1 candidate) 'face 'helm-gtags-file)
(propertize (match-string 2 candidate) 'face 'helm-gtags-lineno)
(helm-gtags--highlight-candidate (match-string 3 candidate)))))))
(defun helm-gtags--parse-file-candidate-transformer (file)
(let ((removed-file (replace-regexp-in-string "\\`\\S-+ " "" file)))
(when (string-match "\\`\\(\\S-+\\) \\(\\S-+\\) \\(.+\\)\\'" removed-file)
(format "%-25s %-5s %s"
(match-string-no-properties 1 removed-file)
(match-string-no-properties 2 removed-file)
(match-string-no-properties 3 removed-file)))))
(defvar helm-source-gtags-find-tag-from-here
(helm-build-in-buffer-source "Find tag from here"
:init 'helm-gtags--find-tag-from-here-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display 'helm-gtags--candidate-transformer
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--find-file-action))
(defvar helm-source-gtags-parse-file
(helm-build-in-buffer-source "Parse file"
:init 'helm-gtags--parse-file-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display 'helm-gtags--parse-file-candidate-transformer
:fuzzy-match helm-gtags-fuzzy-match
:action 'helm-gtags--parse-file-action))
(defun helm-gtags--show-stack-action (cand)
(let* ((index (get-text-property 0 'index cand))
(context-info (helm-gtags--get-context-info))
(context-stack (plist-get context-info :stack)))
(helm-gtags--put-context-stack helm-gtags--tag-location
index context-stack)
(helm-gtags--move-to-context (nth index context-stack))))
(defvar helm-source-gtags-show-stack
(helm-build-sync-source "Show Context Stack"
:candidates 'helm-gtags--show-stack-init
:volatile t
:candidate-number-limit helm-gtags-maximum-candidates
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action 'helm-gtags--show-stack-action))
;;;###autoload
(defun helm-gtags-select ()
(interactive)
(helm-gtags--common '(helm-source-gtags-select) nil))
;;;###autoload
(defun helm-gtags-select-path ()
(interactive)
(helm-gtags--common '(helm-source-gtags-select-path) nil))
(defsubst helm-gtags--beginning-of-defun ()
(cl-case major-mode
((c-mode c++-mode java-mode) 'c-beginning-of-defun)
(php-mode 'php-beginning-of-defun)
(otherwise #'beginning-of-defun)))
(defsubst helm-gtags--end-of-defun ()
(cl-case major-mode
((c-mode c++-mode java-mode malabar-mode) 'c-end-of-defun)
(php-mode 'php-end-of-defun)
(otherwise #'end-of-defun)))
(defun helm-gtags--current-funcion-bound ()
(save-excursion
(let (start)
(funcall (helm-gtags--beginning-of-defun))
(setq start (line-number-at-pos))
(funcall (helm-gtags--end-of-defun))
(cons start (line-number-at-pos)))))
(defun helm-gtags--tags-refered-from-this-function ()
(let* ((file (helm-gtags--real-file-name))
(bound (helm-gtags--current-funcion-bound))
(start-line (car bound))
(end-line (cdr bound)))
(with-temp-buffer
(unless (process-file "global" nil t nil "-f" "-r" file)
(error "Failed: global -f -r %s" file))
(goto-char (point-min))
(let (tagnames finish)
(while (and (not finish) (not (eobp)))
(let* ((cols (split-string (helm-current-line-contents) nil t))
(lineno (string-to-number (cl-second cols))))
(if (and (> lineno start-line) (< lineno end-line))
(let* ((tag (cl-first cols))
(elm (cl-find tag tagnames :test 'equal)))
(unless elm
(push tag tagnames)))
(when (>= lineno end-line)
(setq finish t)))
(forward-line 1)))
(reverse tagnames)))))
(defun helm-gtags--tag-in-function-persistent-action (cand)
(let* ((bound (helm-gtags--current-funcion-bound))
(limit (save-excursion
(goto-char (point-min))
(forward-line (cdr bound))
(goto-char (line-end-position))
(point))))
(when (search-forward cand nil limit)
(helm-highlight-current-line))))
;;;###autoload
(defun helm-gtags-tags-in-this-function ()
"Show tagnames which are referenced in this function and jump to it."
(interactive)
(let ((tags (helm-gtags--tags-refered-from-this-function)))
(unless tags
(error "There are no tags which are refered from this function."))
(let* ((name (format "Tags in [%s]" (which-function)))
(tag (helm-comp-read
"Tagnames: " tags
:must-match t :name name
:persistent-action 'helm-gtags--tag-in-function-persistent-action)))
(helm-gtags-find-tag tag))))
(defun helm-gtags--source-select-tag (candidate)
(helm-build-in-buffer-source "Select Tag"
:init (lambda () (helm-gtags--tags-init candidate))
:candidate-number-limit helm-gtags-maximum-candidates
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--find-file-action))
(defun helm-gtags--source-select-rtag (candidate)
(helm-build-in-buffer-source "Select Rtag"
:init (lambda () (helm-gtags--rtags-init candidate))
:candidate-number-limit helm-gtags-maximum-candidates
:persistent-action 'helm-gtags--persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--find-file-action))
(defsubst helm-gtags--action-by-timer (src)
(run-with-timer 0.1 nil (lambda () (helm-gtags--common (list src) nil))))
(defun helm-gtags--select-tag-action (c)
(helm-gtags--action-by-timer (helm-gtags--source-select-tag c)))
(defun helm-gtags--select-rtag-action (c)
(helm-gtags--action-by-timer (helm-gtags--source-select-rtag c)))
(defun helm-gtags--select-cache-init-common (args tagfile)
(let ((cache (helm-gtags--get-result-cache tagfile)))
(if cache
(insert cache)
(apply #'process-file "global" nil t nil args)
(let* ((cache (buffer-string))
(cache-size (length cache)))
(when (<= cache-size helm-gtags-cache-max-result-size)
(helm-gtags--put-result-cache tagfile cache))))))
(defun helm-gtags--source-select-init ()
(with-current-buffer (helm-candidate-buffer 'global)
(if (not helm-gtags-cache-select-result)
(progn
(process-file "global" nil t nil "-c")
(helm-gtags--remove-carrige-returns))
(helm-gtags--select-cache-init-common '("-c") "GTAGS"))))
(defvar helm-source-gtags-select
(helm-build-in-buffer-source "Find tag from here"
:init 'helm-gtags--source-select-init
:candidate-number-limit helm-gtags-maximum-candidates
:persistent-action #'ignore
:fuzzy-match helm-gtags-fuzzy-match
:action (helm-make-actions
"Goto the location" #'helm-gtags--select-tag-action
"Goto the location(other buffer)"
(lambda (c)
(setq helm-gtags--use-otherwin t)
(helm-gtags--select-tag-action c))
"Move to the referenced point" #'helm-gtags--select-rtag-action)))
(defun helm-gtags--select-path-init ()
(helm-gtags--find-tag-directory)
(with-current-buffer (helm-candidate-buffer 'global)
(let ((options (if (eq helm-gtags-path-style 'relative) "-Po" "-Poa")))
(if (not helm-gtags-cache-select-result)
(progn
(process-file "global" nil t nil options)
(helm-gtags--remove-carrige-returns))
(helm-gtags--select-cache-init-common (list options) "GPATH")))))
(defun helm-gtags--file-name (name)
(let ((remote (file-remote-p default-directory)))
(if (not remote)
name
(cl-case helm-gtags-path-style
(relative name)
(otherwise (concat remote name))))))
(defun helm-gtags--find-file-common (open-fn cand)
(let ((default-directory (helm-gtags--base-directory)))
(funcall open-fn (helm-gtags--file-name cand))))
(defun helm-gtags--find-file (cand)
(helm-gtags--find-file-common #'find-file cand))
(defun helm-gtags--find-file-other-window (cand)
(helm-gtags--find-file-common #'find-file-other-window cand))
(defvar helm-gtags--file-util-action
(helm-make-actions
"Open file" #'helm-gtags--find-file
"Open file other window" #'helm-gtags--find-file-other-window))
(defun helm-gtags--file-persistent-action (cand)
(let ((default-directory (with-helm-current-buffer
default-directory)))
(helm-ff-kill-or-find-buffer-fname (helm-gtags--file-name cand))))
(defvar helm-source-gtags-select-path
(helm-build-in-buffer-source "Select path"
:init 'helm-gtags--select-path-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display 'helm-gtags--files-candidate-transformer
:persistent-action #'helm-gtags--file-persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--file-util-action))
(defun helm-gtags--searched-directory ()
(cl-case (prefix-numeric-value current-prefix-arg)
(4 (let ((dir (read-directory-name "Input Directory: ")))
(setq helm-gtags--local-directory (file-name-as-directory dir))))
(16 (file-name-directory (buffer-file-name)))))
(defsubst helm-gtags--using-other-window-p ()
(< (prefix-numeric-value current-prefix-arg) 0))
(defun helm-gtags--make-gtags-sentinel (action)
(lambda (process _event)
(when (eq (process-status process) 'exit)
(if (zerop (process-exit-status process))
(message "Success: %s TAGS" action)
(message "Failed: %s TAGS(%d)" action (process-exit-status process))))))
(defsubst helm-gtags--read-gtagslabel ()
(let ((labels '("default" "native" "ctags" "new-ctags" "pygments")))
(completing-read "GTAGSLABEL(Default: default): " labels nil t nil nil "default")))
(defsubst helm-gtags--label-option (label)
(concat "--gtagslabel=" label))
;;;###autoload
(defun helm-gtags-create-tags (dir label)
(interactive
(list (read-directory-name "Root Directory: ")
(helm-gtags--read-gtagslabel)))
(let ((default-directory dir)
(proc-buf (get-buffer-create " *helm-gtags-create*")))
(let ((proc (start-file-process "helm-gtags-create" proc-buf
"gtags" "-q" (helm-gtags--label-option label))))
(set-process-sentinel proc (helm-gtags--make-gtags-sentinel 'create)))))
(defun helm-gtags--find-tag-simple ()
(or (getenv "GTAGSROOT")
(locate-dominating-file default-directory "GTAGS")
(if (not (yes-or-no-p "File GTAGS not found. Run 'gtags'? "))
(user-error "Abort")
(let* ((tagroot (read-directory-name "Root Directory: "))
(label (helm-gtags--read-gtagslabel))
(default-directory tagroot))
(message "gtags is generating tags....")
(let ((label-opt (helm-gtags--label-option label)))
(unless (zerop (process-file "gtags" nil nil nil "-q" label-opt))
(error "Failed: 'gtags -q %s'" label-opt)))
tagroot))))
(defun helm-gtags--current-file-and-line ()
(let* ((buffile (buffer-file-name))
(path (cl-case helm-gtags-path-style
(absolute buffile)
(root
(file-relative-name buffile (helm-gtags--find-tag-directory)))
(relative
(file-relative-name buffile (helm-gtags--base-directory))))))
(format "%s:%d" path (line-number-at-pos))))
(defsubst helm-gtags--clear-variables ()
(setq helm-gtags--last-default-directory nil))
(defun helm-gtags--common (srcs tagname)
(helm-gtags--clear-variables)
(let ((helm-quit-if-no-candidate t)
(helm-execute-action-at-once-if-one t)
(dir (helm-gtags--searched-directory))
(src (car srcs))
(preselect-regexp (when helm-gtags-preselect
(regexp-quote (helm-gtags--current-file-and-line)))))
(when (symbolp src)
(setq src (symbol-value src)))
(unless helm-gtags--use-otherwin
(setq helm-gtags--use-otherwin (helm-gtags--using-other-window-p)))
(when tagname
(setq helm-gtags--query tagname))
(let ((tagroot (helm-gtags--find-tag-simple)))
(helm-attrset 'helm-gtags-base-directory dir src)
(when tagname
(helm-attrset 'name (format "%s in %s" tagname (or dir tagroot)) src))
(helm :sources srcs :buffer helm-gtags--buffer
:preselect preselect-regexp))))
;;;###autoload
(defun helm-gtags-find-tag (tag)
"Jump to definition"
(interactive
(list (helm-gtags--read-tagname 'tag)))
(helm-gtags--common '(helm-source-gtags-tags) tag))
;;;###autoload
(defun helm-gtags-find-tag-other-window (tag)
"Jump to definition in other window."
(interactive
(list (helm-gtags--read-tagname 'tag)))
(setq helm-gtags--use-otherwin t)
(helm-gtags-find-tag tag))
;;;###autoload
(defun helm-gtags-find-rtag (tag)
"Jump to referenced point"
(interactive
(list (helm-gtags--read-tagname 'rtag (which-function))))
(helm-gtags--common '(helm-source-gtags-rtags) tag))
;;;###autoload
(defun helm-gtags-find-symbol (tag)
"Jump to the symbol location"
(interactive
(list (helm-gtags--read-tagname 'symbol)))
(helm-gtags--common '(helm-source-gtags-gsyms) tag))
;;;###autoload
(defun helm-gtags-find-pattern (pattern)
"Grep and jump by gtags tag files."
(interactive
(list (helm-gtags--read-tagname 'pattern)))
(helm-gtags--common '(helm-source-gtags-pattern) pattern))
(defun helm-gtags--find-file-after-hook ()
(helm-gtags--push-context helm-gtags--saved-context))
(defvar helm-source-gtags-files
(helm-build-in-buffer-source "Find files"
:init #'helm-gtags--files-init
:candidate-number-limit helm-gtags-maximum-candidates
:real-to-display #'helm-gtags--files-candidate-transformer
:persistent-action #'helm-gtags--file-persistent-action
:fuzzy-match helm-gtags-fuzzy-match
:action helm-gtags--file-util-action))
;;;###autoload
(defun helm-gtags-find-files (file)
"Find file from tagged with gnu global."
(interactive
(list (helm-gtags--read-tagname 'find-file)))
(add-hook 'helm-after-action-hook 'helm-gtags--find-file-after-hook)
(unwind-protect
(helm-gtags--common '(helm-source-gtags-files) file)
(remove-hook 'helm-after-action-hook 'helm-gtags--find-file-after-hook)))
;;;###autoload
(defun helm-gtags-find-tag-from-here ()
"Jump point by current point information.
Jump to definition point if cursor is on its reference.
Jump to reference point if curosr is on its definition"
(interactive)
(helm-gtags--common '(helm-source-gtags-find-tag-from-here) nil))
;;;###autoload
(defun helm-gtags-dwim ()
"Find by context. Here is
- on include statement then jump to included file
- on symbol definition then jump to its references
- on reference point then jump to its definition."
(interactive)
(let ((line (helm-current-line-contents)))
(if (string-match helm-gtags--include-regexp line)
(let ((helm-gtags-use-input-at-cursor t))
(helm-gtags-find-files (match-string-no-properties 1 line)))
(if (and (buffer-file-name) (thing-at-point 'symbol))
(helm-gtags-find-tag-from-here)
(call-interactively 'helm-gtags-find-tag)))))
(defun helm-gtags--set-parsed-file ()
(let* ((this-file (file-name-nondirectory (buffer-file-name)))
(file (if current-prefix-arg
(read-file-name "Parsed File: " nil this-file)
this-file)))
(setq helm-gtags--parsed-file (expand-file-name file))))
(defun helm-gtags--find-preselect-line ()
(let ((defun-bound (bounds-of-thing-at-point 'defun)))
(if (not defun-bound)
(line-number-at-pos)
(let ((defun-begin-line (line-number-at-pos (car defun-bound)))
(filename (helm-gtags--real-file-name)))
(with-temp-buffer
(unless (zerop (process-file "global" nil t nil "-f" filename))
(error "Failed: global -f"))
(goto-char (point-min))
(let (start-line)
(while (and (not start-line)
(re-search-forward "^\\S-+\\s-+\\([1-9][0-9]*\\)" nil t))
(let ((line (string-to-number (match-string-no-properties 1))))
(when (>= line defun-begin-line)
(setq start-line line))))
(or start-line (line-number-at-pos))))))))
;;;###autoload
(defun helm-gtags-parse-file ()
"Parse current file with gnu global. This is similar to `imenu'.
You can jump definitions of functions, symbols in this file."
(interactive)
(helm-gtags--find-tag-directory)
(helm-gtags--save-current-context)
(setq helm-gtags--use-otherwin (helm-gtags--using-other-window-p))
(helm-gtags--set-parsed-file)
(helm-attrset 'name
(format "Parsed File: %s"
(file-relative-name helm-gtags--parsed-file
helm-gtags--tag-location))
helm-source-gtags-parse-file)
(let ((presel (when helm-gtags-preselect
(format "^\\S-+\\s-+%d\\s-+" (helm-gtags--find-preselect-line)))))
(helm :sources '(helm-source-gtags-parse-file)
:buffer helm-gtags--buffer :preselect presel)))
;;;###autoload
(defun helm-gtags-pop-stack ()
"Jump to previous point on the context stack and pop it from stack."
(interactive)
(let* ((context-info (helm-gtags--get-context-info))
(context-stack (plist-get context-info :stack))
(context (pop context-stack)))
(helm-gtags--put-context-stack helm-gtags--tag-location -1 context-stack)
(helm-gtags--move-to-context context)))
;;;###autoload
(defun helm-gtags-show-stack ()
"Show current context stack."
(interactive)
(helm-other-buffer 'helm-source-gtags-show-stack
(get-buffer-create helm-gtags--buffer)))
;;;###autoload
(defun helm-gtags-clear-stack ()
"Clear current context stack."
(interactive)
(let ((tag-location (helm-gtags--find-tag-directory)))
(message "Clear '%s' context stack." tag-location)
(remhash tag-location helm-gtags--context-stack)))
;;;###autoload
(defun helm-gtags-clear-all-stacks ()
"Clear all context stacks."
(interactive)
(message "Clear all context statks.")
(setq helm-gtags--context-stack (make-hash-table :test 'equal)))
(defun helm-gtags--read-tag-directory ()
(let ((dir (read-directory-name "Directory tag generated: " nil nil t)))
;; On Windows, "gtags d:/tmp" work, but "gtags d:/tmp/" doesn't
(directory-file-name (expand-file-name dir))))
(defsubst helm-gtags--how-to-update-tags ()
(cl-case (prefix-numeric-value current-prefix-arg)
(4 'entire-update)
(16 'generate-other-directory)
(otherwise 'single-update)))
(defun helm-gtags--update-tags-command (how-to)
(cl-case how-to
(entire-update '("global" "-u"))
(generate-other-directory (list "gtags" (helm-gtags--read-tag-directory)))
(single-update (list "global" "--single-update" (helm-gtags--real-file-name)))))
(defun helm-gtags--update-tags-p (how-to interactive-p current-time)
(or interactive-p
(and (eq how-to 'single-update)
(buffer-file-name)
(or (not helm-gtags-update-interval-second)
(>= (- current-time helm-gtags--last-update-time)
helm-gtags-update-interval-second)))))
;;;###autoload
(defun helm-gtags-update-tags ()
"Update TAG file. Update All files with `C-u' prefix.
Generate new TAG file in selected directory with `C-u C-u'"
(interactive)
(let ((how-to (helm-gtags--how-to-update-tags))
(interactive-p (called-interactively-p 'interactive))
(current-time (float-time (current-time))))
(when (helm-gtags--update-tags-p how-to interactive-p current-time)
(let* ((cmds (helm-gtags--update-tags-command how-to))
(proc (apply #'start-file-process "helm-gtags-update-tag" nil cmds)))
(if (not proc)
(message "Failed: %s" (string-join cmds " "))
(set-process-sentinel proc (helm-gtags--make-gtags-sentinel 'update))
(setq helm-gtags--last-update-time current-time))))))
;;;###autoload
(defun helm-gtags-resume ()
"Resurrect previously invoked `helm-gtags` command."
(interactive)
(unless (get-buffer helm-gtags--buffer)
(error "Error: helm-gtags buffer is not existed."))
(helm-resume helm-gtags--buffer))
(defsubst helm-gtags--check-browser-installed (browser)
(let ((used-browser (or browser "mozilla")))
(unless (executable-find used-browser)
(error "Not found browser '%s'" used-browser))))
(defun helm-gtags-display-browser ()
"Display current screen on hypertext browser.
`browse-url-generic-program' is used as browser if its value is non-nil.
`mozilla' is used in other case."
(interactive)
(let ((file (buffer-file-name)))
(if (not file)
(error "This buffer is not related to file.")
(let* ((lineopt (concat "+" (number-to-string (line-number-at-pos))))
(browser (symbol-value 'browse-url-generic-program))
(args (list lineopt file)))
(helm-gtags--check-browser-installed browser)
(when browser
(setq args (append (list "-b" browser) args)))
;; `gozilla' commend never returns error status if command is failed.
(apply #'process-file "gozilla" nil nil nil args)))))
(defvar helm-gtags-mode-name " HelmGtags")
(defvar helm-gtags-mode-map (make-sparse-keymap))
;;;###autoload
(define-minor-mode helm-gtags-mode ()
"Enable helm-gtags"
:init-value nil
:global nil
:keymap helm-gtags-mode-map
:lighter helm-gtags-mode-name
(if helm-gtags-mode
(when helm-gtags-auto-update
(add-hook 'after-save-hook 'helm-gtags-update-tags nil t))
(when helm-gtags-auto-update
(remove-hook 'after-save-hook 'helm-gtags-update-tags t))))
;; Key mapping of gtags-mode.
(when helm-gtags-suggested-key-mapping
;; Current key mapping.
(let ((command-table '(("h" . helm-gtags-display-browser)
("P" . helm-gtags-find-files)
("f" . helm-gtags-parse-file)
("g" . helm-gtags-find-pattern)
("s" . helm-gtags-find-symbol)
("r" . helm-gtags-find-rtag)
("t" . helm-gtags-find-tag)
("d" . helm-gtags-find-tag)))
(key-func (if (string-prefix-p "\\" helm-gtags-prefix-key)
#'concat
(lambda (prefix key) (kbd (concat prefix " " key))))))
(cl-loop for (key . command) in command-table
do
(define-key helm-gtags-mode-map (funcall key-func helm-gtags-prefix-key key) command))
;; common
(define-key helm-gtags-mode-map "\C-]" 'helm-gtags-find-tag-from-here)
(define-key helm-gtags-mode-map "\C-t" 'helm-gtags-pop-stack)
(define-key helm-gtags-mode-map "\e*" 'helm-gtags-pop-stack)
(define-key helm-gtags-mode-map "\e." 'helm-gtags-find-tag)
(define-key helm-gtags-mode-map "\C-x4." 'helm-gtags-find-tag-other-window)))
(provide 'helm-gtags)
;; Local Variables:
;; coding: utf-8
;; indent-tabs-mode: nil
;; End:
;;; helm-gtags.el ends here