メイン

2009年1月 5日

C、C++で開発する際に便利そうなelispを書いてみました
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加


ウノウでは特に最近、積極的にエンジニアを採用しています。
採用ページをご覧になり興味のある方、ぜひご応募ください!!


みなさん、Emacsしてますか?明けましておめでとうございます。C++でプログラミングし始めたはずなのにいつの間にかEmacsLispでプログラミングしていたことがあるbokkoです。

今日は、タイトルにもある通り、C、C++で開発する際に便利そうな自作のelispを紹介します。また、単にC、C++のソースコードを読んだりするのにも役立つと思います。紹介するのは以下の2つです。同じようなことをするのが既にありそうな気がして最初は探したんですが、見つからなかったので自分で書きました。


追記:(2009-01-05 15:51)

odzさんから同じような関数(ff-find-other-file)が標準で既にあるという指摘を頂きました。

上から順に解説していきます。

続きを読む "C、C++で開発する際に便利そうなelispを書いてみました" »

2008年7月 3日

続・Emacsを自分で拡張するためのTips
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加


今年の春頃からトリプルディスプレイで仕事しているbokkoです。なんだか同僚の視線が気になりますが、あえて空気を読まないことにしています。


前に「EmacsLispを自分で拡張する際のTips」という記事を書きましたが、今回はその続きです。

EmacsLispは難しい?

EmacsLisp(以下、elisp)は難しいという意見をたまに耳にしますが、elisp自体はそれほど難しいものではありません。ただ、関数名がバラバラでややこしかったり、マニュアルが巨大でどこを見ていいのかわからず、目的のことをするための関数が見つからない、といったようにユーザが難しいと感じるのはelispという言語そのものではなく、環境(OS、ウインドウ、バッファなど)とのインタフェースにあるため、結果的にEmacsLispは難しいと感じてしまうことが多いようです。
実際、elispでプログラミングしていて感じるのはウインドウやバッファなどのオブジェクトを操作するのにどうやったらいいのかわからなくて途方に暮れるというものです。例えば、

  • バッファを切り替えるにはどうすればいいか?
  • 外部プロセスを起動する方法がわからない
  • find-fileとかみたいに補完入力をしたいんだけどやりかたがわからない

というようなものがあります。調べ方やコツがわかってくれば割とすんなりいくことも多いのですが、慣れるまでは苦労すると思います。(僕もそうでした)

非対話的に引数で指定したファイルを開く

elispの拡張を書いていると、C-x C-fやM-x find-fileのように対話的にファイルを指定して開くのではなく、プログラム中で引数に指定したパスのファイルを開きたい場合があります。
それで以下のような処理を書いたとします。

(find-file-noselect "/home/bokko/test.txt")

名前からして↑の処理を実行するとファイルの内容が現在開いているバッファに表示されそうですが、実際にはそうなりません。この処理を実行すると、指定したファイルのバッファオブジェクトが返ってきます。目的のことをやるには今開いているバッファの内容をこのバッファに切り替える必要があります。バッファの切り替えにはswitch-to-buffer関数を使います。

(switch-to-buffer (find-file-noselect "/home/bokko/test.txt"))
;; もしくは
(switch-to-buffer (buffer-name (find-file-noselect "/home/bokko/test.txt")))

バッファからファイルに関する情報を取得する

現在開いているバッファのオブジェクトを取得するにはwindow-buffer関数を使います。

(window-buffer)

このオブジェクトからファイルの名前を取得するには、

(buffer-name (window-buffer))

とします。また、フルパスで取得するにはexpand-file-nameを使います。

(expand-file-name (buffer-name (window-buffer)))

外部プロセスを起動する

Emacsでは外部のプロセスを起動させることができます。既にあるシェルスクリプトなどを使いたい場合はstart-processのような関数を使うといいでしょう。映画生活では僕が去年ちょこちょこっと作ったCSSを縮小化するスクリプトを使っているのですが、僕の環境ではこれをEmacsから呼び出せるように↓のelispを読み込んでいます。

