続・Emacsで連番を挿入する方法
久々のEmacsネタ。
歴史は繰り返すものです。 今はブラウザでなんでもできてしまう時代、かつてのEmacsを彷彿とさせます。 だが、ブラウザがEmacsに決定的に劣っているのは、『速さが足りない』ことだと思ってます。
フレーム、フラッシュなどマウス前提のIFなので、素早い動作が難しいです。 対して、Emacsはテキスト入力のスペシャリスト。 テキスト入力が関わる場面があれば、そこに必ずEmacsの活躍があります。 dabbrev、yasnippetなど、入力に不可欠なツールが無数に存在するからです。
さてさて…
2年前に書いた記事 では、外部コマンドやloopを使ってEmacsで連番を入力する方法を紹介しました。 しかし、めんどいですね。 なので、Emacs Lispで書きました。
M-x duplicate-this-line-forward 直前の行をコピーする
直前の行と同じ行を作成することはよくあります。 C-a C-k C-k C-y C-y ...と操作すると思いますが、キルリングが置き換わってしまう問題があります。 そこで、このコード。
(defun duplicate-this-line-forward (n) "Duplicates the line point is on. The point is next line. With prefix arg, duplicate current line this many times." (interactive "p") (when (eq (point-at-eol)(point-max)) (save-excursion (end-of-line) (insert "\n"))) (save-excursion (beginning-of-line) (dotimes (i n) (insert-buffer-substring (current-buffer) (point-at-bol)(1+ (point-at-eol))))))
僕は F5 に割り当てています。 連打することでたくさんコピーできます。 数引数を指定すると、その回数だけコピーします。
M-x seq 数字のみが違う同じ行を作成する
「第1回」から「第10回」までの行を作成したいとします。 そのとき、「第1回」という行をM-x duplicate-this-line-forwardでコピーして数字だけを書き換えるのは面倒です。 そこで、このコード。
(defun count-string-matches (regexp string) (with-temp-buffer (insert string) (count-matches regexp (point-min) (point-max)))) (defun seq (format-string from to) "Insert sequences with FORMAT-STRING. FORMAT-STRING is like `format', but it can have multiple %-sequences." (interactive (list (read-string "Input sequence Format: ") (read-number "From: " 1) (read-number "To: "))) (save-excursion (loop for i from from to to do (insert (apply 'format format-string (make-list (count-string-matches "%[^%]" format-string) i)) "\n"))) (end-of-line))
M-x seqでは、数字部分をformat関数の書式指定にしておくことでそこが数字に置き換わります。 このコマンドは、書式指定→開始番号→終了番号の順で聞いてきます。 この例では、「M-x seq RET 第%d回 RET RET 10 RET」と操作します。
%dは複数個持つことができます。 「M-x seq RET [%02d]第%d回 RET RET 3 RET」で以下の行が挿入されます。
[01]第1回
[02]第2回
[03]第3回
M-x number-rectangle 連番の長方形を作成する
最後に、連番の長方形を作成するコマンドを作りました。
(eval-when-compile (require 'cl)) (defun number-rectangle (start end format-string from) "Delete (don't save) text in the region-rectangle, then number it." (interactive (list (region-beginning) (region-end) (read-string "Number rectangle: " (if (looking-back "^ *") "%d. " "%d")) (read-number "From: " 1))) (save-excursion (goto-char start) (setq start (point-marker)) (goto-char end) (setq end (point-marker)) (delete-rectangle start end) (goto-char start) (loop with column = (current-column) while (<= (point) end) for i from from do (insert (format format-string i)) (forward-line 1) (move-to-column column))) (goto-char start)) (global-set-key "\C-xrN" 'number-rectangle)
C-x r t (string-rectangle)は、同じ文字列を複数行に書く(行頭の「*」や引用符「> 」など)のに便利です。 その連番バージョンです。
使い方は連番を入れたい行の行頭をリージョンとして指定し、C-x r Nと操作します。 すると、連番の書式、開始番号を聞いてきます。 書式は「%d. 」ならば「1. 」、「2. 」〜という感じです。
あるいは、「第1回」の行をM-x duplicate-this-line-forwardでコピーして、数字部分を長方形リージョンに指定する使い方もあります。 そのときは書式は「%d」と指定します。
なお、このコマンドと同じような働きをするコマンドM-x rectangle-number-linesが開発版Emacsに存在し、C-x r Nに割り当ててあります。
両者の違いは、M-x number-rectangleの方が賢くて使いやすいことです。
- カーソル位置によってデフォルトの書式が異なる
- 行頭(インデントがあってもよい)のときは「%d. 」
- 行頭以外では「%d」
- M-x number-rectangleはC-uをつけないと細かな指定ができない
ただのリサーチ不足ではありますが、この程度のコマンドはすぐ書けるので。 結果的には好みのコマンドが出来上がりました。
まとめ
今回はM-x seq、M-x number-rectangleの2種類の連番コマンドを定義しました。 それに付随してM-x duplicate-this-line-forwardも定義しました。
では、よりよいEmacsライフを! お役に立てれば幸いだ。