从帮助中浏览Emacs源代码时如何进入仅查看模式?


10

当我通过浏览Emacs帮助中的功能时C-h f,我经常想窥探Elisp / C实现。我想以view-mode这种方式访问​​源代码时自动输入以避免不必要的修改。我可以建议使用钩子或函数来完成此操作吗?


2
这是我用来防止意外修改打开的任何文件的方法,如果要编辑源代码emacs-lisp-mode,我只会这样做C-x C-q(defun set-buffer-read-only () (setq buffer-read-only t)) (add-hook 'emacs-lisp-mode-hook 'set-buffer-read-only)
法律列表

Answers:


2

更新(经过一夜的睡眠):这个答案有一个主要缺陷:view-mode在导航到任何功能时,不仅可以访问Emacs源,还可以启用它。这可以解决,但最好使用@phils的答案

通过C-h f describe-function RET阅读然后阅读的源代码,describe-function我发现它为链接到函数定义创建了一种特殊类型的“按钮”:help-function-def

zrgrep使用此字符串(“ help-function-def”)运行时将我指向help-mode.el.gz

经过所有这些挖掘之后,我们可以用我们自己的按钮类型替换此按钮类型(请注意代码中的注释):

(define-button-type 'help-function-def
  :supertype 'help-xref
  'help-function (lambda (fun file)
               (require 'find-func)
               (when (eq file 'C-source)
                 (setq file
                       (help-C-file-name (indirect-function fun) 'fun)))
               ;; Don't use find-function-noselect because it follows
               ;; aliases (which fails for built-in functions).
               (let ((location
                      (find-function-search-for-symbol fun nil file)))
                 (pop-to-buffer (car location))
                 (if (cdr location)
                     (goto-char (cdr location))
                   (message "Unable to find location in file")))
                   (view-mode t)) ; <= new line: enable view-mode
  'help-echo (purecopy "mouse-2, RET: find function's definition"))

据我所知,没有向其添加建议的功能:Emacs使用lambda此处。另一方面(如@rationalrevolt所指出的),可以替换按钮类型的help-function属性help-function-def

(require 'help-mode)
(let ((help-func (button-type-get 'help-function-def 'help-function)))
  (button-type-put 'help-function-def 'help-function
                   `(lambda (func file)
                      (funcall ,help-func func file) (view-mode t))))

1
我想我可以尝试使用转发给现有lambda的lambda button-type-get并将button-type-put其替换为我自己的lambda。
2014年

@rationalrevolt:好主意!(似乎有些脆弱,但我想这也是脆弱的。)
君士坦丁

@rationalrevolt:请参阅更新的答案。(似乎不能在评论中添加换行符)
君士坦丁

谢谢!我正在尝试类似的方法,但是作为elisp的新手,我受到动态绑定的束缚,无法让我的封闭工作:)
RationalRevolt

16

您可以使用目录本地变量将Emacs的源文件默认设置为只读。(另请参见C-hig (emacs) Directory Variables RET)。

.dir-locals.el在要保护的目录树的根目录中创建一个名为的文件,其内容如下:

((nil . ((eval . (view-mode 1)))))

编辑: MichałPolitowski在注释中指出,view-mode以这种方式启用是有问题的,因为当您使用q它关闭缓冲区时,也会禁用该模式,这意味着下次访问该缓冲区时view-mode将不会启用。

编辑2:君士坦丁在以下评论中提供了该问题的解决方案:

((nil . ((eval . (when buffer-file-name (view-mode-enter nil #'kill-buffer))))))

这很有用,可以添加一个测试来确保缓冲区已经在访问文件,但是关键的改变是使用view-mode-enter代替view-mode,因为前者采用一个EXIT-ACTION参数来确定q键入时的操作。在这种情况下,退出操作是终止缓冲区,以确保下次访问文件时,它将再次以结尾view-mode

编辑3:按照该路径,我们还可以看到指定的EXIT-ACTION对象最终传递给了view-mode-exit函数,它的文档字符串为我们提供了另一种解决方案:

view-no-disable-on-exit is a variable defined in `view.el'.
Its value is nil

Documentation:
If non-nil, View mode "exit" commands don't actually disable View mode.
Instead, these commands just switch buffers or windows.
This is set in certain buffers by specialized features such as help commands
that use View mode automatically.

因此,我们可以使用以下内容:

((nil . ((eval . (when buffer-file-name
                   (setq-local view-no-disable-on-exit t)
                   (view-mode-enter))))))

我使用另一种方法,您可以在初始化文件中完全指定它(而不是创建.dir-locals.el文件),并且我只是使文件成为只读文件,而不是使用view-mode。我的配置如下所示:

;; Emacs
(dir-locals-set-class-variables
 'emacs
 '((nil . ((buffer-read-only . t)
           (show-trailing-whitespace . nil)
           (tab-width . 8)
           (eval . (whitespace-mode -1))))))

(dir-locals-set-directory-class "/usr/local/src/emacs" 'emacs)
(dir-locals-set-directory-class "/usr/local/share/emacs" 'emacs)
(dir-locals-set-directory-class "/usr/share/emacs" 'emacs)

显然,您可以对elpa目录以及包含第三方源代码的任何其他目录执行相同的操作。


大!这显然比我想出的要好。(我在想什么?我知道并使用.dir-locals.el自己...)
君士坦丁

基于a find-file-hookread-only-dirslist,我一直走得很近,但是我喜欢这种方法。
glucas 2014年

这看起来很干净。我有一个小问题。用((nil . ((eval . (view-mode 1)))))什么最简单的方法来View-quit杀死通过帮助访问的缓冲区?否则,通过按退出源视图后q,缓冲区将留在后面,并且以后在再次从帮助中从同一文件访问源时,将不会启动视图模式。
米哈尔Politowski

MichałPolitowski:好的。我已经更新了答案以合并该事实,但是我没有解决方法。君士坦丁(已编辑)的答案可能是使用的最佳解决方案view-mode
菲尔2014年

1
今天,我发现对于@MichałPolitowski指出的问题,我需要一种解决方法---我发现了一个解决方法:use ((nil . ((eval . (when buffer-file-name (view-mode-enter nil #'kill-buffer))))))(请注意,(view-mode-enter ...)而不是(view-mode 1))。这样,按q杀死缓冲区,view-mode 使下一次我访问同一个文件。
君士坦丁

0

我认为您只需要添加一个钩子即可

(add-hook 'find-function-after-hook 'view-mode)

这比我的怪兽要好,但是仍然不能真正解决问题:使用view-mode导航到任何功能时,它会打开C-h f,而不仅仅是Emacs源。
君士坦丁

从实验上讲,帮助链接实际上并没有运行此挂钩。看起来只有交互式find-THING命令才能使用此钩子,而帮助按钮会绕过它。
菲尔2014年

0

这不会解决您的特定情况,而是view-mode在您从帮助缓冲区访问源文件时切换到更一般的情况。我提供它作为@Constantine答案的替代方法,因为它不可读为注释。

我看起来好像是我最初从EmacsWiki获得的

(defadvice find-function-search-for-symbol (after view-function-source last (symbol type library) activate)
  "When visiting function source via Help, switch to view-mode"
  (with-current-buffer (car ad-return-value)
    (view-mode 1)))

(defadvice find-variable-noselect (after view-var-source last (variable &optional file) activate)
  "When visiting variable source via Help, switch to view-mode"
  (with-current-buffer (car ad-return-value)
    (view-mode 1)))

0

这是适用于内置文档的解决方案,以及显示如何将其扩展到ELPA的示例。它通过将某些正则表达式匹配到当前文件的路径并在read-only-mode它们中的任何一个匹配时应用而起作用。

请注意,如果您也通过缓冲区访问缓冲区dired,而不仅仅是通过帮助访问缓冲区,则该缓冲区是只读的。

我添加了一个钩子,该钩子在进入后运行,该钩子emacs-lisp-mode检查文件的路径是否匹配/\.el\.gz$/,如果适用,则应用只读模式。

(defun readonly-if-el-gz ()
  (cond
   ((string-match "\\.el\\.gz\\'" (or (buffer-file-name) ""))
    (read-only-mode +1))))

(add-hook 'emacs-lisp-mode-hook 'readonly-if-el-gz)

这也是一个使用启发式检查任何路径.emacs.d/elpa实际上都是ELPA代码的示例,它也检查ELPA 。

(defun readonly-if-internal ()
  (let
      ((name (or (buffer-file-name) "")))
    (cond
     ((string-match "\\.el\\.gz\\'" name) (read-only-mode +1))
     ((string-match "\\.emacs\\.d/elpa" name) (read-only-mode +1)))))

(add-hook 'emacs-lisp-mode-hook 'readonly-if-internal)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.