昨日、org-elementをthing-at-pointで扱えるようにしてみた けど、
今日はその続きで、M-↑/M-↓を行指定でやれるようにしてみます。

というのは、これらのコマンドは現在の要素(subtree/段落など)を
1つずつ上下に移動するものであり、多くの要素を繰り返し入れ替えるには
面倒だからです。

行指定ということならば、ace-jump-modeの汎用版ともいえるavy.el(レビュー)
使うことにしました。

画面内の特定の場所を指定するならばぜひとも使いたいインターフェースです。

avy--line 関数は目的行を尋ね、位置とウィンドウのコンスセルを返します。

これを使って M-x avy-move-line-thing を作りました。

移動元・移動先を指定し、行・region・要素(org-mode)を移動するコマンドです。

文章を書く前にアイデアを集め、それを順番に並べ変えるときに
このコマンドを愛用しています。

(require 'org-element)
(require 'avy)

(defun org-element-at-point-bounds ()
  (let ((e (org-element-at-point)))
    (cons (org-element-property :begin e) (org-element-property :end e))))
(put 'org-element 'bounds-of-thing-at-point 'org-element-at-point-bounds)

(defun avy-move-line-thing ()
  "移動元・移動先を指定し、行・region・要素(org-mode)を移動する。"
  (interactive)
  (let* ((avy-handler-function
          (lambda (char)
            (if (eq char ?\C-m)
                (throw 'done (list (point-at-bol)))
              (avy-handler-default char))))
         (from (unless (region-active-p)
                 (message "From: ")
                 (car (avy--line))))
         (to (progn (message "To: ")
                    (set-marker (make-marker) (car (avy--line)))))
         bot content)
    (setq bot (if (region-active-p)
                  (cons (region-beginning) (region-end))
                (goto-char from)
                (bounds-of-thing-at-point
                 (if (derived-mode-p 'org-mode) 'org-element 'line))))
    (setq content (buffer-substring (car bot) (cdr bot)))
    (delete-region (car bot) (cdr bot))
    (goto-char to)
    (insert content)))

以下のorg文書を例にすると

* 1
one
* 2
two
* 3
three

20150611152159.png
Fig1: dを指定し

20150611152209.png
Fig2: aを指定すれば

20150611152214.png
Fig3: 入れ替わる

org-mode以外でも

20150611152256.png
Fig4: s→aを指定すると

20150611152301.png
Fig5: 入れ替わる

20150611152548.png
Fig6: regionを指定し、aを指定すれば

20150611152554.png
Fig7: 入れ替わる!

本日もお読みいただき、ありがとうございました。参考になれば嬉しいです。