Vim / vim-railsdm-2018-07

D9d0ceb387e3b6de5c4562af78e8a910?s=47 ujihisa
July 14, 2018

Vim / vim-railsdm-2018-07

Japanese. RailsDM https://techplay.jp/event/679666 2018-07-14

How does Vim control modes states, such as entering to insert mode from normal mode? How is the C implementation? How can we patch? This is a journey to learn about them.

D9d0ceb387e3b6de5c4562af78e8a910?s=128

ujihisa

July 14, 2018
Tweet

Transcript

  1. 1.
  2. 3.
  3. 4.
  4. 9.

    Topic • オススメのVimの使い方 ◦ これを入れるだけで生産性7倍 ◦ EP, FPなどの重要なコンセプト ◦ 目でみたとこにカーソルが移動し、入力を全く

    阻害しないなめらかなVimさばきの技法 • if_ruby完全活用方法 • vimのC実装のrestart_edit 変数 ◦ vimの挿入モード (Insert mode)の仕様 ◦ vimの挿入モードに入る実装
  5. 10.
  6. 11.
  7. 14.

    Vimとは • Text editor (not text inputter) ◦ モード遷移 ▪

    ◦ Vim scriptランタイム ◦ ファイル・バッファ操作 ◦ 画面レンダリング • 人生
  8. 15.

    Vimとは • Text editor (not text inputter) ◦ モード遷移 ▪

    (ノーマルモード、挿入モードなど) ▪ 有限状態機械 ◦ Vim scriptランタイム ◦ ファイル・バッファ操作 ◦ 画面レンダリング • 人生
  9. 17.

    Vimのモード • 起動時はNormal mode • iとかaとかするとInsert modeに入る ◦ Q. Insert

    modeに入るキーは全部で何種類 か • <esc>とか<C-[>とかするとNormal modeに戻る
  10. 18.

    Vimのモード • 起動時はNormal mode • iとかaとかするとInsert modeに入る ◦ Q. Insert

    modeに入るキーは全部で何種類か ◦ 9 ◦ a, A, <insert>, i, I, gI, gi, o, O • <esc>とか<C-[>とかするとNormal modeに戻る
  11. 19.

    Normal modeからInsert modeに入る9つの四天王 • a カーソルの後に挿入 • A 行末に挿入 (:startinsert!)

    • <insert> iと同じ • i カーソルの前に挿入 (:startinsert) • I (インデントのあとの)行先頭に挿入 • gI 行先頭に挿入 • gi 最後に入力あった場所に挿入 • o 下の行を作ってそこに挿入 • O 上の行を作ってそこに挿入
  12. 20.

    Insert modeからnormal modeに入るには • <C-[> escと同じ • <esc> はい •

    <C-c> abbrとかInsertLeaveイベントを無視 • <C-@> 前回入力したものを入れて<esc> ◦ <C-a><esc>と同じ ◦ 互換性のためにあるっぽいので忘れていい ▪ 現代人ならnormap modeで.でリピート • <C-o> normal modeの命令一つだけできる特 殊状態に遷移 (後述)
  13. 21.

    Insert modeの<C-o>について詳しく :h i_CTRL-o • 何か一個だけコマンドできる ◦ hjkl移動とか (1回だけ) ◦

    :からのex command実行とか • 大抵の場合key mapping用 ◦ <C-o>:<C-u>call your_function()<Cr> ◦ とかをmapして 挿入モードの関数呼び出しとか
  14. 32.

    int main(int argc, char **argv) { return vim_main2(); } int

    vim_main2(void) { main_loop(FALSE, FALSE); } void main_loop(int cmdwin, int noexmode) { oparg_T oa; while(...) { if(...) { normal_cmd(&oa, TRUE); }} } src/main.c (超要約バージョン)
  15. 33.

    int main(int argc, char **argv) { return vim_main2(); } int

    vim_main2(void) { main_loop(FALSE, FALSE); } void main_loop(int cmdwin, int noexmode) { oparg_T oa; while(...) { if(...) { normal_cmd(&oa, TRUE); }} } src/main.c (超要約バージョン)
  16. 34.

    void normal_cmd(oparg_T *oap, int toplevel UNUSED) { cmdarg_T ca; int

    idx; if (...) { idx = find_command(ca.cmdchar); ca.arg = nv_cmds[idx].cmd_arg; (nv_cmds[idx].cmd_func)(&ca); } } src/normal.c (超要約バージョン)
  17. 35.

    void normal_cmd(oparg_T *oap, int toplevel UNUSED) { cmdarg_T ca; int

    idx; if (...) { idx = find_command(ca.cmdchar); ca.arg = nv_cmds[idx].cmd_arg; (nv_cmds[idx].cmd_func)(&ca); } } src/normal.c (超要約バージョン)
  18. 36.

    void normal_cmd(oparg_T *oap, int toplevel UNUSED) { cmdarg_T ca; int

    idx; if (...) { idx = find_command(ca.cmdchar); ca.arg = nv_cmds[idx].cmd_arg; (nv_cmds[idx].cmd_func)(&ca); } } src/normal.c (超要約バージョン)
  19. 37.

    void normal_cmd(oparg_T *oap, int toplevel UNUSED) { cmdarg_T ca; int

    idx; if (...) { idx = find_command(ca.cmdchar); ca.arg = nv_cmds[idx].cmd_arg; (nv_cmds[idx].cmd_func)(&ca); } } src/normal.c
  20. 38.

    void normal_cmd(oparg_T *oap, int toplevel UNUSED) { cmdarg_T ca; int

    idx; if (...) { idx = find_command(ca.cmdchar); ca.arg = nv_cmds[idx].cmd_arg; (nv_cmds[idx].cmd_func)(&ca); } } src/normal.c
  21. 39.

    void normal_cmd(oparg_T *oap, int toplevel UNUSED) { cmdarg_T ca; int

    idx; if (...) { idx = find_command(ca.cmdchar); ca.arg = nv_cmds[idx].cmd_arg; (nv_cmds[idx].cmd_func)(&ca); } } src/normal.c {'A', nv_edit, 0, 0}, {'I', nv_edit, 0, 0}, {'a', nv_edit, NV_NCH, 0}, {'i', nv_edit, NV_NCH, 0}, {K_INS, nv_edit, 0, 0}, {K_KINS, nv_edit, 0, 0}, {K_PS, nv_edit, 0, 0}, {'O', nv_open, 0, 0}, {'o', nv_open, 0, 0},
  22. 44.

    ここまでのまとめ • Vimはモードがあってiとか<esc>とかで切替 • Vim起動したらまず初期化 • main_loop()内で無限ループ • normal_cmd()内で ◦

    キー入力に対応する関数を探索 ◦ 探索には配列nv_cmdsを使用 ( • i とは、nv_edit() → invoke_edit() → edit()
  23. 46.

    本題 • restart_edit • vim頻出単語 • $ git grep '\brestart_edit\b'

    | wc -l ◦ 116 • undocumented (.txtに書かれてない)
  24. 52.

    https://vimconf.org/2018 VimConf 2018 • 2018-11-24 • 秋葉原 • 国際カンファレンス (英日も日英も全発表に対して

    同時通訳あり) • スポンサー急募中 • 発表募集中 https://vimconf.org/2018 https://vimconf.org/2018 https://vimconf.org/2018 https://vimconf.org/2018 https://vimconf.org/2018
  25. 54.

    本題 • restart_edit • vim頻出単語 • $ git grep '\brestart_edit\b'

    | wc -l ◦ 116 • undocumented (.txtに書かれてない)
  26. 58.
  27. 59.

    EXTERN int restart_edit INIT(= 0); /* call edit when next

    cmd finished */ (src/globals.h) // <C-o>とかじゃなくて普通のNormal mode restart_edit = 0; // iの中で<C-o>したとき restart_edit = 'i'; // 一欄 0, 'i', 'I', 'a', 'A', 'R', 'V'
  28. 63.

    Suggested solution • mode(): 現在のモードを返す関数 ◦ mode(1) で詳細 • 現在、normal

    modeでも, insert modeからの <C-o>、どちらもmode()返り値は"n" • 提案: Insert modeからの<C-o>は ◦ mode() = "n" (互換性保つ) ◦ mode(1) = "n<C-o>i"
  29. 65.
  30. 66.

    ujihisa • https://twitter.com/ujm • https://github.com/ujihisa • VimConf主催者 • 元Hootsuiteソフトウェアエンジニア ◦

    Twitterクライアントのあれ • 元Fablicソフトウェアエンジニア ◦ FRILとかラクマのあれ
  31. 69.

    ujihisa's past talks • 2017 ◦ 「高濃度雑談」大江戸 Ruby会議 ◦ 「Vim」TokyuRuby会議11

    (LT) ◦ 「How to develop CRuby easily with Vim」RubyKaigi 2017 (LT) • 2015 ◦ 「Vim + Clojure」VmConf 2015 • 2014 ◦ 「PM2」VimConf 2014 • 2013 ◦ 「vital.ProcessManager」VimConf 2013 • Older ◦ RubyConf 2009 ◦ RubyKaigi 2009 ◦ RubyKaigi 2008