In Emacs org-mode, is there a way to automatically refile highlighted text under an org heading? i.e. to cut the highlighted text and automatically paste it under the org-heading of my choice?
You could call it org-refile-region
. Similar to org-refile
, but to refile not the entire subtree, but only the highlighted region under any heading in the current document.
UPDATE:
Ideally this functionality would be independent of the org-agenda files used by org-refile, so as to avoid displaying irrelevant headings as possible targets.
Currently this is doable by doing: 1. select text 2. cut 3. other-window 4. navigate to desired target heading 5. paste text 6. other window
The proposed new function would make this much more efficient: 1. select text 2. org-refile-region 3. choose target
The most useful form of this would allow you to choose a target from among any currently open documents. My use case involves selecting text from one buffer and refiling it from among org-headings in another buffer, i.e. moving text from a source document displayed in one window and refiling to targets within the hierarchy of a target document displayed in another window, like so:
If you are using emacs 24.1 or later, you can try
(setq org-refile-active-region-within-subtree t)
which will almost do what you want, but turn the line in which you have highlighted text (the emacs term is "active region") into a headline.
If you want to move the text you have highlighted to another heading, you have to extend org-mode. Fortunately, org provides the tools you need. Here is an example:
(defvar org-refile-region-format "\n%s\n")
(defvar org-refile-region-position 'top
"Where to refile a region. Use 'bottom to refile at the
end of the subtree. ")
(defun org-refile-region (beg end copy)
"Refile the active region.
If no region is active, refile the current paragraph.
With prefix arg C-u, copy region instad of killing it."
(interactive "r\nP")
;; mark paragraph if no region is set
(unless (use-region-p)
(setq beg (save-excursion
(backward-paragraph)
(skip-chars-forward "\n\t ")
(point))
end (save-excursion
(forward-paragraph)
(skip-chars-backward "\n\t ")
(point))))
(let* ((target (save-excursion (org-refile-get-location)))
(file (nth 1 target))
(pos (nth 3 target))
(text (buffer-substring-no-properties beg end)))
(unless copy (kill-region beg end))
(deactivate-mark)
(with-current-buffer (find-file-noselect file)
(save-excursion
(goto-char pos)
(if (eql org-refile-region-position 'bottom)
(org-end-of-subtree)
(org-end-of-meta-data-and-drawers))
(insert (format org-refile-region-format text))))))
We use org-refile-get-location
to apply the org refiling mechanism and extract the file and the location. Then we go to that location and insert the copied text. Two variables added for convenience.
org-refile-targets
lets you control which files to consider, e.g.:
nil ;; only the current file
'((org-agenda-files :maxlevel . 2)) ;; all agenda files, 1st/2nd level
'((org-files-list :maxlevel . 4)) ;; all agenda and all open files
'((my-org-files-list :maxlevel . 4)) ;; all files returned by `my-org-files-list'
To restrict refiling to the currently open org buffers, define a function
(defun my-org-files-list ()
(mapcar (lambda (buffer)
(buffer-file-name buffer))
(org-buffer-list 'files t)))
And then either
(setq org-refile-targets '((my-org-files-list :maxlevel . 4)))
or use
M-x customize-option <ret> org-refile-targets
select "Function" from the "value menu", and type my-org-files-list