Skip to content

Instantly share code, notes, and snippets.

@h-east
Created December 22, 2012 18:42
Show Gist options
  • Save h-east/4360424 to your computer and use it in GitHub Desktop.
Save h-east/4360424 to your computer and use it in GitHub Desktop.
Investigation for inside of Vim.
Vimのソースコードを調査して分かったことをコメントしていく。
@h-east
Copy link
Author

h-east commented Dec 22, 2012

Vimのデータ構造は以下のようになっている。

tabpage -> frame -> window -> buffer

  • タブページ(tabpage)
    タブページはtopframeの代替手段である。
    片方向リンクリストになっている。
    先頭は first_tabpage である。
    現在のタブページは curtab でアクセスできる。
    タブページが内包しているウィンドウのポインタを各種保持している。(curwin/prevwin/firstwin/lastwin)
    ウィンドウが所属しているフレームのポインタを持っている。
  • フレーム(frame)
    フレーム自体はtree構造になっている。(parent/child/next/prev)
    ウィンドウの縦/横分割のレイアウト情報を保持している。
    現在のタブページの 先頭フレームは topframe でアクセスできる。
    :tabnext等により現在のタブページが変更された場合は topframe も更新される。
  • ウィンドウ(window)
    ウィンドウ自体は双方向リストになっている。
    所属しているフレームのポインタを持っている。
    参照しているバッファのポインタを持っている。
  • バッファ(buffer)
    バッファは双方向リストになっている。
    割り当てられているファイルの情報等を持っている。
    先頭は firstbuf である。タブページに関係なく firstbuf から全てのバッファを辿ることが出来る。
    最後は lastbuf である。
    現在のバッファは curbuf である。

ウィンドウループ処理について

  • 全タブページ内の全ウィンドウについて処理をする場合は FOR_ALL_TAB_WINDOWS() マクロを使用する。
  • 現在のタブページ内の全ウィンドウについて処理をする場合は FOR_ALL_WINDOWS() マクロを使用する。
  • ウィンドウレイアウトを意識した 現在のタブページ内の 全ウィンドウについて処理をおこなう場合は topframe からレイアウトをチェックしながら再帰処理でループさせる。
    ※ウィンドウがぶら下がっているフレームは fr_layoutメンバの値が FR_LEAF である。
    (例) frame_new_width()、last_status()

@h-east
Copy link
Author

h-east commented Dec 31, 2012

src/testdir で make test する時の端末(PuTTY)の高さでテストが失敗する。

80x24の場合
Test results:
ALL DONE

80x20の場合
Test results:
ALL DONE

80x15の場合
Test results:
test37 FAILED
test47 FAILED
TEST FAILURE

80x10の場合
Test results:
test37 FAILED
test47 FAILED
TEST FAILURE
※途中でコマンドラインに「-- 継続 --」と2回表示されたので、その都度キーを押した。

80x5の場合
Test results:
test13 FAILED
test31 FAILED
test37 FAILED
test47 FAILED
test62 FAILED
test84 FAILED
test88 FAILED
TEST FAILURE
※途中でコマンドラインに「-- 継続 --」と8回ほど表示されたので、その都度キーを押した。

@h-east
Copy link
Author

h-east commented Feb 9, 2013

:new:split の処理関数は ex_splitview() からの win_split() である。
仕様として :split の場合はwindowローカルなオプション(wp->w_onebuf_opt)は前カレントwindowのものが引き継がれる。 :newの場合は引き継がない。
しかし、win_split()を抜けた時点ではどちらのコマンドもwindowローカルオプション値は前カレントwinowのものを引き継いでいる。(win_init() が呼ばれている)
ということは、:newの場合、以降の処理のどこかでクリアされているはずである。
以下がそのコールスタックである。

(gdb) bt
#0  clear_string_option (pp=0xd494d8) at option.c:5426
#1  0x0000000000515ad6 in clear_winopt (wop=0xd49430) at option.c:10065
#2  0x000000000041182f in get_winopts (buf=0xd4d1f0) at buffer.c:2567
#3  0x000000000045b652 in do_ecmd (fnum=0, ffname=0x0, sfname=0x0, eap=
    0x7fff5ac9f890, newlnum=1, flags=1, oldwin=0x0) at ex_cmds.c:3437
#4  0x0000000000474497 in do_exedit (eap=0x7fff5ac9f890, old_curwin=0xd0a0b0)
    at ex_docmd.c:7838
