[Japanese] [English]


Index


Title: el4r 〜EmacsRubyで楽しいEmacsライフ〜

$Id: el4r.ja.rd.r 1380 2006-09-21 07:23:15Z rubikitch $

Emacsの拡張言語に「Ruby」も加えましょう♪

徹底した自動テストでEmacsLispのバグをなくしましょう♪

最新Version 1.0.4

1 What's new

1.1 [2006/09/21] 1.0.4 released

1.2 [2006/03/12] 1.0.3 released

1.3 [2005/12/11] 1.0.2 released

1.4 [2005/12/02] 1.0.1 released

1.5 [2005/12/01] 1.0.0 released

1.6 [2005/10/11] 0.9.3 released

1.7 [2005/10/05] 0.9.2 released

2 概要

通常、Emacsを拡張するにはEmacsLispというLisp方言を使います。 このプログラムはEmacsLispに加えてRubyでもEmacsを操作できるようにします。 Emacsを操作するためのRubyをEmacsLispに倣ってEmacsRubyと言うことにします。 *1

Emacsは長年広く使われているので数々のEmacsLispライブラリがあります。 しかし、自分でEmacsを拡張するにはEmacsLispを書かなければいけませんでした。 このプログラムで新たにRubyという選択肢ができました。 Emacs使いではあるがLispはちょっと…という人は是非とも試してみてください。

また、RubyのTest::Unitを使うことでEmacsLispプログラムのテストを完全自動化できます。 EmacsLispを愛する人にも役立つと思います。

3 動機

筆者は昔からEmacsをRubyで拡張できたらなーと思っていました。 Rubyを知る前からEmacsLispを少し書くことができましたが、自分にとってEmacsLispはストレスのたまる言語です。 なので、本処理をRubyなど他の言語で記述して、Emacsからはcall-processやshell-commandなどから呼出すことをよくやっていました。 *2

数年前、原作者の白井さんが[ruby-list:37730]にてel4rを発表しました。 当時はまだ実用にはまだほど遠い感じでしたので、頭の片隅に置くにとどまりました。 しばらく後にCVSからチェックアウトしたら、いろいろ改良され、実用の見通しが立つまでになっていました。 それから、何通かのメールのやり取りで開発を引き継ぐことになりました。

4 動作原理

EmacsがRubyとプロセス間通信します。 より正確には、Emacsとel4r-instanceというRubyスクリプトがプロセス間通信します。 そのため、el4rはEmacsLispとRubyスクリプトが組になっています。

el4r-instanceは、El4r::ELInstanceオブジェクトを作成し、Emacsからの式を待つループに入ります。 式が来ると、El4r::ELInstanceオブジェクトの文脈(以後、el4rの文脈とする)で評価して結果をEmacsに返します。 el4rの文脈では、el4rオブジェクトが知らないメソッドはEmacsLispの関数へ丸投げされます。 そのため、ユーザにとってはEmacsにRubyが組み込まれたように見えます。

5 動作環境

動作には以下の環境が必要です。

筆者の環境はDebian GNU/Linuxです。 他のGNU/Linux、Unix系OSでも問題なく動くと思われます。

手許にWindows環境がないため、本物のWindowsでの動作確認はしていません。 WINE上の"ruby 1.8.2 (2004-12-25) [i386-mswin32]"とMeadowの組み合わせで動作しています。

さまざまな環境での動作状況を知りたいので、次のコマンドをel4rのディレクトリから実行し、OS名を明記の上、出力とtestlogファイルをるびきちまで送ってください。

ruby bin/el4r-runtest testing/test-el4r.rb --batch -l testlog --emacs=Emacsバイナリ名

6 ダウンロード・インストール・設定

バージョン1.0.0よりインストーラがつきました。

ダウンロード時に例外が発生するようでしたら、Rubyのバージョンを上げてください。 (Emacs内でこのページを見ている人はコマンド群にリージョンをセットし、M-| shすれば楽です) 付属の el4r-rctool スクリプトは現在の環境に合うように設定ファイルを自動更新します。 すでに設定済みのファイルは、el4r blockの部分のみが el4r-rctool の設定に基いて変更されます。 他の部分には危害を加えません。

なお、バージョン0.9.1以前の el4r からバージョンアップするには、まず ~/.emacs から

