我采用文学式编程的方法来管理我的 Emacs 配置。与将配置文件分散到多个 .el
文件中的传统方法相比,这种方法使我能够更便捷地对代码进行注释说明和重新排列组合。
然而,我们不可能始终保持所有配置处于激活状态,否则 Emacs 的运行速度会显著下降。因此,某些代码应当在需要时才执行。
如果我将所有代码分散存储在不同的文件中,可以通过 require
的方式导入这些代码。但如果将这些配置代码放在 Org 文件的 src-block
中,某些 block 可能仅包含一个函数。在这种情况下,为每个 block 都编写一个 provide
语句,似乎有些过度。
另一方面,如果每次需要执行某段代码时,都必须找到配置文件中的对应代码块并手动执行,这显然也相当繁琐。
为此,我编写了几个函数来帮助管理我的配置文件。这些函数的通用性较强,稍作修改后,应该还可以用于管理其他代码库。
0. 配置文件地址
首先需要设定一个变量用来存储配置文件的地址。注意这是一个 org 文件
(defvar src/default-file-path "/path/to/.doom.d/manual.org"
"Default file path for source code blocks.")
1. 主函数:选择并执行代码块
当需要执行某一部分代码的时候,就执行这个函数。
按下 C-u 再执行这个函数就可以选择 org 文件,从而执行不是 src/default-file-path 中设置的文件。
值得注意的是,只会列出有 ~#+name:~ 属性的代码块。
(defun src/choose-and-execute-block (&optional file-path)
(interactive
(list (if current-prefix-arg
(src--choose-folder-or-org-file default-directory)
src/default-file-path)))
(if (file-directory-p file-path)
(src/choose-and-execute-block (src--choose-folder-or-org-file file-path))
(let ((block-name (src/choose-named-src-block file-path)))
(src/execute-code-block-with-name block-name))))
以下这些函数,列出一个文件下所有的 src-block, 根据一个函数的名称在一个文件中将其找到并执行,以及选择一个 src-block 是为了上面的 src/choose-and-execute-block 能正确执行而写的辅助函数。
2. 选择文件夹或 .org 文件
(defun src--choose-folder-or-org-file (dir)
"Prompt the user to choose a folder or .org file under DIR."
(let ((file (read-file-name "Choose a folder or .org file: " dir nil t
nil
(lambda (name)
(or (file-directory-p name)
(and (file-regular-p name)
(string= (file-name-extension name) "org")))))))
(if (file-directory-p file)
(message "You chose a directory: %s" file)
(message "You chose an .org file: %s" file))
file))
3. 执行指定名称的代码块
(defun src/execute-code-block-with-name (code-block-name &optional file-path)
"Execute the CODE-BLOCK-NAME in the specified FILE-PATH.
If FILE-PATH is not provided, defaults to `src/default-file-path`."
(interactive
(list (read-string "Code block name: ")
(if current-prefix-arg (read-file-name "File: " nil src/default-file-path))))
(with-temp-buffer
(insert-file-contents (or file-path src/default-file-path))
(org-mode) ; Ensure Org mode is active for proper parsing
(org-babel-goto-named-src-block code-block-name)
(org-babel-execute-src-block)))
4. 列出文件中所有命名的代码块
(defun src/list-named-code-blocks (&optional file-path)
"Return a list of all named source code blocks in the specified FILE-PATH.
If FILE-PATH is not provided, defaults to `src/default-file-path`."
(with-temp-buffer
(insert-file-contents (or file-path src/default-file-path))
(org-mode) ; Ensure Org mode is active for proper parsing
(org-element-map (org-element-parse-buffer) 'src-block
(lambda (src-block)
(let ((name (org-element-property :name src-block)))
(when name
name))))))
5. 选择命名的代码块
(defun src/choose-named-src-block (&optional file-path)
"Choose a named src block from the specified FILE-PATH in the mini-buffer.
If called without C-u, defaults to `src/default-file-path`.
If called with C-u, prompts for the file path."
(let* ((named-blocks (src/list-named-code-blocks file-path))
(chosen-block (completing-read "Choose a named src block: " named-blocks)))
chosen-block))