【问题标题】:How to turn a (non-interactive) function into a command (interactive function) in Emacs-LISP?如何在 Emacs-LISP 中将(非交互式)函数变成命令(交互式函数)?
【发布时间】:2019-11-01 18:15:11
【问题描述】:

我想调用函数markdown-back-to-heading,它在 Emacs 的 Markdown 模式下是原生的。我了解interactive 将非交互式功能变为交互式,或正式为functions into commands

特殊形式:交互式 arg-descriptor

这种特殊形式声明一个函数是一个命令,因此它可以被交互调用(通过 M-x 或通过输入绑定到它的键序列)。

我试过了:

(define-key markdown-mode-map (kbd "C-c C-h") 'markdown-back-to-heading)

这会引发错误:Wrong type argument: commandp, markdown-back-to-heading

所以我用交互功能包装了它,它可以工作:

(defun my-markdown-back-to-heading ()
  "Wrap function to be called interactively."
  (interactive)
  (markdown-back-to-heading))

(define-key markdown-mode-map (kbd "C-c C-h") 'my-markdown-back-to-heading)

有没有更好的方法将原生函数变成交互式命令?

【问题讨论】:

  • 你做的是对的:编写你自己的命令,添加一个interactive规范。你使用什么样的interactive规范取决于你想要的交互行为. (例如,@Stefan 的回答提到了使用 Shift 选择。)它不仅仅用于您自己的个人使用,或者如果出于其他原因您需要能够以交互方式使用原始功能,那么请考虑建议该功能(如 Stefan 也指示)。

标签: emacs command markdown interactive


【解决方案1】:

如果想让markdown-back-to-heading 具有交互性,您有几个不同的好选择:

  • 提交错误报告以获取上游以使其如此。在错误报告中加入补丁有助于加快处理速度。
  • 使用如下建议:

    (advice-add 'markdown-back-to-heading :before
                (lambda () (interactive "^") nil))
    

如果您想提高函数的交互性,例如如果你想支持shift-selection,你可以添加交互代码^(interactive "^") 而不是(interactive),这样Emacs 就知道这是一个导航命令(因此如果你将它与移位绑定一起使用,它将选择相应的文本)。这是a manual page with the list of interactive codes,以及其他用于交互的选项at the manual page you mentioned

【讨论】:

    【解决方案2】:

    您也可以使用interactive-form 符号属性。

    详情见C-hig(elisp)Using Interactive

    这是一个简单的例子:

    ;; Enable M-x kill-process (to kill the current buffer's process).
    ;; (This is not normally a command, but it is useful as one.)
    (put 'kill-process 'interactive-form '(interactive))
    

    我实际使用的比较复杂的版本是:

    (put 'kill-process 'interactive-form
         '(interactive
           (let ((proc (get-buffer-process (current-buffer))))
             (if (process-live-p proc)
                 (unless (yes-or-no-p (format "Kill %S? " proc))
                   (error "Process not killed"))
               (error (format "Buffer %s has no process" (buffer-name))))
             (list proc))))
    

    【讨论】:

    • 很好,也是交互式使用kill-process 的好例子。你的第一个例子的结果和advice-add一样吗?
    • 实用的效果与Stefan的:beforeadvice例子极为相似;但他们正在以不同的方式实现它。如果您同时使用了这两种方法,则符号属性具有优先权(就像 original 函数将自身声明为交互式的情况一样)。
    【解决方案3】:

    按照@Stefan 的建议,我filed a Github issuesubmitted a patch,在源代码中添加了(interactive "P") 这一行:

    (defun markdown-back-to-heading (&optional invisible-ok)
      "Move to previous heading line, or beg of this line if it's a heading.
    Only visible heading lines are considered, unless INVISIBLE-OK is non-nil."
      (interactive "P")
      (markdown-move-heading-common #'outline-back-to-heading invisible-ok))
    

    现在我可以用键绑定它了

    (define-key markdown-mode-map (kbd "C-c C-h") 'markdown-back-to-heading)
    

    我已经从 MELPA 安装了markdown-mode,所以这个更改需要uninstalling the package,然后是这些步骤from the repo README。我forked the repo 并在本地克隆了 repo:

    git clone git@github.com:miguelmorin/markdown-mode
    

    并将这些行添加到 Emacs 初始化中:

    (add-to-list 'load-path (expand-file-name "~/code/markdown-mode"))
    
    (autoload 'markdown-mode "markdown-mode"
       "Major mode for editing Markdown files" t)
    (add-to-list 'auto-mode-alist '("\\.markdown\\'" . markdown-mode))
    (add-to-list 'auto-mode-alist '("\\.md\\'" . markdown-mode))
    
    (autoload 'gfm-mode "markdown-mode"
       "Major mode for editing GitHub Flavored Markdown files" t)
    (add-to-list 'auto-mode-alist '("README\\.md\\'" . gfm-mode))
    
    (require 'markdown-mode)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多