【vim】最近作ったプラグインの話

# チュートリアルアニメーション
のプラグインを作りました。
基本操作を、vim上のpopupなどでアニメーションさせるものです。

https://github.com/serna37/vim-tutorial

# ちょっと紹介
最初にテスト用ドキュメントを開きます。
時間を直接指定して、⚪︎秒後にポップアップ、、等をしているだけ。

```vim
let s:training_popup_cursor1_winid = 0
let s:training_popup_cursor2_winid = 0
let s:training_popup_window1_winid = 0
let s:training_popup_window2_winid = 0
let s:training_popup_mode1_winid = 0
let s:training_popup_mode2_winid = 0
let s:training_popup_search_winid = 0
let s:training_popup_opeobj_winid = 0
let s:training_popup_tips_winid = 0
let s:training_popup_tips_win_flg = 0
let s:training_popup_tips_win_tid = 0

fu! s:trainingWheelsPopupsActivate()
    cal s:trainingWheelsPracticeFileCreate()
    cal s:trainingWheelsPopupsAllClose()
    let s:training_info_idx = 0
    hi TrainingNotification ctermfg=green guifg=green
    hi Traininginfo ctermfg=cyan guifg=cyan cterm=BOLD gui=BOLD
    cal popup_notification([
        \'             = Infomation =          ',
        \' Training Wheels Protocol Activated.',
        \'  ↓Create and Open practice file. ',
        \'   ~/practice.md. ',
        \'  ',
        \' To DeActivate',
        \' :TrainingWheelsProtocol deactive'
        \],#{border:[],
        \zindex:999,
        \highlight:'TrainingNotification',
        \time:3000})
    cal timer_start(1000, function('s:trainingInfo'))
    cal timer_start(6000, function('s:trainingInfo'))
    cal timer_start(6500, function('s:trainingPopupCursor1'))
    cal timer_start(6500, function('s:trainingPopupCursor1SampleMv'))
    cal timer_start(10000, function('s:trainingInfo'))
    cal timer_start(10500, function('s:trainingPopupCursor2'))
    cal timer_start(10500, function('s:trainingPopupCursor2SampleMv'))
    cal timer_start(15000, function('s:trainingInfo'))
    cal timer_start(15500, function('s:trainingPopupWindow1'))
    cal timer_start(15500, function('s:trainingPopupWindow1SampleMv'))
    cal timer_start(20000, function('s:trainingInfo'))
    cal timer_start(20500, function('s:trainingPopupWindow2'))
    cal timer_start(20500, function('s:trainingPopupWindow2SampleMv'))
    cal timer_start(26000, function('s:trainingInfo'))
    cal timer_start(29000, function('s:trainingInfo'))
    cal timer_start(29500, function('s:trainingPopupMode1'))
    cal timer_start(32000, function('s:trainingInfo'))
    cal timer_start(35000, function('s:trainingInfo'))
    cal timer_start(35500, function('s:trainingPopupMode2'))
    cal timer_start(35500, function('s:trainingPopupMode2SampleMv'))
    cal timer_start(38000, function('s:trainingInfo'))
    cal timer_start(38500, function('s:trainingPopupSearch'))
    cal timer_start(38500, function('s:trainingPopupSearchSampleMv'))
    cal timer_start(42000, function('s:trainingInfo'))
    cal timer_start(45000, function('s:trainingInfo'))
    cal timer_start(45500, function('s:trainingPopupOpeObj'))
    cal timer_start(45500, function('s:trainingPopupOpeObjSampleMv'))
    cal timer_start(49000, function('s:trainingInfo'))
    let s:training_popup_tips_win_tid = timer_start(49500, function('s:trainingPopupTips'))
    cal timer_start(49500, function('s:trainingPopupTipsCursor'))
    cal timer_start(52000, function('s:trainingInfo'))
endf

```

といった感じ。各関数では、呼び出されると
ポップアップ表示に加えて、実際のテスト用ドキュメントに操作を行います。

