Org文学编程扩展

21 阅读7分钟

Org文学编程扩展

核心及一些特性的实现。

链接展开

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‌​‍‌​​​​‍‌​‍‌​​‍​‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍​‍‍​‍​​‌‍‌‌‌‍​‌‍‍‌‌​‌​​​‍‌‌‍‌​‌‍‌‍​‌​‌‌​‌​‍‌‍‍‌‌‍‍‌‍‌​‍​​​‍​​​‍‍‌‍‌‌‌‍​‌​​‌‍​‌‌‍‍‌‍‌​​‍​​​‍‍​‌‍‌​‍‌‍‌​‍‌‌‍‌‌‌‍‍‌‌​‌​‍‌‍‌​‍‌‌‍‌‌‍‌‌‌‌​​‍​​‌‍‍‌‍‍‌‌‍​​‍​​​‍​‍‌‌‌‌​‍‌‌‌‌​‌​‍​‌‌‌​‌​‌​‌​​​‍​‍​‍​​‌‌​​‍‍‌​​‍‍结合 Org-Babel 提供额外的引用文本块的语义:

<​<@(org-link)>>

配合 ‘org-open-at-point-global’ 使用,方便打开代
码块中的 Org 链接。
​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​​‍‍

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‌​‍‌​​​​‍‌​‍‌​​‍​‌‍​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍"Org Noweb Extension

<<@([[id:D:org-noweb-expand-link]])>>"
(cond*
 ((bind* (s? (stringp link)) p))
 ((bind* (m (and s? (locate link))) body)
  (log "expanding %s" link)
  (setq conf (plist-put conf :link link)
        conf (plist-put conf :ref-props
                        ref-properties))
  (org-with-point-at m
    <<@([[id:P:pre:run-hook]])>>
    ;; 如果展开失败,设置 :failed 及
    ;; :err-msg, 并将 body 置为空串。
    (setq body (expand conf))))
 ((bind* (_ (and s? (null m))))
  (plist-put conf :failed t)
  (plist-put conf :err-msg
             (format "failed to expand %s"
                     link))
  (setq body ""))
 (body
  (dolist (p (post-process))
    (setq body (funcall p body conf)))

  ;; display warning
  (when (plist-get conf :failed)
    (display-warning
     'org-noweb
     (plist-get conf :err-msg)
     :warning))
  body)
 <<@([[id:B:org-noweb-expand-link:cmd-set]])>>)​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

