In Emacs org-mode, how to refile highlighted text under an org heading?
Asked Answered
H

1

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:

Horsehide answered 12/8, 2014 at 4:43 Comment(2)
Are you using something like *word* to highlight text, or are you using another means? If other, please specify how you achieve the highlighting that relates to your question.Tinned
Shift-select, i.e. S-<right> and S-<left>.Horsehide
V
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

Verdellverderer answered 12/8, 2014 at 10:57 Comment(12)
Great! Is there any way to make it offer me headings in the current document only?Horsehide
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."Verdellverderer
What about drawing from any open file? I added an update to explain what I mean.Horsehide
Yes, this can be done as well. See org-refile-targets.Verdellverderer
Is there a way to set org-refile-targets to consider not just the current buffer, but all currently open buffers?Horsehide
I have updated my answer. org-refile-region now uses org-refile-targets plus I added instructions on how to customize the latter.Verdellverderer
And yes, this only works for org buffers. You cannot refile to buffers that are not in org mode.Verdellverderer
Hmm I'm getting bad refiling target description (:max-level . 4).Horsehide
The keyword is called :maxlevel, not :max-level. Sorry about the typo.Verdellverderer
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?Horsehide
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?Horsehide
@Horsehide Replace org-end-of-meta-data-and-drawers with org-end-of-meta-data tTalton

© 2022 - 2024 — McMap. All rights reserved.