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. None
  2. Vim Tatsuhiro Ujihisa RailsDM 2018-07-14

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

    阻害しないなめらかなVimさばきの技法 • if_ruby完全活用方法 • vimのC実装のrestart_edit 変数
  6. src/main.c

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

    阻害しないなめらかなVimさばきの技法 • if_ruby完全活用方法 • vimのC実装のrestart_edit 変数
  8. Topic • オススメのVimの使い方 ◦ これを入れるだけで生産性7倍 ◦ EP, FPなどの重要なコンセプト ◦ 目でみたとこにカーソルが移動し、入力を全く

    阻害しないなめらかなVimさばきの技法 • if_ruby完全活用方法 • vimのC実装のrestart_edit 変数
  9. Topic • オススメのVimの使い方 ◦ これを入れるだけで生産性7倍 ◦ EP, FPなどの重要なコンセプト ◦ 目でみたとこにカーソルが移動し、入力を全く

    阻害しないなめらかなVimさばきの技法 • if_ruby完全活用方法 • vimのC実装のrestart_edit 変数 ◦ vimの挿入モード (Insert mode)の仕様 ◦ vimの挿入モードに入る実装
  10. None
  11. None
  12. Vimとは • Text editor ◦ ◦ ◦ ◦ • 人生

  13. Vimとは • Text editor (not text inputter) ◦ ◦ ◦

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

    ◦ Vim scriptランタイム ◦ ファイル・バッファ操作 ◦ 画面レンダリング • 人生
  15. Vimとは • Text editor (not text inputter) ◦ モード遷移 ▪

    (ノーマルモード、挿入モードなど) ▪ 有限状態機械 ◦ Vim scriptランタイム ◦ ファイル・バッファ操作 ◦ 画面レンダリング • 人生
  16. Vimのモード • 起動時はNormal mode • iとかaとかするとInsert modeに入る ◦ • <esc>とか<C-[>とかするとNormal

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

    modeに入るキーは全部で何種類 か • <esc>とか<C-[>とかするとNormal modeに戻る
  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に戻る
  19. Normal modeからInsert modeに入る9つの四天王 • a カーソルの後に挿入 • A 行末に挿入 (:startinsert!)

    • <insert> iと同じ • i カーソルの前に挿入 (:startinsert) • I (インデントのあとの)行先頭に挿入 • gI 行先頭に挿入 • gi 最後に入力あった場所に挿入 • o 下の行を作ってそこに挿入 • O 上の行を作ってそこに挿入
  20. Insert modeからnormal modeに入るには • <C-[> escと同じ • <esc> はい •

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

    :からのex command実行とか • 大抵の場合key mapping用 ◦ <C-o>:<C-u>call your_function()<Cr> ◦ とかをmapして 挿入モードの関数呼び出しとか
  22. ドットリピート • . で直前のテキスト編集を繰り返す • Insert modeでの一連の入力を再現 ◦ 一連の、とは →デモ

  23. Vimのcountの概念 • countは至るところにある • 任意の何かに付与可能 ◦ motion ◦ operation ◦

    command ◦ ex command ◦ →デモ
  24. ここまでのまとめ • Vimはいろんな方法でInsert modeに入れる • いろんな方法でNormal modeに戻れる • バッファ書き換えの単位の概念がある ◦

    .でリピートしたり、countで複数回同じのやった り
  25. 突然のクイズ • 3gihello<C-o>world<Esc> • 結果どうなる?

  26. 突然のクイズ • 3gihello<C-o>hworld<Esc> • 結果どうなる? 3gi 後続するgiから<esc>までを3回やる gi 最後に挿入したとこからinsert mode突入

    <C-o> 一瞬normal modeに入り、あとでinsert mode に戻る h 左に1文字移動
  27. 正解 helworldlo countの3は無視される :h i

  28. ここまで全部 :h に載ってる :h inserting ↑ででてくる insert.txt はとりあえず通読した方がいいかも https://vim-jp.org/vimdoc-ja/insert.html

  29. ここから 実装レベルに入る 具体的なパッチとかも例に

  30. https://github.com/vim/vim

  31. src/main.c

  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 (超要約バージョン)
  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 (超要約バージョン)
  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 (超要約バージョン)
  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 (超要約バージョン)
  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 (超要約バージョン)
  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
  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
  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},
  40. src/normal.c

  41. src/normal.c

  42. src/normal.c invoke_edit( cap, FALSE, cap->cmdchar, FALSE); // in invoke_edit() edit(

    cmd, startln, cap->count1)
  43. src/normal.c invoke_edit( cap, FALSE, cap->cmdchar, FALSE); // in invoke_edit() edit(

    cmd, startln, cap->count1) 以下1300行くらい
  44. ここまでのまとめ • Vimはモードがあってiとか<esc>とかで切替 • Vim起動したらまず初期化 • main_loop()内で無限ループ • normal_cmd()内で ◦

    キー入力に対応する関数を探索 ◦ 探索には配列nv_cmdsを使用 ( • i とは、nv_edit() → invoke_edit() → edit()
  45. src/README.txt に書いてる このスライドできてから気づいた

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

    | wc -l ◦ 116 • undocumented (.txtに書かれてない)
  47. の前に ちょっとCM 提供: VimConf準備会

  48. VimConf https://vimconf.org/2018

  49. https://vimconf.org/2018

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

    同時通訳あり) • スポンサー急募中 • 発表募集中
  51. https://vimconf.org/2018 VimConf 2018 • 2018-11-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
  53. https://twitter.com/supermomonga/status/100887008 3215613954

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

    | wc -l ◦ 116 • undocumented (.txtに書かれてない)
  55. src/edit.c

  56. src/edit.c

  57. src/edit.c

  58. None
  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'
  60. https://github.com/vim-jp/issues/issues/1147

  61. https://vim-jp.slack.com/archives/C83UXKU86/p15 28776532000106

  62. https://twitter.com/fatih/status/10072503322 61519360

  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"
  64. https://github.com/vim/vim/pull/3000

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

    Twitterクライアントのあれ • 元Fablicソフトウェアエンジニア ◦ FRILとかラクマのあれ
  67. ujihisa 最近の活動 • 明日: 友人宅で2~4kgの魚を捌く予定 • 明々後日: asakusa.rbで同上 • 再来週:

    隅田川花火大会で同上 • 一ヶ月くらい就活中
  68. Vim Tatsuhiro Ujihisa RailsDM 2018-07-14

  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