Locate

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‌​‍‌​​‌​​​​‍‌​​‌​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(lambda (link &optional signal-errors)
  (let ((inhibit-message t)
        (message-log-max nil)
        (org-link-search-must-match-exact-headline t)
        (org-link-frame-setup
         `((file . find-file-other-window)
           ,@org-link-frame-setup))
        (marker (make-marker)))
    (condition-case msg
        (save-window-excursion
          ;; 这个 guard 实际只对当前 buffer 有效,
          ;; 无法处理 open-link 跑到别的 buffer 的
          ;; 情况。但考虑到有时我们会 open 当前
          ;; buffer 中的 link, 为了防止因为可见性引
          ;; 发的链接查找失败,我们还是在这里加上这
          ;; 个 gaurd, 当然,也许还有更好的实现方法,
          ;; 但到时再说。
          (org-with-wide-buffer
           (if (not (stringp link))
               (org-link-open link t)
             (org-link-open-from-string link t))
           (set-marker marker (point))))
      (error
       (when signal-errors
         (signal (car msg) (cdr msg)))))))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

Expand

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‌​‍‌​​‍​‌‍​‍‌​​‌​​​​‍‌​​​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍;; conf 输入输出
;; 输入: :expanders.
;; 输出: :failed, :err-msg.
"展开位于 current point 的 Org element."
(!let ((link (plist-get conf :link))
       ele type expand)
(cond
 ;; 展开文件
 ((with-temp-buffer
    (org-mode)
    (save-excursion (insert link))
    (setq link (org-element-link-parser))
    (and
     (equal (org-element-property :type link)
            "file")
     (null (org-element-property
            :search-option link))))
  (substring-no-properties (buffer-string)))

 ;; 展开 Org 元素
 ((ignore
   (setq ele (org-element-at-point)
         type (org-element-type ele)
         expand (expander type))))
 ((and expand (expand ele conf)))

 ;; 未定义
 (t
  (plist-put conf :failed t)
  (plist-put
   conf :err-msg
   (format "%s unsupported target %S %S"
           link (current-buffer) (point)))
  "")))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

日志

log

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‌​‍‌​​‍​​​​‍‌​​‌​‌‌​‍‌​‌‌​​‍​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(lambda (log-target)
  (lambda (fmt &rest args)
    (when debug-on-error
      (let* ((ts (format-time-string
                  "[%Y-%m-%d %H:%M:%S.%3N]"))
             (buf (get-buffer-create
                   log-target)))
        (with-current-buffer buf
          (goto-char (point-max)))
        (princ
         (concat
          (mapconcat
           (lambda (line) (concat ts line))
           (string-split
            (apply #'format fmt args) "\n")
           "\n")
          "\n")
         buf)))))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

logging

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌​‍‌​​​​​​​‍‌​​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(!def log
 <<@([[id:log]])>>)
(!def log (log " log:org-noweb"))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

logging default

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‍​‍‌​​​​‍‌​‍‌​​‌​‌​‍‌​​​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(!def log 'message)​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

Org Babel接口

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‌​‍‌​​‍​‌‍​‍‌​​‌​​​​‍‌​​​​‍​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍(with-temp-buffer
  (org-mode)
  (message "register org babel function: @")
  (let* ((vars `((level (ignore))))
         (ha (mapconcat
              (lambda (v)
                (format
                 ":var %s=%s" (car v) (cadr v)))
              vars " "))
         (a (mapconcat
             (lambda (v)
               (format
                ":%s %s" (car v) (car v)))
             vars " ")))
    (insert
     "#+name: @\n"
     "#+begin_src emacs-lisp "
     ":var link=\"\" " ha "\n"
     "(org-noweb-expand-link "
     "(format \"%s\" link) " a ")\n"
     "#+end_src")
    (org-babel-lob-ingest)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

代码块展开

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‍​‍‌​​​​​​‍‌​​‌​​​‍‌​‌​​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍;; conf 输入输出
;; 输入: :link, :ref-props.
;; 输出: :lang, :failed, :err-msg.
(cond*
 ((bind* (link (plist-get conf :link))
         (ref-props (plist-get conf :ref-props))
         (info (org-babel-get-src-block-info))
         (params (nth 2 info))))
 ;; 如果代码块参数 `:expand' 存在且其 sexp 求值
 ;; 为 nil 或 "no",
 ((and
   (assq :expand params)
   (member (alist-get :expand params)
           '(nil "no")))
  ;; 表明该代码块在当前环境下拒绝展开。
  (plist-put conf :failed t)
  (plist-put conf :err-msg
             (format "ignore %s." link))
  "")
 (t
  (dolist (p ref-props)
    (plist-put conf p (alist-get p params)))
  (plist-put conf :lang (nth 0 info))
  (org-babel-expand-noweb-references info)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

动态块展开

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‍​‍‌​​‌​​​​‍‌​​​‍‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​​‍‍;; conf 输入输出
;; 输入: :link.
;; 输出: :block-name, :failed, :err-msg.
(cond*
 ((bind* (link (plist-get conf :link))
         (ref-props (plist-get conf :ref-props))
         (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 (org-element-property
                :arguments ele))
         (args (format "'(%s)" args))
         (args (eval (read args)))
         (block-name
          (org-element-property
           :block-name ele))))
 ;; 如果块参数 `: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))
  "")
 ((ignore
   (dolist (p ref-props)
     (plist-put conf p
                (eval (plist-get args p))))
   (plist-put conf :block-name block-name)
   (setq ele (buffer-substring beg end)
         ele (string-trim ele))))
 ((and
   (plist-member args :noweb)
   (member (eval (plist-get args :noweb))
           '(t "yes")))
  ;; 不在 temp buffer 里执行,保持执行上下文。
  (replace-regexp-in-string
   "^\0\\(#[+]\\(begin\\|end\\)_src\\)" "\\1"
   (org-babel-expand-noweb-references
    (with-temp-buffer
      (org-mode)
      (save-excursion
        (insert
         "#+begin_src C :noweb yes\n"
         (replace-regexp-in-string
          "\\(^#[+]\\(begin\\|end\\)_src\\)"
          "\0\\1" ele)
         "\n#+end_src"))
      (org-babel-get-src-block-info)))))
 (t (substring-no-properties ele)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

Elisp文档格式化

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‌​‍‌​​​​‌​‍‌​‌‌​‌‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍​‍‍​‍​​‌‍‌‌‌‍​‌‍‍‌‌​‌​​​‍‌‌‍‌​‌‍‌‍​‌​‌‌​‌​‍‌‍‍‌‌‍‍‌‍‌​‍​​​‍​​​‍‍‌‍‌‌‌‍​‌​​‌‍​‌‌‍‍‌‍‌​​‍​​​‍‍​‌‍‌​‍‌‍‌​‍‌‌‍‌‌‌‍‍‌‌​‌​‍‌‍‌​‍‌‌‍‌‌‍‌‌‌‌​​‍​​‌‍‍‌‍‍‌‌‍​​‍​​​‍​‍‌‌‌‌​‍‌‌‌‌​‌​‍​‌‌‌​‌​‌​‌​​​‍​‍​‍​​‌‌​​‍‍‌​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​​‍‍将文本格式化为 elisp docstring. 转义文本中的特殊字
符,并移除文本在根节点的缩进。

使用:

将文本置于 Org Dynamic Block elisp-docstring 中。
见 Info node ‘(org)Dynamic Blocks’.

-- 特殊字符转义

将“"”转义“\"”,“\”转义“\\”,此外,在 elisp
docstring 中,字符“`”,“'”,“"”需用“\=”转义。
见 Info node ‘(elisp)Text Quoting Style’.

:escape "no" 时不进行转义。

-- 缩进移除

如片段展开的最终结果嵌套在另外的片段中,且含缩进,
移除其缩进。

考虑如下代码块:

a:
(progn
    <​<​b>>)

b:
(def remove-indent
  "Remove indent.
<​<​c>>")

c:
移除文本在根节点的缩进。

正常情况下,展开 a 得:

(progn
    (def remove-indent
      "Remove indent
    移除文本在根节点的缩进。"))

受 a 缩进影响,作为 b 的 docstring, c 每行都被填
充了缩进用的空白字符。移除缩进后,可得如下结果:

(progn
    (def remove-indent
      "Remove indent
移除文本在根节点的缩进。"))
​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​​‍‍

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‌​‍‌​​​​‍​​‍‌​​‍​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍"Format emacs lisp docstring.

<<@([[id:D:format-elisp-docstring]])>>"
;; 依赖: elisp-doc.
(let* ((name '("elisp-docstring"
               "elisp" "elispdocstring"))
       (mark "org-noweb-expand-link:")
       (indent "^[ \t]*")
       (mark (concat mark (car name))))
  (cond
   ;; 无 body 时当 hook 用;
   ((null body)
    (remove-hook 'org-babel-tangle-body-hook
                 elisp-doc)
    (goto-char (point-min))
    (while (re-search-forward
            (concat indent mark) nil t)
      (replace-match ""))
    (goto-char (point-min))
    (while (re-search-forward mark nil t)
      (replace-match "")))
   ;; 有 body 时转义特殊字符,并往所有行行首加 mark.
   ((and
     (member (plist-get conf :block-name) name)
     (not (plist-get conf :failed)))
    (add-hook 'org-babel-tangle-body-hook
              elisp-doc)
    ;; (setf body (help--docstring-quote body))
    (cond
     ((equal (plist-get conf :escape) "no")
      (setf body (string-replace
                  "\"" "\\\"" body)))
     (t
      (with-temp-buffer
        (save-excursion (insert body))
        (while (re-search-forward "[`'\"\\]"
                                  nil t)
          (pcase (match-string 0)
            ("`" (replace-match "\\\\=`" nil t))
            ("'" (replace-match "\\\\='" nil t))
            ("\"" (replace-match "\\\"" nil t))
            ((and "\\" (guard (looking-at-p "=")))
             (replace-match "\\\\=\\\\" nil t))
            ("\\" (replace-match "\\\\" nil t))))
        (setq body (buffer-string)))))
    (string-join
     (mapcar
      (lambda (line) (concat mark line))
      (string-split body "\n"))
     "\n"))
   (t body)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

