]> code.delx.au - gnu-emacs/blobdiff - lisp/progmodes/etags.el
Update copyright year to 2015
[gnu-emacs] / lisp / progmodes / etags.el
index 071a0fb6037698a6181087548b6ae7a168139ecf..47b305fb08132b50f503726d470f67bc5771592b 100644 (file)
@@ -1,10 +1,10 @@
 ;;; etags.el --- etags facility for Emacs  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1985-1986, 1988-1989, 1992-1996, 1998, 2000-2012
-;;   Free Software Foundation, Inc.
+;; Copyright (C) 1985-1986, 1988-1989, 1992-1996, 1998, 2000-2015 Free
+;; Software Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.org>
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: tools
 
 ;; This file is part of GNU Emacs.
@@ -28,6 +28,7 @@
 
 (require 'ring)
 (require 'button)
+(require 'xref)
 
 ;;;###autoload
 (defvar tags-file-name nil
@@ -67,11 +68,8 @@ Use the `etags' program to make a tags table file."
 ;;;###autoload
 (defcustom tags-compression-info-list
   (purecopy '("" ".Z" ".bz2" ".gz" ".xz" ".tgz"))
-  "List of extensions tried by etags when jka-compr is used.
-An empty string means search the non-compressed file.
-These extensions will be tried only if jka-compr was activated
-\(i.e. via customize of `auto-compression-mode' or by calling the function
-`auto-compression-mode')."
+  "List of extensions tried by etags when `auto-compression-mode' is on.
+An empty string means search the non-compressed file."
   :version "24.1"                      ; added xz
   :type  '(repeat string)
   :group 'etags)
@@ -144,11 +142,8 @@ Otherwise, `find-tag-default' is used."
   :group 'etags
   :type '(choice (const nil) function))
 
-(defcustom find-tag-marker-ring-length 16
-  "Length of marker rings `find-tag-marker-ring' and `tags-location-ring'."
-  :group 'etags
-  :type 'integer
-  :version "20.3")
+(define-obsolete-variable-alias 'find-tag-marker-ring-length
+  'xref-marker-ring-length "25.1")
 
 (defcustom tags-tag-face 'default
   "Face for tags in the output of `tags-apropos'."
@@ -185,15 +180,18 @@ Example value:
                       (sexp :tag "Tags to search")))
   :version "21.1")
 
-(defvar find-tag-marker-ring (make-ring find-tag-marker-ring-length)
-  "Ring of markers which are locations from which \\[find-tag] was invoked.")
+(defvaralias 'find-tag-marker-ring 'xref--marker-ring)
+(make-obsolete-variable
+ 'find-tag-marker-ring
+ "use `xref-push-marker-stack' or `xref-pop-marker-stack' instead."
+ "25.1")
 
 (defvar default-tags-table-function nil
   "If non-nil, a function to choose a default tags file for a buffer.
 This function receives no arguments and should return the default
 tags table file to use for the current buffer.")
 
-(defvar tags-location-ring (make-ring find-tag-marker-ring-length)
+(defvar tags-location-ring (make-ring xref-marker-ring-length)
   "Ring of markers which are locations visited by \\[find-tag].
 Pop back to the last location with \\[negative-argument] \\[find-tag].")
 \f
@@ -202,7 +200,8 @@ Pop back to the last location with \\[negative-argument] \\[find-tag].")
 
 (defvar tags-table-files nil
   "List of file names covered by current tags table.
-nil means it has not yet been computed; use `tags-table-files' to do so.")
+nil means it has not yet been computed;
+use function `tags-table-files' to do so.")
 
 (defvar tags-completion-table nil
   "Obarray of tag names defined in current tags table.")
@@ -227,7 +226,7 @@ of the format-parsing tags function variables if successful.")
 One optional argument, a boolean specifying to return complete path (nil) or
 relative path (non-nil).")
 (defvar tags-table-files-function nil
-  "Function to do the work of `tags-table-files' (which see).")
+  "Function to do the work of function `tags-table-files' (which see).")
 (defvar tags-completion-table-function nil
   "Function to build the `tags-completion-table'.")
 (defvar snarf-tag-function nil
@@ -254,7 +253,7 @@ One argument, the tag info returned by `snarf-tag-function'.")
 (defvar tags-apropos-function nil
   "Function to do the work of `tags-apropos' (which see).")
 (defvar tags-included-tables-function nil
-  "Function to do the work of `tags-included-tables' (which see).")
+  "Function to do the work of function `tags-included-tables' (which see).")
 (defvar verify-tags-table-function nil
   "Function to return t if current buffer contains valid tags file.")
 \f
@@ -338,12 +337,15 @@ file the tag was in."
                     (save-excursion
                       (tags-verify-table (buffer-file-name table-buffer))))
                (with-current-buffer table-buffer
-                 (if (tags-included-tables)
-                     ;; Insert the included tables into the list we
-                     ;; are processing.
-                     (setcdr tables (nconc (mapcar 'tags-expand-table-name
-                                                   (tags-included-tables))
-                                           (cdr tables)))))
+                  ;; Needed so long as etags-tags-included-tables
+                  ;; does not save-excursion.
+                  (save-excursion
+                    (if (tags-included-tables)
+                        ;; Insert the included tables into the list we
+                        ;; are processing.
+                        (setcdr tables (nconc (mapcar 'tags-expand-table-name
+                                                      (tags-included-tables))
+                                              (cdr tables))))))
              ;; This table is not in core yet.  Insert a placeholder
              ;; saying we must read it into core to check for included
              ;; tables before searching the next table in the list.
@@ -702,7 +704,9 @@ Returns t if it visits a tags table, or nil if there are no more in the list."
        (kill-local-variable 'tags-file-name)
        (if (eq local-tags-file-name tags-file-name)
            (setq tags-file-name nil))
-       (user-error "File %s is not a valid tags table"
+       (user-error (if (file-exists-p local-tags-file-name)
+                        "File %s is not a valid tags table"
+                      "File %s does not exist")
                     local-tags-file-name)))))
 
 (defun tags-reset-tags-tables ()
@@ -710,15 +714,13 @@ Returns t if it visits a tags table, or nil if there are no more in the list."
   (interactive)
   ;; Clear out the markers we are throwing away.
   (let ((i 0))
-    (while (< i find-tag-marker-ring-length)
+    (while (< i xref-marker-ring-length)
       (if (aref (cddr tags-location-ring) i)
          (set-marker (aref (cddr tags-location-ring) i) nil))
-      (if (aref (cddr find-tag-marker-ring) i)
-         (set-marker (aref (cddr find-tag-marker-ring) i) nil))
       (setq i (1+ i))))
+  (xref-clear-marker-stack)
   (setq tags-file-name nil
-       tags-location-ring (make-ring find-tag-marker-ring-length)
-       find-tag-marker-ring (make-ring find-tag-marker-ring-length)
+       tags-location-ring (make-ring xref-marker-ring-length)
        tags-table-list nil
        tags-table-computed-list nil
        tags-table-computed-list-for nil
@@ -777,6 +779,7 @@ tags table and its (recursively) included tags tables."
        (quit (message "Tags completion table construction aborted.")
              (setq tags-completion-table nil)))))
 
+;;;###autoload
 (defun tags-lazy-completion-table ()
   (let ((buf (current-buffer)))
     (lambda (string pred action)
@@ -895,7 +898,7 @@ See documentation of variable `tags-file-name'."
              ;; Run the user's hook.  Do we really want to do this for pop?
              (run-hooks 'local-find-tag-hook))))
       ;; Record whence we came.
-      (ring-insert find-tag-marker-ring (point-marker))
+      (xref-push-marker-stack)
       (if (and next-p last-tag)
          ;; Find the same table we last used.
          (visit-tags-table-buffer 'same)
@@ -951,7 +954,6 @@ See documentation of variable `tags-file-name'."
        (switch-to-buffer buf)
       (error (pop-to-buffer buf)))
     (goto-char pos)))
-;;;###autoload (define-key esc-map "." 'find-tag)
 
 ;;;###autoload
 (defun find-tag-other-window (tagname &optional next-p regexp-p)
@@ -973,13 +975,14 @@ onto a ring and may be popped back to with \\[pop-tag-mark].
 Contrast this with the ring of marks gone to by the command.
 
 See documentation of variable `tags-file-name'."
+  (declare (obsolete xref-find-definitions-other-window "25.1"))
   (interactive (find-tag-interactive "Find tag other window: "))
 
   ;; This hair is to deal with the case where the tag is found in the
   ;; selected window's buffer; without the hair, point is moved in both
   ;; windows.  To prevent this, we save the selected window's point before
   ;; doing find-tag-noselect, and restore it after.
-  (let* ((window-point (window-point (selected-window)))
+  (let* ((window-point (window-point))
         (tagbuf (find-tag-noselect tagname next-p regexp-p))
         (tagpoint (progn (set-buffer tagbuf) (point))))
     (set-window-point (prog1
@@ -992,7 +995,6 @@ See documentation of variable `tags-file-name'."
                        ;; the window's point from the buffer.
                        (set-window-point (selected-window) tagpoint))
                      window-point)))
