Vim プチハックのメモ

このページには、Vim プチハックをしている間に蓄積したメモを書きます。

対象となるソースのバージョンは7.0から7.1です。

Contents

1   メモ

1.2   ノーマルモードコマンドの定義はnv_xxx

ノーマルモードのコマンド xxx は大体 normal.c の nv_xxx という関数で実装されている。

関数名 コマンド
nv_page CTRL-F / CTRL-B
nv_goto gf

ノーマルモードコマンドに対応する関数を探すには、ex コマンドラインから:

:ta nv_

と打って、<C-d> で補完してみるとよい(もちろん事前に tags を作っておく必要がある)。

1.3   ex コマンドの定義は ex_*.c

ex コマンドは ex_cmds.c, ex_cmds2.c, ex_docmd.c で実装されている。 分け方はよくわからない。 ex モードでの補完などコマンドライン編集は ex_getln.c にある。

1.4   インサートモードコマンドは edit.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;

1.5   オプションの定義はoptions.[ch]

  • グローバルオプションの値は option.h の p_xxx という変数で保持している。
  • バッファローカルオプションは buf_T の b_p_xxx。
  • ウィンドウローカルオプションは winopt_T の w_p_xxx。

1.6   行番号は1オリジン、桁番号は0オリジン

typedef struct
{
    linenr_T    lnum; /* 行番号(1オリジン) */
    colnr_T     col;  /* 桁番号(0オリジン) */
#ifdef FEAT_VIRTUALEDIT
    colnr_T     coladd;
#endif
} pos_T;

1.7   内部変数に値を代入するには

// v:lnum に数値を代入する
set_vim_var_nr(VV_LNUM, lnum);

// v:char に文字列を代入する
set_vim_var_string(VV_CHAR, buf, -1);

1.8   Vim スクリプトの式を評価するには

int         r;
r = eval_to_number(curbuf->b_p_fex);

1.9   ex コマンドを実行するには

do_cmdline_cmdを使うと簡単。

do_cmdline_cmd("tag do_cmdline_cmd");

1.10   関数メモ

enter_buffer バッファに入ったとき実行される。
echo_string toString()に相当。

1.11   便利な関数

1.11.1   メモリを確保してそこに文字列をコピーする

strdup() と同じ。

buf->b_p_inde = vim_strsave(p_inde);

1.11.2   エコーエリアにメッセージを表示する

printf() と同じフォーマットを受け付ける。

smsg(char_u *s, ...)

1.11.3   カレントディレクトリを変更する

    int
vim_chdir(new_dir)
    char_u    *new_dir;

1.11.5   カーソルを前後に移動させる

misc2.c の inc_cursor() と dec_cursor();

1.12   マルチバイト関連

1.12.1   mb_head_off

文字列中の指定したバイトが、マルチバイト文字の何バイト目かを返す。 カーソルがマルチバイト文字の中間に置かれてしまうのを避けるために使われる。

              --pos.col;
#ifdef FEAT_MBYTE
              if (has_mbyte)
                  pos.col -= (*mb_head_off)(linep, linep + pos.col);
#endif

1.13   キー入力関連

1.13.1   データ構造

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 */
};

1.13.2   インサートモードでの入力

インサートモードでの入力は、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 にコピーされる。

1.13.3   マッピングの展開

マッピングの展開は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()などの中。

1.13.4   CTRL-C チェック

: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);

1.14   その他

1.14.1   バッファ内の全文字をなめる

カーソル位置からバッファの先頭に向かって全文字を調べていくにはこんな感じ。 多少遅くなってもよければ、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] が現在見ている文字
}