通过 org-font-lock-hook 为源码块添加 keymap

发布于 2025-02-21 22:13:51 字数 5388 浏览 9 评论 0

我有一个在代码块中使用自定义快捷键的想法。例如,在 org 文件中无需进入 org-special-edit 就能直接在使用 lispy,或者在 python 块中使用 elpy 快捷键。我还见过其他的解决方案,比如 polymode 就声称可以做到这一点。你可能在想,如果这些解决方案真的能行那我也就不会写这篇文章了!在 org-mode 的邮件列表中有一些关于这个想法的不错的讨论,Nicolas Goaziou 就指出这可以通过 org-font-lock-hook 来完成。

你可以在这里观看视频:video:https://www.youtube.com/embed/a2jHqB1qWiY

解决这个问题相对容易。文本区域进行 font-lock 时可以为其添加 Keymap,因此我只需使为 org-mode 的 font lock 系统中添加一个钩子函数,该函数查找代码块并将 keymap 作为文本属性添加其中即可。这包括三个步骤:

  1. 定义要使用的 keymap。我使用一个由 (language . map) 组成的 alist 来存放这些信息
  2. 定义 font-lock 函数。该函数将向代码块中添加 keymap 属性。
  3. 定义一个 minor mode 来打开和关闭此功能。

下面是 keymap 们的定义。通常我只是复制我想要的 mode-map,然后往里面添加一些东西。有时我们仍然需要跳到 org-special-edit 模式。例如,若您在 Python 块中使用命令将缓冲区发送到 repl,在 org 模式下会报错。如果您经常使用 C-c C-e 导出命令,那么你可能会想要把它加到 keymap 中。当然,你也可以复制 org-map 并向其添加额外的快捷键。选择权在你。

(require 'lispy)
(require 'elpy)

(setq scimax-src-block-keymaps
      `(("ipython" . ,(let ((map (make-composed-keymap
                                  `(,elpy-mode-map ,python-mode-map ,pyvenv-mode-map)
                                  org-mode-map)))
                        ;; In org-mode I define RET so we f
                        (define-key map (kbd "<return>") 'newline)
                        (define-key map (kbd "C-c C-c") 'org-ctrl-c-ctrl-c)
                        map))
        ("python" . ,(let ((map (make-composed-keymap
                                  `(,elpy-mode-map ,python-mode-map ,pyvenv-mode-map)
                                  org-mode-map)))
                        ;; In org-mode I define RET so we f
                        (define-key map (kbd "<return>") 'newline)
                        (define-key map (kbd "C-c C-c") 'org-ctrl-c-ctrl-c)
                        map))
        ("emacs-lisp" . ,(let ((map (make-composed-keymap `(,lispy-mode-map
                                                            ,emacs-lisp-mode-map
                                                            ,outline-minor-mode-map)
                                                          org-mode-map)))
                            (define-key map (kbd "C-c C-c") 'org-ctrl-c-ctrl-c)
                            map))))

接下来,我们定义将 keymap 应用到每个代码块的函数。只有在上面的变量中定义了的 keymap,才会应用它们。这个函数派生自 org-fontify-meta-line-and-blocks-1 .

(defun scimax-add-keymap-to-src-blocks (limit)
  "Add keymaps to src-blocks defined in `scimax-src-block-keymaps'."
  (let ((case-fold-search t)
        lang)
    (while (re-search-forward org-babel-src-block-regexp limit t)
      (let ((lang (match-string 2))
            (beg (match-beginning 0))
            (end (match-end 0)))
        (if (assoc (org-no-properties lang) scimax-src-block-keymaps)
            (progn
              (add-text-properties
                beg end `(local-map ,(cdr (assoc
                                          (org-no-properties lang)
                                          scimax-src-block-keymaps))))
              (add-text-properties
                beg end `(cursor-sensor-functions
                          ((lambda (win prev-pos sym)
                            ;; This simulates a mouse click and makes a menu change
                            (org-mouse-down-mouse nil)))))))))))

在这里,我们创建一个 advice 来欺骗所有需要知道 major 模式的函数。

我们只将该欺骗应用于 org 模式及其代码块块中,其他情况下我们依然调用原始函数。到目前为止,lispy-eval 是我唯一需要应用欺骗的函数。但这是一种通用策略,可以用来做其他的事情,比如缩小到源码块,甚至在需要的情况下临时进入特殊的编辑模式。

(defun scimax-spoof-mode (orig-func &rest args)
  "Advice function to spoof commands in org-mode src blocks.
It is for commands that depend on the major mode. One example is
`lispy--eval'."
  (if (org-in-src-block-p)
      (let ((major-mode (intern (format "%s-mode" (first (org-babel-get-src-block-info))))))
        (apply orig-func args))
    (apply orig-func args)))

我们还定义了一个 minor 模式,让我们可以开关它。

我们这里将这个函数添加到 org-font-lock-hook 中,并对 lispy-eval 函数添加 advise。

出于某些原因,我不得不将 font-lock-function 添加到 org-font-lock-hook 的末尾,并将 local-map 添加为一个额外管理的属性,以便在关闭该 mode 时将其删除。

(define-minor-mode scimax-src-keymap-mode
  "Minor mode to add mode keymaps to src-blocks."
  :init-value nil
  (if scimax-src-keymap-mode
      (progn
        (add-hook 'org-font-lock-hook #'scimax-add-keymap-to-src-blocks t)
        (add-to-list 'font-lock-extra-managed-props 'local-map)
        (add-to-list 'font-lock-extra-managed-props 'cursor-sensor-functions)
        (advice-add 'lispy--eval :around 'scimax-spoof-mode)
        (cursor-sensor-mode +1))
    (remove-hook 'org-font-lock-hook #'scimax-add-keymap-to-src-blocks)
    (advice-remove 'lispy--eval 'scimax-spoof-mode)
    (cursor-sensor-mode -1))
  (font-lock-fontify-buffer))

(add-hook 'org-mode-hook (lambda ()
                            (scimax-src-keymap-mode +1)))

就是这样!我很确定这是个好主意。当您编写大量的短小代码块和几乎相同数量的文本时(如本文这样),它会很有帮助。

它还有助于编写代码,因为许多事情如缩进、括号等都是自动处理的。而这些辅助功能也是我之前进入 special-edit 模式的主要原因!

我使用这个方案的时间还不够长,不知道它是否会引起其他意外。如果你尝试该方案并找到了问题,请留下评论!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

文章
评论
26 人气
更多

推荐作者

身边

文章 0 评论 0

qq_oxT0yE

文章 0 评论 0

卷着的草席

文章 0 评论 0

£冰雨忧蓝°

文章 0 评论 0

我还不会笑

文章 0 评论 0

Unbroken

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文