-;;;###autoload (define-key ctl-x-4-map "." 'find-tag-other-window)
 
 ;;;###autoload
 (defun find-tag-other-frame (tagname &optional next-p)
@@ -1014,10 +1016,10 @@ onto a ring and may be popped back to with \\[pop-tag-mark].
 Contrast this with the ring of marks gone to by the command.
 
 See documentation of variable `tags-file-name'."
+  (declare (obsolete xref-find-definitions-other-frame "25.1"))
   (interactive (find-tag-interactive "Find tag other frame: "))
   (let ((pop-up-frames t))
     (find-tag-other-window tagname next-p)))
-;;;###autoload (define-key ctl-x-5-map "." 'find-tag-other-frame)
 
 ;;;###autoload
 (defun find-tag-regexp (regexp &optional next-p other-window)
@@ -1037,29 +1039,15 @@ onto a ring and may be popped back to with \\[pop-tag-mark].
 Contrast this with the ring of marks gone to by the command.
 
 See documentation of variable `tags-file-name'."
+  (declare (obsolete xref-find-apropos "25.1"))
   (interactive (find-tag-interactive "Find tag regexp: " t))
   ;; We go through find-tag-other-window to do all the display hair there.
   (funcall (if other-window 'find-tag-other-window 'find-tag)
           regexp next-p t))
