org-bable
支持很多的语言,但每次在配置中修改需要加载的语言很麻烦。而且增加越来越多的语言到 org-babel-load-languages
,会造成启动 org-mode
非常慢,特别是在 Mac 和 Windows 系统。
比如,增加 C 语言的支持时,就会自动加载 cc-mode
,但我很多时候只是要打开 org-mode
编辑一下文档而已,并不需要这些编程语言。
不知道 org-mode 有没有这么一个选项,可以在执行 org-babel-execute-src-block
时再加载相应的语言?
目前我是通过下面的 advice 来实现这样的需求,请各位大佬指教:
(defun my/org-babel-execute-src-block (fn &rest ARG)
"Load language if needed"
(let* ((lang (org-element-property :language (org-element-at-point)))
(language (if (string-equal lang "cpp") "C" lang)))
(unless (cdr (assoc (intern language) org-babel-load-languages))
(add-to-list 'org-babel-load-languages (cons (intern language) t))
(org-babel-do-load-languages 'org-babel-load-languages org-babel-load-languages)))
(apply fn ARG))
(advice-add 'org-babel-execute-src-block :around #'my/org-babel-execute-src-block )
1 个赞
把对应的函数 autoload 就可以了
(use-package ob-emacs-lisp
:commands (org-babel-execute:elisp
org-babel-expand-body:elisp
org-babel-execute:emacs-lisp
org-babel-expand-body:emacs_lisp))
问题是在打开 org-mode 之前,我是不知道自己会用到那个语言的。
这样不是要对所有的 org-babel 都进行设置。
我自己会用的语言就那些,所以还好,本质上和你设置 org-babel-load-languages 一样的,只是长点
可以去提个 issue,按需加载是比较省心一点。
advice 没必要用 :around
,用 :before
就可以了。而且 info
参数已经包含所有信息,直接使用即可:
(defun my/org-babel-execute-src-block (&optional _arg info _params)
"Load language if needed"
(let* ((lang (nth 0 info))
(sym (if (member (downcase lang) '("c" "cpp" "c++")) 'C (intern lang)))
(backup-languages org-babel-load-languages)
(pair (assoc sym backup-languages)))
;; - `(LANG . nil)' 是有意义的,不宜覆盖,详见 `org-babel-do-load-languages'。
;; - 只加载当前语言,「按需」到底。
(unwind-protect
(org-babel-do-load-languages 'org-babel-load-languages (list (cons sym t)))
(setq-default org-babel-load-languages
(if pair
backup-languages
(append (list (cons sym t)) backup-languages))))))
(advice-add 'org-babel-execute-src-block :before #'my/org-babel-execute-src-block )
多谢大佬指正,我测试了你的方案,很棒! 
但是目前我们使用的这种 advice 的方案有个问题:
只要打开的buffer中包含了相应语言的 Code block,就会自动加载相应的语言。这个会造成打开这些buffer 时会很慢。
我觉得只有在用户执行 C-c , C-c
org-babel-execute-src-block
时才会加载会不会更好一些?
我对在邮件列表上提 issue 不怎么熟悉 
yqu212
8
doom Emacs的orgmode模块有lazy load语言的配置,可以解决你的这些需求。
org-babel-load-languages
在初始化的时候只存放 (LANG . nil)
,表示需禁止的语言。其它所有需要的语言都动态加载,加载成功后存入 org-babel-load-languages
:
(defun my/org-babel-execute-src-block (&optional _arg info _params)
"Load language if needed"
(let* ((lang (nth 0 info))
(sym (if (member (downcase lang) '("c" "cpp" "c++")) 'C (intern lang)))
(backup-languages org-babel-load-languages))
;; - (LANG . nil) 明确禁止的语言,不加载。
;; - (LANG . t) 已加载过的语言,不重复载。
(unless (assoc sym backup-languages)
(condition-case err
(progn
(org-babel-do-load-languages 'org-babel-load-languages (list (cons sym t)))
(setq-default org-babel-load-languages (append (list (cons sym t)) backup-languages)))
(file-missing
(setq-default org-babel-load-languages backup-languages)
err)))))
(advice-add 'org-babel-execute-src-block :before #'my/org-babel-execute-src-block )
EDIT: Add (condition-case ...)
5 个赞
这也是个办法 
目前这样用,效果已经很好了。
Doom 的方案我也试过了,效果是一样的。这样简洁一点的方案好。
org-mode 另一大耗时步骤是 (org-load-modules-maybe)
,在我这里它占了整个 org 近 90% 的消耗,而其中 ol-gnus
模块又占了九成中的九成。
这个问题在设置了 package-user-dir
并且 (package-initialize)
之后才会凸显:
$ emacs -Q -nw --eval \
"(add-hook 'emacs-startup-hook
(lambda ()
(view-echo-area-messages)
(setq package-user-dir \"~/.emacs.d/28.0.50/elpa\") ;; <---
(package-initialize)
(print (let ((t0 (current-time))) (org-mode) (- (float-time (current-time)) (float-time t0)))))))"
最简单的办法就是去掉耗时的模块,如果你需要用到它们就得另寻他法了:
(with-eval-after-load 'org
(setq org-modules (cl-set-difference org-modules '(ol-gnus ol-eww))))
这个问题,我之前也注意到了。我是直接在 early-init.el 里设置了:
(setq org-modules-loaded t)
这样在加载 org-mode 的时候就不会加载 org-modules
中的模块了吧?
这些模块我是一个都用不到 