#5  0x0000000000473a96 in ex_splitview (eap=0x7fff5ac9f890) at ex_docmd.c:7477
#6  0x000000000046bad4 in do_one_cmd (cmdlinep=0x7fff5ac9ff08, sourcing=0,
    cstack=0x7fff5ac9fa60, fgetline=0x480e8c <getexline>, cookie=0x0)
    at ex_docmd.c:2681
#7  0x000000000046919f in do_cmdline (cmdline=0x0, fgetline=
    0x480e8c <getexline>, cookie=0x0, flags=0) at ex_docmd.c:1122
#8  0x00000000004f58c5 in nv_colon (cap=0x7fff5aca0030) at normal.c:5417
#9  0x00000000004ee4b3 in normal_cmd (oap=0x7fff5aca0100, toplevel=1)
    at normal.c:1198
#10 0x00000000005c94e4 in main_loop (cmdwin=0, noexmode=0) at main.c:1306
#11 0x00000000005c8ec2 in main (argc=6, argv=0x7fff5aca0438) at main.c:1010

@h-east
Copy link
Author

h-east commented Feb 18, 2013

インサートモードで <C-X><C-O> してオムニ補完した場合のコールスタック(バックトレース)

(gdb) bt
#0  expand_by_function (type=13, base=0x1d3a400 "frame") at edit.c:3961
#1  0x0000000000428974 in ins_compl_get_exp (ini=0x853ae0) at edit.c:4341
#2  0x0000000000429523 in ins_compl_next (allow_get_expansion=1, count=1,
    insert_match=1) at edit.c:4695
#3  0x000000000042aa68 in ins_complete (c=15) at edit.c:5366
#4  0x0000000000423b87 in edit (cmdchar=111, startln=1, count=1) at edit.c:1402
#5  0x00000000004fc485 in invoke_edit (cap=0x7fff44ffafd0, repl=0, cmd=111,
    startln=1) at normal.c:9182
#6  0x00000000004fb470 in n_opencmd (cap=0x7fff44ffafd0) at normal.c:8494
#7  0x00000000004fcdd4 in nv_open (cap=0x7fff44ffafd0) at normal.c:9528
#8  0x00000000004ee7b7 in normal_cmd (oap=0x7fff44ffb0a0, toplevel=1)
    at normal.c:1198
#9  0x00000000005c9144 in main_loop (cmdwin=0, noexmode=0) at main.c:1306
#10 0x00000000005c8b22 in main (argc=2, argv=0x7fff44ffb3d8) at main.c:1010

@h-east
Copy link
Author

h-east commented Feb 18, 2013

オムニ補完のpopup menu 選択表示処理のコールスタック(バックトレース)

(gdb) bt
#0  pum_set_selected (n=0, repeat=0) at popupmnu.c:490
#1  0x000000000051ff76 in pum_display (array=0x26d0a60, size=56, selected=0)
    at popupmnu.c:258
#2  0x00000000004263f9 in ins_compl_show_pum () at edit.c:2982
#3  0x00000000004296b2 in ins_compl_next (allow_get_expansion=0, count=1,
    insert_match=1) at edit.c:4757
#4  0x000000000042991f in ins_compl_check_keys (frequency=0) at edit.c:4855
#5  0x000000000042900b in ins_compl_get_exp (ini=0x853ae0) at edit.c:4513
#6  0x0000000000429523 in ins_compl_next (allow_get_expansion=1, count=1,
    insert_match=1) at edit.c:4695
#7  0x000000000042aa68 in ins_complete (c=15) at edit.c:5366
#8  0x0000000000423b87 in edit (cmdchar=111, startln=1, count=1) at edit.c:1402
#9  0x00000000004fc485 in invoke_edit (cap=0x7fffa4207a00, repl=0, cmd=111,
    startln=1) at normal.c:9182
#10 0x00000000004fb470 in n_opencmd (cap=0x7fffa4207a00) at normal.c:8494
#11 0x00000000004fcdd4 in nv_open (cap=0x7fffa4207a00) at normal.c:9528
#12 0x00000000004ee7b7 in normal_cmd (oap=0x7fffa4207ad0, toplevel=1)
    at normal.c:1198
#13 0x00000000005c9144 in main_loop (cmdwin=0, noexmode=0) at main.c:1306
#14 0x00000000005c8b22 in main (argc=2, argv=0x7fffa4207e08) at main.c:1010

@h-east
Copy link
Author

h-east commented Feb 24, 2013

80x24の端末で s して _ した後のtopframeの情報(struct frame_S)
./vim -N -u NONE -c "set ls=2"

