Gauche開発支援拡張vim-gdev/emacs-gdev紹介

VimEmacs上のそれぞれで動作するGauche開発支援の拡張スクリプトを開発しています。
開発に一区切りつけるためにこの辺で紹介記事としてまとめておきたいと思います。


なにはともあれ動作画面のスクリーンショットから。

このスクリプトではVimEmacsでほぼ同じことを実現しています。


インストール方法
VimEmacsそれぞれのインストール方法から紹介します。
まず、どちらにも共通していることですがGaucheの0.9.2以上が必要になります。


Vimにインストール
Vimでは外部プロセスであるgoshと通信するためにvimprocが必須です。
また、補完候補を出すためのneocomplcacheと、検索インタフェースであるunite.vimが必要になります。
それぞれの必須スクリプトのインストール方法ですが、全てVimでは有名なスクリプトで検索すると解説ページが見つかると思うのでここでは割愛します。


次にvim-gdevの本体をインストールします。
ソースはgithub(こちら)にあるので、Vundleがインストールされている場合は

Bundle 'aharisu/vim-gdev'
と.vimrcに書いておいて、Vim上で
:BundleInstall
を実行すると必要なファイルをすべてインストールすることができます。
Vundleがない場合は、gitでクローンするか、downloadsのページからzip/tar.gzをダウンロードしてVimでロードできる位置に配置してください。


Emacsにインストール
Emacsではjsonを読むためのjson.elが必須です。
また補完候補を出すためにはAuto Complete Modeが必要で、検索インタフェースであるAnythingもあると機能をフルに活用することができます。
こちらの必須スクリプトもインストール方法を解説したページは多いと思うのでここでは割愛します。


次にemacs-gdevをインストールします。
ソースは全てgithub(こちら)にあります。
現状、auto-installなどのパッケージ管理プラグインに対応できていないので、
githubリポジトリをクローンするか、downloadsのページからzip/tar.gzをダウンロードして、Emacsでロードできる位置にファイルをすべて配置してください。


それぞれの設定ファイルの書き方に関しては、この紹介記事の最後に掲載します。


機能
続いて、このスクリプトできることについて紹介します。

  • バッファ内と、そこから読み込んでいる(use/import/loadした)モジュールを解析してシンボルを補完候補として表示をする
  • シンボルが定義されている場所へジャンプする
  • シンボルの詳細な情報を表示する

大まかに分類するとこの三つに分けられます。


補完機能と定義場所へジャンプはあまり説明が要らないと思います。
補完機能では、参照可能でグローバルなシンボル一覧をバッファごとに持っているので、
そのシンボル一覧をVimであればneocomplcacheEmacsであればAuto Complete Modeのソースとして表示・選択できます。
また、定義場所へジャンプは、指定されたシンボルが定義されているファイル・行を自動で表示します。


三つめのシンボルの詳細な情報を表示する機能について紹介します。
vim-gdev/emacs-gdevでは定義からシンボル名を解析するだけでなく、シンボルに付属する情報も一緒に解析しています。

  • シンボルタイプ(変数、関数、クラス...)
  • 定義モジュール(ファイル)名
  • シンボル名
  • 関数であれば引数、クラスであればスロット名
  • それぞれに付随するドキュメント

このようなプログラムを書いているときに必要になる情報を、この機能を利用することで素早く表示できるようになります。


この情報を素早く表示するための一覧・検索機能としてVimのunite.vim/EmacsのAnythingを利用しています。


それぞれの環境で使えるソースと対応するコマンドの一覧です。
Vim

  • gosh_info (コマンド:gosh_info_start_search, gosh_info_start_search_with_cur_keyword)
  • gosh_all_symbol
  • gosh_all_module

Emacs

  • anything-ginfo-source (コマンド:ginfo-anything, ginfo-with-word-anything)
  • anything-ginfo-all-symbol-source (コマンド:ginfo-anything-all-symbol)
  • anything-ginfo-all-module-source (コマンド:ginfo-anything-all-module)


・gosh_info/anything-ginfo-source
補完候補として表示したものと同じシンボルを一覧して検索できます。
特にVimのgosh_info_start_search_with_cur_keywordとEmacsのginfo-with-word-anythingコマンドは、
カーソル位置のシンボルを初期入力とし、マッチしたシンボルが一つだけならすぐに情報を表示します。
これによってコードを書きながら必要になればすぐに情報を参照するということができます。
このコマンドはキーにマッピングして使用することをお勧めします。


