从变量 org-babel-common-header-args-w-values
的定义可以看到,header 所允许的参数形式有限:
- 空:没有参数
-
:any
:手动输入任意内容 - list:可供选择的符号列表
文字描述可能有点抽象。可以创建一个 orb sorc block 头部输入 :<TAB>
或执行 M-x org-babel-insert-header-arg
体会一下这几种形式的不同。
我想增强的是 list,并把空值纳进来(目前如果列表中存在 nil,则输入的是 nil 字面,不表示空),然后增加函数形式的值。这么做是我想支持以下几种参数形式,以 ob-playonline 为例:
-
:playonline
按默认定义的优先级选择 ground -
:playonline rextester
直接指定 rextester 这个 ground -
:playonline '(rextester "c:gcc" "-Wall -std=gnu99 -O2 -o a.out source_file.c")
指定 ground (rextester),语言版本或便一起版本(c:gcc),以及编译参数。
因为 playonline 里的 ground 定义是用户自己决定的,所必要不能提前设置到 org-babel-common-header-args-w-values
,必须在添加 header 的前一刻才知道,这一步可以通过函数 org-babel-merge-params
来实现动态扩展。
剩下的问题是 org-babel-common-header-args-w-values
无法同时表示上面 1.2.3 这几种可能性,因此它的定义需要做些调整,以下是我的想法:
;; org-babel-common-header-args-w-values 每个元素的形式
;; (keyword (arg0 arg1 arg2 ...)) 保持不变,而是在其中添加两种类型:
;; 1. nil ,表示真的nil,而不是插入 nil 字面
;; 2. 函数,用来完成参数的插入
;; 其余的 symbol 则按照原先的设计,原样插入。
(playonline
(nil ;; 空值
go ;; 符号
rust ;; (如果需要动态生成,则应通过 org-babel-merge-params 合并)
rextester ;;
mycompiler ;;
labstack ;;
;; (占位提示符 . 函数)
;; 在函数里面可以做很多是,例如生成动态内容,还可以调用 snippet。
(ground-with-args
. (lambda ()
(yas-expand-snippet
"(${1:$$(yas-choose-value '(\"go\" \"rust\"))} \"${2:lang:spec}\" \"${3:compile-args}\")")))))
以下一个极简的 header args 插入过程,用来验证可行性:
(let* ((al (mapcar
(lambda (it)
(cond ((consp it) it)
((symbolp it) (cons it it))))
'(nil
go
rust
rextester
mycompiler
labstack
(ground-with-args
. (lambda ()
(yas-expand-snippet
"(${1:$$(yas-choose-value '(\"go\" \"rust\"))} \"${2:lang:spec}\" \"${3:compile-args}\")"))))))
(arg
(intern
(completing-read
"Arg: "
(mapcar (lambda (it) (symbol-name (car it))) al)))))
(pcase (cdr (assoc arg al))
(`,(and (pred functionp) fun) (funcall fun))
(`,(and (pred and) val) (insert (format "%s" val)))))
看起来是可行的。
另,yasnippet + helm 体验不太好,看不到光标在什么位置。如果展开的内容稍复杂,很容易看花眼。这种情况可以考虑我前面发的帖子《 用 company 实现 completing-read 》提供的方案,光标始终跟随 snippet 编辑点。