(gdb) p *topframe
$8 = {fr_layout = 2 '\002', fr_width = 80, fr_newwidth = 0, fr_height = 23,
  fr_newheight = 0, fr_parent = 0x0, fr_next = 0x0, fr_prev = 0x0, fr_child =
    0x1aad960, fr_win = 0x0}

(gdb) p *topframe->fr_child
$10 = {fr_layout = 0 '\000', fr_width = 80, fr_newwidth = 0, fr_height = 21,
  fr_newheight = 0, fr_parent = 0x1a9b740, fr_next = 0x1ad86f0, fr_prev = 0x0,
  fr_child = 0x0, fr_win = 0x1ad6f80}

(gdb) p topframe->fr_child->fr_win->w_height
$14 = 20

(gdb) p *topframe->fr_child->fr_next
$11 = {fr_layout = 0 '\000', fr_width = 80, fr_newwidth = 0, fr_height = 2,
  fr_newheight = 0, fr_parent = 0x1a9b740, fr_next = 0x0, fr_prev = 0x1aad960,
  fr_child = 0x0, fr_win = 0x1a980b0}

(gdb) p topframe->fr_child->fr_next->fr_win->w_height
$16 = 1
  • fr_layout = 2 (FR_COL) ということはそのframe自体はwindowを持たず、子frameに上下分割されたframeを持っていることを表している。(FR_ROWの場合は左右分割)
  • 各子frameの fr_layout = 0 (FR_LEAF) というのはwindow情報を持っているframeだということ。

@h-east
Copy link
Author

h-east commented Mar 20, 2013

インサートモードで してファイル名補完を処理した時の動作デバックする時の取っ掛かりbreakpoint
ins_complete() [edit.c:5180] 辺りにbreakpointをセットする。

 5176         else if (ctrl_x_mode == CTRL_X_FILES)
 5177         {
 5178             while (--startcol >= 0 && vim_isfilec(line[startcol]))
 5179                 ;
 5180             compl_col += ++startcol;
 5181             compl_length = (int)curs_col - startcol;
 5182             compl_pattern = addstar(line + compl_col, compl_length,
 5183                                                                 EXPAND_FILES);

@h-east
Copy link
Author

h-east commented Mar 22, 2013

b pathcmp してインサートモードで ファイル名補完を開始してbreakした時のバックトレース

(gdb) bt
#0  pathcmp (p=0x23e4ba0 "J4.txt", q=0x23e4bc0 "I1.txt", maxlen=-1)
    at misc2.c:6102
#1  0x00000000004d9758 in pstrcmp (a=0x23e4aa8, b=0x23e4ab0) at misc1.c:9787
#2  0x0000003346637d72 in msort_with_tmp.part.0 () from /lib64/libc.so.6
#3  0x0000003346637b5a in msort_with_tmp.part.0 () from /lib64/libc.so.6
#4  0x0000003346637b44 in msort_with_tmp.part.0 () from /lib64/libc.so.6
#5  0x00000033466380ac in qsort_r () from /lib64/libc.so.6
#6  0x00000000004d9eb9 in unix_expandpath (gap=0x7fff0da465a0, path=
    0x23e46a0 "*", wildoff=0, flags=43, didstar=0) at misc1.c:10001
#7  0x000000000051de2a in mch_expandpath (gap=0x7fff0da465a0, path=
    0x23e46a0 "*", flags=43) at os_unix.c:5471
#8  0x00000000004db08e in gen_expand_wildcards (num_pat=1, pat=0x855008,
    num_file=0x7fff0da46674, file=0x7fff0da46678, flags=43) at misc1.c:10577
#9  0x00000000004d9427 in expand_wildcards (num_pat=1, pat=0x855008, num_file=
    0x7fff0da46674, file=0x7fff0da46678, flags=43) at misc1.c:9323
#10 0x0000000000428ae8 in ins_compl_get_exp (ini=0x855020) at edit.c:4333
#11 0x0000000000429759 in ins_compl_next (allow_get_expansion=1, count=1,
    insert_match=1) at edit.c:4707
#12 0x000000000042ac9e in ins_complete (c=6) at edit.c:5378
#13 0x0000000000423cfb in edit (cmdchar=105, startln=0, count=1) at edit.c:1402
#14 0x00000000004fcc69 in invoke_edit (cap=0x7fff0da46980, repl=0, cmd=105,
    startln=0) at normal.c:9218
#15 0x00000000004fcc02 in nv_edit (cap=0x7fff0da46980) at normal.c:9191
#16 0x00000000004eeed7 in normal_cmd (oap=0x7fff0da46a50, toplevel=1)
    at normal.c:1199