-;;;###autoload (define-key esc-map [?\C-.] 'find-tag-regexp)
-
-;;;###autoload (define-key esc-map "*" 'pop-tag-mark)
 
 ;;;###autoload
-(defun pop-tag-mark ()
-  "Pop back to where \\[find-tag] was last invoked.
+(defalias 'pop-tag-mark 'xref-pop-marker-stack)
 
-This is distinct from invoking \\[find-tag] with a negative argument
-since that pops a stack of markers at which tags were found, not from
-where they were found."
-  (interactive)
-  (if (ring-empty-p find-tag-marker-ring)
-      (error "No previous locations for find-tag invocation"))
-  (let ((marker (ring-remove find-tag-marker-ring 0)))
-    (switch-to-buffer (or (marker-buffer marker)
-                          (error "The marked buffer has been deleted")))
-    (goto-char (marker-position marker))
-    (set-marker marker nil nil)))
 \f
 (defvar tag-lines-already-matched nil
   "Matches remembered between calls.") ; Doc string: calls to what?
@@ -1180,7 +1168,7 @@ error message."
   "Find the right line in the specified FILE."
   ;; If interested in compressed-files, search files with extensions.
   ;; Otherwise, search only the real file.
-  (let* ((buffer-search-extensions (if (featurep 'jka-compr)
+  (let* ((buffer-search-extensions (if auto-compression-mode
                                       tags-compression-info-list
                                     '("")))
         the-buffer
@@ -1204,7 +1192,7 @@ error message."
          (setq file-search-extensions (cdr file-search-extensions))
        (setq the-buffer (find-file-noselect (concat file (car file-search-extensions))))))
     (if (not the-buffer)
-       (if (featurep 'jka-compr)
+       (if auto-compression-mode
            (error "File %s (with or without extensions %s) not found" file tags-compression-info-list)
          (error "File %s not found" file))
       (set-buffer the-buffer))))
@@ -1550,6 +1538,7 @@ hits the start of file."
                 files)))
     (nreverse files)))
 
+;; FIXME?  Should this save-excursion?
 (defun etags-tags-included-tables () ; Doc string?
   (let ((files nil)
        beg)
@@ -1800,6 +1789,7 @@ Two variables control the processing we do on each file: the value of
 interesting (it returns non-nil if so) and `tags-loop-operate' is a form to
 evaluate to operate on an interesting file.  If the latter evaluates to
 nil, we exit; otherwise we scan the next file."
+  (declare (obsolete "use `xref-find-definitions' interface instead." "25.1"))
   (interactive)
   (let (new
        ;; Non-nil means we have finished one file
@@ -1855,7 +1845,6 @@ nil, we exit; otherwise we scan the next file."
     (and messaged
         (null tags-loop-operate)
         (message "Scanning file %s...found" buffer-file-name))))
-;;;###autoload (define-key esc-map "," 'tags-loop-continue)
 
 ;;;###autoload
 (defun tags-search (regexp &optional file-list-form)
@@ -1867,7 +1856,7 @@ If FILE-LIST-FORM is non-nil, it should be a form that, when
 evaluated, will return a list of file names.  The search will be
 restricted to these files.
 
-Aleso see the documentation of the `tags-file-name' variable."
+Also see the documentation of the `tags-file-name' variable."
   (interactive "sTags search (regexp): ")
   (if (and (equal regexp "")
           (eq (car tags-loop-scan) 're-search-forward)
@@ -1944,6 +1933,7 @@ directory specification."
 ;;;###autoload
 (defun tags-apropos (regexp)
   "Display list of all tags in tags table REGEXP matches."
+  (declare (obsolete xref-find-apropos "25.1"))
   (interactive "sTags apropos (regexp): ")
   (with-output-to-temp-buffer "*Tags List*"
     (princ "Click mouse-2 to follow tags.\n\nTags matching regexp `")
@@ -2073,6 +2063,54 @@ for \\[find-tag] (which see)."
       (completion-in-region (car comp-data) (cadr comp-data)
                            (nth 2 comp-data)
                            (plist-get (nthcdr 3 comp-data) :predicate)))))
+
+\f
+;;; Xref backend
+
+;; Stop searching if we find more than xref-limit matches, as the xref
+;; infrastructure is not designed to handle very long lists.
+;; Switching to some kind of lazy list might be better, but hopefully
+;; we hit the limit rarely.
+(defconst etags--xref-limit 1000)
+
+;;;###autoload
+(defun etags-xref-find (action id)
+  (pcase action
+    (`definitions (etags--xref-find-definitions id))
+    (`apropos (etags--xref-find-definitions id t))))
+
+(defun etags--xref-find-definitions (pattern &optional regexp?)
+  ;; This emulates the behaviour of `find-tag-in-order' but instead of
+  ;; returning one match at a time all matches are returned as list.
+  ;; NOTE: find-tag-tag-order is typically a buffer-local variable.
+  (let* ((xrefs '())
+         (first-time t)
+         (search-fun (if regexp? #'re-search-forward #'search-forward))
+         (marks (make-hash-table :test 'equal))
+         (case-fold-search (if (memq tags-case-fold-search '(nil t))
+                               tags-case-fold-search
+                             case-fold-search)))
+    (save-excursion
+      (while (visit-tags-table-buffer (not first-time))
+        (setq first-time nil)
+        (dolist (order-fun (cond (regexp? find-tag-regexp-tag-order)
+                                 (t find-tag-tag-order)))
+          (goto-char (point-min))
+          (while (and (funcall search-fun pattern nil t)
+                      (< (hash-table-count marks) etags--xref-limit))
+            (when (funcall order-fun pattern)
+              (beginning-of-line)
+              (cl-destructuring-bind (hint line &rest pos) (etags-snarf-tag)
+                (unless (eq hint t) ; hint==t if we are in a filename line
+                  (let* ((file (file-of-tag))
+                         (mark-key (cons file line)))
+                    (unless (gethash mark-key marks)
+                      (let ((loc (xref-make-file-location
+                                  (expand-file-name file) line 0)))
+                        (push (xref-make hint loc) xrefs)
+                        (puthash mark-key t marks)))))))))))
+    (nreverse xrefs)))
+
 \f
 (provide 'etags)