・gosh_all_symbol/anything-ginfo-all-symbol-source
Gaucheでロード可能な全てのモジュールの、全てのexportされたシンボルを一覧して検索します。
シンボル取得処理はかなり重い動作になるので非同期で実行されますが、目的のシンボルが一覧に現れるまで時間がかかってしまう可能性があります。


・gosh_all_module/anything-ginfo-all-module-source
まずロード可能な全てのモジュールを一覧・検索して、続けてそのモジュール内のシンボルを一覧・検索します。
モジュール → その中のシンボルと2段階の絞り込みによってシンボルを検索をするためのソースです。


終わりに
以上で一通りの機能について紹介が終わりました。
このvim-gdev/emacs-gdevは自分がGaucheを書いていて欲しいと思った機能をスクリプトとしてまとめたものです。
現状ではこれぐらいの機能しがありませんが、今後も何か思いつけば追加していくと思います。
余談ですが、
開発者自身は普段Vimをメインに使用しているので、emacs-gdevのバグに気づかないことが多いです。
使ってみて動作がおかしければぜひ報告してください。



では最後に、設定ファイルの書き方を掲載します。
キーマッピングなどは使いやすいものに変更して利用してください。


.vimrc

":Unite gosh_infoを実行します
nmap gi <Plug>(gosh_info_start_search)
":Unite カーソル位置のシンボルを初期値に:Unite gosh_infoを実行します
nmap gk <Plug>(gosh_info_start_search_with_cur_keyword)
imap <C-A> <Plug>(gosh_info_start_search_with_cur_keyword)

"ginfoウィンドウのスクロールアップ・ダウン
nmap <C-K> <Plug>(gosh_info_row_up)
nmap <C-J> <Plug>(gosh_info_row_down)
imap <C-K> <Plug>(gosh_info_row_up)
imap <C-J> <Plug>(gosh_info_row_down)
"ginfoウィンドウを閉じます
nmap <C-C> <Plug>(gosh_info_close)
imap <C-C> <Plug>(gosh_info_close)

"カーソル位置のシンボルが定義されている場所にジャンプ
nmap <F12> <Plug>(gosh_goto_define)
nmap <F11> <Plug>(gosh_goto_define_split)

.emacs

(require 'gdev)
;;emacs-gdevをインストールしたディレクトリを指定 (必須)
(setq gdev:root-dir "~/.emacs.d/emacs-gdev")

;;auto-completeがインストールされいる場合
(require 'goshcomp)
;;anythingがインストールされている場合
(require 'gosh-anything)

(defun scheme-mode-hooks ()
  ;;auto-completeにGauche用のソースを追加
  (setq ac-sources (append goshcomp:all-source ac-sources))

  ;;ginfo-with-word-anythingコマンドをC-cgkに割り当て
  (define-key scheme-mode-map "\C-cgk" 'ginfo-with-word-anything)

  ;;ginfoバッファのクローズ
  (define-key scheme-mode-map "\C-c0" 'gdev:close-ginfo)
  ;;ginfoバッファのスクロールアップ・ダウン
  (define-key scheme-mode-map "\C-c\C-n" 'gdev:scroll-up-ginfo)
  (define-key scheme-mode-map "\C-c\C-p" 'gdev:scroll-down-ginfo)

  ;;現在のバッファでカーソル位置のシンボルが定義されている場所にジャンプ
  (define-key scheme-mode-map [f12] (lambda ()
                                      (interactive)
                                      (gdev:gosh-goto-define (symbol-name (symbol-at-point)) nil)))
  ;;横方向に分割したバッファでカーソル位置のシンボルが定義されている場所にジャンプ
  (define-key scheme-mode-map [f11] (lambda ()
                                      (interactive)
                                      (gdev:gosh-goto-define (symbol-name (symbol-at-point)) 'h)))
  ;;縦方向に分割したバッファでカーソル位置のシンボルが定義されている場所にジャンプ
  (define-key scheme-mode-map [f10] (lambda ()
                                      (interactive)
                                      (gdev:gosh-goto-define (symbol-name (symbol-at-point)) 'v)))
  )
;;フックをschemeモードに登録
(add-hook 'scheme-mode-hook 'scheme-mode-hooks)