关于增强 org-babel header args 输入的构想

从变量 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 为例:

  1. :playonline 按默认定义的优先级选择 ground
  2. :playonline rextester 直接指定 rextester 这个 ground
  3. :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 编辑点。

1赞