#17 0x00000000005ca166 in main_loop (cmdwin=0, noexmode=0) at main.c:1322
#18 0x00000000005c9ac3 in main (argc=11, argv=0x7fff0da46d88) at main.c:1013

@h-east
Copy link
Author

h-east commented May 22, 2013

7.3.1004で nfa_regcomp_start() にbreak張って

/\_.*

したときのback trace.

(gdb) bt
#0  nfa_regcomp_start (expr=0xfbad00 "\\_.*", re_flags=1) at regexp_nfa.c:229
#1  0x0000000000543e07 in nfa_regcomp (expr=0xfbad00 "\\_.*", re_flags=1)
    at regexp_nfa.c:3664
#2  0x0000000000544217 in vim_regcomp (expr_arg=0xfbad00 "\\_.*", re_flags=1)
    at regexp.c:7827
#3  0x0000000000556fb5 in search_regcomp (pat=0xfbad00 "\\_.*", pat_save=0,
    pat_use=2, options=0, regmatch=0x7fff7ddfab40) at search.c:216
#4  0x0000000000557925 in searchit (win=0xfa40c0, buf=0xfa59b0, pos=
    0x7fff7ddfad80, dir=1, pat=0xfbad00 "\\_.*", count=1, options=12, pat_use=
    2, stop_lnum=0, tm=0x0) at search.c:560
#5  0x0000000000558f68 in do_search (oap=0x7fff7ddfaf70, dirc=47, pat=
    0xfbad04 "", count=1, options=542, tm=0x0) at search.c:1356
#6  0x00000000004fa4bc in normal_search (cap=0x7fff7ddfaea0, dir=47, pat=
    0xfbad00 "\\_.*", opt=512) at normal.c:6433
#7  0x00000000004fa40b in nv_search (cap=0x7fff7ddfaea0) at normal.c:6400
#8  0x00000000004f13e1 in normal_cmd (oap=0x7fff7ddfaf70, toplevel=1)
    at normal.c:1200
#9  0x00000000005d7652 in main_loop (cmdwin=0, noexmode=0) at main.c:1329
#10 0x00000000005d6faf in main (argc=7, argv=0x7fff7ddfb2a8) at main.c:1020

@h-east
Copy link
Author

h-east commented May 22, 2013

↑の続き。gdbでcして無限ループに陥った状態で ctrl-c で止めた時の back trace

(gdb) bt
#0  0x0000000000541c2a in nfa_regmatch (start=0xfef2f0, submatch=
    0x7fff7ddfa810, m=0x7fff7ddfa630) at regexp_nfa.c:2989
#1  0x00000000005439ad in nfa_regtry (start=0xfef2f0, col=0)
    at regexp_nfa.c:3528
#2  0x0000000000543dc4 in nfa_regexec_both (line=0xfe064f "", col=0)
    at regexp_nfa.c:3636
#3  0x0000000000544145 in nfa_regexec_multi (rmp=0x7fff7ddfab40, win=0xfa40c0,
    buf=0xfa59b0, lnum=1, col=0, tm=0x0) at regexp_nfa.c:3843
#4  0x000000000054431e in vim_regexec_multi (rmp=0x7fff7ddfab40, win=0xfa40c0,
    buf=0xfa59b0, lnum=1, col=0, tm=0x0) at regexp.c:7918
#5  0x0000000000557b7c in searchit (win=0xfa40c0, buf=0xfa59b0, pos=
    0x7fff7ddfad80, dir=1, pat=0xfbad00 "\\_.*", count=1, options=12, pat_use=
    2, stop_lnum=0, tm=0x0) at search.c:639
#6  0x0000000000558f68 in do_search (oap=0x7fff7ddfaf70, dirc=47, pat=
    0xfbad04 "", count=1, options=542, tm=0x0) at search.c:1356
#7  0x00000000004fa4bc in normal_search (cap=0x7fff7ddfaea0, dir=47, pat=
    0xfbad00 "\\_.*", opt=512) at normal.c:6433
#8  0x00000000004fa40b in nv_search (cap=0x7fff7ddfaea0) at normal.c:6400
#9  0x00000000004f13e1 in normal_cmd (oap=0x7fff7ddfaf70, toplevel=1)
    at normal.c:1200
#10 0x00000000005d7652 in main_loop (cmdwin=0, noexmode=0) at main.c:1329
#11 0x00000000005d6faf in main (argc=7, argv=0x7fff7ddfb2a8) at main.c:1020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment