このページには、Vim プチハックをしている間に蓄積したメモを書きます。
対象となるソースのバージョンは7.0から7.1です。
make test
ノーマルモードのコマンド xxx は大体 normal.c の nv_xxx という関数で実装されている。
| 関数名 | コマンド |
|---|---|
| nv_page | CTRL-F / CTRL-B |
| nv_goto | gf |
ノーマルモードコマンドに対応する関数を探すには、ex コマンドラインから:
:ta nv_
と打って、<C-d> で補完してみるとよい(もちろん事前に tags を作っておく必要がある)。
ex コマンドは ex_cmds.c, ex_cmds2.c, ex_docmd.c で実装されている。 分け方はよくわからない。 ex モードでの補完などコマンドライン編集は ex_getln.c にある。
例えば i_CTRL-X_CTRL-F (ファイル名補完)はここで実現している。:
case CTRL_X_FILES:
if (expand_wildcards(1, &compl_pattern, &num_matches, &matches,
EW_FILE|EW_DIR|EW_ADDSLASH|EW_SILENT) == OK)
{
/* May change home directory back to "~". */
tilde_replace(compl_pattern, num_matches, matches);
ins_compl_add_matches(num_matches, matches,
#ifdef CASE_INSENSITIVE_FILENAME
TRUE
#else
FALSE
#endif
);
}
break;
typedef struct
{
linenr_T lnum; /* 行番号(1オリジン) */
colnr_T col; /* 桁番号(0オリジン) */
#ifdef FEAT_VIRTUALEDIT
colnr_T coladd;
#endif
} pos_T;
// v:lnum に数値を代入する set_vim_var_nr(VV_LNUM, lnum); // v:char に文字列を代入する set_vim_var_string(VV_CHAR, buf, -1);
int r; r = eval_to_number(curbuf->b_p_fex);
| enter_buffer | バッファに入ったとき実行される。 |
| echo_string | toString()に相当。 |
int
vim_chdir(new_dir)
char_u *new_dir;
char_u *s = skipwhite(p);
misc2.c の inc_cursor() と dec_cursor();
文字列中の指定したバイトが、マルチバイト文字の何バイト目かを返す。 カーソルがマルチバイト文字の中間に置かれてしまうのを避けるために使われる。
--pos.col;
#ifdef FEAT_MBYTE
if (has_mbyte)
pos.col -= (*mb_head_off)(linep, linep + pos.col);
#endif
static char_u inbuf[INBUFLEN + MAX_KEY_CODE_LEN]; // vim の入力バッファ
static int inbufcount = 0; /* number of chars in inbuf[] */
EXTERN typebuf_T typebuf /* typeahead buffer */
/*
* Used for the typeahead buffer: typebuf.
*/
typedef struct
{
char_u *tb_buf; /* buffer for typed characters */
char_u *tb_noremap; /* mapping flags for characters in tb_buf[] */
int tb_buflen; /* size of tb_buf[] */
int tb_off; /* current position in tb_buf[] */
int tb_len; /* number of valid bytes in tb_buf[] */
int tb_maplen; /* nr of mapped bytes in tb_buf[] */
int tb_silent; /* nr of silently mapped bytes in tb_buf[] */
int tb_no_abbr_cnt; /* nr of bytes without abbrev. in tb_buf[] */
int tb_change_cnt; /* nr of time tb_buf was changed; never zero */
} typebuf_T;
// stuff buffer はプログラム内部からキー入力をエミュレートするために使われるバッファ。
// ただしキー入力とは扱いが異なる点もある。(KeyStuffedでgrepせよ)
// 例えば . やリドゥのときはリドゥバッファからstuff bufferへコピーされる。(:ta nv_dot)
EXTERN struct buffheader stuffbuff /* stuff buffer */
/*
* header used for the stuff buffer and the redo buffer
*/
struct buffheader
{
struct buffblock bh_first; /* first (dummy) block of list */
struct buffblock *bh_curr; /* buffblock for appending */
int bh_index; /* index for reading */
int bh_space; /* space in bh_curr for appending */
};
インサートモードでの入力は、edit.c : insertchar() → vgetc()
+-vgetc() <int vgetc () at getchar.c:1503>
+-garbage_collect()
+-vgetorpeek()
+-get_real_state()
+-init_typebuf() <void init_typebuf () at getchar.c:878> // typebuf.ty_buf が NULL のときだけ初期化する
+-start_stuff() <void start_stuff () at getchar.c:390>
+-read_stuff()
+-line_breakcheck()
+-ui_breakcheck()
+-inchar() // キー取得(:source! による入力も含む)
| (略)
+-gui_update_cursor()
+-update_mouseshape()
+-ui_inchar() // 読み込んだ文字数を返す
+-mch_inchar() // os_unix.c などプラットフォームごとに定義
+-handle_resize()
+-WaitForChar() // 文字が入力可能になるまで待つ
| +-input_available() // input buffer または typeahead buffer に文字がたまっているなら true
| +-mch_write()
| | (略)
| +-RealWaitForChar() // 1文字が読み込み可能になるまで待つ。os_unix.c では ret = select(maxfd + 1, &rfds, NULL, &efds, tvp); select() が使えないシステムでは poll() を使っている。sniff インターフェイス、xclipboardなどいろんなところから文字を取ろうとしている。
| +-mzthreads_allowed() // mzscheme はかなり内部まで食い込んでいることがわかる
| +-gettimeofday()
| +-mzvim_check_threads()
| +-ConnectionNumber()
| +-poll()
| +-sniff_disconnect() // sniff も
| +-xterm_update() <void xterm_update () at os_unix.c:6216>
| | +-XtAppPending()
| | +-vim_is_input_buf_full()
| | +-XtAppNextEvent()
| | +-serverEventProc()
| | \-XtDispatchEvent()
| +-input_available()
+-trigger_cursorhold() // CursorHold を発生させられるかどうか。発生はさせないので注意。
+-typebuf_changed()
+-before_blocking()
\-read_from_input_buf() // キー入力を読み込む
+-fill_input_buf() // read(2) で inbuf にキー入力を読み込み、CTRL-C チェックを行う。
+-no_console_input()
+-gui_mch_update()
+-vim_is_input_buf_full() <int vim_is_input_buf_full () at ui.c:1529>
+-RealWaitForChar()
+-add_to_input_buf()
+-mch_memmove()
+-vim_free()
+-vms_read()
+-read()
+-isatty()
+-settmode() // 端末のモード設定(tcsetattr)
+-close()
+-dup()
+-read_error_exit() <void read_error_exit () at ui.c:1884>
+-getout()
+-STRCPY()
\-preserve_exit()
\-convert_input_safe()
+-typebuf_changed()
\-fix_input_buffer() // NUL, K_SPECIAL, CSI の各文字を3バイトのコードに置換する
+-mch_memmove()
+-K_THIRD()
\-K_SECOND()
read(2) で読み取ったキーはまず inbuf に格納され、read_from_input_buf() により typebuf にコピーされる。
マッピングの展開はvgetorpeek()の以下のところでやっている。
/*
* Loop until a partly matching mapping is found or
* all (local) mappings have been checked.
* The longest full match is remembered in "mp_match".
* A full match is only accepted if there is no partly
* match, so "aa" and "aaa" can both be mapped.
*/
短縮入力の展開はもっとあと。edit()→ins_tab()などの中。
:tag などのコマンドを実行している最中に CTRL-C を押すとコマンドを中断できる。 これは do_tag() の中で ui_breakcheck() を呼んで、CTRL-C 押下をチェックしているため。
ui_breakcheck()
+-mcn_breakcheck()
+-fill_input_buf(FALSE) // read(2) で inbuf にキー入力を読み込み、CTRL-C チェックを行う。
この処理を見ると、補完候補に改行を含めることは不可能。今後も見込みなしと思われる。
# TO tag FROM line in file/text 1 1 ins_complete 1348 edit.c 2 1 ins_compl_next 5070 edit.c 3 1 ins_compl_insert 4485 edit.c 4 1 ins_bytes 4318 edit.c 5 1 ins_bytes_len 1862 ins_bytes_len(p, (int)STRLEN(p)); 6 1 ins_char_bytes 1889 ins_char_bytes(p + i, n);
カーソル位置からバッファの先頭に向かって全文字を調べていくにはこんな感じ。 多少遅くなってもよければ、inc() と dec() を使ってもいい。
while (1)
{
if (backwards)
{
if (pos.col == 0) /* at start of line, go to prev. one */
{
if (pos.lnum == 1) /* start of file */
break;
--pos.lnum;
linep = ml_get(pos.lnum);
pos.col = (colnr_T)STRLEN(linep); /* pos.col on trailing NUL */
}
else
{
--pos.col;
#ifdef FEAT_MBYTE
if (has_mbyte)
pos.col -= (*mb_head_off)(linep, linep + pos.col);
#endif
}
}
// ここで linep[pos.col] が現在見ている文字
}