(defun compress-css ()
  "compress css"
  (interactive)
  (start-process "compress-css"
         "*compress-css*"
         "css-compressor")
  (message "all css file compressed."))

最後のcss-compressorが実行ファイル名です。ここではファイル名だけですが、実際にはパスの通った場所に置くかフルパスで指定します。また、この場合、実行結果は*compress-css*というバッファに吐き出されます。

補完入力をできるようにする

C-xC-f(find-file)でファイルを開くとき、Emacsでは補完入力ができます。去年ぐらいの頃にこれをどうやってやるのかわからず、結構悩んだ時期があったのですが、実は専用の関数が用意されているため、割と簡単にできます。以下のelispを実行すると、ミニバッファ内で補完入力ができるようになります。

(defvar tags-file-list '(
                 ("eigaseikatu" . "/home/bokko/eigaseikatu")
                 ("photozou" . "/home/bokko/photozou")
                 ))
(setq file (completing-read
   "Tags Key: "
   tags-file-list nil t "")) 

タブキーを押すと「eigaseikatu」と「photozou」が候補リストに表示されるようになり、入力した方の値がfileという変数に代入されます。前回紹介したvisit-tags-table-key.elではこの補完入力を行っていなかったため、毎回自分で正確に入力する必要がありましたが、今は上記のようにcompleting-readを使って補完入力をするようにしました。

elispで使う正規表現

elispで正規表現を扱う際は注意が必要です。というより、elispに限らず、プログラム中の正規表現を別言語に直接持って行く際には注意が必要です。というのもプログラミング言語で正規表現を扱う場合、その言語が正規表現をリテラルとして扱うのか文字列として扱うのか考慮する必要があります。特に文字列の場合はなにかと面倒です。elispでバックスラッシュにマッチさせるには、

\\\\

と書く必要があります。これはelispでは正規表現が文字列として扱われるので、余分にエスケープ処理が必要となるためです。sedやawk、もしくはPerlなどで正規表現を使ったプログラムを書いたことのある人には奇妙に思うかも知れませんが、elispやJavaのように正規表現をリテラルではなく、文字列として扱う言語ではこのようにエスケープを多用する必要があります。

デバッグ

elispのデバッグをする際はedebug-defunを使うのがいいでしょう。printfデバッグみたいなこともできますが、gdbみたいにステップ実行しながらデバッグすることができるので非常に便利です。
edebug-defunを使うには以下のelispを評価します。

(setq debug-on-error t)

あとはデバッグしたい関数の末尾でM-x edebug-defunと実行した後、その関数を実行すると、ステップ実行ができるようになります。スペースキーを押す度にミニバッファにelispの評価結果が表示されるので、実際にどのように動作しているのか把握しやすくなります。

目当ての関数を探し出す

先述したようにelispにはものすごくたくさんの関数があり、目的の関数を探し出すのが大変なのですが、実はelispで使える変数や関数はhelp-for-help関数(C-h)を使ってEmacs上で参照できるので、これである程度その大変さを和らげることができます。この関数を使うとUNIXのシステムコールをmanコマンドで調べるのと同じような感覚で関数の使い方について調べることができます。

ちょっとした応用

ここからは私が普段使っているelispのプログラムをちょこっと紹介します。

別のバッファで同じファイルを開く

プログラムを編集していると、同じファイルを複数のバッファから編集したくなるときがあります。例えば、同じファイル内にBという関数を呼び出しているAという関数があって、その両方を編集する必要があるような状況です。単純に同じファイルを同じバッファで開いてしまっても意味はないので、ウインドウを縦に分割して、2つのバッファに同じファイルの内容を表示してみます。

(defun open-same-file ()
  (interactive)
  (let ((same-file (find-file-noselect (buffer-name (window-buffer)))))
    (progn (split-window-horizontally)
    (other-window 1)
    (switch-to-buffer same-file))))

