4

The task: I have a large-ish file, from which I need to pick certain chunks of text and move them into a certain place so they go one after another, in the order in which I pick them. More specifically, it's an Org-mode file, and the chunks are headings (possibly with subtrees). The target file may be the same, or it might not.

Question: is there a package/mode or something, that would let me specify the target place and then add chunks of text there while I roam around and point out the pieces to cut?

My attempts at finding such a thing turned up nothing so far, however the search terms are rather generic so the results are messy. I've thrown together code that inserts multiple items from the kill ring—I kill the chunks then insert them; but this doesn't seem quite clean, especially if I ever manage to save the file with the cuts made and have Emacs or the machine crash before inserting the text. Plus, the workflow doesn't feel right.

Double kudos if the solution handles Org-mode headings: e.g. adjusts the inserted ones to the level of the ones in the target place. Triple kudos if I can bind it in Evil right away, i.e. it works with text objects.

0
3

I believe you're looking for org-refile (bound to C-c C-w). By default, org-refile will only consider the current file. To refile into another file add the target file with your desired headings to org-refile-targets:

;; Add the current buffer as possible refile target ; only consider up to level 2 as target.
(add-to-list 'org-refile-targets `(,buffer-file-name :maxlevel . 2))

You can also use a regular expression to refile to a specific heading:

;; Use path/to/file as refile target, and ONLY headings that fit "List of great quotes"
(setq org-refile-targets '(("path/to/file" :regexp . "List of great quotes")))

org-refile-targets's customization interface (M-x customize-option RET org-refile-targets RET) might be a little bit easier than lisp-based modification, though.

Even better, store the location yourself and then use the stored location repeatedly:

(defvar aaa/refile-location nil 
  "Location used for `aaa/refile-to-saved-location'.
Use `aaa/save-refile-location' to set it in an org-mode buffer.")

(defun aaa/save-refile-location ()
  "Save current position for later refiling via `aaa/refile-to-saved-location'."
  (interactive)
  (when (not (equal major-mode 'org-mode))
    (user-error "Refile location can only get saved in org-mode buffers!"))
  (let ((heading (org-entry-get nil "ITEM"))
        (file    (buffer-file-name))
        (pos     (point)))
    (setq aaa/refile-location (list heading file nil pos))
    (message "Saved \"%s\" in \"%s\"" heading file)))

(defun aaa/refile-to-saved-location ()
  "Move the current item to the saved location."
  (interactive)
  (if aaa/refile-location
      (org-refile nil nil aaa/refile-location)
    (user-error "Refile location is unset!")))

(global-set-key (kbd "<f6>") 'aaa/refile-to-saved-location)
(global-set-key (kbd "C-<f6>") 'aaa/save-refile-location)

This will enable you to use C-F6 to store your target location and F6 afterwards to move items (with their subtree).

4
  • The idea of using org-refile for this is interesting, hasn't occurred to me since the requirements are a bit different. It does seem like a clean solution with the tool made just for that task. However, dealing with the target selection dialog every time is not very enticing. I'll see if org-refile can take the target place as an argument, otherwise I guess I'll need to make a command that replaces the targets list with just one heading. (And maybe add the ability to use the heading's ID in org-refile-targets.) – aaa Nov 12 '20 at 15:26
  • "However, dealing with the target selection dialog every time is not very enticing." You can use C-u C-u M-x org-refile to reuse the last location. But I'll try to come up with something easier/better. – Zeta Nov 12 '20 at 16:06
  • @aaa There. That seems a lot more user-friendly. However, I'm not sure about best-practice. – Zeta Nov 12 '20 at 17:05
  • Excellent, this is pretty much exactly what I had in mind. org-refile really was the missing link—it even works with multiple selected headings, out of the box. By now, only a solution that somehow works equally well for simple text would beat this, but I doubt that it's feasible, both practically and semantically. Now that I looked closer at the workings of org-refile, I might whip me up some additions, like the choice of recent locations, filing above/below the selected heading, etc. Neat stuff. – aaa Nov 12 '20 at 20:59
4

A basic option is to use append-next-kill.

Rather than killing each region with just C-w (or similar), you instead use C-M-wC-w (i.e. type C-M-w immediately prior to whichever kill command you're using) to append the new kill to the most recent kill ring item.

Do that for every region in sequence, and then a single C-y will insert the whole thing.

C-M-w runs the command append-next-kill (found in global-map)

Cause following command, if it kills, to add to previous kill.
If the next command kills forward from point, the kill is
appended to the previous killed text.  If the command kills
backward, the kill is prepended.  Kill commands that act on the
region, such as ‘kill-region’, are regarded as killing forward if
point is after mark, and killing backward if point is before
mark.

If the next command is not a kill command, ‘append-next-kill’ has
no effect.
1
  • Alas that seems very similar to what I've already slapped together—only I need to do the hotkeys on every piece of text instead of selecting the pieces from the kill-ring once at the target. – aaa Nov 12 '20 at 15:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.