Copyright 1998 Wataru Shito(作者および出典が明記されている箇所を除く)
コメント,感想,誤りの指摘などは wshito@iser.osaka-u.ac.jpまで.

Emacs アウトラインモードのハッキング

Ver 0.1 6/14/98

目次

  1. アウトラインモードの概要
  2. コードの概要
  3. アウトラインモードの下準備
  4. つづく......

  1. アウトラインモードの概要
  2. アウトラインモードは,アスタリスク"*"で始まる行を見出しとして扱う.見出しに続 くアスタリスクで始まらない行を,見出しの本文(コンテンツ)として認識する. 見出しと,コンテンツをひとかたまりに扱い,任意に表示,非表示を可能にし, アウトライン編集を可能にするのが,アウトラインモードである.

    アウトラインの深さは,行頭のアスタリスクの数によって表現される. つまり見出しの下に,さらに階層を作るには,アスタリスクの数を増やせば良い.

    例を以下に示そう.まず,"M-x outline-mode"を実行し,現在のバッファをアウトラ インモードにする.そして以下を入力してみる.

    M-x outline-modeでの入力例
      *これが一番上の階層のヘッダ1
      ここがヘッダ1のコンテンツです.
      ** 2番目の階層のヘッダ1
      これは,2番目の階層のコンテンツ.  
    
      *これが一番上の階層のヘッダ2
    
      ここがヘッダ2のコンテンツ.
      
      ** 2番目の階層のヘッダ2
      今日は,晴れです.
    
      ***これは3番目の階層のヘッダ
      この書類の中では,一番下の階層です.アスタリスクを繋げることによって,もっと
      階層を増やすこともできる.
      
    アウトライン編集で重要なのは,各アウトラインの内容を消して,階層構造を分かり やすく見せることである."M-x hide-body"を実行すると,以下のようにアウトライ ンだけの表示になる.ちなみに,各ヘッダの後ろのドットは,そのヘッダがコンテンツを持つことを表している.
    M-x hide-bodyで各階層のコンテンツを隠す
      *これが一番上の階層のヘッダ1...
      ** 2番目の階層のヘッダ1...
      
      *これが一番上の階層のヘッダ2...
      
      ** 2番目の階層のヘッダ2...
      
      ***これは3番目の階層のヘッダ...
      
    この他にも,各階層の細かな表示のための,様々なコマンドが用意されている.例え ば,"M-x hide-subtree"と"M-x show-subtree"は,カーソルのある階層より下の階層 を全て隠したり表示したりする.また,subtreeを非表示にした後に,その一つ下の 階層(children)だけ表示したければ,"M-x show-children"を実行すれば良い.

    利用可能なコマンドの一覧は,"M-x describe-function"を実行し,"outline-mode" と入力すれば得られる.


  3. コードの概要
  4. Line 1--33

    コードの使用に関するライセンス条項と,このパッケージが提供するモードに関する コメントが書かれている.

    Line 35--152

    実際のコードは,35行目 から始まる.35行目から152行目までは,アウトラインモードの動作を定義する 関数ではなく,コード中に用いられる変数,ショートカットキーのマッピング(55行目から)や,メニューバー 作成用のマップ(75行目から )などの,下準備に関するコードが書かれている.

    Line 154--216

    アウトラインモードのメイン関数.ここから,アウトラインモードで使用される全て の機能が呼び出される.

    Line 218--235

    アウトラインのマイナーモードに関する関数定義に用いられる変数や,キーマップの 定義など,マイナーモードの下準備を行っている.

    Line 237--254

    アウトラインモードが,マイナーモードで呼び出された場合の動作を定義するメイン 関数.

    Line 256--最終行

    アウトラインモードで用いられる,様々な機能(コマンド)の動作を定義する関数群.


  5. アウトラインモードの下準備
  6. Line 35--44.outline-regexpグローバル変数の初期化 (defvarの機能およびグローバール値とバッファローカル値について)

    outline-regexpグローバル変数をnilで初期化する.defvarの第3引数で,変数の説明 が記述されている.この説明は,シンボルのvariable-documentation属性に格納され る.Emacsのヘルプ機能は,この属性を検索して表示する.この説明文の先頭に付い ているアスタリスクは,この変数がユーザオプション変数であることを表している. したがって,ユーザが"M-x set-variable"で変数を設定することを考慮している. このアスタリスクは,"M-x edit-options"で変数を設定することを便利にする.

    それではここで,このシンボルの説明を表示させてみよう. まず,outlineパッケージを"M-x load-library"でロードする. 次に,"M-x describe-variable"コマンドで outline-regexpを指定すれば,下のような解説が表示される.

    outline-regexpグローバル変数の説明
    
      outline-regexp's value is "^.*<[Hh][1-6]\\>"
      Local in buffer outline_code.html; global value is "[*^L]+"
      
      Documentation:
      *Regular expression to match the beginning of a heading.
      Any line whose beginning matches this regexp is considered to start a heading.
      The recommended way to set this is with a Local Variables: list
      in the file it applies to.  See also outline-heading-end-regexp.
      

    上の結果の1行目で,outline-regexpの値は"^.*<[Hh][1-6]\\>"となっている. しかし,その下でヘルプが呼び出されたoutline_code.htmlバッファにおいては, グローバル値が"[*^L]+"になっていると言っている.これは,outline-regexpの一番 深い束縛(一番最近の束縛)が"^.*<[Hh][1-6]\\>"だが,グローバル値(デフォルト値) は,"[*^L]+"であることを表している.これを確認するには,以下の関数を評価すれ ばよい.

    outline-regexp変数の束縛
      (symbol-value 'outline-regexp)
      => ^.*<[Hh][1-6]\\>
      (default-value 'outline-regexp)
      => [*^L]+
      

    symbol-valueは,変数の一番最近の束縛値を返す.それに対し,default-valueはバッ ファに関係ないデフォルト値を返す.したがって,outline-regexpは初めグローバル 変数としてデフォルト値"[*^L]+"で初期化されたが,後に現在のバッファで "^.*<[Hh][1-6]\\>"に設定されたことが分かる.

    それぞれの正規表現の意味を確認しておこう. "[*^L]+"は,44行目 から分かるように,実際には,"[*\^L]+"である."^L"は"?\^L"で12と評価されるので, "?\f"の評価値と同じである."?\f"は,C-lで改ページの意味である. したがって,"[*\^L]+"は,アスタリスク文字または,改ページコードが 少なくとも1回以上出現する場合に一致する.正規表現のアスタリスクは,後置表現 なので,ここのアスタリスクは正規表現のアスタリスクではない.アスタリスク文字そ のものを表している.

    "^.*<[Hh][1-6]\\>"の正規表現がマッチする文字列は,改行以外の任意の文字列が 行頭からゼロ回以上繰り返され,その後にHTMLのコマンド終了を表すタグのように <H1\>にマッチする.ただしHは大文字小文字に関係なく数字は1から6の場合 にマッチする.

    35行目 のdefvarは,outline-regexpシンボルに値が設定されていないときにnilを 設定する.defvarやdefconstが値を設定する場合,ローカル値ではなくグローバル値 (デフォルト値)をセットすることに注意.

    次に43行目 でdefault-valueが,outline-regexpのデフォルト値を返す.もし, 35行目 でnilに設定されなかった場合(既にデフォルト値が存在していた場合) 44行目 でデフォルト値を"[*\^L]+"に設定する.

    上で示したように,outline_code.htmlバッファではデフォルト値が"[*\^L]+"であっ たことを見ると,以上の手順で初期化が行われたことが分かる.もし,デフォルト値 "[*\^L]+"以外が設定されていると, 43行目のdefault-value の式が真になり,新しい値は設定されない.

    最後に,outline-regexpの"[*\^L]+"が本当にデフォルト値で,"^.*<[Hh][1-6]\\>"が バッファローカル値であることを示すために,以下の関数を評価してみよう.

    バッファローカル値を削除する
      (kill-local-variable 'outline-regexp)
      (symbol-value 'outline-regexp)
      => [*^L]+
      (insert (default-value 'outline-regexp)
      => [*^L]+
      

    Line 46--51.outline-heading-end-regexpグローバル変数の初期化

    上で説明したdefvarで,outline-heading-end-regexpグローバル変数が既に値を持っ ていない場合に,"[\n\^M]"で初期化する. ?\^Mを評価すると13で,これは,?\rのキャリッジ・リターンを表すので,"[\n\^M]" は,改行かまたはキャリッジ・リターンのいずれかの文字が出現した場合にマッチす る正規表現である.47行目 からの解説から分かるように,この正規表現は,アウトラインのヘッディング行 の終りを示す正規表現に使われる.

    Line 53.outline-mode-prefix-mapグローバル変数の初期化

    outline-mode-prefix-mapグローバル変数が値を持っていない場合,nilに初期化.値 を持っている場合は,何もしない.

    outline-mode-prefix-mapは,後にアウトラインモード中のコマンドへのショートカッ トキーを定義する,キーマップに使われる.

    Line 55--73.outline-mode-prefix-mapキーマップの作成

    55行目のif文は,もし, outline-mode-prefix-mapが真ならば56行目のnilを評価し,そうでなければ, 57--73行目を順に評価 する.

    make-sparse-keymap関数は,空のキーマップを作成して返す.この関数で新たに作成 されたキーマップを,outline-mode-prefix-mapにバインドして,新たにキーを登録 していく.

    Line 75--122.outline-mode-menu-bar-mapメニューバー用マップの作成

    つづく.....


[ ホームへ | ハッキングのトップへ | Lispのトップへ | Emacs Lispのトップへ ]