【存档】Org Tangle 扩展


特性:动态 Block 展开

背景

当前, elisp docstring 可以通过 drawer 导入代码中,然 drawer 无类似 src-block 的 header-args, 而 dynamic block 有 arguments, 故新增此特性。至此,headline, src-block, dynamic-block 都可实现某种共通的 proerties 机制。

实现

#+name: 2025-08-17-16-48
#+begin_src emacs-lisp :eval no
(!let ((expand (make-symbol "expand-dynamic-block")))
 (!def expand
  (lambda (ele conf)
    ;; conf 输入输出
    ;; 输入: :link.
    ;; 输出: :block-name, :failed, :err-msg.
    (let* ((link (plist-get conf :link))
           (beg (org-element-contents-begin ele))
           (end (org-element-contents-end ele))
           ;; org-element parser 与 org.el 中
           ;; 的不一致,导致解析失败,所以有了这段
           ;; workground 代码。
           (org-element-dynamic-block-open-re
            org-dblock-start-re)
           (ele
            (save-excursion
              (save-match-data
                (re-search-forward
                 org-dblock-start-re nil t)
                (forward-line 0)
                (org-element-dynamic-block-parser
                 nil nil))))
           (args (eval
                  (read
                   (format
                    "'(%s)" (org-element-property
                             :arguments ele)))))
           (block-name
            (org-element-property :block-name ele)))
      (cond
       ;; 如果块参数 `:expand' 存在,且其 sexp 求值
       ;; 为 nil 或 "no",
       ((and
         (plist-member args :expand)
         (member (eval (plist-get args :expand))
                 '(nil "no")))
        ;; 表明该块在当前环境下拒绝展开。
        (plist-put conf :failed t)
        (plist-put conf :err-msg
                   (format "ignore %s." link))
        "")
       (t
        ;; FIXME: 等待一次重构。25/9/13
        (plist-put
         conf :escape (eval (plist-get args :escape)))
        (plist-put conf :block-name block-name)
        (setq ele (string-trim
                   (buffer-substring beg end)))
        (with-temp-buffer
          (org-mode)
          (save-excursion
            (insert
             "#+begin_src C :noweb yes\n"
             ele
             "\n#+end_src"))
          (org-babel-expand-noweb-references
           (org-babel-get-src-block-info))))))))
 (org-noweb-expand-link
  'set-expander 'dynamic-block expand
  'expand-dynamic-block))
#+end_src

新构建目标:

由于先前版本提供了新增特性的接口,于此在上版本尾部加入 动态 Block 展开 特性。若整体结构中的 entry 符号更名为 org-noweb-expand-link, 此特性——动态 Block 展开——才能纳入特性集中,但历史版本的变更涉及大重构,此贴暂不处理。

#+name: 2025-08-17-16-45
#+header: :tangle (org-sbe "2025-07-26-11-30" ":eval yes")
#+header: :eval (org-sbe "2025-07-26-11-30" ":eval yes" (ok \"yes\"))
#+begin_src emacs-lisp :noweb yes :results silent :lexical t
<<@([[id:org-noweb-expand-link::org-noweb-expand-link]])>>

<<@([[id:org-noweb-expand-link::feature:expand-dynamic-block]])>>
#+end_src

映射表:

#+name: 2025-08-17-16-43
#+begin_src emacs-lisp :eval no
(org-id-remap
 "map-table-base"
 "https://emacs-china.org/t/org-tangle/29663::2025-08-03-16-38")
#+end_src
#+name: 2025-08-17-16-42
#+header: :depend (org-sbe "2025-08-17-16-43" ":eval yes")
#+begin_src emacs-lisp :eval no
<<@([[id:map-table-base]])>>
nil nil
"org-noweb-expand-link"
"https://emacs-china.org/t/org-tangle/29663::2025-08-17-16-45"
"org-noweb-expand-link::feature:expand-dynamic-block"
"https://emacs-china.org/t/org-tangle/29663::2025-08-17-16-48"
#+end_src

构建入口:

#+name: 2025-08-17-16-44
#+header: :var tangle=(ignore) load=(ignore)
#+begin_src emacs-lisp :results silent :noweb yes :eval no
(org-id-remap 'reset)
(org-id-remap
 "build-script"
 "https://emacs-china.org/t/org-id-remap/29814::2025-08-03-11-27"
 "build-target"
 "https://emacs-china.org/t/org-tangle/29663::2025-08-17-16-45"
 "map-table"
 "https://emacs-china.org/t/org-tangle/29663::2025-08-17-16-42")
(org-id-remap t)
(org-exec "[[id:build-script]]" nil
  :eval "yes"
  'target "[[id:build-target]]"
  'map-table ''("[[id:map-table]]")
  'tangle (or tangle "~/org/org-noweb-expand-link.el")
  'load (or load "yes"))
#+end_src