| categories: | tags: | View Comments

I often want to rearrange org headlines. There are super convenient shortcuts for some things, like Alt-up/down to move up and down, and Alt-left/right to change levels, and shift variants of that when there are nested headlines. There is also refile for some use cases. The case these don't handle for me is when I have a headline at the bottom and I want to move it a distance. To cut down on key strokes, I usually kill the headline, jump to where I want, and paste it.

Check out the video to see this in action: https://www.youtube.com/watch?v=Vv-2888rpyc

Note: this code is a little more advanced than what is in the video; I added a numeric prefix to change the headline level.

(defun org-teleport (&optional arg)
With a prefix ARG move the headline to before the selected
is positive, move after, and if negative, move before."
(interactive "P")
(org-mark-subtree)
(kill-region (region-beginning) (region-end))
(avy-with avy-goto-line (avy--generic-jump "^\\*+" nil avy-style))
(cond
;; Move before  and change headline level
((and (numberp arg) (> 0 arg))
(save-excursion
(yank))
;; arg is what we want, second is what we have
;; if n is positive, we need to demote (increase level)
(let ((n (- (abs arg) (car (org-heading-components)))))
(cl-loop for i from 1 to (abs n)
do
(if (> 0 n)
(org-promote-subtree)
(org-demote-subtree)))))
;; Move after and change level
((and (numberp arg) (< 0 arg))
(org-mark-subtree)
(goto-char (region-end))
(when (eobp) (insert "\n"))
(save-excursion
(yank))
;; n is what we want and second is what we have
;; if n is positive, we need to demote
(let ((n (- (abs arg) (car (org-heading-components)))))
(cl-loop for i from 1 to (abs n)
do
(if (> 0 n) (org-promote-subtree)
(org-demote-subtree)))))

;; move to before selection
((equal arg '(4))
(save-excursion
(yank)))
;; move to after selection
(t
(org-mark-subtree)
(goto-char (region-end))
(when (eobp) (insert "\n"))
(save-excursion
(yank))))
(outline-hide-leaves))

org-teleport


Now we add some new speed commands to help us out. I think we should be able to mark subtrees ("@" is bound to this, but I like "m" better) and kill them with a key stroke, in addition to teleporting them. Since we figured out the nice way to jump to a headline up there, we bind that to "q" which isn't used so far, and maps to a similar concept in lispy. The lowercase "t" is already bound to changing the TODO state, so we use capital "T" for the speed key to teleport. Note it is possible to "compose" the same effect by typing "k" to kill a headline, then "q" to jump somewhere else (or navigate where you want, and then "C-y" to paste it at the new location. Or, "T".

(add-to-list 'org-speed-commands-user (cons "m" 'org-mark-subtree))
(add-to-list 'org-speed-commands-user (cons "k" (lambda ()
(org-mark-subtree)
(kill-region
(region-beginning)
(region-end)))))
(cons "q" (lambda ()
(avy-with avy-goto-line
(avy--generic-jump "^\\*+" nil avy-style)))))


done