(add-to-list 'load-path "~/src/el4r/elisp/")
(require 'el4r)
(el4r-boot)

の部分、 ~/.el4r/init.rb から

el4r_load "el4r-mode.rb"

の部分を手作業で削除してください。 御迷惑をおかけします。 以後のバージョンは el4r-rctool が設定を更新してくれます。

任意のディレクトリにて以下のコマンドを実行してください。

ruby -ropen-uri -e 'URI("http://www.rubyist.net/~rubikitch/archive/el4r-1.0.4.tar.gz").read.display' > el4r-1.0.4.tar.gz
tar xzf el4r-1.0.4.tar.gz
cd el4r-1.0.4
ruby setup.rb

ruby -S el4r-rctool -p
ruby -S el4r-rctool -i

EmacsRubyスクリプトを置くディレクトリはデフォルトで ~/.el4r です。 環境変数 EL4R_HOME で変えることもできます。

Victor BorjaさんがGentoo ebuildを作成してくれました。 ありがとうございます。

Boris DaixさんがDebian packageを作成してくれました。 ありがとうございます。 /etc/apt/sources.listに次の設定を加えてください。

deb http://alysse.dyndns.org/~bdaix/debian/packages unstable/
deb-src http://alysse.dyndns.org/~bdaix/debian/packages unstable/

7 起動

Emacs上で

M-x el4r-boot

を実行します。 インストール段階でel4rはすでに起動しています。 挙動がおかしくなったとき、init.rbを書き換えた時などはel4rの再起動が必要です。 再起動も同じコマンドを使います。

8 EmacsRubyを書く

el4rを起動するとき、自動的に ~/.el4r/init.rb をel4rの文脈でevalします。 el4rの文脈だと通常のRubyに加えてEmacsLisp関数・変数にアクセスしたり、EmacsLisp関数を定義したりできます。

8.1 EmacsRubyスクリプトを読み込む

EmacsRubyスクリプトを読み込むには、el4r_loadメソッドを使います。 引数に指定したファイル名のスクリプトをel4rの文脈でevalします。

el4r_loadで読み込まれるスクリプトは、デフォルトで ~/.el4r → ~/.el4r/site → el4rパッケージの el4r ディレクトリの順に探されます。 他パッケージのEmacsRubyスクリプトは ~/.el4r/site 以下にインストールされます。

もちろん通常のloadも使えます。 この場合、el4rの文脈ではなく通常のRubyライブラリとして読み込みます。 el4rでは、 ~/.el4r(環境変数EL4R_HOMEで指定したディレクトリ)も$:($LOAD_PATH)に加えています。

8.2 名前の変換

RubyとEmacsLispでは名前の付け方の慣習に違いがあります。 通常、単語の区切りとしてEmacsLispでは「-」、Rubyでは「_」が使われます。 そのためel4rではEmacsLisp側の関数・変数をRubyで指定するとき、「_」を「-」に置換します。

例:

find_file -> find-file

また、RubyとEmacsLispでは関数名や変数名やシンボルに使える文字集合が違います。 EmacsLispの方が多くの種類の文字が使えます。 ということは、Rubyでは表現できないものも出てきます。 御安心ください。 こういう場合、文字列で指定できるのです。

例:

se/make-summary-buffer関数を呼び出す。
funcall("se/make-summary-buffer")

8.3 EmacsLisp変数へのアクセス

EmacsLisp変数へのアクセスはelvarというオブジェクトを使います。 このオブジェクトはRubyの構造体のようにアクセスできます。

例:

elvar.a_string = "Hello"            # (setq a-string "Hello")
elvar["a-string"] = "Hi"            # (setq a-string "Hi")
elvar["*an/odd+variable!*"] = 10    # (setq *an/odd+variable!* 10)

8.4 関数呼出し

el4rオブジェクトは自分の知らない関数を呼出そうとすると、EmacsLisp側の関数を呼びます(method_missing)。

例:

buffer_string             # (buffer-string)
find_file("~/.emacs")     # (find-file "~/.emacs")
funcall("1+1")            # (1+1)

8.5 関数定義

EmacsRuby側でEmacsLisp関数を定義することもできます。 Rubyと同様にEmacsLispにも「関数を定義する関数」が存在します。 よって、関数呼出しができれば関数定義もできてしまうのです。

関数の引数はイテレータの引数となります。

例:

defun(:my_command2,
      :interactive => "d", :docstring => "description...") { |point|
  insert_string("Current point is #{point}."); newline
}

また、Procによるinteractive指定もできます。 この場合、ArrayまたはEmacsLispのリストを返す必要があります。

例:

interactive_proc = lambda { [1+1, 1] }
defun(:my_command3,
      :interactive => interactive_proc) { |a, b|
  sum = a + b
}

8.6 Special Form呼出し

save-excursionやwith-current-bufferのようなイテレータっぽいSpecial Formの呼出し方はやや特殊です。 EmacsLispのイテレータ*3はEmacsRubyでもやはりイテレータなのです。 with(関数名, 引数) {...}のように使います。 関数名で直接呼び出せないのは現在の仕様上の制約です。

例:

with(:save_excursion) do
  goto_char 1
  re_search_forward('^\\(.+\\)$')
end
match_string 1

8.7 関数定義型マクロ呼出し

よく使われる関数定義型のマクロもRuby的に自然に呼び出せるようにしました。 このタイプは define_derived_mode と define_minor_mode です。 例:

define_derived_mode(:foo_mode, :fundamental_mode, "FOO", "doc") do
  @passed = true
end

define_minor_mode(:a_minor_mode, "test minor mode") do
  @passed = true
end

8.8 アドバイス

defadviceによるadviceも定義できます。 第1引数は関数名、以後の引数はadviceのパラメータをSymbolで指定します。

最後の省略可能引数にHashが取れます。 関数定義同様、:interactive:docstringを指定できます。

例:

# define a function
defun(:adtest3){ 1 }
# now define an advice
defadvice(:adtest3, :around, :adv3, :activate,
          :docstring=>"test advice", :interactive=>true) {
  ad_do_it
  elvar.ad_return_value = 2
}

8.9 出力関数の扱い

print / printf / puts / putc / p などの出力を行う組み込み関数の出力は *el4r:output* バッファに書き出されます。 デバッグなどに使ってください。

内部的には、 El4r::El4rOutput オブジェクトを $> に代入しています。 El4r::El4rOutput#write メソッドは *el4r:output* バッファに文字列を書き込むメソッドです。 出力を行う組み込み関数は最終的にはwriteが呼ばれるのでprintなども使えるのです。

8.10 ユーザ定義クラス・モジュール内でEmacsの機能を使う

EmacsRubyを導入する理由のひとつに、オブジェクト指向プログラミングがしたいというのもあるでしょう。 オブジェクト指向プログラミングといえば、まずはクラス定義です。 クラスを定義してしまうと、もはやel4rの文脈ではないからEmacsLispが呼び出せなくなるのではないか? 心配無用です。 クラス・モジュールを定義し、その中で

include ElMixin

と書きましょう。

これでいつものようにEmacsLispを呼び出せます。 自分の知らないメソッドはEmacsLispに投げられます。

testing/test-el4r.rbのTestEl4rクラスを参考にしてください。

また、ElMixinのクラス版ともいえる、ElAppクラスも用意しています。 Hashでパラメータを渡すことができます。

8.11 ElAppクラスの使用例1

バージョン1.0.3以前ではElAppサブクラスのインスタンスメソッドの中でdefunする必要がありました。

class Foo < ElApp
  def initialize(x)
    elvar.v = x[:value]
    defun(:twice_v) do
      elvar.v *= 2
    end

    defun(:str0) do
      do_str0 x[:str]
    end
  end

  def do_str0(str)
    (str*2).capitalize
  end
end

8.12 ElAppクラスの使用例2

バージョン1.0.4よりElAppサブクラスの中で直接defunできるようになりました。 メソッドの中でdefunする必要がないので、見通しがよくなりました。 クラス定義中のdefunは「EmacsLispを定義するdef」と思ってください。

class SmartDefunSample < ElApp
  def my_square(x)
    x*x
  end

  defun(:testdefun, :interactive=>true) do |x|
    # This block is evaluated within a context of the SmartDefunSample INSTANCE.
    # Not a context of the SmartDefunSample class!!
    x ||= 16
    elvar.val = my_square(x)  # call an instance method.
  end
end

9 自動テストを作成する

Rubyを使う大きな利点のひとつにTest::Unitという強力なテスト環境があります。 EmacsLisp/EmacsRubyスクリプトのテストは別個にEmacsを立ち上げてやるべきです。 Emacsの操作はEmacsセッション全体に影響を及ぼすため、テストが誤動作したときには最悪現在の編集環境を破壊しかねません。 テスト用のEmacsなら、たとえそこで事故が起きようが編集環境には何ら悪影響がないので、安心してテストが行えます。 こういう理由から、テストのためのミニ環境を用意しました。

el4r-runtestは、新たにEmacsを立ち上げ、Test::Unit(RUNIT)でテストを行い、その結果を表示します。

具体的にはテストクラスはこんな感じになります。 el4r_lisp_evalの部分にel_requireel_loadを使うこともできます。

require 'test/unit'
class TestXXXX < Test::Unit::TestCase
  el4r_lisp_eval %((progn
    初期化コード
  ))

  def setup
    #
  end

  def teardown
    #
  end

  def test_xxxx
    #
  end
end

9.1 コマンドラインオプション

Usage: el4r-runtest [options] file
    -Q, --init                       load site-start.el and .emacs
    -b, --batch                      batch mode
    -i                               interactive mode
    -e, --emacs=EMACS                set emacs binary [default: ]
        --ruby=RUBY                  set ruby binary [default: ]
    -I load-path                     set load-path
    -r, --el4r-root=DIR              el4r package root directory [for debug]
    -n, --name=NAME                  Runs tests matching NAME.
                                     (patterns may be used).
    -t, --testcase=TESTCASE          Runs tests in TestCases matching TESTCASE.
                                     (patterns may be used).
    -v                               verbose output
        --nw                         don't communicate with X, ignoring $DISPLAY
                                     (emacs -nw)
    -d, --debug                      debug output
    -l, --log=LOGFILE                Specify a log file.
        --show                       Show the test information only, for diagnosis.

--batch, --nwはEmacsのオプションです。 -n, -tはTest::Unitのオプションです。 -dはバックトレースにel4r-instanceの行も含めます。

10 el4rコマンド

el4rコマンドは新たにEmacsを立ち上げ、引数に指定されたEmacsRubyスクリプトを実行します。 使い方はel4r-runtestとほとんど同じです。

Usage: el4r [options] file
    -Q, --init                       load site-start.el and .emacs
    -b, --batch                      batch mode
    -i                               interactive mode
    -e, --emacs=EMACS                set emacs binary [default: ]
        --ruby=RUBY                  set ruby binary [default: ]
    -I load-path                     set load-path
    -r, --el4r-root=DIR              el4r package root directory [for debug]
        --nw                         don't communicate with X, ignoring $DISPLAY
                                     (emacs -nw)
    -d, --debug                      debug output
    -l, --log=LOGFILE                Specify a log file.
        --show                       Show the test information only, for diagnosis.

11 EmacsRuby APIリファレンス

11.1 EmacsLisp関数と同名のもの

funcall(name_or_lambda, *args, &block)

関数name_or_lambdaを引数argsをつけて呼出します。

with(name, *args, &block)

save-excursionやwith-current-bufferなど最後にFORMSを取るスペシャルフォームを呼出します。

defun(name, attrs = nil, &block)

関数nameを定義します。attrsは:interactiveと:docstringを取るHashです。 関数の引数はブロック引数です。

define_key(map, key, command = nil, &block)

mapはキーマップの変数名をシンボルで指定します。 keyは '\C-x\C-c' のように''文字列で指定します。 ブロックを指定することもできます。

let(*name_and_value_list, &block)

name_and_value_listは要素の数が偶数の配列で、偶数番目に変数名、奇数番目に変数の値を指定します。

defadvice(func, *args, &block)

funcのアドバイスを定義します。 argsはアドバイスのパラメータ(:around、:activateなど)。 ブロックの中でad_do_itを使うことができます。

define_minor_mode(mode, doc, init_value=nil, lighter=nil, keymap=nil, &block)

マイナーモードを定義します。

define_derived_mode(child, parent, name, docstring=nil, &block)

派生モードを定義します。

eval_after_load(lib, &block)

ライブラリlibをロードするときに実行するブロックを指定します。 すでにロードしているときは直ちにブロックを評価します。

11.2 EmacsLisp関数と同じ働きだが、名前がかぶるので接頭辞をつけたもの

el_require(*args)

必要ならばEmacsLispを読み込みます。

el_load(*args)

EmacsLispを読み込みます。

el_lambda(attr = nil, &block)

ラムダ式。使い方はdefunと同じです。

11.3 Ruby側のメソッド

el4r_load(path_to_rb, is_noerror = nil)

path_to_rbで指定されたEmacsRubyスクリプトを読み込みます。 スクリプトの内容はel4rの文脈で評価されます。

通常はtrueを返します。 is_noerrorがtrueのとき、ファイルが存在しない場合はfalseを返します。

el4r_lisp_eval(lispexpr)

EmacsLispを評価します。

el4r_ruby2lisp(obj)

objをEmacsLispの表現に変換します。

el4r_log(msg)

ログにmsgを出力します。

el4r_prin1(*objs)

objsひとつひとつの文字列表現をログに出力します。

el4r_p(*objs)

objs.inspectをログに出力します。

el4r_backtrace(msg=nil)

バックトレースをログに出力します。メッセージmsgを指定できます。

el4r_debug(msg = nil, &block)

デバッグモードのとき、msgまたはブロックの結果をログに出力します。

el4r_ruby_eval(source)

el4rの文脈でsourceを評価します。 デバッグモードで例外が起きたとき、スタックトレースをログに出力します。

11.4 低レベルメソッド

el(expression)

文字列やシンボルで指定したexpressionがそのままEmacsLispに展開されます。

11.5 ユーティリティメソッド

newbuf(param, &block)

paramはハッシュで、いろいろな要素を一度に指定してバッファを作成します。 バッファの名前はもちろん、内容やポイント位置から表示方法まで指定できます。 ブロックをつけると、バッファを初期化した後にブロックを実行します。

bufstr(buf=current_buffer)

バッファの内容を文字列で返します。

12 EmacsLispからRubyにアクセスする

EmacsRuby側で関数・変数定義ができるので必要かどうかわかりませんが、EmacsLispからRubyにアクセスすることもできます。 EmacsLispプログラム内ではel4r-ruby-eval関数を使います。 唯一の引数が評価するRuby式です。 尤も、EmacsRubyで書いたプログラムをEmacsLisp内でも使いたいならば、defunelvarでEmacsLispから見えるようにするのが賢明です。

対話的にEmacsRubyにアクセスするには

M-x el4r-ruby-eval-prompt

してください。バッファ全体のEmacsRuby式を評価するには

M-x el4r-ruby-eval-buffer

リージョン内は

M-x el4r-ruby-eval-region

してください。

また、examples/el4r-mode.rbをロードすると(初期設定でロードされています)、*ruby-scratch*バッファが作成されます。

13 使用例

EmacsRubyでのmajor-modeを作成例は lib/el4r/emacsruby/el4r-mode.rb を見てください。

各々のメソッドの使用例についてはtesting/test-el4r.rbを見てください。 テストプログラムは仕様書です。 正しい使い方や期待されるべき値など多くのことを語っています。

14 EmacsRubyスクリプトのパッケージ方法

14.1 ディレクトリ構成

バージョン1.0.0よりEmacsRubyスクリプトはsystem wideにインストールすることが可能になりました。 Ruby 1.8を使っているものと仮定すると次のような配置になります。

site_ruby/1.8/el4r/                    # el4rが使う通常のライブラリ
site_ruby/1.8/el4r/emacsruby/          # EmacsRubyスクリプト
site_ruby/1.8/el4r/emacsruby/autoload/ # el4r起動時に実行されるEmacsRubyスクリプト

通常のライブラリは普通に load / require で呼出されます。 EmacsRubyスクリプトは el4r_load で呼出されます。

14.2 autoloadディレクトリ

autoloadにあるEmacsRubyスクリプト群はel4r起動時に順次実行されます。 EmacsRubyスクリプトの初期設定・起動のために使います。

このディレクトリにファイルを配置するとき、ファイル名の先頭に数字を2文字含むようにしてください。たとえば次のようなファイルを配置すると、

00init.rb
50langhelp.rb
70el4r-mode.rb  

数字の若い順に実行されます。それ以外のファイルは無視されます。 Debian GNU/LinuxのEmacsが起動時に /etc/emacs/site-start.d/ に配置したEmacsLispを順次読み込むことに似ています。

14.3 setup.rbを使ったパッケージング

あおきさんのsetup.rbを使うと簡単にパッケージングできます。 EmacsRubyを配布するためには、次のようにディレクトリをレイアウトしてください。

bin/                         # 実行ファイル
lib/el4r/                    # el4rが使う通常のライブラリ
lib/el4r/emacsruby/          # EmacsRubyスクリプト
lib/el4r/emacsruby/autoload/ # el4r起動時に実行されるEmacsRubyスクリプト
ext/                         # 拡張ライブラリ
data/                        # データ

付属のel4r-mode.rb、langhelpはこの方法でパッケージングしています。

15 リンク


Back to Top

Valid XHTML 1.0!
rubikitch(rubikitch __nos_pam__ At __nos_pam__ ruby-lang DoT org)

Mail Form
Name Mail
URL


*1elispに倣ってerubyじゃありませんよ。erubyは別物ですから。
*2EmacsLisp原理主義者がうらやましい。
*3通常EmacsLispではそういう言い方はしないが。