Org执行器

7 阅读4分钟

文档

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‍​‍‌​​‍​​‌​‍‌​‌‌​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍​‍‍​‍​​‌‍‌‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍​​​‍‍‌‍‌‌‌​‌‍​‌‍​‌‌​​‌‍‌‌​‍​​​‍​‍‌‍‍‌‍​‍​‍​‍​​​‍‍‌‍‌‌‌‍​‌​​‌‍​‌‌‍‍‌‍‌​​‍​​​‍‍​‌‍‍‌‍‌‌​​‍‍​‌‍‌​‍‌‍‌​‍‌‌‍‌‌‌‍‍‌‌​‌​‍‌‍‌​‍‌‌‍‌‌‍‌‌‌‌​​‍​​‌‍‍‌‍‍‌‌‍​​‍​​​‍​‍‌‌‌‌​‍‌‌‌‌​‌​‍​‌​‌‌‌‌‌‌‌​‌‌‌​‌​‌​‌​​​‍​‍​‍​​‌‌​​‍‍‌​‍‍‌​​‍‍不同的 Org元素 具有不同的执行器,‘org-exec’ 将根
据所指元素的类型,将控制流和 ARGS 参数转交给其他
执行器。

LINK 可以是一条 Org链接,还可以是用于触发命令的
symbol.

LINK 是 Org链接 时,支持的类型包括:

string, org-element-link, vector. 即:

(org-exec "[[id:target]]" ...)

(org-exec (org-element-link-parser) ...)

(org-exec [[id:target]] ...)

当 LINK 为 nil 时,执行当前位置的元素。

LINK 是 symbol 时,执行 ‘org-exec’ 内部命令。
支持的命令包括:

'set-executor

(org-exec 'set-executor
 ORG-ELEMENT-TYPE FUNCTION)

FUNCTION 将会被 (apply FUNCTION ARGS).

