How to find which file provide(d) the feature in e

2019-06-20 01:14发布

问题:

Currently i am using the load-history variable to find the file from which a feature came from.

suppose to find the file the feature gnus came from.

I execute the following code in scratch buffer which prints filename and the symbols in separate lines consecutively.

(dolist (var load-history)
  (princ (format "%s\n" (car var)))
  (princ (format "\t%s\n" (cdr var))))

and then search for "(provide . gnus)" and then move the point to the start of line(Ctrl+A). The file name in the previous line is the file from which the feature came from.

Is there any thing wrong with this method, or does a better method exist.

回答1:

I don't really know what you're trying to do with this, but here are some notes.

  • Your method is fine. Any way to hack your own solution to a problem is good in my book.

  • @Tom is correct that you shouldn't really need to do this, because the problem is already solved for you by the help system. i.e. C-h f

But that's not so interesting. Let's say you really want an automatic, more elegant solution. You want a function -- locate-feature with this signature:

(defun locate-feature (feature)
  "Return file-name as string where `feature' was provided"
  ...)

Method 1 load-history approach

I'll just describe the steps I took to solve this:

  1. You've already got the most important part -- find the variable with the information you need.

  2. I notice immediately that this variable has a lot of data. If I insert it into a buffer as a single line, Emacs will not be happy, because it's notoriously bad at handling long lines. I know that the prett-print package will be able to format this data nicely. So I open up my *scratch* buffer and run

    M-: (insert (pp-to-string load-history))

  3. I can now see the data structure I'm dealing with. It seems to be (in pseudo code):

    ((file-name
      ((defun|t|provide . symbol)|symbol)*)
     ...)
    
  4. Now I just write the function

    (eval-when-compile (require 'cl))
    (defun locate-feature (feature)
      "Return file name as string where `feature' was provided"
      (interactive "Sfeature: ")
      (dolist (file-info load-history)
        (mapc (lambda (element)
                (when (and (consp element)
                           (eq (car element) 'provide)
                           (eq (cdr element) feature))
                  (when (called-interactively-p 'any)
                    (message "%s defined in %s" feature (car file-info)))
                  (return (car file-info))))
              (cdr file-info))))
    

    The code here is pretty straight forward. Ask Emacs about the functions you don't understand.

Method 2 help approach

Method one works for features. But what if by I want to know where any available function is defined? Not just features.

C-h f already tells me that, but I want the file-name in a string, not all of the verbose help text. I want this:

(defun locate-function (func)
  "Return file-name as string where `func' was defined or will be autoloaded"
  ...)

Here we go.

  1. C-h f is my starting point, but I really want to read the code that defines describe-function. I do this:

    C-h k C-h f C-x o tab enter

  2. Now I'm in help-fns.el at the definition of describe-function. I want to work only with this function definition. So narrowing is in order:

    C-x n d

  3. I have a hunch that the interesting command will have "find" or "locate" in its name, so I use occur to search for interesting lines:

    M-s o find\|locate

    No matches. Hmmm. Not a lot of lines in this defun. describe-function-1 seems to be doing the real work, so we try that.

  4. I can visit the definition of describe-function-1 via C-h f. But I already have the file open. imenu is available now:

    C-x n w M-x imenu desc*1 tab enter

  5. Narrow and search again:

    C-x n d M-s o up enter

    I see find-lisp-object-file-name which looks promising.

  6. After reading C-h f find-lisp-object-file-name I come up with:

    (defun locate-function (func)
      "Return file-name as string where `func' was defined or will be autoloaded"
      (interactive "Ccommand: ")
      (let ((res (find-lisp-object-file-name func (symbol-function func))))
        (when (called-interactively-p 'any)
          (message "%s defined in %s" func res))
        res))
    

Now go have some fun exploring Emacs.



回答2:

Just use symbol-file. It scan load-history which has format:

Each entry has the form `(provide . FEATURE)',
`(require . FEATURE)', `(defun . FUNCTION)', `(autoload . SYMBOL)',
`(defface . SYMBOL)', or `(t . SYMBOL)'.  Entries like `(t . SYMBOL)'
may precede a `(defun . FUNCTION)' entry, and means that SYMBOL was an
autoload before this file redefined it as a function.  In addition,
entries may also be single symbols, which means that SYMBOL was
defined by `defvar' or `defconst'.

So call it as:

(symbol-file 'scheme 'provide)        ; Who provide feature.
(symbol-file 'nxml-mode-hook 'defvar) ; Where variable defined.
(symbol-file 'message-send 'defun)    ; Where function defined.
(symbol-file 'scheme)    ; Look for symbol despite its type.


回答3:

There is locate-library for that.

Try... M-: (locate-library "my-feature")

eg: (locate-library "gnus")



回答4:

There is nothing wrong with it, but why is it simpler than getting help on a key or a function? If you use a gnus command for example and you want to know where it comes from then you can use C-h k and it tells you from which elisp file its definition comes.



标签: emacs elisp