Meadow

4. 標準 Emacs Lisp ライブラリ

Meadow には、様々な機能を提供する Emacs Lisp (elisp) ライブラリが標準で多数含まれています。 プログラムの入力を容易にするモードやアウトライン入力を実現するもの、 さらには、テトリスもどきのゲーム類も用意されています。

ここでは、Meadow に標準で導入されているライブラリの中で、 私が良く利用しているものを紹介します。 ただし、Meadow に標準で含まれていても、 site-lisp ディレクトリに格納されているものは、 次章の 非標準 Emacs Lisp ライブラリにて紹介します。

4.1 CC mode

CC mode とは?

CC mode は、プログラムのソースコードの編集を容易にするためのモードです。 次のような機能が、CC mode により提供されます。

また、対応する言語は次のとおりです。

自動インデント付けや自動改行は、とても便利なのですが、デフォルトの設定 のままだと、自分の思うとおりにならずに、いらいらすることもあります。気 持ち良く CC mode を利用するには、自分のコーディングスタイルに合わせたカ スタマイズが必須になります。

設定

私の場合は、下記のように ~/.emacs を設定しています。

;; C/C++ 用のカスタマイズ
(defconst my-c-style
  '(
    ;; 基本オフセット量の設定
    (c-basic-offset             . 4)

    ;; tab キーでインデントを実行
    (c-tab-always-indent        . t)

    ;; コメント行のオフセット量の設定
    (c-comment-only-line-offset . 0)

    ;; カッコ前後の自動改行処理の設定
    (c-hanging-braces-alist
     . (
        (class-open before after)       ; クラス宣言の'{'の前後
        (class-close after)             ; クラス宣言の'}'の後
        (defun-open before after)       ; 関数宣言の'{'の前後
        (defun-close after)             ; 関数宣言の'}'の後
        (inline-open after)             ; クラス内のインライン
                                        ; 関数宣言の'{'の後
        (inline-close after)            ; クラス内のインライン
                                        ; 関数宣言の'}'の後
        (brace-list-open after)         ; 列挙型、配列宣言の'{'の後
        (brace-list-close before after) ; 列挙型、配列宣言の'}'の前後
        (block-open after)              ; ステートメントの'{'の後
        (block-close after)             ; ステートメントの'}'前後
        (substatement-open after)       ; サブステートメント
                                        ; (if 文等)の'{'の後
        (statement-case-open after)     ; case 文の'{'の後
        (extern-lang-open before after) ; 他言語へのリンケージ宣言の
                                        ; '{'の前後
        (extern-lang-close before)      ; 他言語へのリンケージ宣言の
                                        ; '}'の前
        ))

    ;; コロン前後の自動改行処理の設定
    (c-hanging-colons-alist
     . (
        (case-label after)              ; case ラベルの':'の後
        (label after)                   ; ラベルの':'の後
        (access-label after)            ; アクセスラベル(public等)の':'の後
        (member-init-intro)             ; コンストラクタでのメンバー初期化
                                        ; リストの先頭の':'では改行しない
        (inher-intro before)            ; クラス宣言での継承リストの先頭の
                                        ; ':'では改行しない
        ))

    ;; 挿入された余計な空白文字のキャンセル条件の設定
    ;; 下記の*を削除する
    (c-cleanup-list
     . (
        brace-else-brace                ; else の直前
                                        ; "} * else {"  ->  "} else {"
        brace-elseif-brace              ; else if の直前
                                        ; "} * else if (.*) {"
                                        ; ->  } "else if (.*) {"
        empty-defun-braces              ; 空のクラス・関数定義の'}' の直前
                                        ;;"{ * }"  ->  "{}"
        defun-close-semi                ; クラス・関数定義後の';' の直前
                                        ; "} * ;"  ->  "};"
        list-close-comma                ; 配列初期化時の'},'の直前
                                        ; "} * ,"  ->  "},"
        scope-operator                  ; スコープ演算子'::' の間
                                        ; ": * :"  ->  "::"
        ))

    ;; オフセット量の設定
    ;; 必要部分のみ抜粋(他の設定に付いては info 参照)
    ;; オフセット量は下記で指定
    ;; +  c-basic-offsetの 1倍, ++ c-basic-offsetの 2倍
    ;; -  c-basic-offsetの-1倍, -- c-basic-offsetの-2倍
    (c-offsets-alist
     . (
        (arglist-intro          . ++)   ; 引数リストの開始行
        (arglist-close          . c-lineup-arglist) ; 引数リストの終了行
        (substatement-open      . 0)    ; サブステートメントの開始行
        (statement-cont         . ++)   ; ステートメントの継続行
        (case-label             . 0)    ; case 文のラベル行
        (label                  . 0)    ; ラベル行
        (block-open             . 0)    ; ブロックの開始行
        (member-init-intro      . ++)   ; メンバオブジェクトの初期化リスト
        ))

    ;; インデント時に構文解析情報を表示する
    (c-echo-syntactic-information-p . t)
    )
  "My C Programming Style")