↑の関数を実行すると、C-x 3に割り当てられている関数であるsplit-window-horizontallyによってウインドウが縦に分割され、分割してできた新しい方に元々編集していたバッファの内容が表示されます。僕の環境では、以下のようにelscreen上の別のスクリーンで開くようにしています。

(defun elscreen-open-same-file ()
  (interactive)
  (let ((same-file (find-file-noselect (buffer-name (window-buffer)))))
    (elscreen-create)
    (switch-to-buffer same-file)))

C言語のヘッダファイルとソースファイルを関連づける

C言語でプログラムを書く際、関数の宣言と定義を分離するため、ソースファイル(.c)とは別にヘッダファイル(.h)を書くのが一般的です。そのため、ソースファイルとヘッダファイルとの間を行ったり来たリすることがあります。ずっと面倒だと思っていたので、以下のような関数を呼び出すだけで対応するファイルを開けるようにしました。

(defun c-open-relational-file (how-open-type)
  (interactive "nOpen-Type: ")
  (defun get-opened-file-name-prefix (file-name)
    (string-match "^\\([^.]+\\)\\.[^.]+$" file-name)
    (match-string 1 file-name))
  (defun get-ext-type (file-name)
    (string-match "\\.\\([^.]+\\)$" file-name)
    (match-string 1 file-name))
  (defun get-opening-file-name (file-name-prefix ext-list)
    (let ((opening-file-name (concat file-name-prefix "." (car ext-list))))
      (cond ((null (car ext-list))             nil)
            ((file-exists-p opening-file-name) opening-file-name)
            (t                                 (get-opening-file-name file-name-prefix
                                                                      (cdr ext-list))))))
  (let* ((ext-map '(
                    ("h" . ("c" "cpp" "cxx"))
                    ("c" . ("h" "s"))
                    ("s" . ("c"))
                    ("cpp" . ("hpp" "h"))
                    ))
         (opened-file-name (buffer-file-name (window-buffer)))
         (opened-file-name-prefix (get-opened-file-name-prefix opened-file-name))
         (opened-file-ext-type (get-ext-type opened-file-name))
         (opening-file-ext-type-list (cdr (assoc opened-file-ext-type ext-map)))
         (opening-file-name (get-opening-file-name opened-file-name-prefix
                                                   opening-file-ext-type-list))
         (opening-file-buffer (find-file-noselect opening-file-name)))
    (cond ((= how-open-type 1) (elscreen-switch-or-create opening-file-buffer))
          ((= how-open-type 2) (progn (split-window-horizontally)
                                      (other-window 1)
                                      (switch-to-buffer opening-file-buffer)))
          (t                   (message "Illegal Type")))))

この関数を実行すると、今編集しているファイルがtest.cというファイルの場合、test.hを別のバッファで開くことができます。最初にどう開くのかを数字で指定して、1の場合はelscreenを使って別のバッファで開くようにし、2の場合はウインドウを縦に分割して新しくできたバッファに対応するファイルを読み込みます。また、ソースファイルとヘッダファイルだけでなく、アセンブリコードのファイル(.s)も開けるようにしています(ただし、ヘッダファイルがある場合はそちらを優先します)。
上記のelscreen-switch-or-createは前回紹介したswitch-to-elscreen-createと同じものです。

追記(2009-01-05):

拡張子の直前以外で「.」があるとうまく関連ファイルを開けないバグを修正しました。ソースはこちら。

elファイルを再帰的にバイトコンパイルする

elispのプログラムはそのままでもEmacsが読み込むことができますが、バイトコードにコンパイルすることによって高速化することができます。大きな拡張だと、Makefileが用意されていて、(configure→)make→make installでインストールできるようになっているものもありますが、そうでないものも存在するため、たまに以下のelispを使って複数のelファイルを一気にコンパイルしています。

