]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/find-func.el
Merge from origin/emacs-25
[gnu-emacs] / lisp / emacs-lisp / find-func.el
index 7ea13d4637b10ca47ff6bd29067be812e70365da..4a7b7109106dfb69e9a295a06730167b16c233d9 100644 (file)
@@ -1,6 +1,6 @@
 ;;; find-func.el --- find the definition of the Emacs Lisp function near point  -*- lexical-binding:t -*-
 
-;; Copyright (C) 1997, 1999, 2001-2015 Free Software Foundation, Inc.
+;; Copyright (C) 1997, 1999, 2001-2016 Free Software Foundation, Inc.
 
 ;; Author: Jens Petersen <petersen@kurims.kyoto-u.ac.jp>
 ;; Maintainer: petersen@kurims.kyoto-u.ac.jp
@@ -43,6 +43,8 @@
 
 ;;; Code:
 
+(require 'seq)
+
 ;;; User variables:
 
 (defgroup find-function nil
@@ -62,7 +64,7 @@ ine\\(?:-global\\)?-minor-mode\\|ine-compilation-mode\\|un-cvs-mode\\|\
 foo\\|\\(?:[^icfgv]\\|g[^r]\\)\\(\\w\\|\\s_\\)+\\*?\\)\\|easy-mmode-define-[a-z-]+\\|easy-menu-define\\|\
 menu-bar-make-toggle\\)"
    find-function-space-re
-   "\\('\\|\(quote \\)?%s\\(\\s-\\|$\\|\(\\|\)\\)")
+   "\\('\\|(quote \\)?%s\\(\\s-\\|$\\|[()]\\)")
   "The regexp used by `find-function' to search for a function definition.
 Note it must contain a `%s' at the place where `format'
 should insert the function name.  The default value avoids `defconst',
@@ -100,10 +102,34 @@ Please send improvements and fixes to the maintainer."
   :group 'find-function
   :version "22.1")
 
+(defcustom find-feature-regexp
+  (concat ";;; Code:")
+  "The regexp used by `xref-find-definitions' when searching for a feature definition.
+Note it must contain a `%s' at the place where `format'
+should insert the feature name."
+  ;; We search for ";;; Code" rather than (feature '%s) because the
+  ;; former is near the start of the code, and the latter is very
+  ;; uninteresting. If the regexp is not found, just goes to
+  ;; (point-min), which is acceptable in this case.
+  :type 'regexp
+  :group 'xref
+  :version "25.1")
+
+(defcustom find-alias-regexp
+  "(defalias +'%s"
+  "The regexp used by `xref-find-definitions' to search for an alias definition.
+Note it must contain a `%s' at the place where `format'
+should insert the feature name."
+  :type 'regexp
+  :group 'xref
+  :version "25.1")
+
 (defvar find-function-regexp-alist
   '((nil . find-function-regexp)
     (defvar . find-variable-regexp)
-    (defface . find-face-regexp))
+    (defface . find-face-regexp)
+    (feature . find-feature-regexp)
+    (defalias . find-alias-regexp))
   "Alist mapping definition types into regexp variables.
 Each regexp variable's value should actually be a format string
 to be used to substitute the desired symbol name into the regexp.
@@ -158,15 +184,15 @@ See the functions `find-function' and `find-variable'."
 LIBRARY should be a string (the name of the library)."
   ;; If the library is byte-compiled, try to find a source library by
   ;; the same name.
-  (if (string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
-      (setq library (replace-match "" t t library)))
+  (when (string-match "\\.el\\(c\\(\\..*\\)?\\)\\'" library)
+    (setq library (replace-match "" t t library)))
   (or
    (locate-file library
-               (or find-function-source-path load-path)
-               (find-library-suffixes))
+                (or find-function-source-path load-path)
+                (find-library-suffixes))
    (locate-file library
-               (or find-function-source-path load-path)
-               load-file-rep-suffixes)
+                (or find-function-source-path load-path)
+                load-file-rep-suffixes)
    (when (file-name-absolute-p library)
      (let ((rel (find-library--load-name library)))
        (when rel
@@ -177,8 +203,44 @@ LIBRARY should be a string (the name of the library)."
           (locate-file rel
                        (or find-function-source-path load-path)
                        load-file-rep-suffixes)))))
+   (find-library--from-load-path library)
    (error "Can't find library %s" library)))
 
+(defun find-library--from-load-path (library)
+  ;; In `load-history', the file may be ".elc", ".el", ".el.gz", and
+  ;; LIBRARY may be "foo.el" or "foo", so make sure that we get all
+  ;; potential matches, and then see whether any of them lead us to an
+  ;; ".el" or an ".el.gz" file.
+  (let* ((elc-regexp "\\.el\\(c\\(\\..*\\)?\\)\\'")
+         (suffix-regexp
+          (concat "\\("
+                  (mapconcat 'regexp-quote (find-library-suffixes) "\\'\\|")
+                  "\\|" elc-regexp "\\)\\'"))
+         (potentials
+          (mapcar
+           (lambda (entry)
+             (if (string-match suffix-regexp (car entry))
+                 (replace-match "" t t (car entry))
+               (car entry)))
+           (seq-filter
+            (lambda (entry)
+              (string-match
+               (concat "\\`"
+                       (regexp-quote
+                        (replace-regexp-in-string suffix-regexp "" library))
+                       suffix-regexp)
+               (file-name-nondirectory (car entry))))
+            load-history)))
+         result)
+    (dolist (file potentials)
+      (dolist (suffix (find-library-suffixes))
+        (when (not result)
+          (cond ((file-exists-p file)
+                 (setq result file))
+                ((file-exists-p (concat file suffix))
+                 (setq result (concat file suffix)))))))
+    result))
+
 (defvar find-function-C-source-directory
   (let ((dir (expand-file-name "src" source-directory)))
     (if (file-accessible-directory-p dir) dir))
@@ -189,12 +251,15 @@ defined in C.")
 (declare-function ad-get-advice-info "advice" (function))
 
 (defun find-function-advised-original (func)
-  "Return the original function symbol of an advised function FUNC.
-If FUNC is not the symbol of an advised function, just returns FUNC."
+  "Return the original function definition of an advised function FUNC.
+If FUNC is not a symbol, return it.  Else, if it's not advised,
+return the symbol's function definition."
   (or (and (symbolp func)
-          (featurep 'advice)
-          (let ((ofunc (cdr (assq 'origname (ad-get-advice-info func)))))
-            (and (fboundp ofunc) ofunc)))
+           (featurep 'nadvice)
+           (let ((ofunc (advice--symbol-function func)))
+             (if (advice--p ofunc)
+                 (advice--cd*r ofunc)
+               ofunc)))
       func))
 
 (defun find-function-C-source (fun-or-var file type)
@@ -228,9 +293,12 @@ TYPE should be nil to find a function, or `defvar' to find a variable."
     (cons (current-buffer) (match-beginning 0))))
 
 ;;;###autoload
-(defun find-library (library)
+(defun find-library (library &optional other-window)
   "Find the Emacs Lisp source of LIBRARY.
-LIBRARY should be a string (the name of the library)."
+LIBRARY should be a string (the name of the library).  If the
+optional OTHER-WINDOW argument (i.e., the command argument) is
+specified, pop to a different window before displaying the
+buffer."
   (interactive
    (let* ((dirs (or find-function-source-path load-path))
           (suffixes (find-library-suffixes))
@@ -252,11 +320,17 @@ LIBRARY should be a string (the name of the library)."
      (when (and def (not (test-completion def table)))
        (setq def nil))
      (list
-      (completing-read (if def (format "Library name (default %s): " def)
+      (completing-read (if def
+                           (format "Library name (default %s): " def)
                         "Library name: ")
-                      table nil nil nil nil def))))
-  (let ((buf (find-file-noselect (find-library-name library))))
-    (condition-case nil (switch-to-buffer buf) (error (pop-to-buffer buf)))))
+                      table nil nil nil nil def)
+      current-prefix-arg)))
+  (prog1
+      (funcall (if other-window
+                   'pop-to-buffer
+                 'pop-to-buffer-same-window)
+               (find-file-noselect (find-library-name library)))
+    (run-hooks 'find-function-after-hook)))
 
 ;;;###autoload
 (defun find-function-search-for-symbol (symbol type library)
@@ -330,8 +404,10 @@ signal an error.
 
 If VERBOSE is non-nil, and FUNCTION is an alias, display a
 message about the whole chain of aliases."
-  (let ((def (if (symbolp function)
-                 (symbol-function (find-function-advised-original function))))
+  (let ((def (when (symbolp function)
+               (or (fboundp function)
+                   (signal 'void-function (list function)))
+               (find-function-advised-original function)))
         aliases)
     ;; FIXME for completeness, it might be nice to print something like:
     ;; foo (which is advised), which is an alias for bar (which is advised).
@@ -340,12 +416,13 @@ message about the whole chain of aliases."
           (not verbose)
           (setq aliases (if aliases
                             (concat aliases
-                                    (format ", which is an alias for `%s'"
-                                            (symbol-name def)))
-                          (format "`%s' is an alias for `%s'"
-                                  function (symbol-name def)))))
-      (setq function (symbol-function (find-function-advised-original function))
-            def (symbol-function (find-function-advised-original function))))
+                                    (format-message
+                                     ", which is an alias for `%s'"
+                                     (symbol-name def)))
+                          (format-message "`%s' is an alias for `%s'"
+                                          function (symbol-name def)))))
+      (setq function (find-function-advised-original function)
+            def (find-function-advised-original function)))
     (if aliases
         (message "%s" aliases))
     (cons function
@@ -547,11 +624,11 @@ See also `find-function-recenter-line' and `find-function-after-hook'."
   (interactive (find-function-read 'defface))
   (find-function-do-it face 'defface 'switch-to-buffer))
 
-;;;###autoload
-(defun find-function-on-key (key)
+(defun find-function-on-key-do-it (key find-fn)
   "Find the function that KEY invokes.  KEY is a string.
-Set mark before moving, if the buffer already existed."
-  (interactive "kFind function on key: ")
+Set mark before moving, if the buffer already existed.
+
+FIND-FN is the function to call to navigate to the function."
   (let (defn)
     (save-excursion
       (let* ((event (and (eventp key) (aref key 0))) ; Null event OK below.
@@ -572,7 +649,28 @@ Set mark before moving, if the buffer already existed."
          (message "%s is unbound" key-desc)
        (if (consp defn)
            (message "%s runs %s" key-desc (prin1-to-string defn))
-         (find-function-other-window defn))))))
+         (funcall find-fn defn))))))
+
+;;;###autoload
+(defun find-function-on-key (key)
+  "Find the function that KEY invokes.  KEY is a string.
+Set mark before moving, if the buffer already existed."
+  (interactive "kFind function on key: ")
+  (find-function-on-key-do-it key #'find-function))
+
+;;;###autoload
+(defun find-function-on-key-other-window (key)
+  "Find, in the other window, the function that KEY invokes.
+See `find-function-on-key'."
+  (interactive "kFind function on key: ")
+  (find-function-on-key-do-it key #'find-function-other-window))
+
+;;;###autoload
+(defun find-function-on-key-other-frame (key)
+  "Find, in the other frame, the function that KEY invokes.
+See `find-function-on-key'."
+  (interactive "kFind function on key: ")
+  (find-function-on-key-do-it key #'find-function-other-frame))
 
 ;;;###autoload
 (defun find-function-at-point ()
@@ -597,6 +695,8 @@ Set mark before moving, if the buffer already existed."
   (define-key ctl-x-4-map "F" 'find-function-other-window)
   (define-key ctl-x-5-map "F" 'find-function-other-frame)
   (define-key ctl-x-map "K" 'find-function-on-key)
+  (define-key ctl-x-4-map "K" 'find-function-on-key-other-window)
+  (define-key ctl-x-5-map "K" 'find-function-on-key-other-frame)
   (define-key ctl-x-map "V" 'find-variable)
   (define-key ctl-x-4-map "V" 'find-variable-other-window)
   (define-key ctl-x-5-map "V" 'find-variable-other-frame))