mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-02-16 09:14:18 +00:00
New feature: etags-regen-mode
* lisp/progmodes/etags-regen.el: New file (bug#67687). * etc/NEWS: Mention the addition. * .dir-locals.el: Add this project's settings for etags-regen-regexp-alist and etags-regen-ignores.
This commit is contained in:
parent
0c5cfd0a23
commit
bdfa49502a
3 changed files with 442 additions and 0 deletions
|
|
@ -8,6 +8,12 @@
|
||||||
(vc-git-annotate-switches . "-w")
|
(vc-git-annotate-switches . "-w")
|
||||||
(bug-reference-url-format . "https://debbugs.gnu.org/%s")
|
(bug-reference-url-format . "https://debbugs.gnu.org/%s")
|
||||||
(diff-add-log-use-relative-names . t)
|
(diff-add-log-use-relative-names . t)
|
||||||
|
(etags-regen-regexp-alist
|
||||||
|
.
|
||||||
|
((("c" "objc") .
|
||||||
|
("/[ \t]*DEFVAR_[A-Z_ \t(]+\"\\([^\"]+\\)\"/\\1/"
|
||||||
|
"/[ \t]*DEFVAR_[A-Z_ \t(]+\"[^\"]+\",[ \t]\\([A-Za-z0-9_]+\\)/\\1/"))))
|
||||||
|
(etags-regen-ignores . ("test/manual/etags/"))
|
||||||
(vc-prepare-patches-separately . nil)))
|
(vc-prepare-patches-separately . nil)))
|
||||||
(c-mode . ((c-file-style . "GNU")
|
(c-mode . ((c-file-style . "GNU")
|
||||||
(c-noise-macro-names . ("INLINE" "NO_INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED"
|
(c-noise-macro-names . ("INLINE" "NO_INLINE" "ATTRIBUTE_NO_SANITIZE_UNDEFINED"
|
||||||
|
|
|
||||||
5
etc/NEWS
5
etc/NEWS
|
|
@ -1265,6 +1265,11 @@ the needs of users with red-green or blue-yellow color deficiency.
|
||||||
The Info manual "(modus-themes) Top" describes the details and
|
The Info manual "(modus-themes) Top" describes the details and
|
||||||
showcases all their customization options.
|
showcases all their customization options.
|
||||||
|
|
||||||
|
** New global minor mode 'etags-regen-mode'.
|
||||||
|
This minor mode generates the tags table automatically based on the
|
||||||
|
current project configuration, and later updates it as you edit the
|
||||||
|
files and save the changes.
|
||||||
|
|
||||||
|
|
||||||
* Incompatible Lisp Changes in Emacs 30.1
|
* Incompatible Lisp Changes in Emacs 30.1
|
||||||
|
|
||||||
|
|
|
||||||
431
lisp/progmodes/etags-regen.el
Normal file
431
lisp/progmodes/etags-regen.el
Normal file
|
|
@ -0,0 +1,431 @@
|
||||||
|
;;; etags-regen.el --- Auto-(re)regenerating tags -*- lexical-binding: t -*-
|
||||||
|
|
||||||
|
;; Copyright (C) 2021-2023 Free Software Foundation, Inc.
|
||||||
|
|
||||||
|
;; Author: Dmitry Gutov <dmitry@gutov.dev>
|
||||||
|
;; Keywords: tools
|
||||||
|
|
||||||
|
;; This file is part of GNU Emacs.
|
||||||
|
|
||||||
|
;; GNU Emacs 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.
|
||||||
|
|
||||||
|
;; GNU Emacs 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 GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
;;; Commentary:
|
||||||
|
|
||||||
|
;; Simple automatic tags generation with updates on save.
|
||||||
|
;;
|
||||||
|
;; This mode provides automatic indexing for Emacs "go to definition"
|
||||||
|
;; feature, the `xref-go-forward' command (bound to `M-.' by default).
|
||||||
|
;;
|
||||||
|
;; At the moment reindexing works off before/after-save-hook, but to
|
||||||
|
;; handle more complex changes (for example, the user switching to
|
||||||
|
;; another branch from the terminal) we can look into plugging into
|
||||||
|
;; something like `filenotify'.
|
||||||
|
;;
|
||||||
|
;; Note that this feature disables itself if the user has some tags
|
||||||
|
;; table already visited (with `M-x visit-tags-table', or through an
|
||||||
|
;; explicit prompt triggered by some feature that requires tags).
|
||||||
|
|
||||||
|
;;; Code:
|
||||||
|
|
||||||
|
(require 'cl-lib)
|
||||||
|
|
||||||
|
(defgroup etags-regen nil
|
||||||
|
"Auto-(re)generating tags."
|
||||||
|
:group 'tools)
|
||||||
|
|
||||||
|
(defvar etags-regen--tags-file nil)
|
||||||
|
(defvar etags-regen--tags-root nil)
|
||||||
|
(defvar etags-regen--new-file nil)
|
||||||
|
|
||||||
|
(declare-function project-root "project")
|
||||||
|
(declare-function project-files "project")
|
||||||
|
(declare-function dired-glob-regexp "dired")
|
||||||
|
|
||||||
|
(defcustom etags-regen-program (executable-find "etags")
|
||||||
|
"Name of the etags program used by `etags-regen-mode'.
|
||||||
|
|
||||||
|
If you only have `ctags' installed, you can also set this to
|
||||||
|
\"ctags -e\". Some features might not be supported this way."
|
||||||
|
;; Always having our 'etags' here would be easier, but we can't
|
||||||
|
;; always rely on it being installed. So it might be ctags's etags.
|
||||||
|
:type 'file
|
||||||
|
:version "30.1")
|
||||||
|
|
||||||
|
(defcustom etags-regen-tags-file "TAGS"
|
||||||
|
"Name of the tags file to create inside the project by `etags-regen-mode'.
|
||||||
|
|
||||||
|
The value should either be a simple file name (no directory
|
||||||
|
specified), or a function that accepts the project root directory
|
||||||
|
and returns a distinct absolute file name for its tags file. The
|
||||||
|
latter possibility is useful when you prefer to store the tag
|
||||||
|
files somewhere else, for example in `temporary-file-directory'."
|
||||||
|
:type '(choice (string :tag "File name")
|
||||||
|
(function :tag "Function that returns file name"))
|
||||||
|
:version "30.1")
|
||||||
|
|
||||||
|
(defcustom etags-regen-program-options nil
|
||||||
|
"List of additional options for etags program invoked by `etags-regen-mode'."
|
||||||
|
:type '(repeat string)
|
||||||
|
:version "30.1")
|
||||||
|
|
||||||
|
(defcustom etags-regen-regexp-alist nil
|
||||||
|
"Mapping of languages to etags regexps for `etags-regen-mode'.
|
||||||
|
|
||||||
|
These regexps are used in addition to the tags made with the
|
||||||
|
standard parsing based on the language.
|
||||||
|
|
||||||
|
The value must be a list where each element has the
|
||||||
|
form (LANGUAGES . TAG-REGEXPS) where both LANGUAGES and
|
||||||
|
TAG-REGEXPS are lists of strings.
|
||||||
|
|
||||||
|
Each language should be one of the recognized by etags, see
|
||||||
|
`etags --help'. Each tag regexp should be a string in the format
|
||||||
|
documented for the `--regex' arguments (without `{language}').
|
||||||
|
|
||||||
|
We currently support only Emacs's etags program with this option."
|
||||||
|
:type '(repeat
|
||||||
|
(cons
|
||||||
|
:tag "Languages group"
|
||||||
|
(repeat (string :tag "Language name"))
|
||||||
|
(repeat (string :tag "Tag Regexp"))))
|
||||||
|
:version "30.1")
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(put 'etags-regen-regexp-alist 'safe-local-variable
|
||||||
|
(lambda (value)
|
||||||
|
(and (listp value)
|
||||||
|
(seq-every-p
|
||||||
|
(lambda (group)
|
||||||
|
(and (consp group)
|
||||||
|
(listp (car group))
|
||||||
|
(listp (cdr group))
|
||||||
|
(seq-every-p #'stringp (car group))
|
||||||
|
(seq-every-p #'stringp (cdr group))))
|
||||||
|
value))))
|
||||||
|
|
||||||
|
;; We have to list all extensions: etags falls back to Fortran
|
||||||
|
;; when it cannot determine the type of the file.
|
||||||
|
;; http://lists.gnu.org/archive/html/emacs-devel/2018-01/msg00323.html
|
||||||
|
(defcustom etags-regen-file-extensions
|
||||||
|
'("rb" "js" "py" "pl" "el" "c" "cpp" "cc" "h" "hh" "hpp"
|
||||||
|
"java" "go" "cl" "lisp" "prolog" "php" "erl" "hrl"
|
||||||
|
"F" "f" "f90" "for" "cs" "a" "asm" "ads" "adb" "ada")
|
||||||
|
"Code file extensions for `etags-regen-mode'.
|
||||||
|
|
||||||
|
File extensions to generate the tags for."
|
||||||
|
:type '(repeat (string :tag "File extension"))
|
||||||
|
:version "30.1")
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(put 'etags-regen-file-extensions 'safe-local-variable
|
||||||
|
(lambda (value) (and (listp value) (seq-every-p #'stringp value))))
|
||||||
|
|
||||||
|
;; FIXME: We don't support root anchoring yet.
|
||||||
|
(defcustom etags-regen-ignores nil
|
||||||
|
"Additional ignore rules, in the format of `project-ignores'."
|
||||||
|
:type '(repeat
|
||||||
|
(string :tag "Glob to ignore"))
|
||||||
|
:version "30.1")
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(put 'etags-regen-ignores 'safe-local-variable
|
||||||
|
(lambda (value) (and (listp value) (seq-every-p #'stringp value))))
|
||||||
|
|
||||||
|
(defvar etags-regen--errors-buffer-name "*etags-regen-tags-errors*")
|
||||||
|
|
||||||
|
(defvar etags-regen--rescan-files-limit 100)
|
||||||
|
|
||||||
|
(defun etags-regen--all-mtimes (proj)
|
||||||
|
(let ((files (etags-regen--all-files proj))
|
||||||
|
(mtimes (make-hash-table :test 'equal))
|
||||||
|
file-name-handler-alist)
|
||||||
|
(dolist (f files)
|
||||||
|
(condition-case nil
|
||||||
|
(puthash f
|
||||||
|
(file-attribute-modification-time
|
||||||
|
(file-attributes f))
|
||||||
|
mtimes)
|
||||||
|
(file-missing nil)))
|
||||||
|
mtimes))
|
||||||
|
|
||||||
|
(defun etags-regen--choose-tags-file (proj)
|
||||||
|
(if (functionp etags-regen-tags-file)
|
||||||
|
(funcall etags-regen-tags-file (project-root proj))
|
||||||
|
(expand-file-name etags-regen-tags-file (project-root proj))))
|
||||||
|
|
||||||
|
(defun etags-regen--refresh (proj)
|
||||||
|
(save-excursion
|
||||||
|
(let* ((tags-file (etags-regen--choose-tags-file proj))
|
||||||
|
(tags-mtime (file-attribute-modification-time
|
||||||
|
(file-attributes tags-file)))
|
||||||
|
(all-mtimes (etags-regen--all-mtimes proj))
|
||||||
|
added-files
|
||||||
|
changed-files
|
||||||
|
removed-files)
|
||||||
|
(etags-regen--visit-table tags-file (project-root proj))
|
||||||
|
(set-buffer (get-file-buffer tags-file))
|
||||||
|
(dolist (file (tags-table-files))
|
||||||
|
(let ((mtime (gethash file all-mtimes)))
|
||||||
|
(cond
|
||||||
|
((null mtime)
|
||||||
|
(push file removed-files))
|
||||||
|
((time-less-p tags-mtime mtime)
|
||||||
|
(push file changed-files)
|
||||||
|
(remhash file all-mtimes))
|
||||||
|
(t
|
||||||
|
(remhash file all-mtimes)))))
|
||||||
|
(maphash
|
||||||
|
(lambda (key _value)
|
||||||
|
(push key added-files))
|
||||||
|
all-mtimes)
|
||||||
|
(if (> (+ (length added-files)
|
||||||
|
(length changed-files)
|
||||||
|
(length removed-files))
|
||||||
|
etags-regen--rescan-files-limit)
|
||||||
|
(progn
|
||||||
|
(message "etags-regen: Too many changes, falling back to full rescan")
|
||||||
|
(etags-regen--tags-cleanup))
|
||||||
|
(dolist (file (nconc removed-files changed-files))
|
||||||
|
(etags-regen--remove-tag file))
|
||||||
|
(when (or changed-files added-files)
|
||||||
|
(apply #'etags-regen--append-tags
|
||||||
|
(nconc changed-files added-files)))
|
||||||
|
(when (or changed-files added-files removed-files)
|
||||||
|
(let ((save-silently t)
|
||||||
|
(message-log-max nil))
|
||||||
|
(save-buffer 0)))))))
|
||||||
|
|
||||||
|
(defun etags-regen--maybe-generate ()
|
||||||
|
(let (proj)
|
||||||
|
(when (and etags-regen--tags-root
|
||||||
|
(not (file-in-directory-p default-directory
|
||||||
|
etags-regen--tags-root)))
|
||||||
|
(etags-regen--tags-cleanup))
|
||||||
|
(when (and (not etags-regen--tags-root)
|
||||||
|
;; If existing table is visited that's not generated by
|
||||||
|
;; this mode, skip all functionality.
|
||||||
|
(not (or tags-file-name
|
||||||
|
tags-table-list))
|
||||||
|
(file-exists-p (etags-regen--choose-tags-file
|
||||||
|
(setq proj (project-current)))))
|
||||||
|
(message "Found existing tags table, refreshing...")
|
||||||
|
(etags-regen--refresh proj))
|
||||||
|
(when (and (not (or tags-file-name
|
||||||
|
tags-table-list))
|
||||||
|
(setq proj (or proj (project-current))))
|
||||||
|
(message "Generating new tags table...")
|
||||||
|
(let ((start (time-to-seconds)))
|
||||||
|
(etags-regen--tags-generate proj)
|
||||||
|
(message "...done (%.2f s)" (- (time-to-seconds) start))))))
|
||||||
|
|
||||||
|
(defun etags-regen--all-files (proj)
|
||||||
|
(let* ((root (project-root proj))
|
||||||
|
(default-directory root)
|
||||||
|
;; TODO: Make the scanning more efficient, e.g. move the
|
||||||
|
;; filtering by glob to project (project-files-filtered...).
|
||||||
|
(files (project-files proj))
|
||||||
|
(match-re (concat
|
||||||
|
"\\."
|
||||||
|
(regexp-opt etags-regen-file-extensions)
|
||||||
|
"\\'"))
|
||||||
|
(ir-start (1- (length root)))
|
||||||
|
(ignores-regexps
|
||||||
|
(mapcar #'etags-regen--ignore-regexp
|
||||||
|
etags-regen-ignores)))
|
||||||
|
(cl-delete-if
|
||||||
|
(lambda (f) (or (not (string-match-p match-re f))
|
||||||
|
(string-match-p "/\\.#" f) ;Backup files.
|
||||||
|
(cl-some (lambda (ignore) (string-match ignore f ir-start))
|
||||||
|
ignores-regexps)))
|
||||||
|
files)))
|
||||||
|
|
||||||
|
(defun etags-regen--ignore-regexp (ignore)
|
||||||
|
(require 'dired)
|
||||||
|
;; It's somewhat brittle to rely on Dired.
|
||||||
|
(let ((re (dired-glob-regexp ignore)))
|
||||||
|
;; We could implement root anchoring here, but \\= doesn't work in
|
||||||
|
;; string-match :-(.
|
||||||
|
(concat (unless (eq ?/ (aref re 3)) "/")
|
||||||
|
;; Cutting off the anchors added by `dired-glob-regexp'.
|
||||||
|
(substring re 2 (- (length re) 2))
|
||||||
|
;; This way we allow a glob to match against a directory
|
||||||
|
;; name, or a file name. And when it ends with / already,
|
||||||
|
;; no need to add the anchoring.
|
||||||
|
(unless (eq ?/ (aref re (- (length re) 3)))
|
||||||
|
;; Either match a full name segment, or eos.
|
||||||
|
"\\(?:/\\|\\'\\)"))))
|
||||||
|
|
||||||
|
(defun etags-regen--tags-generate (proj)
|
||||||
|
(let* ((root (project-root proj))
|
||||||
|
(default-directory root)
|
||||||
|
(files (etags-regen--all-files proj))
|
||||||
|
(tags-file (etags-regen--choose-tags-file proj))
|
||||||
|
(ctags-p (etags-regen--ctags-p))
|
||||||
|
(command (format "%s %s %s - -o %s"
|
||||||
|
etags-regen-program
|
||||||
|
(mapconcat #'identity
|
||||||
|
(etags-regen--build-program-options ctags-p)
|
||||||
|
" ")
|
||||||
|
;; ctags's etags requires '-L' for stdin input.
|
||||||
|
(if ctags-p "-L" "")
|
||||||
|
tags-file)))
|
||||||
|
(with-temp-buffer
|
||||||
|
(mapc (lambda (f)
|
||||||
|
(insert f "\n"))
|
||||||
|
files)
|
||||||
|
(shell-command-on-region (point-min) (point-max) command
|
||||||
|
nil nil etags-regen--errors-buffer-name t))
|
||||||
|
(etags-regen--visit-table tags-file root)))
|
||||||
|
|
||||||
|
(defun etags-regen--visit-table (tags-file root)
|
||||||
|
;; Invalidate the scanned tags after any change is written to disk.
|
||||||
|
(add-hook 'after-save-hook #'etags-regen--update-file)
|
||||||
|
(add-hook 'before-save-hook #'etags-regen--mark-as-new)
|
||||||
|
(setq etags-regen--tags-file tags-file
|
||||||
|
etags-regen--tags-root root)
|
||||||
|
(visit-tags-table etags-regen--tags-file))
|
||||||
|
|
||||||
|
(defun etags-regen--ctags-p ()
|
||||||
|
(string-search "Ctags"
|
||||||
|
(shell-command-to-string
|
||||||
|
(format "%s --version" etags-regen-program))))
|
||||||
|
|
||||||
|
(defun etags-regen--build-program-options (ctags-p)
|
||||||
|
(when (and etags-regen-regexp-alist ctags-p)
|
||||||
|
(user-error "etags-regen-regexp-alist is not supported with Ctags"))
|
||||||
|
(nconc
|
||||||
|
(mapcan
|
||||||
|
(lambda (group)
|
||||||
|
(mapcan
|
||||||
|
(lambda (lang)
|
||||||
|
(mapcar (lambda (regexp)
|
||||||
|
(concat "--regex="
|
||||||
|
(shell-quote-argument
|
||||||
|
(format "{%s}%s" lang regexp))))
|
||||||
|
(cdr group)))
|
||||||
|
(car group)))
|
||||||
|
etags-regen-regexp-alist)
|
||||||
|
(mapcar #'shell-quote-argument
|
||||||
|
etags-regen-program-options)))
|
||||||
|
|
||||||
|
(defun etags-regen--update-file ()
|
||||||
|
;; TODO: Maybe only do this when Emacs is idle for a bit. Or defer
|
||||||
|
;; the updates and do them later in bursts when the table is used.
|
||||||
|
(let* ((file-name buffer-file-name)
|
||||||
|
(tags-file-buf (and etags-regen--tags-root
|
||||||
|
(get-file-buffer etags-regen--tags-file)))
|
||||||
|
(relname (concat "/" (file-relative-name file-name
|
||||||
|
etags-regen--tags-root)))
|
||||||
|
(ignores etags-regen-ignores)
|
||||||
|
pr should-scan)
|
||||||
|
(save-excursion
|
||||||
|
(when tags-file-buf
|
||||||
|
(cond
|
||||||
|
((and etags-regen--new-file
|
||||||
|
(kill-local-variable 'etags-regen--new-file)
|
||||||
|
(setq pr (project-current))
|
||||||
|
(equal (project-root pr) etags-regen--tags-root)
|
||||||
|
(member file-name (project-files pr)))
|
||||||
|
(set-buffer tags-file-buf)
|
||||||
|
(setq should-scan t))
|
||||||
|
((progn (set-buffer tags-file-buf)
|
||||||
|
(etags-regen--remove-tag file-name))
|
||||||
|
(setq should-scan t))))
|
||||||
|
(when (and should-scan
|
||||||
|
(not (cl-some
|
||||||
|
(lambda (ignore)
|
||||||
|
(string-match-p
|
||||||
|
(etags-regen--ignore-regexp ignore)
|
||||||
|
relname))
|
||||||
|
ignores)))
|
||||||
|
(etags-regen--append-tags file-name)
|
||||||
|
(let ((save-silently t)
|
||||||
|
(message-log-max nil))
|
||||||
|
(save-buffer 0))))))
|
||||||
|
|
||||||
|
(defun etags-regen--remove-tag (file-name)
|
||||||
|
(goto-char (point-min))
|
||||||
|
(when (search-forward (format "\f\n%s," file-name) nil t)
|
||||||
|
(let ((start (match-beginning 0)))
|
||||||
|
(search-forward "\f\n" nil 'move)
|
||||||
|
(let ((inhibit-read-only t))
|
||||||
|
(delete-region start
|
||||||
|
(if (eobp)
|
||||||
|
(point)
|
||||||
|
(- (point) 2)))))
|
||||||
|
t))
|
||||||
|
|
||||||
|
(defun etags-regen--append-tags (&rest file-names)
|
||||||
|
(goto-char (point-max))
|
||||||
|
(let ((options (etags-regen--build-program-options (etags-regen--ctags-p)))
|
||||||
|
(inhibit-read-only t))
|
||||||
|
;; XXX: call-process is significantly faster, though.
|
||||||
|
;; Like 10ms vs 20ms here. But `shell-command' makes it easy to
|
||||||
|
;; direct stderr to a separate buffer.
|
||||||
|
(shell-command
|
||||||
|
(format "%s %s %s -o -"
|
||||||
|
etags-regen-program (mapconcat #'identity options " ")
|
||||||
|
(mapconcat #'identity file-names " "))
|
||||||
|
t etags-regen--errors-buffer-name))
|
||||||
|
;; FIXME: Is there a better way to do this?
|
||||||
|
;; Completion table is the only remaining place where the
|
||||||
|
;; update is not incremental.
|
||||||
|
(setq-default tags-completion-table nil))
|
||||||
|
|
||||||
|
(defun etags-regen--mark-as-new ()
|
||||||
|
(when (and etags-regen--tags-root
|
||||||
|
(not buffer-file-number))
|
||||||
|
(setq-local etags-regen--new-file t)))
|
||||||
|
|
||||||
|
(defun etags-regen--tags-cleanup ()
|
||||||
|
(when etags-regen--tags-file
|
||||||
|
(let ((buffer (get-file-buffer etags-regen--tags-file)))
|
||||||
|
(and buffer
|
||||||
|
(kill-buffer buffer)))
|
||||||
|
(tags-reset-tags-tables)
|
||||||
|
(setq tags-file-name nil
|
||||||
|
tags-table-list nil
|
||||||
|
etags-regen--tags-file nil
|
||||||
|
etags-regen--tags-root nil))
|
||||||
|
(remove-hook 'after-save-hook #'etags-regen--update-file)
|
||||||
|
(remove-hook 'before-save-hook #'etags-regen--mark-as-new))
|
||||||
|
|
||||||
|
(defvar etags-regen-mode-map (make-sparse-keymap))
|
||||||
|
|
||||||
|
;;;###autoload
|
||||||
|
(define-minor-mode etags-regen-mode
|
||||||
|
"Minor mode to automatically generate and update tags tables.
|
||||||
|
|
||||||
|
This minor mode generates the tags table automatically based on
|
||||||
|
the current project configuration, and later updates it as you
|
||||||
|
edit the files and save the changes.
|
||||||
|
|
||||||
|
If you select a tags table manually (for example, using
|
||||||
|
\\[visit-tags-table]), then this mode will be effectively
|
||||||
|
disabled for the entire session. Use \\[tags-reset-tags-tables]
|
||||||
|
to countermand the effect of a previous \\[visit-tags-table]."
|
||||||
|
:global t
|
||||||
|
(if etags-regen-mode
|
||||||
|
(progn
|
||||||
|
(advice-add 'etags--xref-backend :before
|
||||||
|
#'etags-regen--maybe-generate)
|
||||||
|
(advice-add 'tags-completion-at-point-function :before
|
||||||
|
#'etags-regen--maybe-generate))
|
||||||
|
(advice-remove 'etags--xref-backend #'etags-regen--maybe-generate)
|
||||||
|
(advice-remove 'tags-completion-at-point-function #'etags-regen--maybe-generate)
|
||||||
|
(etags-regen--tags-cleanup)))
|
||||||
|
|
||||||
|
(provide 'etags-regen)
|
||||||
|
|
||||||
|
;;; etags-regen.el ends here
|
||||||
Loading…
Reference in a new issue