;; hook 用の関数の定義
(defun my-c-mode-common-hook ()
  ;; my-c-stye を登録して有効にする
  (c-add-style "PERSONAL" my-c-style t)

  ;; 次のスタイルがデフォルトで用意されているので選択してもよい
;  (c-set-style "gnu")
;  (c-set-style "cc-mode")
;  (c-set-style "stroustrup")
;  (c-set-style "ellemtel")
  ;; 既存のスタイルを変更する場合は次のようにする
;  (c-set-offset 'member-init-intro '++)

  ;; タブ長の設定
  (setq tab-width 4)

  ;; タブの代わりにスペースを使う
;  (setq indent-tabs-mode nil)

  ;; 自動改行(auto-newline)を有効にする
  (c-toggle-auto-state t)

  ;; 連続する空白の一括削除(hungry-delete)を有効にする
  (c-toggle-hungry-state t)

  ;; セミコロンで自動改行しない
  (setq c-hanging-semi&comma-criteria nil)

  ;; キーバインドの追加
  ;; ------------------
  ;; C-m        改行+インデント
  ;; C-c c      コンパイルコマンドの起動
  ;; C-h        空白の一括削除
  (define-key c-mode-base-map "\C-m" 'newline-and-indent)
  (define-key c-mode-base-map "\C-cc" 'compile)
  (define-key c-mode-base-map "\C-h" 'c-electric-backspace)

  ;; コンパイルコマンドの設定
;  (setq compile-command "make -k" )   ; Cygwin の make
  (setq compile-command "nmake /NOLOGO /S") ; VC++ の nmake
  )
;; モードに入るときに呼び出す hook の設定
(add-hook 'c-mode-common-hook 'my-c-mode-common-hook)

;; ヘッダファイルもc++モードで開く 
(setq auto-mode-alist (append
                       '(("\\.h$"    . c++-mode))
                       auto-mode-alist))

使い方

上記の設定では、拡張子が .cpp, .cxx, .h であるファイ ルを開くと自動的に C++ を編集するための c++-mode が起動します。また、拡 張子が .c であるファイルの場合には C を編集するための c-mode が起動しま す。これらのモードが起動したら(モードライン上で確認できるはず)、プログ ラムを入力してみてください。TAB や RET キーを入力すると、構文に合わせて インデントや自動改行するようになっていると思います。後は、自動改行の設 定などを自分の好みに合わせて変更してください。

プログラム編集後にコンパイルする時には、M-x compile と入力します。この コマンドは、compile-command で指定する外部コマンド(通常は make)を起動し ます。当然 make コマンドでコンパイルできるようにMakefile を作成しておく 必要があります。なお、このコマンドは頻繁に使うので、私は、C-c c にバイ ンドしています。コンパイルでエラーが発生した場合には、C-x ` (next-error)でエラー個所にジャンプすることができます。このキーは私にとっ て入力し難いので、C-x C-j キーにもバインドしています。

C や C++ 以外にも、Java, Objective-C, CORBA IDL 用の編集モードも提供さ れています。拡張子と起動されるモードの関係は、変数 auto-mode-alist を見 てください。また、CC Mode の詳しい使い方は Info を参照してください。

4.2 Allout outline mode

Allout outline mode とは?

Allout outline mode は、章や節などで階層構造化された 文章の編集を容易にするモードです(スクリーン ショット)。通常、このような目的には、outline-mode が利用されますが、 ここでは、outline-mode より少し賢い Allout outline mode を紹介します。

私が Allout outline mode を使う理由を以下に示します。

  1. 見出しのレベルを自在に変更できる。特定の見出しと、その子孫全体の レベルを1キー操作で、深くしたり浅くしたりできる。
  2. 見出しを、レベルに応じてインデント表示できるので、見やすい。
  3. 見出し番号を自動的に生成できる。
  4. その他 outline-mode が提供する機能を備える。

反面、次のような欠点があります。

  1. 見出し記号の表現に柔軟性がないため、TeX や HTML の見出し記号をそ のまま利用できません(と思う)。そのため、通常の outline-mode が必 要な場合もあります。
  2. 上記にかかわらず、グローバルな関数・変数名が衝突するため、従来の outline-mode と、共存できません。この問題を回避するため、関数・変 数名を変更したパッケージを用意しました。必要ならば、Nallout outline mode の項を参 考にしてください。

設定

~/.emacs に次の設定を追加します。

ここで、青色文字の部分は、Font Lock mode 用の設定です。これを利用しな い場合は、省略してください。


;;;
;;; outline-mode の設定
;;;
;; outline-mode の代わりに allout outline-mode を使用
;; 詳細は allout.el を参照のこと
(require 'allout)
(outline-init t)

;; 拡張子が .out のファイルをロードするときに自動的に outline-mode にする
(setq auto-mode-alist
      (append '(("\\.out$" . outline-mode))
	      auto-mode-alist))

(add-hook
 'outline-mode-hook
 (function 
  (lambda ()
    
    ;; font-lock 用の設定
    ;; 見出しのレベルによってハイライト色を変える
    ;; outline.el と allout.el を参考にした
    (make-local-variable 'font-lock-defaults)

    (defvar outline-font-lock-keywords
       (list
	;; 最上位以外のの見出しの設定
	;; ちょっと自信がないけど、とりあえずは動いている
	'(eval .
	       (list
		;; 見出しの正規表現
		(concat "^\\("
			(regexp-quote outline-header-prefix)
			"\\)\\([ \t]*["
			outline-bullets-string
			"]\\)[^ \t]*[ \t]*\\([^\n\r]*\\)")
		;; 見出しの文字列(3番目にマッチしたもの)の face を、見
		;; 出しの深さに応じて変更する
		'(3 (let ((len (- (match-end 2)
				  (match-beginning 2))))
		      (setq len (mod len 3))
		      (or (cdr
			   (assq len
				 '((0 . font-lock-function-name-face)
				   (1 . font-lock-keyword-face)
				   (2 . font-lock-comment-face))))
			  font-lock-variable-name-face))
		    nil t)))
	;; 最上位の見出しの設定
	'(eval .
	       (list
		;; 見出しの正規表現
		(concat "^\\("
			(regexp-quote outline-primary-bullet)
			"\\)[ \t]*\\([^\n\r]*\\)")
		;; 見出しの文字列(2番目にマッチしたもの)の face を設定
		;; する
		'(2 font-lock-function-name-face))))
       "Additional expressions to highlight in allout outline mode.")

    (setq font-lock-defaults '(outline-font-lock-keywords t))

    ;; font-lock-mode の明示な起動
    (turn-on-font-lock)
    

    ;; キーバインドの追加
    ;; ------------------
    ;; C-c C-r    見出し全体を右方向へシフト(C-c > と同じ)
    ;; C-c c-l    見出し全体を左方向へシフト(C-c < と同じ)
    (define-key outline-mode-map "\C-c\C-r" 'allout-shift-in)
    (define-key outline-mode-map "\C-c\C-l" 'allout-shift-out)

    ;; メニューバーへの登録
    (require 'easymenu)

    (easy-menu-define
     outline-menu outline-mode-map
     "Outline Mode Commands"
     '("Outline"
       ["Next visible heading" outline-next-visible-heading t]
       ["Previous visible heading" outline-previous-visible-heading t]
       "---"
       ["Hide current subtree" outline-hide-current-subtree t]
       ["Show current subtree" outline-show-current-subtree t]
       "---"
       ["Open sibtopic" outline-open-sibtopic t]
       ["Open subtopic" outline-open-subtopic t]
       ["supertopic" outline-open-supertopic t]
       "---"
       ["Shift in" outline-shift-in t]
       ["Shift out" outline-shift-out t]
       ["Number siblings" outline-number-siblings t]
       "---"
       ["Kill topic" outline-kill-topic t]
       ["Yank" outline-yank t]
       ))
    )))

使い方

拡張子が out であるファイルを読込むと、自動的に allout mode になります。 手動で allout mode にする場合は、M-x outline-mode と入力します。

以下に、簡単な使用例を示します。allout mode にした後で、C-c <SPC> (outline-open-sibtopic)と入力すると、見出しが作成されます。 C-c <SPC> で現在の見出しと同レベルの見出しを、新規作成します。見出 しが存在しない場合は、トップレベルの見出し"*"を作成します。


*  
次に見出しと、本文を入力します

* 見出し1 
本文1 
次に、C-c . (outline-open-subtopic)と入力して、一つ深 いレベルに見出しを作成します。トップレベル以外の見出しは、"." + いくつ かのスペース + 一文字の見出し記号、という形をとります。見出しの長さは、 見出しのレベルによって変化します。なお、先頭の "." は、編集モードによっ て変化します。例えば、Emacs Lisp モードでは、コメント記号 + "_" つまり ";;;_" が "." の代わりに使われます。

* 見出し1
本文1

.*  
同じ操作を繰り返して、見出しと本文をいくつか作成します。

* 見出し1
本文1

.* 見出し2
本文2

. + 見出し3
本文3

.  - 見出し4
本文4 
次に、C-c , (outline-open-supertopic)と入力して、一つ浅いレベルに見出しを 作成します。

* 見出し1
本文1

.* 見出し2
本文2

. + 見出し3
本文3

.  - 見出し4
本文4

. +  
見出しと本文を入力後、カーソルを“見出し2”の上に移動 します。“見出し2”の行であれば、カラム位置はどこでも構いません。

* 見出し1
本文1

.* 見出し2 
本文2

. + 見出し3
本文3

.  - 見出し4
本文4

. + 見出し5
本文5
C-c C-h (outline-hide-current-subtree)と入力して、カー ソル上の見出しの本文と、その子孫全体を隠します。"..."は、何らかの文が隠 されていることを示します。

* 見出し1
本文1

.* 見出し2...
C-c C-s (outline-show-current-subtree)と入力して、隠し た文章を表示した後で、カーソルを“見出し3”に移動します

* 見出し1
本文1

.* 見出し2
本文2

. + 見出し3 
本文3

.  - 見出し4
本文4

. + 見出し5
本文5
C-c < (outline-shift-out)と入力して、カーソル上の見出 しと、その子孫全体の、見出しレベルを一段階浅くします。C-c > (outline-shift-in)で逆の操作も可能です。

* 見出し1
本文1

.* 見出し2
本文2

.* 見出し3 
本文3

. + 見出し4
本文4

. + 見出し5
本文5

このほかの詳しいキー操作は、allout mode の クイックリファレンスを参考にして下さい。

問題点

[現象] C-s <RET> で search-forward 等を呼び出すと、エラーが発生する。
[原因] allout.el で、既存の関数 isearh-done 置き換える 所に不具合があるようです。置きかえる関数と、置きかえられる関数の引数が 一致していません。
[対策] 不具合を修正するパッチ を作成しました。このパッチを allout.el にあて、バイトコンパイルしてください。パッチの あて方は、パッチの項を参照してください。

4.3 Font Lock mode

Font Lock mode とは?

Font Lock mode は、文脈に応じてテキストに色をつけて、 見やすくするマイナーモードです。例えば、コメント部分は赤で表示したり、 関数名は青で表示したりといったことができます。CC mode, Emacs Lisp mode など大抵のメジャーモードは、Font Lock mode 対応となっているので、Font Lock mode を有効にするだけで、このような表示が可能になります。

同じような機能を持つものに、hilit19 があります。デフォルトで私の嫌いな イタリック体を使っていたので、ほとんど使ったことがありません。 hilit19.el には、Emacs19 用であり、もはやメンテナンスされていないとも記 述してあります。Emacs20 (もちろん、Meadow も)で、hilit19 を使っている方 は、Font Lock mode を使うべきでしょう。

設定

(1) ~/.emacs の設定
次の設定を ~/.emacs に加えます。ここでは、~/.emacs-flc をキャッシュファイルの格納場所として指定しているので、これを別途作成し ておきます。なお、このディレクトリが存在しない場合は、デフォルトの設定 となります。従って、必要に応じて編集対象と同じディレクトリにキャッシュ ファイル(編集対象のファイル名+".flc")が作成されて、少々うっとうしいこ とになります。

;;;
;;; font-lock-mode の設定
;;;
;; font-lock-mode を常時利用する
(global-font-lock-mode t)
;; 表示の高速化のために fast-lock-mode を利用する
(setq font-lock-support-mode 'fast-lock-mode)
;; キャッシュファイルの格納先を指定する
(let* ((file-dir "~/.emacs-flc"))
  (if (file-exists-p file-dir) ; ~/.emacs-flc の存在をチェック
      ;; 存在するならば、キャッシュファイルの格納先に指定
      (setq fast-lock-cache-directories (list file-dir))))
(2) fast-lock.el の修正
実は、Meadow1.00 の場合には、上記の設定だけでは、キャッ シュファイルが正しくセーブされません。キャッシュファイル名を決定すると きに、Windows のドライブレター(C:など)が悪さをしているようです。そこで、 fast-lock.el (私の環境では、C:/usr/local/Meadow/1.00/lisp に存在)の関数 fast-lock-cache-name の定義部分を次のように修正します。別に、キャッシュ ファイルが作成されなくても構わない方には、この修正は必要ありません。 Meadow の開発者向けのメーリングリスト(meadow-develop)での、 こおり やまさんの情報(X-ML-COUNT: 551)を参考にしました。なお、Meadow1.10 付属の fast-lock.el では、この対策がなされています。

[修正前]
            (if (eq system-type 'emx)
[修正後]
	    (if (or (eq system-type 'emx)
		    (eq system-type 'windows-nt))
修正したら、次に、fast-lock.el をバイトコンパイルし直し ます。dired-mode (C-x d)でこのファイルを選択して、B (大文字のb)とやるの がお手軽です。

4.4 Abbrevs(略称)

Abbrevs とは?

Abbrevs とは、略称で入力した単語を正式名に展開する機能 です。この機能を使いこなすことで、テキストの入力効率が劇的に向上します。

例えば、下記に示す C++ のソースを、あなたならどのように入力しますか? aVariableOfVeryLongName を馬鹿正直に2回とも入力する人は、あまりいないと 思います。では、kill & yank を使いますか?実は、Abbrevs を使えばもっと 簡単に入力できるのです。


if (!aVariableOfVeryLongName) {
    aVariableOfVeryLongName = true;
}
Abbrevs を使った入力には、2通りの方法があります。動的 Abbrevs と、通常の Abbrevs です。

動的 Abbrevs は、編集中のバッファから展開する単語を選ぶものです。上記の ソース例では、


if (!aVariableOfVeryLongName) {
    a 
}
と入力した後で、"M-/" (dabbrev-expand) と入力すると、 次のよう一気に展開できます。

if (!aVariableOfVeryLongName) {
    aVariableOfVeryLongName 
}
これは、略称 "a" からバッファの前方を探した場合に、最 初に見つかる a で始まる単語が aVariableOfVeryLongName であるからです。 なお、もう一度 "M-/" を入力すると、この単語よりも前に登場する a で始ま る単語に展開されなおされます。

これに対して、通常の Abbrevs は、事前に定義しておいた略称を基にして、正 式名へと展開するものです。上記のソースの例だと、aVariableOfVeryLongName の略称をvvln と定義しておけば、


if (!vvln 
と入力した後で、"M-x a e" (expand-abbrev) と入力すると、 次のように展開できます。

if (!aVariableOfVeryLongName 
通常の Abbrevs は、略称を事前に定義する必要があるので、 それほど使い勝手が良いものではありません。やはり、動的 Abbrevs が断然便 利だと思います。さー皆さん、"M-/" を打ちまくりましょう。

設定

動的 Abbrevs を使う場合には、何も設定する必要はありま せん。一方、通常の Abbrevs を使う場合には、~/.emacs に次の設定を追加し ます。これは、略称の定義ファイル ~/.abbrev_defs を読込むための設定です。 初回起動時には、~/.abbrev_defs という名称の空のファイルを別途作成してお く必要があります。以後、新規に略称を定義したときには、このファイルに記 録されます。

(let ((file "~/.abbrev_defs"))
  (if (file-exists-p file)
      (quietly-read-abbrev-file file)))	; 定義ファイルの読込み

使い方

簡単な使い方は既に述べてあるので、ここでは良く使うコマンドを紹介します。 詳しいことは、Info (emacs -> Abbrevs)を読んでください。

4.5 jka-compr

jka-compr とは?

jka-compr とは、圧縮したファイルを普通のファイルと同様 に扱うためのライブラリです。これを使えば、圧縮したテキストファイルを、 直接読込んで編集できますし、バッファの内容を圧縮形式で直接保存すること ができます。シェルから起動できる圧縮・展開ツールさえあれば、たいていの 圧縮形式を扱うことができます。デフォルトでは、次の形式を扱えるように設 定されています。 また、tar-mode と協調することで、.tar + gzip 形式(拡張子は .tgz)も利用 できるようになります。

jka-compr を使うことのメリットは、ファイルのサイズを小さくできることで す。デメリットは、ファイルの入出力が遅くなることです。これは、入出力の たびにツールを呼び出して圧縮・展開するためです。頻繁に参照するファイル には使い難いかもしれませんが、めったに参照しないファイルには適用できる と思います。私は、nnml メールバックエンドを使って保存してある古いメール は、みんな圧縮してあります。

準備

jka-compr の利用に先立って、圧縮・展開ツールをインストー ルして、パスを通しておく必要があります。gzip(gunzip) と bzip2(bunzip2) は、Cygwin に含まれます。 compress(uncompress) を使う場合は、ご自分でお探し下さい。

設定

設定は、とっても簡単です。jka-compr をロードするだけで す。~/.emacs に次の設定を加えてください。

(require 'jka-compr) 
標準で扱える圧縮形式以外を使いたい場合は、変数 jka-compr-compression-info-list で設定します。詳しくは、この変数の説明 を見てください。

使い方

特別な操作は必要ありません。普通のファイルと同じように、 圧縮されたファイルを C-x C-f (find-file) などで開いてみてください。自動 的に展開されてバッファに読込まれるはずです。バッファを編集したら、C-x C-s (save-buffer) でセーブしてみてください。圧縮形式で保存されるはずで す