(defun my-byte-compile-directory ()
  (interactive)
  (defun byte-compile-directories (dir)
    (if (file-directory-p dir)
        (byte-compile-directory-r (mapcar (function (lambda (f) (concat dir "/" f)))
                                          (directory-files dir)))))
  (defun byte-compile-directory-r (file-list)
    (cond ((null (car file-list))
           nil)
          ((and (file-directory-p (car file-list))
                (not (string-match "/\.\.?$" (car file-list))))
           (byte-compile-directories (car file-list))
           (if (not (null (cdr file-list)))
               (progn
                 (byte-compile-directories (cadr file-list))
                 (byte-compile-directory-r (cdr file-list)))))
          ((string-match "\.el$" (car file-list))
           (progn
             (byte-compile-file (car file-list))
             (byte-compile-directory-r (cdr file-list))))
          (t
           (if (not (null (cdr file-list)))
               (byte-compile-directory-r (cdr file-list))))))
  (byte-compile-directories (replace-regexp-in-string "/$" "" default-directory)))

依存関係とかが複雑でないなら、バイトコンパイルしたい拡張のディレクトリに移動して↑の関数を実行するだけで全てのelファイルをバイトコンパイルしてelcファイルを生成することができます。

追記:(2008/7/3 18:20)

my-byte-compile-directory関数にバグがあったので修正しました。バグの内容ですが、再帰的にバイトコンパイルすると書いてあるのにサブディレクトリのelファイルをコンパイルできるようになっていませんでしたorz。上記の修正版ではちゃんとサブディレクトリのelファイルもバイトコンパイルされます。

参考文献

やさしいEmacs‐Lisp講座

おまけ

display3.png

2008年1月22日

Emacsを自分で拡張する際のTips
このエントリーをブックマークに追加 このエントリーをlivedoorクリップに追加

ウノウでは少数派なEmacsユーザのbokkoです。

今回は自分でEmacsLisp(以下、elisp)を書いてEmacsを拡張する際のTipsについて紹介します。

拡張する際に気に留めておくこと

Emacsを拡張する上で覚えておくべきなのはEmacs上で行える対話的な動作は「M-x 関数名」で実現できるということです。例えば、C-bはカーソルを1文字分左へ戻しますが、これは「M-x backward-char」とタイプすることと同じです。もしC-bが押しにくい(多分私だけです)のであれば、以下のようなelispを評価してキーバインドを変更することができます。

