如何在Emacs Elisp中找到提供某项功能的文件

7

目前,我正在使用load-history变量来查找一个功能所在的文件。

假设要查找功能gnus所在的文件。

我在scratch缓冲区中执行以下代码,可以按顺序打印文件名和符号:

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

然后搜索“(provide . gnus)”并将光标移至该行的开头(Ctrl+A)。前一行中的文件名是该功能所属的文件。

这种方法有什么问题吗,或者是否存在更好的方法。

4个回答

8
我不太清楚你想用这个做什么,但是我有一些笔记。
  • 你的方法很好。任何一种解决问题的方法都是好的。

  • @Tom是正确的,你不应该需要这样做,因为帮助系统已经为你解决了问题。也就是说,C-h f

但那没什么意思。假设你真的想要一个自动的、更优雅的解决方案。你需要一个函数--locate-feature,其签名如下:

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

方法一 load-history 方式

以下是我解决问题的步骤:

  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.

方法二:帮助途径

方法一适用于特性。但是如果我想知道任何可用函数的定义在哪里呢?不仅仅是特性。

C-h f 已经告诉了我,但我想要一个包含文件名的字符串,而不是所有冗长的帮助文本。我想要这个:

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

让我们开始吧。

  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))
    

现在去探索Emacs吧,祝你玩得开心。


很棒的解释和写作。两个 defun 非常有用于调试。你能让它们也可以交互吗?顺便说一句,你的第一个 defun 多了一个右括号。 - kindahero
我使用(symbol-file 'gnus)。第二个参数可以是defun/defvar/defface。 - gavenkoa

4

只需使用symbol-file命令。它会扫描格式为load-history的文件:

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'.

那么,请称之为:
(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.

4

有一个locate-library可以帮助你。

试试下面的代码... M-: (locate-library "my-feature")

例如:(locate-library "gnus")


locate-library 根据文件名进行定位。虽然大多数情况下每个文件只提供一个功能,并且该功能以文件命名,但这两件事情都不一定是真实的。 - event_jr

1

这并没有什么问题,但为什么它比获取关键字或函数的帮助更简单呢?例如,如果您使用gnus命令,并想知道它来自哪里,那么您可以使用C-h k,它会告诉您它的定义来自哪个elisp文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接