例えば一番最初にカーソルの動かし方をポップアップしますが
```vim
    cal timer_start(6500, function('s:trainingPopupCursor1'))
    cal timer_start(6500, function('s:trainingPopupCursor1SampleMv'))
```
ここで呼び出している関数は以下のようになっています。
```trainingPopupCursor1.vim
" cursor move
fu! s:trainingPopupCursor1(timer)
    let s:training_popup_cursor1_winid = popup_create([
        \'     ↑ k ',
        \' h ←   → l ',
        \'   j ↓  ',
        \],#{title: ' cursor move ',
        \border: [], zindex: 1,
        \line: 3,
        \col: &columns - 46
        \})
    cal matchaddpos("Identifier", [[1,10],[2,2],[2,14],[3,4]], 16, -1, #{window: s:training_popup_cursor1_winid})
endf
```

```trainingPopupCursor1SampleMv.vim
fu! s:trainingPopupCursor1SampleMv(timer)
    execute("norm gg")
    cal cursor(26, 6)
    cal timer_start(600, function('s:trainingPopupCursor1SampleMv1'))
    cal timer_start(1200, function('s:trainingPopupCursor1SampleMv2'))
    cal timer_start(1800, function('s:trainingPopupCursor1SampleMv3'))
    cal timer_start(2400, function('s:trainingPopupCursor1SampleMv4'))
endf
fu! s:trainingPopupCursor1SampleMv1(timer)
    execute("norm h")
    echo("h   cursor move")
endf
fu! s:trainingPopupCursor1SampleMv2(timer)
    execute("norm j")
    echo("j   cursor move")
endf
fu! s:trainingPopupCursor1SampleMv3(timer)
    execute("norm l")
    echo("l   cursor move")
endf
fu! s:trainingPopupCursor1SampleMv4(timer)
    execute("norm k")
    echo("k   cursor move")
endf
```
このように、ポップアップ（+色付け）の表示後に、
`execute("norm h")`のようにしてサンプル操作を行います。

※ポップアップに色付けしたサンプル
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453310/d5d8f572-a748-9250-258a-75efcf031416.png)


ほんとは動かすタイミングで、ポップアップの色付けも押してるキーの色だけつける、とかしたかったんですが
長いので断念しました。。

# 完成イメージ
アニメーションが全て終わると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453310/9c6ec81d-71e0-72f7-cc3f-5afa1dbaee22.png)
こんな感じになります。

左側が空いてるので、実際の操作をしながらチートシートを見られる感じ。

結構殴り書きで作った感じがあるので、本当に初心者がチュートリアルにできるのかは分からないです、、あくまでネタ的なものですね。。

# キーマップ多すぎ問題改善系
忘れる、というより久しぶりに使うときにパッとキーを思い出せずにストレスなことがあるのではないでしょうか？

いつもは使わないが、ここぞという時に使いたいものを
メニューにまとめてしまおうということでプラグインに。

もともと自分用にvimrcにいたものをプラグインに吐き出しただけでもあります。

# IDE メニュー
※README等いろいろ置き去りになってて、今ではもっと機能が多いです

https://github.com/serna37/vim-IDE-menu

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453310/aa101240-e264-49f7-e3f7-3fe249a8ab79.png)

基本的に、IDE系にするプラグインではLSPの機能が多すぎて
全てをkeymapするのは無理です。
だいたいみなさん
- ファイラ`<leader>f`
- エクスプローラ`<leader>e`
- 定義元に飛ぶ`gd` `<leader>d`
- 参照元一覧だす`gr` `<leader>r`
- 次/前のエラーに`<leader>,` `<leader>.`
- 関数のドキュメント見る`<leader>?`

などしてると思います。私はほぼこれの通りにしてます。
では
- フォーマット
- リネーム

はというと、
`format`の`f`はファイラと被り、`rename`の`r`も参照元と被りです。
けどいうほどフォーマッタもリネームもしないと思いますので、メニューから呼び出すでよくね？という思想。
ついでに、スニペットの編集やプログラム実行、デバッグも入れちゃいました。

