Ctrl+up/downでカーソルと一緒に領域を動かす(修正版)

Ctrl+up/downでカーソルと一緒に領域を動かす
というのをちょっと前に書いたけど、いろいろ良くないところがあったのでちょっと修正。

(save-excursion &rest BODY)

こいつを使うと現在の位置、マーク、カレントバッファを記憶し、
BODY部の処理を行ったあとに元に戻してくれます。便利。
知らなかったので(let ((p (point ))) (....(goto-char p)))とかやってた。

(exchange-point-and-mark &optional ARG)

そうだ、マーク位置に移動するにはこれが手っ取り早かった。

(push-mark &optional LOCATION NOMSG ACTIVATE)

(mark)や(set-mark POS)はあまり使わないほうが良いようだ。
(set-mark POS)ではその前のマーク情報は失われてしまう。
(push-mark)はマークリングを使うのでその問題は解消される。

(rotatef PLACE....)

Common Lisp の本眺めてて見つけた。
だから(require 'cl)しないと使えないかも。
(rotatef A B C)とやると、A->B、B->C、C->A、のように値がローテートされる。


2つをswapするのも、ひとつをtmpに保存して。。。とかやらなくて良いわけだ。
(rotatef A B)でオッケー


というわけで修正版↓

(defun line-number-at-mark ()
  (save-excursion
    (exchange-point-and-mark)
    (line-number-at-pos)))

(defun push-mark-at-line (num)
  (let ((p (point)))
    (goto-line num)
    (push-mark)
    (goto-char p)))

(defun exchange-lines-up ()
  (interactive)
  (cond ((= 1 (line-number-at-pos)) nil)
        (t (transpose-lines 1)
           (previous-logical-line 2))))

(defun exchange-lines-down ()
  (interactive)
  (next-logical-line 1)
  (transpose-lines 1)
  (previous-logical-line 1))

(defun move-region-up ()
  (interactive)
  (let ((lp (line-number-at-pos))
        (lm (line-number-at-mark)))
    (when (< lm lp)
      (rotatef lm lp))
    (cond ((= lp 1) nil)
          (t (goto-line (1- lp))
             (dotimes (i (- lm lp -1))
               (exchange-lines-down))
             (goto-line (1- lp))
             (push-mark-at-line (1- lm))))))

(defun move-region-down ()
  (interactive)
  (let ((lp (line-number-at-pos))
        (lm (line-number-at-mark)))
    (when (> lm lp)
      (rotatef lm lp))
    (goto-line (1+ lp))
    (dotimes (i (- lp lm -1))
      (exchange-lines-up))
    (goto-line (1+ lp))
    (push-mark-at-line (1+ lm))))

(global-set-key [(meta up)] 'exchange-lines-up)
(global-set-key [(meta down)] 'exchange-lines-down)
(global-set-key [(control up)] 'move-region-up)
(global-set-key [(control down)] 'move-region-down)