7

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:

Community
  • 1
  • 1
incandescentman
  • 6,168
  • 3
  • 46
  • 86

1 Answers1

5

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

olaf b
  • 606
  • 4
  • 6
  • Great! Is there any way to make it offer me headings in the current document only? – incandescentman Aug 13 '14 at 21:24
  • 1
    Yes. The possible targets are selected according to `org-refile-targets`. If this variable is `nil` (which it is by default), only the current buffer is considered. I've modified my example, because you specifically asked for "any heading in the current document." – olaf b Aug 13 '14 at 23:25
  • What about drawing from any open file? I added an update to explain what I mean. – incandescentman Aug 15 '14 at 14:31
  • Yes, this can be done as well. See `org-refile-targets`. – olaf b Aug 16 '14 at 11:47
  • Is there a way to set `org-refile-targets` to consider not just the current buffer, but all currently open buffers? – incandescentman Aug 17 '14 at 19:43
  • 2
    I have updated my answer. `org-refile-region` now uses `org-refile-targets` plus I added instructions on how to customize the latter. – olaf b Aug 17 '14 at 20:35
  • And yes, this only works for org buffers. You cannot refile to buffers that are not in org mode. – olaf b Aug 17 '14 at 20:44
  • Hmm I'm getting `bad refiling target description (:max-level . 4)`. – incandescentman Aug 18 '14 at 04:59
  • 1
    The keyword is called :maxlevel, not :max-level. Sorry about the typo. – olaf b Aug 18 '14 at 07:54
  • 2
    That works perfectly! Is there a way to direct the function to use the `ido` interface (good) or the `helm` interface (even better) for the choice of headings? – incandescentman Aug 20 '14 at 22:43
  • It appears the function `org-refile-region` defined above no longer works as of org-mode version 9.2. Rather than refiling the highlighted region of text to the target heading I select, org simply places it at the bottom of the file. Does anyone know a fix? – incandescentman Apr 29 '19 at 05:19
  • @incandescentman Replace `org-end-of-meta-data-and-drawers` with `org-end-of-meta-data t` – jjk Mar 13 '22 at 07:04