如何让 Org-mode 捕获模板中的多个条目自动完成?

如何让 Org-mode 捕获模板中的多个条目自动完成?

我广泛使用 Emacs org-mode 和捕获模板,但有一件事仍然让我恼火:我无法自动完成提示中的多个条目,标签除外。考虑以下捕获模板:

("m" "meeting" entry
   (file+datetree "~/Dropbox/org/meetings.org")
   "* %^{Description} %^G
   Time: %T
   %?
   ** Meeting info
   Place: %^{Place|Headquarters|Customer}
   Participants: %^{Participants|me|Tony|bossman}
   "
   :clock-in t)

对于标签 (%^G),多个条目的自动完成功能非常有效,但对于参与者,自动完成功能仅适用于单个条目,无论我用 : 或 , 分隔它们

我想自动填充每个条目的参与者,并用 或 分隔:

如果它为参与者构建了一个动态自动完成列表就更好了,这样我就不必在捕获模板中指定所有可能的参与者。

答案1

Org-mode 的捕获模板包含一个%用于执行任意 elisp 的 -escape,我们可以使用它来做几乎任何事情。下面介绍如何使用它来获得您想要的东西:

首先,这里有两个函数可以构建动态补全表。第一个函数只查找指定正则表达式的所有匹配项。第二个函数使用第一个函数来专门获取您要查找的项目类型。

(defun my/dynamic-completion-table-regexp (regexp &optional group buffer)
  "Return alist containing all matches for REGEXP in BUFFER.

The returned alist contains the matches as keys, and each key is
associated with a nil value.

If the optional parameter GROUP (an integer) is supplied, only
that part matching the corresponding parenthetical subexpression
is taken as the key.

If BUFFER is omitted, it defaults to the current buffer.  Note
that the entire buffer is scanned, regardless of narrowing."
  (let ((buffer (or buffer (current-buffer)))
        (group  (or group 0))
        table match)
    (with-current-buffer buffer
      (save-excursion
        (save-restriction
          (widen)
          (goto-char (point-min))
          (while (re-search-forward regexp nil :noerror)
            (setq match (substring-no-properties (match-string group)))
            (add-to-list 'table (list match))))))
    table))

(defun my/dynamic-completion-table-by-field (field &optional delims buffer)
  "Return alist containing all entries for FIELD in BUFFER.

Look in BUFFER for all lines of the form \"FIELD: TEXT\",
possibly with leading or trailing whitespace.  If DELIMS is
omitted, consider TEXT as a single item; otherwise, it should be
a regexp matching the delimiters between the items inside TEXT.
Empty items are discarded, and whitespace surrounding the
delimiters is removed.

The returned alist contains one key for each item found; all keys
are associated with the value nil.

If BUFFER is omitted, it defaults to the current buffer.  Note
that the entire buffer is scanned, regardless of narrowing."
  (require 'cl-lib)                     ; For `cl-mapcan'
  (let ((table (my/dynamic-completion-table-regexp
                (concat "^\\s-*"
                        (regexp-quote field)
                        ": \\(.+\\)\\s-*$")
                1 buffer)))
    (cl-mapcan (lambda (x)
                 (mapcar #'list
                         (split-string (car x) delims :omit-nulls "\\s-+")))
               table)))

现在我们需要一个函数,它可以从 minibuffer 读取数据,知道如何使用这些函数,并使用 Org-mode 的标签补全功能来获得您想要的每个项目的补全。它还需要知道捕获过程,这样它就可以扫描目标缓冲区而不是捕获缓冲区。

(defun my/org-capture-read-multiple (field completion &optional delims)
  "Read one or more items for FIELD in capture template.

The COMPLETION parameter can be any sort of completion
table or function valid for the second parameter of
`completing-read'.  It may also be the keyword :dynamic, which
indicates that `my/dynamic-completion-table-by-field' should be
used to generate a completion table dynamically.

The optional parameter DELIMS is ignored except with the :dynamic
option, in which case it is passed to
`my/dynamic-completion-table-by-field' as the parameter of the
same name.

If this function is invoked from within an Org-mode capture
process, the current buffer will be the target buffer for the
capture attempt, not the buffer used to fill the capture
template."
  (let* ((buffer (if (equal (buffer-name) "*Capture*")
                     (org-capture-get :buffer)
                   (current-buffer)))
         (completion (if (eq completion :dynamic)
                         (my/dynamic-completion-table-by-field
                          field delims buffer)
                       completion)))
    (with-current-buffer buffer
      (let ((org-last-tags-completion-table completion)
            org-completion-use-ido)
        (org-completing-read-no-i (format "%s: " field)
                                  #'org-tags-completion-function)))))

要使用这些,您需要修改模板。例如,您可以将上面模板中的“参与者”行替换为

Participants: %(my/org-capture-read-multiple \"Participants\" :dynamic \"[,:]\")

相关内容