readline覚書き
こないだ
complete
にて書いたpythonのrlcompleter.pyのattr_matches()で複数エントリが出てしまうの件、Linux側では普通に出るので不可思議だったのですが、GNU readline 5.2では重複がある場合は勝手にuniqされてしまうみたいですね。Leopard(というかMacOSX)に付属のReadlineがめっさ変、というだけだたのか*1。
ついでに、readlineってとっても簡単だったので使い方を書いておきます*2
基本
まず、ただ使うだけなら本当に簡単。readline()を呼ぶだけ。
#include <readline/readline.h> #include <readline/history.h> #include <string.h> main() { char* buf; while(buf = readline("> ")) { // <なにかやる> if (buf && *buf) add_history(buf); } }
こんなんで行編集+ヒストリ+デフォルトのファイル名/ユーザ名補完*3までできます。
カスタム補完
カスタム補完をするには、まず main でグローバル変数 rl_attempted_completion_functionにコールバック関数を設定します。
char** on_complete(const char* text, int start, int end); main() { char* buf; rl_attempted_completion_function = on_complete; //追加 while(buf = readline("> ")) { printf("processing your input <<%s>>\n", buf); // <なにかやる> if (buf && *buf) add_history(buf); } }
んでコールバックを実装。コールバックの戻り値はマッチする全文字列のリストなので char** です。
char* word_generator(const char* text, int state); // Generator char** on_complete(const char* text, int start, int end) { if (start==0) { //先頭の文字列だけ return rl_completion_matches(text, word_generator); } return NULL; }
コールバック関数には編集文字列のポインタと共に、補完対象の文字列範囲が渡されます。
今回は面倒なのでわかりやすいように先頭の文字列補完だけ反応することにしました。
rl_completion_matches()にgenerator関数を指定して実行すると、textにマッチする文字列リストを作ってくれるので、これを利用しています。
# NULL返すとデフォルトのファイル名/ユーザ名補完が動くみたい。
generator関数はcompletion_matches()から一回呼ばれるごとに「一つずつ」マッチし得る補完候補を返します。pythonのgeneratorの書き方(yield)とおんなじような感じ。
char* words[]= { "monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday", "months", "minites", "seconds", NULL, }; char* word_generator(const char* text, int state) { static int index, wordlen; char* name; if (state == 0) { // state = 0は初出の単語なので初期化 wordlen = strlen(text); index = 0; } while (name = words[index]) { index++; //[*] if (!strncmp(text, name, wordlen)) { return strdup(name); // dup必要 } } return NULL; }
てな感じで単に候補を一個ずつ返すだけ。あまりに簡単で涙が。。。
words[]にたとえば
"multiple", "multiple", "multiple", "multiple",
見たいなのを足してみると、
> [TAB][TAB] friday monday [multiple] seconds thursday wednesday minites months saturday sunday tuesday
確かにmultipleがuniqされてる!
ということみたい。Leopardのreadlineでやると複数出るんだろうな*4。
追記
rl_attempted_completion_function が rl_attempt_completion_functionと間違えてましたm_o_m。
あとLeopardのreadlineの素性は
/* $NetBSD: readline.h,v 1.18 2006/08/21 12:45:30 christos Exp $ */ #define RL_READLINE_VERSION 0x0402
となってました。
予想通り、この実装では uniqされずに
friday minites monday months multiple multiple multiple multiple saturday seconds sunday thursday tuesday wednesday multiple
のように出てました。ちなみにこのバージョンでコンパイルするには s/rl_completion_matches/completion_matches/する必要がありました。