LoginSignup
2
2

More than 5 years have passed since last update.

Emacs Customize Notes - Swap Lines

Posted at

プログラミングをしていると、やっぱりこの行はこっちだなと動かしたくなることが多々ある。場合によっては、ある区間をまとめてごそっと動かしたいことも然り。一時期使っていたSublime Textでは、 <ctr>+<shift>+<up>/<down> でおなじみの行の移動であるが、emacsではそれがない、もしくは結構使いづらかったのでカスタマイズした。
elispには、現在カーソルがある行をその前の行とスワップする関数transpose-linesがある。 (この表現はあんまり正しくない気がする。自分でもこの関数の挙動がよくわかっていない) しかし、この関数はカーソルが存在する1行に対してしか操作することができないので、region(マーカとカーソルの間にある領域。一般的なエディタでいう"選択"である)に含まれる複数行をまとめて動かすときは、その行を切り取ってから上もしくは下に移動し、貼り付けるということを行った。目指すは、Sublime Textのように、<shift>で複数行を選択して、<ctr>+<shift>+<up>/<down> でごっそり動かすあれだ。 (ちなみに、ここでは感覚的に行の移動と表現しているが、実際に行っていることは行のスワップであり、ググるときにはそっちで探したほうがヒットしやすいと思う)

以下にコードを示す。なお、コードは https://groups.google.com/forum/#!msg/gnu.emacs.help/dd2R_UV0LVQ/F06ihLb7hKcJ を参考にさせていただいた。

;;; Move selected lines up and down
(defun move-lines (arg)
  "Move several lines together up and down. 
    You can designate the amount of shift by arg. This arg can be less than 0.
    If a region is active, move the region up and down. Otherwise only move
    the line the cursor is in. 
    Somehow, a region which is active before evaluating this function  will be 
    deactivated if you don't use the key bindings to evaluate this function.
    If you want to wrap this function, make sure to set  the variable
    deactivate-mark nil in the wrapper. "  
  (cond
   ((and mark-active transient-mark-mode)
    ;; If region is active
    (if (< (point) (mark))
    (exchange-point-and-mark))
    (end-of-line)
    (exchange-point-and-mark)
    (beginning-of-line)
    (let ((text (delete-and-extract-region (point) (mark))))
      (kill-line)
      (forward-line arg)
      (set-mark (point))
      (insert text)
      (newline)
      (forward-char -1)
      (exchange-point-and-mark))
    (setf deactivate-mark nil))
   (t 
    ;; If region is inactive
    (beginning-of-line)
    (when (or (> arg 0) (not (bobp)))
      (forward-line)
      (when (or (< arg 0) (not (eobp)))
    (transpose-lines arg))
      (forward-line -1)))))

(defun move-lines-down ()
  " Move selected lines down, using move-lines. "
  (interactive)
  (move-lines 1)
  (setf deactivate-mark nil))

(defun move-lines-up ()
  " Move selected lines up, using move-lines.  "
  (interactive)
  (move-lines -1)
  (setf deactivate-mark nil))

(global-set-key (kbd "C-S-<up>") #'move-lines-up)
(global-set-key (kbd "C-S-<down>") #'move-lines-down)

キーバインドはsublime textのそれと同じにした。いい感じである。sublime textのキーバインドはとても直感的で使いやすいと個人的には思っている。それに引き換え、emacsのは未だに迷ってしまう。なぜだろうか?

ここで、2点だけ自分がハマった点を記しておく。
a. キーバインドで実行しないと、移動した後にregionがinactiveになっていない。(つまり、連続で動かせない)
b. move-lines-upとmove-lines-downの中でもちゃんと、deactive-mark をnilに設定しないと、移動後にregionがinactiveになってしまう。

aに関してはまだ理由がわかっていない。
bに関しては、elispの関数は基本的にregionに対する操作をしたら、そのregionをinactiveにしてしまう。だから、関数を評価する前にハイライトされていたregionは、評価後にハイライトが消えてしまっている。そこで、deactivate-markにnilをセットすることによって、regionに対して操作をした後でもそれがinactiveにされないように指定することができる。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2