(global-set-key "\C-l" 'backward-char)

Emacsではこのようなキーバインドに限らず、あらゆる操作をelispを使って変更したり、新たに定義することができます。関数名もしくはキーバインドの一方しかわからない場合は、「help-for-help f」(C-h f)とタイプして関数名を入力したり、「help-for-help k」(C-h k)とタイプしてキーを入力すると、それに対応する関数の説明やキーバインドが表示されますので、これで確かめましょう。

実際の拡張例

次は、最近私が実際に拡張した例について紹介します。(もしかしたら既に同じようなものがあるかもしれません)

find-tagとelscreenの連携

elscreenは簡単に言うとEmacsをタブエディタっぽく使えるようにするものです。人によってはGNU SCREENのEmacs版と言えばしっくりくるかもしれません。ここではタブではなく、スクリーンと呼ぶようにします。find-tagはetagsで作成した関数のインデックスを利用して、指定した関数が定義されているファイルのポイントへジャンプする関数です。しかし、find-tagはデフォルトでは指定した関数が定義されているファイルを開く際、単に今開いているバッファを切り替えるだけです。elscreenを使っている場合、どうせならあたらしいスクリーンで開いてほしいものです。さらに言うと既にそのバッファを開いているスクリーンがあるなら、そっちのスクリーンにジャンプするとか、今開いているバッファが*scratch*バッファなら普通にswitch-to-bufferでバッファを切り替えるようにしたいところです。というわけで、find-tagの関数の中身を見てみます。(コメントは省いています)

(defun find-tag (tagname &optional next-p regexp-p)
  (interactive (find-tag-interactive "Find tag: "))
  (let ((buf (find-tag-noselect tagname next-p regexp-p)))
    (condition-case nil
    (switch-to-buffer buf)
      (error (pop-to-buffer buf)))))

これを見るとswitch-to-bufferという関数にbufという変数を渡しています。プログラマならbufというのはバッファのことだと想像できると思います。switch-to-bufferは現在編集しているバッファを切り替える関数です。ここでしたいのは単にバッファを切り替えるのでなく、新しいスクリーンを作成してそのスクリーン上でバッファを開くことですので、この部分を別の関数に置き換えてやるとよさそうです。しかし、実際には以下のような手順を踏む必要があります。

  • バッファ(buf)が既にどこかのスクリーン上に存在するか調べる
  • 既に存在する場合はそのスクリーン上のバッファにジャンプし、ない場合は新たにスクリーンを作成してその上でバッファを開く

この手順を踏むにはバッファからスクリーン、もしくはスクリーンからバッファを特定したり、指定したスクリーンにジャンプする操作とスクリーンを新規作成する操作が必要です。ここで最初に言ったことを思い出してください。Emacs上で行える対話的な動作は「M-x 関数名」で実現できます。ということはこれらの操作を行うための関数を組み合わせて目的の操作を定義することができそうです。

スクリーンを新規作成する

elscreenはデフォルトではC-z C-cでスクリーンを新規作成できます。このキーバインドが割り当てられている関数を「help-for-help k」(C-h k)で調べてみると、この操作にはelscreen-createという関数が割り当てられています。実際に(elscreen-create)と書かれた行の末尾にカーソルを持っていってC-x C-eとタイプしてみると、新しいスクリーンが作成されるのがわかると思います。(elispコードの末尾にカーソルがある状態でC-x C-eとするとそのS式を評価して、その結果がミニバッファに表示されます)

指定したスクリーンにジャンプする

elscreenを使っていると画面上部に番号とファイル名がセットになったスクリーンのリストが表示されます。別のスクリーンにジャンプするには「C-z 番号」とタイプします。このキーバインドを「help-for-help k」(C-h k)で調べてみると、elscreen-jumpという関数があるのがわかります。試しに以下の関数を実行します。

(elscreen-jump 1)

これで番号1のスクリーンに移動できればいいのですが、エラーが出ます。なので、この関数をもう少し深い追いしてみましょう。elscreen-jumpの中身は以下のようになっています。

(defun elscreen-jump ()
  "Switch to specified screen."
  (interactive)
  (let ((next-screen (string-to-number (string last-command-char))))
    (if (and (<= 0 next-screen) (<= next-screen 9))
        (elscreen-goto next-screen))))

変数名などから推測するに最後にタイプしたキーが0から9の間であればelscreen-gotoという関数が呼ばれるようです。実際に使ってみます。

(elscreen-goto 1)

上記のelispを実行すると、番号1のスクリーンにジャンプすることができます。

バッファからスクリーン、もしくはスクリーンからバッファを特定する

さきほどのelscreen-gotoの引数は数字でした。ということはスクリーンの番号からバッファを特定することができそうです。また、その逆のことをする関数もありそうです。こればかりはhelp-for-helpで探すわけにはいかないので、実際のコードを追ってみるしかありません。そんなわけでelscreenのソースを眺めてみると、elscreen-find-screen-by-bufferという関数が見付かりました。番号が2のスクリーンで開いているファイルがetags.elの場合、以下のelispを実行すると結果は以下の通りになります。

(elscreen-find-screen-by-buffer "etags.el")
2

また、さらに追っていくとelscreen-get-window-configurationという逆のことをする関数も見付かりました。

(elscreen-get-window-configuration 2)
(#<window-configuration> #<marker at 12071 in etags.el>)

結果を見る限り、etags.elのバッファに関する情報と見てよさそうです。あとはスクリーンの一覧が取得できれば、スクリーンからバッファを特定する処理が書けそうです。さらにelscreenのコードを見ていくとelscreen-get-screen-listという関数が見つかります。スクリーンが3つある状態でこの関数を評価すると、

(elscreen-get-screen-list)
(2 1 0)

という風にスクリーン番号のリストが取得できます。
これらの関数を組み合わせて出来上がったのが以下の関数です。

(defun switch-to-elscreen-create (buf)
  (defun create-new-buf (buf)
    (if (equal elscreen-default-buffer-name (buffer-name (window-buffer)))
        (switch-to-buffer buf)
      (elscreen-create)
      (switch-to-buffer buf)))
  (defun switch-to-elscreen-create-inner (screen-list buf)
    (cond ((null (elscreen-get-window-configuration (car screen-list)))
           nil)
          ((equal (car screen-list) (elscreen-find-screen-by-buffer (buffer-name buf)))
           (elscreen-goto (car screen-list)))
          (t
           (switch-to-elscreen-create-inner (cdr screen-list) buf))))
  (if (null (switch-to-elscreen-create-inner (elscreen-get-screen-list) buf))
      (create-new-buf buf)
    t))

上記の処理ではさらに、今開いているバッファが*scratch*バッファなら単純にswitch-to-bufferでバッファを切り替えるようにしています。あとはfind-tag関数内のswitch-to-bufferをこの関数に置き換えてやれば完成です。もしくは、find-tag-elscreenという風に別の関数を定義した方がいいかもしれません。

(defun find-tag (tagname &optional next-p regexp-p)
  (interactive (find-tag-interactive "Find tag: "))
  (let ((buf (find-tag-noselect tagname next-p regexp-p)))
    (condition-case
    nil
    (switch-to-elscreen-create buf)
      (error (pop-to-buffer buf)))))

TAGSファイルをプロジェクト名で指定できるようにする

TAGSファイルを作成しておくと、ソースコードの検索が楽になります。
しかし、TAGSファイルを読み込むにはいちいちそのファイルがあるディレクトリを指定しなければなりません。また、ソースコードの規模が余程大きい場合を除けば、TAGSファイルは一つのプロジェクトに一つあれば十分です。なので、私はTAGSファイルのあるディレクトリを指定するのではなく、そのTAGSディレクトリが関連づけられているキーワードを指定するようにしています。抱えているプロジェクトが一つだけならEmacsを起動すると同時にそのTAGSファイルを読み込んでもよさそうですが、ここでは複数のプロジェクトを抱えていると仮定して話を進めます。

例えば、eigaとphotoというプロジェクトを抱えているとします。それぞれのプロジェクトのTAGSファイルを元にインデックスを参照するには、M-x visit-tags-tableでTAGSのあるディレクトリを指定する必要があります。emacsを起動したディレクトリにTAGSファイルがあれば簡単なのですが、そうとは限りません。そこで、visit-tags-table-keyという関数を作成して、この関数を呼び出した際にeigaとタイプすればeigaプロジェクトのTAGSファイルを読み込み、photoとタイプすればphotoプロジェクトのTAGファイルを読み込むようにしてみました。


まず、visit-tags-tableの中身を見てみましょう。(find-tagと同じく、コメントは省いています)

(defun visit-tags-table (file &optional local)
  (interactive (list (read-file-name "Visit tags table: (default TAGS) "
                     default-directory
                     (expand-file-name "TAGS"
                               default-directory)
                     t)
             current-prefix-arg))
  (or (stringp file) (signal 'wrong-type-argument (list 'stringp file)))
 (let ((tags-file-name file))
    (save-excursion
      (or (visit-tags-table-buffer file)
      (signal 'file-error (list "Visiting tags table"
                    "file does not exist"
                    file)))
      (setq file tags-file-name)))
  (if local
      (set (make-local-variable 'tags-file-name) file)
    (setq-default tags-file-name file)))

visit-tags-tableを呼び出すとミニバッファにVisit tags table: (default TAGS)と表示されます。ここでTAGSファイルが置かれているディレクトリを指定するわけですが、指定したディレクトリの文字列を関数に渡してやる必要があります。Emacsでこの役目を務めるのはinteractiveという関数です。interactiveはその関数が対話的な動作であるということを宣言するための関数ですが、これにある形で引数を渡すと、関数に渡す引数をユーザが入力することができるようになります。ここでは、ディレクトリではなくプロジェクト名を入力してTAGSファイルを読み込むようにしたいので、visit-tags-tableに渡す引数の部分、つまりinteractiveの中を変えてみると良さそうです。

まず、プロジェクト名から各プロジェクトのTAGSファイルを見つけられるようにする必要があります。elispでは連想配列(のようなもの?)が使えるので、プロジェクト名をインデックスに使います。

(defvar tags-file-list '(
               ("eiga" . "/home/bokko/eiga/")
               ("photo" . "/home/bokko/photo/")
               ))

次に、interactive関数の中身に注目します。

(interactive (list (read-file-name "Visit tags table: (default TAGS) "
                   default-directory
                   (expand-file-name "TAGS"
                             default-directory)
                   t)
           current-prefix-arg))

パッと見ても何をやってるのかよくわかりませんが、default-directoryとexpand-file-nameが何なのかわかれば大体の見当は付きそうです。この2つを評価してみます。

default-directory
"~/"
(expand-file-name "TAGS" default-directory)
"/home/bokko/TAGS"

~/はEmacsを起動したディレクトリです。また、expand-file-nameは相対パスを絶対パスに変換してくれるようです。以上のことからディレクトリを入力するのではなく、プロジェクト名を入力するようにして、そのプロジェクトに対応するディレクトリにあるTAGSファイルを読み込む関数は以下のように定義できます。

(defun visit-tags-table-key (file &optional local)
  (interactive "sTags-key: ")
  (defun find-tags-directory (tags-key file-key-list)
    (if (equal tags-key (car (assoc tags-key file-key-list)))
    (cdr (assoc tags-key file-key-list))
      nil))
  (setq file (expand-file-name "TAGS"
                   (find-tags-directory file tags-file-list)))
  (or (stringp file) (signal 'wrong-type-argument (list 'stringp file)))
  (let ((tags-file-name file))
    (save-excursion
      (or (visit-tags-table-buffer file)
      (signal 'file-error (list "Visiting tags table"
                    "file does not exist"
                    file)))
      (setq file tags-file-name)))
  (if local
      (set (make-local-variable 'tags-file-name) file)
    (setq-default tags-file-name file)))

プログラミングしながら環境構築

このようにEmacsではelispで自分好みの開発環境をプログラミングしながら整えることができるので、非常にかゆいようなところでも手が届くようになります。例えば、WebアプリケーションをMVCなフレームワークを利用して開発していると、アクションに相当するファイルを開いている時にビューのファイルを別のスクリーンで開くような仕組みが欲しくなります。最初からそういう機能をエディタ側で持っていることはまずないですが、その気になれば自分で作ることができます。MVCなフレームワークではディレクトリ階層が深くなりがちでアクションとビューのファイルを開くのにいちいち下り坂と上り坂を行き来するようなことをしないといけないことが多々あるのでこれは是非欲しい機能です(実際、rails.elのようにそれに似たような支援を行うものも存在します)。私の場合、社内で使っているフレームワークで似たような状況に遭遇したので、つい最近、簡単なelispを書いてアクションとビューをコマンド1つで(elscreen上の)別々のスクリーンで開けるようにしました。

参考文献

Emacsの基礎を骨の随まで叩き込みたい人は前者を読むのがいいと思います。また、後者は実際にEmacsを拡張する際にリファレンスとして手元に置いておくと幸せになれるかもしれません。

  [PR] 転職


About Emacs

ブログ「ウノウラボ Unoh Labs」のカテゴリ「Emacs」に投稿されたすべてのエントリーのアーカイブのページです。過去のものから新しいものへ順番に並んでいます。

前のカテゴリはC、C++です。

次のカテゴリはiphoneです。

他にも多くのエントリーがあります。メインページアーカイブページも見てください。

ウノウサービス