这是我想出的解决方案。这会导致整个捕获过程发生在单个弹出帧中。
首先,几个辅助函数。
(defun my/get-frame-by-name (fname)
"If there is a frame with named FNAME, return it, else nil."
(require 'dash) ; For `-some'
(-some (lambda (frame)
(when (equal fname (frame-parameter frame 'name))
frame))
(frame-list)))
(defun my/display-buffer-in-named-frame (buffer alist)
"Display BUFFER in frame with specific name.
The name to use is the value associated with the 'named-frame key
in ALIST. If a frame with that name already exists, use it.
Otherwise, call `display-buffer-in-pop-up-frame' to create it.
If ALIST does not contain the key 'named-frame, use the name of BUFFER."
(let* ((fname (or (cdr (assq 'named-frame alist))
(buffer-name buffer)))
(frame (my/get-frame-by-name fname)))
(if frame
(window--display-buffer buffer
(frame-selected-window frame)
'reuse)
(display-buffer-pop-up-frame
buffer
(add-to-list 'alist `(pop-up-frame-parameters
(name . ,fname)))))))
接下来,安装临时通知的宏。如果你愿意,你可以很容易地内联它。
(defmacro my/with-advice (adlist &rest body)
"Execute BODY with temporary advice in ADLIST.
Each element of ADLIST should be a list of the form
(SYMBOL WHERE FUNCTION [PROPS])
suitable for passing to `advice-add'. The BODY is wrapped in an
`unwind-protect' form, so the advice will be removed even in the
event of an error or nonlocal exit."
(declare (debug ((&rest (&rest form)) body))
(indent 1))
`(progn
,@(mapcar (lambda (adform)
(cons 'advice-add adform))
adlist)
(unwind-protect (progn ,@body)
,@(mapcar (lambda (adform)
`(advice-remove ,(car adform) ,(nth 2 adform)))
adlist))))
这是主要功能。这个想法是暂时覆盖 Org-mode 正在做的所有我们不想要的事情。这包括 Dan 的回答中提到的 delete-other-windows 调用,以及对 org-switch-to-buffer-other-window 的两次调用,它明确阻止了弹出框。
(defun my/org-capture-in-popout-frame (&optional goto keys)
"As `org-capture', but do all work in a new frame.
This function by itself doesn't clean up the frame following
capture. To do that, add `my/org-capture-delete-capture-frame'
to `org-capture-after-finalize-hook'."
(interactive "P")
(if goto
(org-capture goto keys)
(let ((override '("\\*Org Select\\*\\|\\*Capture\\*\\|CAPTURE-.*"
my/display-buffer-in-named-frame
(named-frame . "Capture"))))
;; Force all relevant buffers to open in a specific capture frame.
(add-to-list 'display-buffer-alist override)
(my/with-advice
(;; Make Org-mode respect `display-buffer-alist'.
(#'org-switch-to-buffer-other-window :override #'pop-to-buffer)
;; And stop Org-mode from messing with our window configuration.
(#'delete-other-windows :override #'ignore))
(unwind-protect (condition-case err
(org-capture goto keys)
(error (my/org-capture-delete-capture-frame)
(signal (car err) (cdr err))))
(setq display-buffer-alist
(delete override display-buffer-alist)))))))
要在捕获完成后消除帧,请使用此功能:
(defun my/org-capture-delete-capture-frame ()
"Delete a frame named \"Capture\".
For use in `org-capture-after-finalize-hook' to clean up after
`my/org-capture-in-popout-frame'."
(let ((frame (my/get-frame-by-name "Capture")))
(when frame (delete-frame frame))))
my/capture-in-popout-frame 在提前中止的情况下已经调用了它(例如,在模板选择缓冲区中输入 q 或在捕获模板中输入 C-g 以响应提示)。对于正常完成(包括重新归档)或延迟中止(来自捕获模板的C-c C-k),您需要将其添加到org-capture-after-finalize-hook。
(add-hook 'org-capture-after-finalize-hook
#'my/org-capture-delete-capture-frame)
请注意,这些函数都不会修改对org-capture 的独立调用的工作方式(除非由于某种原因您有一个名为“Capture”的额外框架),因此如果您需要它,您仍然可以获得默认行为特定的捕获。
一个警告:这将不支持多个同时捕获进程。我个人不需要,但如果你需要,我认为添加该功能应该不会太难。