Vim+Gaucheでの作業環境の設定

追記: 書き直した

rlwrapコマンドとの連携

rlwarp -c gosh

とすればreadline操作とTabキーによるファイル名補完が働くのだけど、さらに関数名の補完もできる(参照:http://d.hatena.ne.jp/leque/20071018/1192731534http://d.hatena.ne.jp/leque/20071113/1194939314)。
まずrlwrapはデフォルトでは補完の区切りにハイフンを含めているので、break-charsオプションで区切り文字を明示的に指定しておく。

rlwrap -c --break-chars "'"'(){}[].,#@;|`"' gosh

補完したい語は、$HOME/.コマンド名_completionsファイルに書く(環境変数 $RLWRAP_HOME が設定されている場合は、$RLWRAP_HOME/コマンド名_completionsファイル)。
Gauche用の補完語ファイルを作るには上の参照先にあるhttp://www.katch.ne.jp/~leque/software/misc/mk_gosh_completionsを使えばよいのだけど

  • gauche.matrix(gauhce.arrayからautoloadされている)を読んだところでエラーが出た。
  • obsoleteなモジュールを読み込んで注意される。
  • モジュールを読み込まなくても使える基本的な関数が補完語に含まれない。

だったので、いくらか変更した。
make_gosh_completions.scm

(use srfi-1)
(use file.util)

(define (main args)
  (define obsoleted-modules
    (list 'gauche.let-opt 'gauche.validator 'gauche.singleton))
  (define predefined-modules
    (list 'null 'scheme 'gauche))
  (define exclude-files
    (list "gauche/matrix.scm"))
  (library-for-each "**/*"
                    (lambda (_ path)
                      (unless (or (any (cut library-has-module? path <>)
                                    obsoleted-modules)
                                  (any (cut string-scan path <>)
                                    exclude-files)
                        (load path))))
                    :paths (filter absolute-path? *load-path*)
                    :allow-duplicates? #t)
  (let1 name-list (append
                    (append-map
                      (lambda (module)
                        (hash-table-keys (module-table (find-module module))))
                      predefined-modules)
                    (append-map module-exports (all-modules)))
    (for-each print
      (delete-duplicates (map symbol->string name-list)))))

実行結果をファイルに書き込んで「~/.gosh_completions」ファイルにする。これでTabキーで補完ができる。

vimでの作業

Emacsに移行するのが結局は楽なのかもしれないけど、vimで何とかする。
まず、入っているvimがtiny版だと構文ハイライトが効かないので、普通のvimを入れる。あとは、~/.vimrcに

syntax on

と書いておくか、exモードで直接設定。
さらにGauche用の設定ファイルが公開されているのでhttp://e.tir.jp/wiliki?vim%3Ascheme.vimを読んで、設定ファイルを~/.vim/syntaxまたは$VIMRUNTIME/syntaxに置いて、指示に従って.vimrcファイルに

autocmd FileType scheme :let is_gauche=1

と書く。「syntax on」は、この行よりも下に置く。
それから、rlwrapの補完機能のために作った.gosh_completionsファイルはvimの補完にも使える。

autocmd FileType scheme set complete+=k~/.gosh_completions

補完は挿入モードのときにCtrl-n、Ctrl-pでおこなう。
また、補完候補ファイルは複数指定することもできるしパターンを使うこともできる。たとえば

set complete+=k~/comp_list,k/usr/dict/*

みたいに。あと「autocmd ……」がない場合はschemeのファイル以外のときでも補完される。

試行錯誤したけどあまりうまくいかなかったこと

やりたいこと

vimで編集したものをそのままインタプリタに送れたらうれしい。

とりあえず、名前付きパイプを利用してみる。

  • vimで編集したものを名前付きパイプに送る。
  • インタプリタは名前付きパイプから入力を読む。
実現

名前付きパイプはmkfifoコマンドで作れる。

#!/bin/bash
npipe=to_gosh
trap 'rm  $npipe; exit 1' 1 2 15
mkfifo $npipe ; (PAGER=cat gosh -i < $npipe  & ) ; cat >$npipe ; rm $npipe

(PAGERをcatにしているのは、info関数を呼んだときmoreやlessのようなページャが立ち上がると動作がおかしくなるので)
キーボードからの入力も名前付きパイプに送っているので、キーボートから直接入力しても別のコマンドから名前付きパイプto_goshに書き込んでも、どちらでもインタプリタに送られる。ただし名前付きパイプから入力した内容はインタプリタ側にはエコーされずインタプリタからの結果しか表示されない。
さらにrlwrapも使う場合はrlwrapからこのスクリプトを呼ぶようにする。ただしrlwrapをかますと、名前付きパイプから入力したときに余計なプロンプトが出力されてしまう。
追記: 名前付きパイプに送った内容をインタプリタ側にも表示させるには例えば次のようにする(うまく動かないかも)。

#!/bin/bash
tmpd=$(mktemp -d)
public_npipe="to_gosh"
private_npipe="${tmpd}/private_npipe"
trap 'rm -rf "$tmpd" "${public_npipe}"; exit 1' 1 2 15

mkfifo "${public_npipe}"
mkfifo "${private_npipe}"

tee "${private_npipe}" <"${public_npipe}" >"/proc/${PPID}/fd/1" &
PAGER=cat gosh -i "$@" <"${private_npipe}" &
cat >"${private_npipe}"

rm -rf "$tmpd" "${public_npipe}"

あとはvimから名前付きパイプにデータを送ればよい。
例えばヤンク(コピー)したデータは@0というレジスタに入っているので、

:call writefile(split(@0, "\n"), "to_gosh")

とやれば、ヤンクした内容がto_goshに送られるし、.vimrcに

autocmd FileType scheme map <F5> :call writefile(split(@0, "\n"), "to_gosh")<cr>

と書けばこの機能がキー(ここではF5キー)に割り当てられる。ヤンクか削除でいったんレジスタに入れておかないと送れないというのは面倒かもしれないけど、ファイル全体とか任意の範囲を送りたい場合に使う。
ただこのままだと名前付きパイプが存在するかどうかに関係なく書き込んでいるので

function! ToGosh()
  let f = "to_gosh"
  if getftype(f) == "fifo"
    call writefile(split(@0, "\n"), f)
  endif
endfunction

autocmd FileType scheme map <F5> :call ToGosh()<cr>

とする。
名前付きパイプが存在しない場合は別端末でインタプリタを立ち上げるようにしたほうがよいかもしれない。

  else
    call system("gnome-terminal -x /usr/bin/env コマンド")
  endif

みたいにして。
ほかには、

autocmd FileType scheme map <F4> %y%:call ToGosh()<cr>

と書いておけば、F4キーで、カッコの対応位置までの範囲を送ることができる。挿入モードでも使う場合は、

autocmd FileType scheme imap <F4> <ESC>mx%y%:call ToGosh()<cr>`xa

を追加。


いろいろ挙動がおかしくなることがある。info関数を呼ぶとページャがうまく働かないとか(info関数が日本語のマニュアルを出力するように改変したことは関係なかった)。環境変数PAGERを/bin/catにしてしまえば一応は回避できる。