条件Tangle

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‌​‍‌​​​​‍‌​‍‌​​‍​‍‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍​‍‍​‍​​‌‍‌‌‌‍​‌‍‍‌‌​‌​​​‍‌‌‍‌​‌‍‌‍​‌​‌‌​‌​‍‌‍‍‌‌‍‍‌‍‌​‍​​​‍​​​‍‍‌‍‌‌‌‍​‌​​‌‍​‌‌‍‍‌‍‌​​‍​​​‍‍​‌‍‌​‍‌‍‌​‍‌‌‍‌‌‌‍‍‌‌​‌​‍‌‍‌​‍‌‌‍‌‌‍‌‌‌‌​​‍​​‌‍‍‌‍‍‌‌‍​​‍​​​‍​‍‌‌‌‌​‍‌‌‌‌​‌​‍​‌‌‌​‌​‌​‌​​​‍​‍​‍​​‌‌​​‍‍‌​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​​‍​‍‌‍‍‌‍​‍​‍​​‍‍条件展开。

通过 :expand Header Argument 指定代码块是否在其
被引用处展开. :expand 为 nil 或 "no" 时不展开
该代码块.

考虑不同平台的键绑定配置:

#+name: android
#+header: :expand (eq system-type 'android)
#+begin_src emacs-lisp :eval no
(defkey [c i] #'open-init.el)
#+end_src

#+name: window
#+header: :expand (eq system-type 'windows-nt)
#+begin_src emacs-lisp :eval no
(defkey [C-c i] #'open-init.el)
#+end_src

#+begin_src emacs-lisp :eval no
(keymap-setting platform
  <<@([[id:ID::window]])>>
  <<@([[id:ID::android]])>>)
#+end_src

Window 上的展开为:

#+begin_src emacs-lisp :eval no
(keymap-setting platform
  (defkey [C-c i] #'open-init.el)
  )
#+end_src

Android 上的展开为(自动删除展开为空的行):

#+begin_src emacs-lisp :eval no
(keymap-setting platform
  (defkey [c i] #'open-init.el))
#+end_src
​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​​‍‍

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‌​‍‌​​​​‍​​‍‌​​‌​‌‌​‍‌​​​​‍‌​‍‌​​‍​‍​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍"Conditional Tangle.

<<@([[id:D:conditional-tangle]])>>"
(cond
 ;; process body
 (body
  (cond
   ((and
     (plist-get conf :failed)
     (backtrace-frame 0 'org-babel-tangle))
    (add-hook 'org-babel-tangle-body-hook
              cond-tangle)
    (let* ((msg (plist-get conf :err-msg))
           (msg (or msg "_"))
           (zw-spc (char-to-string ?\u200B)))
      ;; 用 org-noweb-expand-link 及零宽空格界
      ;; 定非法字符串。
      (concat zw-spc
              "org-noweb-expand-link " msg
              zw-spc)))
   (body)))
 ;; org-babel-tangle-body-hook
 (t
  (save-excursion
    (let* ((zw-spc (char-to-string ?\u200B))
           (re (concat
                zw-spc
                "org-noweb-expand-link .*"
                zw-spc))
           ;; if remove trailing space
           (re (concat re "[ \t]*"))
           (re (format
                "^\\(.*?\\)\\(%s\\)\\(\n?\\)"
                re)))
      (save-match-data
        (goto-char (point-min))
        (while (re-search-forward re nil t)
          (if (and (length=
                    (save-match-data
                      (string-trim
                       (match-string 1)))
                    0)
                   (length= (match-string 3)
                            1))
              (replace-match "")
            (replace-match "" nil nil nil 2)))))
    (remove-hook 'org-babel-tangle-body-hook
                 cond-tangle))))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

2.n 版本

TARGET

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​‌​‌‌​‍‌​​‌​​​​‍‌​​‍​‌‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌​‍‌‍‌‌‌​‌‌‌‌‍​‌‌​‌​​‍​​‌‍‍‌‍‌‍‍‌‍‌‌​‍​​​‍‍‌‍​‌‍‌‌‌‍​‌‍‍‌‌‍​‌‍​‌‌‍​​‍​​‌‌​​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍;;; ! org noweb -*- lexical-binding: t; -*-

(!let (org-noweb-expand-link
       (:apply (! apply macro) !def org
               ref-props expanders expand-link
               post-processes))
:collect-symbol: !def (!(indent 1)!)

(!def org (! org)) (!def log (![...]))
(org def 'noweb (! package nil :def '!def))
(!def !def
 (!"s &optional v"
  `(setf ,s (org noweb def ',s ,v))))
<<@([[id:F:logging]])>>

;;;; core

(!def ref-props (! package nil))
(ref-props def 'all
 ;; 这里复用 ref-props 的 symbol-value
 (progn
   (setf (symbol-value ref-props) nil)
   (![](symbol-value ref-props))))
(ref-props def 'add
 (![p] (push p (symbol-value ref-props))))

(!def expanders (! package nil))
(expanders def 'get
 (![type] (eval `(,expanders ,type) t)))
<<@([[id:F:pre]])>>

(!def post-processes (! package nil))
(post-processes def 'all
 (![] (cdr (! export all post-processes))))

(!def expand
 (!let ((expander (expanders get)))
  (![conf]
   <<@([[id:expand]])>>)))

(!def locate
 <<@([[id:locate]])>>)

;;;; expand-link

(!def expand-link
 (! package
  (!let ((post-process (post-processes all))
         <<@([[id:E:pre]])>>
         (ref-properties (symbol-value ref-props)))
   (![link &rest conf]
    <<@([[id:org-noweb-expand-link]])>>))))
(expand-link def 'locate locate)
)

;;;; register

(!def 'org-noweb-expand-link(! org noweb expand-link))
<<@([[id:P:register-@]])>>

;;;; features

<<@([[id:FEAT]])>>

;;;; documentation

<<@([[id:DOC]])>>​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

FEAT

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​‌​‌‌​‍‌​​‌​‌‍​‍‌​​‌​‌‍​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍<<@([[id:F:expand-src-block]])>>
<<@([[id:F:expand-dynamic-block]])>>
<<@([[id:F:format-elisp-docstring]])>>
<<@([[id:F:conditional-tangle]])>>​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

expand-src-block

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​‌​‌‌​‍‌​​‌​‌‍​‍‌​​​​​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍(!let ((!def (! org noweb expanders def)))
(!def 'src-block
 (![ele conf]
  <<@([[id:expand-src-block]])>>)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

expand-dynamic-block

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​‌​‌‌​‍‌​​‌​‌‍​‍‌​​​​‌​​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍(!let ((!def (! org noweb expanders def)))
(!def 'dynamic-block
 (![ele conf]
  <<@([[id:expand-dynamic-block]])>>)))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

conditional-tangle

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​‌​‌‌​‍‌​​‌​‌‍​‍‌​​​​‌‌​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍(!let ((!def (! org noweb post-processes def)))
(!def 'cond-tangle
 (!let ((cond-tangle (!def 'cond-tangle)))
  (![&optional body conf]
   <<@([[id:conditional-tangle]])>>))))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​

format-elisp-docstring

​‍​​‍‍‌‍‍‌‍​‌‌‍‌‌‍‌‌​‍‍​‍​​​​‍​​​​​‍​‌‍​‍‌​​​​​‌​‍‌​​‌​‌‌​‍‌​​‌​‌‍​‍‌​​​​‌‍​​‍‍​‍​​‍‍‌‍​‍‌‍‌‌‌‍‌‌‍‍‌‌‍‍‌‌‌​‌​‍‌‍​​‍​​‌‍‌‌‌‍‌‌‍​‌‌‍​‌​​‍‌‌‍​‌‍‍‌‌​‌​​​‍​​​‍‍‌‍‌‌‌‌‍‌‍​‌‌‍​​‍​​‌‍‍‌‍​‍​​​‍‍‌‍‍‌‍‌‌‌‍‌‌‌‍​‍​‍​​‌‍‍‌‍​‍‌‌‍‌‌‌‍​‌​​‌‍‌​‍‌‌​​​‍‍(!let ((!def (! org noweb post-processes def)))
(!def 'elisp-doc
 (!let ((elisp-doc (!def 'elisp-doc))(org (! org))
        (:apply org ! apply macro))
  (defun org-dblock-write:elisp-docstring (p)
    (insert (string-trim (plist-get p :content))))
  (org noweb ref-props add :escape)
  (![&optional body conf]
   <<@([[id:format-elisp-docstring]])>>))))​​‍‍​‍​​‍‍‌‍‌‌‌‍‍‌‍‌​‌‌‌​‌​‍‌‍​