函数帮助里面的链接为何会指向配置文件?

原本函数的帮助页面会指向函数定义所在的 el 文件(比如图中的 yas-text 给出了 yasnippet.el 的链接)但是我发现有些函数给出的地址是我的配置文件(比如图中的的 yas-global-mode 给出的链接是 ../emacs-config.el),导致无法直接跳转到函数定义。请问大家有遇到这种情况没?有没有什么解决方法?

我的配置文件是 org 生成的 el,并通过 init.el 里面的 load 函数进行加载。Emacs 版本是 master 版本前几天编译的,但是这个问题很久之前就出现过。猜测可能和 use-package 有关?

补充一个配置:

;; init.el 文件内容
(setq gc-cons-threshold (* 400 (expt 2 20))
      gc-cons-percentage 0.6)

 (let* ((file-name-handler-alist nil)
        (read-process-output-max (expt 2 22)))

    (load "~/.emacs.d/emacs-config.el")

    ;; load-history 是在这里变化的
    )

(setq gc-cons-threshold (expt 2 23)
      gc-cons-percentage 0.1)
;; emacs-config.el 文件内容
(setq custom-file "~/.emacs.d/customs.el")
(load custom-file t)

(defvar bootstrap-version)
(let ((bootstrap-file
       (expand-file-name "straight/repos/straight.el/bootstrap.el" user-emacs-directory))
      (bootstrap-version 5))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

(setq use-package-compute-statistics t)
(straight-use-package 'use-package)
(setq straight-use-package-by-default t) ; Ensure :straight t

(use-package yasnippet
  :init (yas-global-mode 1))
(use-package yasnippet-snippets
  :after yasnippet)

你的配置文件编译过吗?

没有。只有 emacs-config.el ,没有 elc 和 eln。

檢查下 load-history 变量的內容,用 add-variable-watcher 定位是什么地方改了这个变量

use-package 不太可能有关系,更可能是 org tangle 的问题。

本身 “autoloaded” 和 “byte-compiled” 不应该同时出现。

1 个赞

load-history 里面 yas-global-mode 出现了两次,位置靠前的一次是在 emacs-config.el 文件下面,另外一次是在 yasnippet.elc 文件下面。 猜测帮助界面里找到了第一个匹配的位置就直接返回了,所以返回的文件是 emacs-config.el

我这边 org=>el 是手动进行的,所以在 Emacs 启动的时候并不会进行 org tangle。

我在加载 emacs-config.el 之前,执行了下面这句,但启动的时候并没有打印出东西来 :rofl:

(add-variable-watcher 'load-history #'(lambda (sym new op where) (print "HIHI")))

那就单纯打印变量先定位到什么时候变了

load 不会改变定义的位置,可能是你在 emacs-config.el 执行了 eval 之类的操作。

最好把配置精简一下,验证是否仍然存在问题,把有问题的 emacs-config.el 贴出来。

补充了一下配置,没有明显的 eval ,依然是显示

yas-global-mode is an autoloaded interactive byte-compiled Lisp function in ‘…/…/…/emacs-config.el’.

就在那句 load 之后才变的,在 emacs-config.el 的最后一行都还没有。 这个也符合预期,load 之后才会加载 load-history 里。

我也遇到了类似的情况,感觉可能和 master 版本相关

同样的配置,上面的是 28.1.1,下面的是最近几天装的 master 版本

1 个赞

那就是最新版本的bug了

1 个赞

我刚编译的29 on macOS,好像没这个问题

应该不是 Emacs 版本的问题,至少我没有观察到 29.0 有什么不同。

加载时机/顺序的可能性更大。可以用 -Q 做个测试,常规加载方式可能有些隐含的步骤你没有发现:

$ tree /path/to/scratch/emacs/2022-08/test-load/
/path/to/scratch/emacs/2022-08/test-load/
├── bar.el
├── foo.el
└── init.el

0 directories, 3 files
  • bar.el

    ;;; bar.el --- Bar package -*- lexical-binding: t; -*-
    ;;; Commentary:
    ;;; Code:
    
    (provide 'bar)
    ;;; bar.el ends here
    
  • foo.el

    ;;; foo.el --- Foo package -*- lexical-binding: t; -*-
    ;;; Commentary:
    ;;; Code:
    
    (defun foo-function-toplevel () nil)
    
    (add-hook 'window-setup-hook
              (lambda ()
                (defun foo-function-after-init () nil)))
    
    (with-eval-after-load 'bar
      (defun foo-function-after-bar () nil))
    
    (provide 'foo)
    ;;; foo.el ends here
    
  • init.el

    (add-to-list 'load-path (file-name-directory load-file-name))
    (load "foo")
    (load "bar")
    
    (require 'help-fns)
    (defun find-function-library (function)
      "Return filename the FUNCTION defined."
      (pcase-let* ((`(,_real-function ,def ,aliased ,real-def)
                    (help-fns--analyze-function function))
                   (file-name
                    (find-lisp-object-file-name function (if aliased 'defun def))))
        file-name))
    
    (run-with-timer
     0.1 nil
     (lambda ()
       (switch-to-buffer "*Messages*")
       (message "foo-function-toplevel   : %S" (find-function-library 'foo-function-toplevel))
       (message "foo-function-after-init : %S" (find-function-library 'foo-function-after-init))
       (message "foo-function-after-bar  : %S" (find-function-library 'foo-function-after-bar))))
    

测试结果:

$ emacs -Q -l /path/to/scratch/emacs/2022-08/test-load/init.el -nw
For information about GNU Emacs and the GNU system, type C-h C-a.
Loading /path/to/scratch/emacs/2022-08/test-load/foo.el (source)...done
Loading /path/to/scratch/emacs/2022-08/test-load/bar.el (source)...done
foo-function-toplevel   : "/path/to/scratch/emacs/2022-08/test-load/foo.el"
foo-function-after-init : nil
foo-function-after-bar  : "/path/to/scratch/emacs/2022-08/test-load/init.el"

可以看到三个函数分指向不同的位置。其中 (with-eval-after-load ...) 中定义的函数就指向了 init.el

1 个赞

我把 symbol-file 函数里面使用 load-history 的地方加了 reverse,解决了问题。感觉就是顺序的问题,延迟定义的函数在 load-history 里面出现了两次,原有的逻辑用了第一次出现的位置,恰好就是我的配置文件。

;; (pcase-dolist (`(,file . ,elems) load-history)  ; 原来的代码
(pcase-dolist (`(,file . ,elems) (reverse load-history))