依存プラグイン等、詳細は[README](https://github.com/serna37/vim-IDE-menu#readme)を参照ください。

# どういう内容？
メニューのリストと、選んだ時どうするかを
`popup_menu`にしているだけ。

```メニュー内容を定義.vim
let s:idemenu = #{
    \ pmenu_default: [],
    \ menuid: 0, mttl: ' IDE MENU (j / k) Enter choose ',
    \ menu: [
        \ '[⚖️  Format]   format CocActionAsync',
        \ '[♻️  ReName]   rename CocActionAsync',
        \ '[🎨 Snippet]  ultisnips edit',
        \ '[⚡️ Run]      quickrun',
        \ '[⚙️  Debug]    vimspector',
    \ ],
    \ }
```

```ポップアップつくる.vim
fu! s:idemenu.open() abort
    let self.menuid = popup_menu(self.menu, #{title: self.mttl, border: [], borderchars: ['─','│','─','│','╭','╮','╯','╰'],
        \ callback: 's:idemenu_exe'})
    cal setwinvar(self.menuid, '&wincolor', 'String')
    hi DarkRed ctermfg=204
    cal matchadd('DarkRed', '\[.*\]', 16, -1, #{window: self.menuid})
    let self.pmenu_default = execute('hi PmenuSel')[1:]->split(' ')->filter({_,v->stridx(v, '=')!=-1})
    hi PmenuSel ctermbg=232 ctermfg=114
endf
```

```コールバックで、選ばれた時の挙動.vim
fu! s:idemenu_exe(_, idx) abort
    if a:idx == 1
        cal CocActionAsync('format')
    elseif a:idx == 2
        cal CocActionAsync('rename')
    elseif a:idx == 3
        exe 'UltiSnipsEdit'
    elseif a:idx == 4
        let args = input('stdin>')
        cal system('touch .quickrun.tmp.stdin.txt && echo "'.args.'" > .quickrun.tmp.stdin.txt')
        sil! exe 'QuickRun <.quickrun.tmp.stdin.txt'
        cal system('rm .quickrun.tmp.stdin.txt')
    elseif a:idx == 5
        cal vimspector#LaunchWithSettings({'configuration': &filetype})
    endif
    exe 'hi PmenuSel '.join(s:idemenu.pmenu_default, ' ')
    retu 0
```

あとは、これを増やしたければどんどん増やすだけです。

作っては無いですが
メニュー内容をjsonとかで別ファイルに持ち、読み込むようにすれば
汎用的な**メニュー作成プラグイン**となりますが

まぁ需要がそこまでなさそくなので各自フォークしてソースいじった方がはやそう。

# さらにデバッグメニュー
ついでに作りました。
※これもREADME等いろいろ置き去りになってて、今ではもっと機能が多いです

https://github.com/serna37/vim-atcoder-menu

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453310/680f1fad-17af-0a09-8aa4-1337b7f76440.png)

input outputファイルがある時等、競技プログラミングみたいな状況だと便利なので用意しました。
これは特に汎用的ではないですが、`acc` `oj`コマンドの呼び出しインターフェースとして作った機能が`vimrc`の中に溜まっていて行数かさんでいたので、外だししました。

結局これらも
- vim内でコマンド作ったが覚えてないので呼び出せない
- にしてはキーマップも、余っているキーがない

のでメニューにしたという流れ。
機能の詳細はやっぱりレポを参照ください。

# (紹介)which-key

https://github.com/liuchengxu/vim-which-key

公式デモ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453310/a0b9eb86-19ae-6605-e2d1-1e8d183dfb22.png)

キーを押して、`「あれ、次何押すんだっけ？」`としている間にメニューが出てくれます。
自分用のkeymapチートシートなんて作る必要がなくなります！

# (紹介)コマンドパレット

https://github.com/LinArcX/telescope-command-palette.nvim

https://github.com/filipelinhares/dumb-palette.vim

VSCodeやchrome等のdevツールのように、コマンドをfuzzy検索して実行できるというもの。
キーバインドというより、増えすぎて思い出しにくいコマンドを呼ぶ時に便利。

# 最後に
自分用に機能を作ると、vimrcが膨れ上がるので
積極的にプラグインにして`[vim-plug](https://github.com/junegunn/vim-plug)`などで管理しましょう。