aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEli Zaretskii2016-12-01 18:49:51 +0200
committerEli Zaretskii2016-12-01 18:49:51 +0200
commit2f68cb3e0502a9dc69613e97a5a5079ebf9249fb (patch)
tree2ac92f13aa0a418c91a95e2a52f071941cc76e80
parent7d35b3d33da641b462d22df005266225e799d27f (diff)
downloademacs-2f68cb3e0502a9dc69613e97a5a5079ebf9249fb.tar.gz
emacs-2f68cb3e0502a9dc69613e97a5a5079ebf9249fb.zip
Fix bugs with buffer-local tags tables
* lisp/progmodes/etags.el (visit-tags-table): After 'visit-tags-table-buffer' returns, retrieve the value of 'tags-file-name' from the buffer we started in. Force recomputation of 'tags-completion-table' next time it is used, since the list of tags table has changed. (visit-tags-table-buffer): Accept an additional optional argument CBUF, the buffer in which to start processing, and switch to that buffer if CBUF is non-nil. All callers changed to supply a non-nil CBUF when they call 'visit-tags-table-buffer' in a loop. Doc fix. (tags-completion-table): Accept an optional argument, the buffer for which to build 'tags-completion-table', and build that buffer's completion table. (tags-lazy-completion-table): Pass the current buffer to 'tags-completion-table'. (tags-file-name): Don't say in the doc string that setting this variable directly is enough; say that 'visit-tags-table' should be used for that. (Bug#158) (Bug#17326) (Bug#23164) * doc/emacs/maintaining.texi (Select Tags Table): Delete the advice to set 'tags-file-name' directly. * test/lisp/progmodes/etags-tests.el: New tests.
-rw-r--r--doc/emacs/maintaining.texi8
-rw-r--r--lisp/progmodes/etags.el152
-rw-r--r--test/lisp/progmodes/etags-tests.el83
3 files changed, 176 insertions, 67 deletions
diff --git a/doc/emacs/maintaining.texi b/doc/emacs/maintaining.texi
index 13668cc9269..de4fb43ec1d 100644
--- a/doc/emacs/maintaining.texi
+++ b/doc/emacs/maintaining.texi
@@ -2552,10 +2552,10 @@ directory as the default.
2552@vindex tags-file-name 2552@vindex tags-file-name
2553 Emacs does not actually read in the tags table contents until you 2553 Emacs does not actually read in the tags table contents until you
2554try to use them; all @code{visit-tags-table} does is store the file 2554try to use them; all @code{visit-tags-table} does is store the file
2555name in the variable @code{tags-file-name}, and setting the variable 2555name in the variable @code{tags-file-name}, and not much more. The
2556yourself is just as good. The variable's initial value is @code{nil}; 2556variable's initial value is @code{nil}; that value tells all the
2557that value tells all the commands for working with tags tables that 2557commands for working with tags tables that they must ask for a tags
2558they must ask for a tags table file name to use. 2558table file name to use.
2559 2559
2560 Using @code{visit-tags-table} when a tags table is already loaded 2560 Using @code{visit-tags-table} when a tags table is already loaded
2561gives you a choice: you can add the new tags table to the current list 2561gives you a choice: you can add the new tags table to the current list
diff --git a/lisp/progmodes/etags.el b/lisp/progmodes/etags.el
index 7d4521c148d..c72f0616b10 100644
--- a/lisp/progmodes/etags.el
+++ b/lisp/progmodes/etags.el
@@ -33,8 +33,9 @@
33;;;###autoload 33;;;###autoload
34(defvar tags-file-name nil 34(defvar tags-file-name nil
35 "File name of tags table. 35 "File name of tags table.
36To switch to a new tags table, setting this variable is sufficient. 36To switch to a new tags table, do not set this variable; instead,
37If you set this variable, do not also set `tags-table-list'. 37invoke `visit-tags-table', which is the only reliable way of
38setting the value of this variable, whether buffer-local or global.
38Use the `etags' program to make a tags table file.") 39Use the `etags' program to make a tags table file.")
39;; Make M-x set-variable tags-file-name like M-x visit-tags-table. 40;; Make M-x set-variable tags-file-name like M-x visit-tags-table.
40;;;###autoload (put 'tags-file-name 'variable-interactive (purecopy "fVisit tags table: ")) 41;;;###autoload (put 'tags-file-name 'variable-interactive (purecopy "fVisit tags table: "))
@@ -288,7 +289,8 @@ FILE should be the name of a file created with the `etags' program.
288A directory name is ok too; it means file TAGS in that directory. 289A directory name is ok too; it means file TAGS in that directory.
289 290
290Normally \\[visit-tags-table] sets the global value of `tags-file-name'. 291Normally \\[visit-tags-table] sets the global value of `tags-file-name'.
291With a prefix arg, set the buffer-local value instead. 292With a prefix arg, set the buffer-local value instead. When called
293from Lisp, if the optional arg LOCAL is non-nil, set the local value.
292When you find a tag with \\[find-tag], the buffer it finds the tag 294When you find a tag with \\[find-tag], the buffer it finds the tag
293in is given a local value of this variable which is the name of the tags 295in is given a local value of this variable which is the name of the tags
294file the tag was in." 296file the tag was in."
@@ -304,19 +306,28 @@ file the tag was in."
304 ;; Calling visit-tags-table-buffer with tags-file-name set to FILE will 306 ;; Calling visit-tags-table-buffer with tags-file-name set to FILE will
305 ;; initialize a buffer for FILE and set tags-file-name to the 307 ;; initialize a buffer for FILE and set tags-file-name to the
306 ;; fully-expanded name. 308 ;; fully-expanded name.
307 (let ((tags-file-name file)) 309 (let ((tags-file-name file)
310 (cbuf (current-buffer)))
308 (save-excursion 311 (save-excursion
309 (or (visit-tags-table-buffer file) 312 (or (visit-tags-table-buffer file)
310 (signal 'file-missing (list "Visiting tags table" 313 (signal 'file-missing (list "Visiting tags table"
311 "No such file or directory" 314 "No such file or directory"
312 file))) 315 file)))
313 ;; Set FILE to the expanded name. 316 ;; Set FILE to the expanded name. Do that in the buffer we
314 (setq file tags-file-name))) 317 ;; started from, because visit-tags-table-buffer switches
318 ;; buffers after updating tags-file-name, so if tags-file-name
319 ;; is local in the buffer we started, that value is only visible
320 ;; in that buffer.
321 (setq file (with-current-buffer cbuf tags-file-name))))
315 (if local 322 (if local
316 ;; Set the local value of tags-file-name. 323 (progn
317 (set (make-local-variable 'tags-file-name) file) 324 ;; Force recomputation of tags-completion-table.
325 (setq-local tags-completion-table nil)
326 ;; Set the local value of tags-file-name.
327 (setq-local tags-file-name file))
318 ;; Set the global value of tags-file-name. 328 ;; Set the global value of tags-file-name.
319 (setq-default tags-file-name file))) 329 (setq-default tags-file-name file)
330 (setq tags-completion-table nil)))
320 331
321(defun tags-table-check-computed-list () 332(defun tags-table-check-computed-list ()
322 "Compute `tags-table-computed-list' from `tags-table-list' if necessary." 333 "Compute `tags-table-computed-list' from `tags-table-list' if necessary."
@@ -540,17 +551,21 @@ Returns nil when out of tables."
540 (setq tags-file-name (car tags-table-list-pointer)))) 551 (setq tags-file-name (car tags-table-list-pointer))))
541 552
542;;;###autoload 553;;;###autoload
543(defun visit-tags-table-buffer (&optional cont) 554(defun visit-tags-table-buffer (&optional cont cbuf)
544 "Select the buffer containing the current tags table. 555 "Select the buffer containing the current tags table.
545If optional arg is a string, visit that file as a tags table. 556Optional arg CONT specifies which tags table to visit.
546If optional arg is t, visit the next table in `tags-table-list'. 557If CONT is a string, visit that file as a tags table.
547If optional arg is the atom `same', don't look for a new table; 558If CONT is t, visit the next table in `tags-table-list'.
559If CONT is the atom `same', don't look for a new table;
548 just select the buffer visiting `tags-file-name'. 560 just select the buffer visiting `tags-file-name'.
549If arg is nil or absent, choose a first buffer from information in 561If CONT is nil or absent, choose a first buffer from information in
550 `tags-file-name', `tags-table-list', `tags-table-list-pointer'. 562 `tags-file-name', `tags-table-list', `tags-table-list-pointer'.
563Optional second arg CBUF, if non-nil, specifies the initial buffer,
564which is important if that buffer has a local value of `tags-file-name'.
551Returns t if it visits a tags table, or nil if there are no more in the list." 565Returns t if it visits a tags table, or nil if there are no more in the list."
552 566
553 ;; Set tags-file-name to the tags table file we want to visit. 567 ;; Set tags-file-name to the tags table file we want to visit.
568 (if cbuf (set-buffer cbuf))
554 (cond ((eq cont 'same) 569 (cond ((eq cont 'same)
555 ;; Use the ambient value of tags-file-name. 570 ;; Use the ambient value of tags-file-name.
556 (or tags-file-name 571 (or tags-file-name
@@ -752,28 +767,33 @@ Assumes the tags table is the current buffer."
752 (or tags-included-tables 767 (or tags-included-tables
753 (setq tags-included-tables (funcall tags-included-tables-function)))) 768 (setq tags-included-tables (funcall tags-included-tables-function))))
754 769
755(defun tags-completion-table () 770(defun tags-completion-table (&optional buf)
756 "Build `tags-completion-table' on demand. 771 "Build `tags-completion-table' on demand for a buffer's tags tables.
772Optional argument BUF specifies the buffer for which to build
773\`tags-completion-table', and defaults to the current buffer.
757The tags included in the completion table are those in the current 774The tags included in the completion table are those in the current
758tags table and its (recursively) included tags tables." 775tags table for BUF and its (recursively) included tags tables."
759 (or tags-completion-table 776 (if (not buf) (setq buf (current-buffer)))
760 ;; No cached value for this buffer. 777 (with-current-buffer buf
761 (condition-case () 778 (or tags-completion-table
762 (let (tables cont) 779 ;; No cached value for this buffer.
763 (message "Making tags completion table for %s..." buffer-file-name) 780 (condition-case ()
764 (save-excursion 781 (let (tables cont)
765 ;; Iterate over the current list of tags tables. 782 (message "Making tags completion table for %s..."
766 (while (visit-tags-table-buffer cont) 783 buffer-file-name)
767 ;; Find possible completions in this table. 784 (save-excursion
768 (push (funcall tags-completion-table-function) tables) 785 ;; Iterate over the current list of tags tables.
769 (setq cont t))) 786 (while (visit-tags-table-buffer cont buf)
770 (message "Making tags completion table for %s...done" 787 ;; Find possible completions in this table.
771 buffer-file-name) 788 (push (funcall tags-completion-table-function) tables)
772 ;; Cache the result in a buffer-local variable. 789 (setq cont t)))
773 (setq tags-completion-table 790 (message "Making tags completion table for %s...done"
774 (nreverse (delete-dups (apply #'nconc tables))))) 791 buffer-file-name)
775 (quit (message "Tags completion table construction aborted.") 792 ;; Cache the result in a variable.
776 (setq tags-completion-table nil))))) 793 (setq tags-completion-table
794 (nreverse (delete-dups (apply #'nconc tables)))))
795 (quit (message "Tags completion table construction aborted.")
796 (setq tags-completion-table nil))))))
777 797
778;;;###autoload 798;;;###autoload
779(defun tags-lazy-completion-table () 799(defun tags-lazy-completion-table ()
@@ -784,7 +804,9 @@ tags table and its (recursively) included tags tables."
784 ;; If we need to ask for the tag table, allow that. 804 ;; If we need to ask for the tag table, allow that.
785 (let ((enable-recursive-minibuffers t)) 805 (let ((enable-recursive-minibuffers t))
786 (visit-tags-table-buffer)) 806 (visit-tags-table-buffer))
787 (complete-with-action action (tags-completion-table) string pred)))))) 807 (complete-with-action action
808 (tags-completion-table buf)
809 string pred))))))
788 810
789;;;###autoload (defun tags-completion-at-point-function () 811;;;###autoload (defun tags-completion-at-point-function ()
790;;;###autoload (if (or tags-table-list tags-file-name) 812;;;###autoload (if (or tags-table-list tags-file-name)
@@ -1084,6 +1106,7 @@ error message."
1084 (case-fold-search (if (memq tags-case-fold-search '(nil t)) 1106 (case-fold-search (if (memq tags-case-fold-search '(nil t))
1085 tags-case-fold-search 1107 tags-case-fold-search
1086 case-fold-search)) 1108 case-fold-search))
1109 (cbuf (current-buffer))
1087 ) 1110 )
1088 (save-excursion 1111 (save-excursion
1089 1112
@@ -1104,8 +1127,7 @@ error message."
1104 (catch 'qualified-match-found 1127 (catch 'qualified-match-found
1105 1128
1106 ;; Iterate over the list of tags tables. 1129 ;; Iterate over the list of tags tables.
1107 (while (or first-table 1130 (while (or first-table (visit-tags-table-buffer t cbuf))
1108 (visit-tags-table-buffer t))
1109 1131
1110 (and first-search first-table 1132 (and first-search first-table
1111 ;; Start at beginning of tags file. 1133 ;; Start at beginning of tags file.
@@ -1707,25 +1729,26 @@ if the file was newly read in, the value is the filename."
1707 ((eq initialize t) 1729 ((eq initialize t)
1708 ;; Initialize the list from the tags table. 1730 ;; Initialize the list from the tags table.
1709 (save-excursion 1731 (save-excursion
1710 ;; Visit the tags table buffer to get its list of files. 1732 (let ((cbuf (current-buffer)))
1711 (visit-tags-table-buffer) 1733 ;; Visit the tags table buffer to get its list of files.
1712 ;; Copy the list so we can setcdr below, and expand the file 1734 (visit-tags-table-buffer)
1713 ;; names while we are at it, in this buffer's default directory. 1735 ;; Copy the list so we can setcdr below, and expand the file
1714 (setq next-file-list (mapcar 'expand-file-name (tags-table-files))) 1736 ;; names while we are at it, in this buffer's default directory.
1715 ;; Iterate over all the tags table files, collecting 1737 (setq next-file-list (mapcar 'expand-file-name (tags-table-files)))
1716 ;; a complete list of referenced file names. 1738 ;; Iterate over all the tags table files, collecting
1717 (while (visit-tags-table-buffer t) 1739 ;; a complete list of referenced file names.
1718 ;; Find the tail of the working list and chain on the new 1740 (while (visit-tags-table-buffer t cbuf)
1719 ;; sublist for this tags table. 1741 ;; Find the tail of the working list and chain on the new
1720 (let ((tail next-file-list)) 1742 ;; sublist for this tags table.
1721 (while (cdr tail) 1743 (let ((tail next-file-list))
1722 (setq tail (cdr tail))) 1744 (while (cdr tail)
1723 ;; Use a copy so the next loop iteration will not modify the 1745 (setq tail (cdr tail)))
1724 ;; list later returned by (tags-table-files). 1746 ;; Use a copy so the next loop iteration will not modify the
1725 (if tail 1747 ;; list later returned by (tags-table-files).
1726 (setcdr tail (mapcar 'expand-file-name (tags-table-files))) 1748 (if tail
1727 (setq next-file-list (mapcar 'expand-file-name 1749 (setcdr tail (mapcar 'expand-file-name (tags-table-files)))
1728 (tags-table-files)))))))) 1750 (setq next-file-list (mapcar 'expand-file-name
1751 (tags-table-files)))))))))
1729 (t 1752 (t
1730 ;; Initialize the list by evalling the argument. 1753 ;; Initialize the list by evalling the argument.
1731 (setq next-file-list (eval initialize)))) 1754 (setq next-file-list (eval initialize))))
@@ -1921,8 +1944,9 @@ directory specification."
1921 (princ (substitute-command-keys "':\n\n")) 1944 (princ (substitute-command-keys "':\n\n"))
1922 (save-excursion 1945 (save-excursion
1923 (let ((first-time t) 1946 (let ((first-time t)
1924 (gotany nil)) 1947 (gotany nil)
1925 (while (visit-tags-table-buffer (not first-time)) 1948 (cbuf (current-buffer)))
1949 (while (visit-tags-table-buffer (not first-time) cbuf)
1926 (setq first-time nil) 1950 (setq first-time nil)
1927 (if (funcall list-tags-function file) 1951 (if (funcall list-tags-function file)
1928 (setq gotany t))) 1952 (setq gotany t)))
@@ -1945,8 +1969,9 @@ directory specification."
1945 (tags-with-face 'highlight (princ regexp)) 1969 (tags-with-face 'highlight (princ regexp))
1946 (princ (substitute-command-keys "':\n\n")) 1970 (princ (substitute-command-keys "':\n\n"))
1947 (save-excursion 1971 (save-excursion
1948 (let ((first-time t)) 1972 (let ((first-time t)
1949 (while (visit-tags-table-buffer (not first-time)) 1973 (cbuf (current-buffer)))
1974 (while (visit-tags-table-buffer (not first-time) cbuf)
1950 (setq first-time nil) 1975 (setq first-time nil)
1951 (funcall tags-apropos-function regexp)))) 1976 (funcall tags-apropos-function regexp))))
1952 (etags-tags-apropos-additional regexp)) 1977 (etags-tags-apropos-additional regexp))
@@ -2107,9 +2132,10 @@ for \\[find-tag] (which see)."
2107 (marks (make-hash-table :test 'equal)) 2132 (marks (make-hash-table :test 'equal))
2108 (case-fold-search (if (memq tags-case-fold-search '(nil t)) 2133 (case-fold-search (if (memq tags-case-fold-search '(nil t))
2109 tags-case-fold-search 2134 tags-case-fold-search
2110 case-fold-search))) 2135 case-fold-search))
2136 (cbuf (current-buffer)))
2111 (save-excursion 2137 (save-excursion
2112 (while (visit-tags-table-buffer (not first-time)) 2138 (while (visit-tags-table-buffer (not first-time) cbuf)
2113 (setq first-time nil) 2139 (setq first-time nil)
2114 (dolist (order-fun (cond (regexp? find-tag-regexp-tag-order) 2140 (dolist (order-fun (cond (regexp? find-tag-regexp-tag-order)
2115 (t etags-xref-find-definitions-tag-order))) 2141 (t etags-xref-find-definitions-tag-order)))
diff --git a/test/lisp/progmodes/etags-tests.el b/test/lisp/progmodes/etags-tests.el
new file mode 100644
index 00000000000..a715bba32ab
--- /dev/null
+++ b/test/lisp/progmodes/etags-tests.el
@@ -0,0 +1,83 @@
1;;; etags-tests.el --- Test suite for etags.el.
2
3;; Copyright (C) 2016 Free Software Foundation, Inc.
4
5;; Author: Eli Zaretskii <eliz@gnu.org>
6
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software: you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
21
22;;; Code:
23
24(require 'ert)
25(require 'etags)
26
27(defvar his-masters-voice t)
28
29(defun y-or-n-p (_prompt)
30 "Replacement for `y-or-n-p' that returns what we tell it to."
31 his-masters-voice)
32
33(ert-deftest etags-bug-158 ()
34 "Test finding tags with local and global tags tables."
35 (let ((buf-with-global-tags (get-buffer-create "*buf-global*"))
36 (buf-with-local-tags (get-buffer-create "*buf-local*"))
37 xref-buf)
38 (set-buffer buf-with-global-tags)
39 (setq default-directory (expand-file-name "."))
40 (visit-tags-table "./manual/etags/ETAGS.good_1")
41 ;; Check that tags in ETAGS.good_1 are recognized.
42 (setq xref-buf (xref-find-definitions "LL_Task_Procedure_Access/t"))
43 (should (bufferp xref-buf))
44 (kill-buffer xref-buf)
45 (setq xref-buf (xref-find-definitions "PrintAdd"))
46 (should (bufferp xref-buf))
47 (kill-buffer xref-buf)
48 ;; Check that tags not in ETAGS.good_1, but in ETAGS.good_3, are
49 ;; NOT recognized.
50 (should-error (xref-find-definitions "intNumber") :type 'user-error)
51 (kill-buffer xref-buf)
52 (set-buffer buf-with-local-tags)
53 (setq default-directory (expand-file-name "."))
54 (let (his-masters-voice)
55 (visit-tags-table "./manual/etags/ETAGS.good_3" t))
56 ;; Check that tags in ETAGS.good_1 are recognized.
57 (setq xref-buf (xref-find-definitions "LL_Task_Procedure_Access/t"))
58 (should (bufferp xref-buf))
59 (kill-buffer xref-buf)
60 (setq xref-buf (xref-find-definitions "PrintAdd"))
61 (should (bufferp xref-buf))
62 (kill-buffer xref-buf)
63 ;; Check that tags in ETAGS.good_3 are recognized. This is a test
64 ;; for bug#158.
65 (setq xref-buf (xref-find-definitions "intNumber"))
66 (should (or (null xref-buf)
67 (bufferp xref-buf)))
68 ;; Bug #17326
69 (should (string= (file-name-nondirectory
70 (buffer-local-value 'tags-file-name buf-with-local-tags))
71 "ETAGS.good_3"))
72 (should (string= (file-name-nondirectory
73 (default-value 'tags-file-name))
74 "ETAGS.good_1"))
75 (if (bufferp xref-buf) (kill-buffer xref-buf))))
76
77(ert-deftest etags-bug-23164 ()
78 "Test that setting a local value of tags table doesn't signal errors."
79 (set-buffer (get-buffer-create "*foobar*"))
80 (fundamental-mode)
81 (visit-tags-table "./manual/etags/ETAGS.good_3" t)
82 (should (equal (should-error (xref-find-definitions "foobar123"))
83 '(user-error "No definitions found for: foobar123"))))