GNU EmacsのTips...
行番号をつける
(2003.03.28)

概要

 テキストに行番号をつける。

 といっても、Emacsの表示上行番号がつくのではなく、テキ スト内に行番号を埋め込むものです。

 これは実はずっと欲しかった機能で、探せばあったのかも知れないけ れど探すゆとりも気力もなく今日に至り、どうしても必要になったので 急いで書きました。

  M-x linum <RET>

と実行すると、

  1. Format string?:
  2. Start number?:
  3. Step?:

と聞いてきます。

  1. 一番目の入力は、挿入する行番号の書式を決定します。Emacs Lisp のformat関数の書式文字列を与えます。たとえば、
    • 数字部分3桁で空白づめ、区切り文字を":"にするなら "%3d: "
    • 数字部分4桁でゼロづめ、区切り文字を空白にするなら "%04d "
    • 数字部分3桁で左づめ、区切り文字を"|"にするなら "%-3d| "
    と入力します。
    最初のうちは行番号部分の幅だけ入力するようなことを考えていま したが、ちょっと面倒なのと、こちらの方が自由度が高いのでこの 方式にしてあります。
  2. 二番目の入力は、開始行番号を決定します。負の値でも平気です。
  3. 三番目の入力は行番号の増分を決定します。1より小さい値は 1に置き換えられます。

  C-u M-x linum <RET>

とすると、バッファ全体に行番号を挿入します。

 例によってエラーや例外の処理は甘ちゃんです。

動作確認

GNU Emacs 20.7.1

解説

 行頭に文字列を挿入するのは、

  1. 行の先頭に移動して(beginning-of-line)
  2. 文字列を挿入(insert)

でできます。行番号は変化するので整数として保持し、その都度 format関数で文字列化して挿入します。 formatの第一引数は書式文字列ですが、先にキーボード から入力した文字列をそのまま使います。

 プログラムとしては、

  1. 開始位置(リージョンの先頭)から始め、
  2. 現在位置が終了位置(リージョンの末尾)に達するまで、
  3. 各行の先頭に移動し、行番号文字列を挿入する。
  4. 行番号を加算し、次の行に移動する

というものになります。ここで、当初はリージョンの終わり数行に行番 号がつかないという問題がありました。どうやら行番号文字列を挿入し た分、リージョン末尾の値が実際とずれていることによるようでした。 これを解消するため、挿入した文字列の長さを終了位置に加算していま す。これはスマートな、あるいは根源的な解決法とは思えません。もっ と適切な方法があると思うのですが、いかんせん今手許にEmacs Lispの マニュアルがないので第一版はこれでよしとします。

 ひととおり出来上がってから悪戯っ気が出てきて、前置引数がある場 合(かつそれが1でない場合)、バッファ全体に行番号を挿入するよう にしました。これ自体は、開始位置と終了位置に、リージョンの先頭と 末尾でなくバッファの先頭と末尾を与えるようにすればいいだけです。 ただ、前置引数の取扱いに関してはまだまだ知見が足りず、ヘンなこと をしているかも知れません。

コード

  1: (defun linum (whole)
  2:   "insert line number at each line in region from START NUMVER by STEP."
  3:   (interactive "p")
  4:   ; (1以外の)前置引数があったら、wholeをtに
  5:   (if (not (numberp whole)) (setq whole 0))
  6:   (if (= whole 1) (setq whole nil)
  7:     (setq whole t))
  8: 
  9:   (let ((fmtstr)
 10: 	(number)
 11: 	(step)
 12: 	(insstr)
 13: 	(rb (region-beginning))
 14: 	(re (region-end))
 15: 	)
 16:     (setq fmtstr (read-input "Format string?: "))
 17:     (setq number (string-to-number (read-input "Start number?: ")))
 18:     (setq step (string-to-number (read-input "Step?: ")))
 19:     (if (< step 1) (setq step 1))
 20: 
 21:     ; wholeがtなら、バッファ全体を対象とする
 22:     ; そうでなければリージョンを対象とする
 23:     (if (eq whole t)
 24:  	(let () 
 25:  	  (setq rb (point-min))
 26:  	  (setq re (point-max)))
 27:       (let () 
 28:  	(setq rb (region-beginning))
 29:  	(setq re (region-end))))
 30: 
 31:     (save-excursion
 32:       (let ()
 33: 	(goto-char rb)
 34: 	(while (< (point) re)
 35: 	  (beginning-of-line)
 36: 	  (setq insstr (format fmtstr number))
 37: 	  (insert insstr)
 38: 	  (setq re (+ re (string-width insstr)))
 39: 	  (setq number (+ number step))
 40: 	  (forward-line 1)
 41: 	  )
 42: 	)
 43:       ))
 44:   )

補足

 テキストファイルを相手に行番号を振るのならAwkの一行野郎で片が つきますが、ファイルの一部分に振りたい場合――プログラムのコード 片など――には向きません。エディタの中でできた方が気持がいいと思 います。長年の懸案が解消されたのでうれしいです。

 最初は、もとテキストを変更するのがイヤなこともあるだろうから行 番号がついたテキストを別バッファに置くことを考えていました。でも、 どうせリージョンが対象になるのなら、リージョンを別のバッファにコ ピー&ペーストして行番号を振ればいいわけです。それは手作業でもで きるでしょうし、このコマンドをラップするようなコマンドを書いても いいでしょう。ということで、別バッファは諦め、代わりに(?)バッ ファ全体に行番号をつける機能を加えてひとまず終了としました。

(2003.03.28)

GNU EmacsのTipsへ
参考図書へ
コンピューター言語研究所へ
トップページへ
(C) ©Copyright Noboru HIWAMATA (nulpleno). All rights reserved.