当 EXECUTOR 为 nil 时, ‘org-exec’ 会使用内置
的执行器执行 `link' 所指元素,非 nil 时用
EXECUTOR 执行。调用方式为:

(apply executor args)
​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​​‍‍

特性

FEAT

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‌​‍‌​​‍​‍‌​‍‌​​‍​​‌​‍‌​​​​‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍<<@([[id:F:exec-default]])>>
<<@([[id:F:exec-src-block]])>>
<<@([[id:F:exec-subtree]])>>​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

exec-src-block

D:exec-src-block

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​‌​​‍​‍‌​​‌​‌​‍‌​​‌​‌​‍‌​​‌​‌​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍​‍‍​‍​​‌‍‌‌‌‍​‌‍‍‌‌​‌​​​​‍‍KEYWORD 对应 Header Arguments
SYMBOL 对应 Variables.

如: (exec-src-block :eval "yes" 'a 10).
​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​​‍‍

exec-src-block

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‌​‍‌​​‍​​​‍‌​​‌​​​‍‌​‌‌​​‍​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍;; args 应为一对一对的参数, 如果入参长度为奇数
;; 我们直接 drop 掉最后一个参数。
(when (= (% (length args) 2) 1)
  (setq args (seq-subseq args 0 -1)))
(let* ((params "") header-args variables)
  (dolist (kv (seq-partition args 2))
    (cond
     ((keywordp (car kv))
      (push (format
             "%S %S" (car kv) (cadr kv))
            header-args))
     ((push (format
             "%S=%S" (car kv)
             (cond
              ((symbolp (cadr kv))
               `(identity ',(cadr kv)))
              (t (cadr kv))))
            variables))))
  (when header-args
    (setq params
          (concat params
                  (string-join
                   (nreverse header-args)
                   " "))))
  (when variables
    (setq params
          (concat params
                  " :var "
                  (string-join
                   (nreverse variables)
                   " "))))
  (setq params (string-trim params))
  (message "[org-exec]params: %S" params)
  (org-babel-execute-src-block
   nil nil
   ;; 因为 `org-babel-execute-src-block' 开头
   ;; 调用 `org-babel-get-src-block-info' 时
   ;; 就已经 eval 了 header args, 所以我们在这里
   ;; 也直接对输入 header args 求值。
   (org-babel-parse-header-arguments params)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

exec-subtree

exec-subtree

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​​​‍‌​‍‌​​‌​‌‍​‍‌​‌​​‌‍​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‌‌‍‌‌‌​​​‍‍;; 对于 headline 类型的 target, 我们提供额外的执
;; 行配置:
;;
;; ORG-EXE-DEPENDS 及 ORG-EXE-EXECUTOR.
;;
;; 前者会作为 当前target 的依赖先于 target执行;
;; 后者作为 当前target 的执行器,如有提供,决定
;; 如何执行 target.
(![&rest args]
 ;; 如 target 有依赖,先执行其依赖。
 (let* ((title (org-entry-get nil "ITEM"))
        (depends
         (org-entry-get nil "ORG-EXE-DEPENDS"))
        (depends
         (when depends
           (org-element-parse-secondary-string
            depends '(link))))
        ;; 如 target 有特定的 executor, 使用该
        ;; executor 执行 target, 否则用
        ;; `org-babel-execute-subtree'.
        (executor
         (org-entry-get
          nil "ORG-EXE-EXECUTOR"))
        (executor
         (if executor (read executor)))
        (executor
         (if (commandp executor)
             executor
           #'org-babel-execute-subtree)))
   (save-excursion
     (catch (org exec break)
       (mapcar exec depends)))

   (message "Executing subtree %s by %S..."
            title executor)
   (call-interactively executor)
   (message "Subtree %s evaluation complete."
            title)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

exec-default

exec-default

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‌​‍‌​​‍​​​‍‌​​‌​​​‍‌​‌‌​‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(let ((buf (current-buffer)))
  (message "Executing buffer %S..." buf)
  (org-babel-execute-buffer)
  (message
   "Buffer %S evaluation complete." buf))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

版本

1.0

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‍​‍‌​​​​‌‌​‍‌​​‍​​​​‍‌​​​​‌‌​​‍‍​‍​​‍‍‌‍‍​‌‍‌‌‌‍​‌‌‍‌​‌‍‌‌‌​‍​‍‍​‍​​​‍‍‌‌​‌‍​‌‌‍‍‌‍‌‌‍​‌‍‌‌​‍​​‌‍​‍‌‍‌​‍‌‍‌​‍‌‍‌​‍‌‍‌​‍‌‌‍‌‌‌‍​‌‍‌‌‌‍​​‍‍​​‌​‍‍​​​​‍‍‌‍‌‌‌‍​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​‍​​​‍‍‌​‍‌‍‌‌‌​‌‌‌‌‍​‌‌​‌​​‍​​‌‍‍‌‍‌‍‍‌‍‌‌​‍​​​‍‍‌‍​‌‍‌‌‌‍​‌‍‍‌‌‍​‌‍​‌‌‍​​‍​​‌‌​​​‍‍;;; org-exec -*- lexical-binding: t; -*-

(defalias 'org-exec
  (lambda (&optional link executor &rest args)
    (interactive)
    (declare (indent defun))
    (cond*
     ((bind* target))
     ((or (null link) (stringp link)
          (org-element-type-p link '(link)))
      (setq target (when link (locate link))
            link (when (org-element-type-p link '(link))
                   (org-element-property
                    :raw-link link)))
      (when (and link (null target))
        (error "Link %s invalid." link))
      (org-with-point-at target
        (cond
         (executor (apply executor args))
         ((org-element-type-p
           (org-element-at-point) '(src-block))
          <<2025-07-23-13-52>>)
         (t
          <<2025-07-23-13-57>>)))))))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

2.2

TARGET

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​​​‍‌​‍‌​​‌​‌‍​‍‌​​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍;;; org-exec  -*- lexical-binding: t; -*-
(!let ((exec (make-symbol "exec"))
       (org (! org)) !def)
(!def exec
 <<@([[id:org-exec]])>>)
(setf !def (! org def))
(!def 'exec (! package exec :fapply (!let apply)))
(setf !def (org exec def))
(!def 'cmd
 (![&optional link executor &rest args]
  (interactive)
  (pcase link
    ('break (org exec break t))
    ('locate (org exec locate))
    (_ (apply exec link executor args)))))
<<@([[id:PRIVATE]])>>
(!def 'locate
 <<@([[id:locate]])>>)
(!def 'break
 (![a] (throw (org exec break) a)))
(!def 'defexec
 (![type executor]
  (org exec types type executor)))
(setf !def (org exec defexec))
<<@([[id:FEAT]])>>
(! defdoc 'org-exec
"Org 执行器,执行 `link' 所指元素。

<<@([[id:D:org-exec]])>>

详见不同类型的执行器: "
 (! button (org exec types 'all)))
(setf !def '!def)
(!def 'org-exec (org exec cmd))
)​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

org-exec

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​​​‍‌​‍‌​​‌​‌‍​‍‌​‌​​‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(![&optional link executor &rest args]
 ;; 我们也支持 (org-exec [[link]] ...)
 ;; 不过,link 的有效性受限于 elisp reader,
 ;; 使用时需注意。
 (when (vectorp link)
   (setq link (format "%S" link)))
 (!let* ((locate (org exec locate))
         (target (when link (locate link)))
         (link (when (org-element-type-p
                      link '(link))
                 (org-element-property
                  :raw-link link)))
         (types (org exec types))
         (pre (org exec pre-hooks))
         (post (org exec post-hooks))
         etype exec ret)
  (when (and link (null target))
    (error "Link %s invalid." link))
  (org-with-point-at target
    (setf etype (org-element-type
                 (org-element-at-point))
          exec (or executor (types etype)
                   (types 'default)))
    (unless exec
      (error "No executor found for link %s"
             link))
    (when link
      (message "Executing link: %s..." link))
    (run-hooks pre)
    (unwind-protect
        (save-excursion
          (save-window-excursion
            (setq ret (apply exec args))))
      (run-hooks post))
    ret)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

PRIVATE

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​​​‍‌​‍‌​​‌​‌‍​‍‌​‌​​‍​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(!def 'pre-hooks nil)
(!def 'post-hooks nil)
(!def 'types
 (let ((D nil))
   (![type &optional executor]
    (cond
     ((eq type 'all) (mapcar 'cdr D))
     (executor
      (setf (alist-get type D)
            (defalias (make-symbol
                       (format "%S" type))
              (indirect-function executor))))
     ((alist-get type D))))))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

F:src-block

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​​​‍‌​‍‌​​‌​‌‍​‍‌​‌​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍(!def 'src-block
 (![&rest args]
  "码块执行器。

<<@([[id:D:exec-src-block]])>>"
  <<@([[id:exec-src-block]])>>))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

F:exec-default

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​​​‍‌​‍‌​​‌​‌‍​‍‌​‌​​‌​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍(!def 'default
 (![&rest args]
  <<@([[id:exec-default]])>>))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

F:exec-substree

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​​​‍‌​‍‌​​‌​‌‍​‍‌​‌​​‌‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍(!def 'headline
 <<@([[id:exec-subtree]])>>)​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​