Neovim の設定集(2022年12月30時点)
目次
前置き
WSL2 + Neovim + VSCode で使っていく予定だったのですが、Neovim をあれこれカスタマイズしていると Neovim が使いやすくなってきてメインエディタになりそうなので、これまでの設定をまとめてみようと思います。
順不同で色々なネタを扱っていて記事が長いですので、上記の目次で必要な個所を拾い読みしてください。
アウトラインバッファの作成
このブログは、Go言語 で書かれた静的サイトジェネレーターの The world’s fastest framework for building websites |Hugo で構築しており、記事は Markdown で書いています。また、職場で細々と行っている勉強会の資料は、書籍制作向けのテキストマークアップ言語仕様、およびその変換システムである Re:VIEW - Digital Publishing System for Books and eBooks で作成しています。
どちらもそこそこ長い記事を書くことがあるのでアウトライン機能が欲しいのですが、プラグインはできる限り少なくしたいため、プラグイン無しでできるか挑戦したところ上手くいきましたので、方法を紹介します。
処理の大まかな流れは、;o
キーをタイプしたらファイルタイプ毎の検索条件を用いて Vimgrep と Quickfix を実行し、その結果をカレントバッファの右半分に表示するというものです。
ただ、Neovim のデフォルトのファイルタイプに Re:VIEW は含まれていませんので、~/.config/nvim/filetype.vim
に以下のコードを追加して、Re:VIEW のファイルタイプを登録します。
1augroup filetypedetect
2 " 拡張子 .re のファイルを Re:VIEW ファイルと判定
3 au BufNewFile,BufRead *.re setf review
4augroup END
それから、Vimgrep と Quickfix を次のとおり組み合わせます。
- Markdown と Re:VIEW の見出し部分を検索条件に設定した Vimgrep を実行
- Vimgrep の結果を
copen
コマンドで表示 wincmd L
コマンドで結果を右側に表示- 上記の処理を関数にまとめた上で
;o
キーバインドに割り当て
具体的なコードは次のとおりです。
1function! CreateOutlineBuffer()
2 if (&ft=='review')
3 vimgrep /^=\+ / %
4 elseif (&ft=='markdown')
5 vimgrep /^#\+ / %
6 endif
7 copen
8 wincmd L
9endfunction
10
11nnoremap <silent> ;o :<Cmd>call CreateOutlineBuffer()<CR><CR>
これで ;o
次のスクリーンショットのようにアウトラインバッファを作成することができます。
実際の動作は次の動画のとおりです。
2022年1月10日追記
上記のコードには欠陥があり、次のウィンドウ配置で編集しているときにアウトラインを更新するために ;f
キーバインドをタイプすると画面配置が変わってしまいます。
1アウトライン更新前
2+────────────+────────────+
3| Window A | Window B |
4| (Doc) | (Outline) |
5| +────────────+
6| | Window C |
7| | (Terminal) |
8+────────────+────────────+
1アウトライン更新後
2+───────+────────+────────+
3| Win A | Win C | Win B |
4| | | |
5| | | |
6| | | |
7| | | |
8+───────+────────+────────+
この問題を解決するため、;f
キーバインドをタイプしたときに Quickfix ウィンドウが存在しているか調査し、存在していればその時点のウィンドウ配置をいったん保存してからアウトラインを更新してウィンドウ配置を元に戻す方法に変更しました。
具体的なコードは次のとおりです。
1function! CreateOutlineBuffer()
2 let QuickfixWindowExists = QuickfixWindowExists()
3 if QuickfixWindowExists == "true"
4 let windowLayout = winsaveview()
5 call DoVimgrep(&filetype)
6 copen
7 execute winrestview(windowLayout)
8 else
9 call DoVimgrep(&filetype)
10 copen
11 wincmd L
12 endif
13endfunction
14
15function! QuickfixWindowExists() abort
16 let bufferNoList = tabpagebuflist()
17 for bufferNo in bufferNoList
18 if getwininfo(bufwinid(bufferNo))['variables']['quickfix'] == 1
19 return "true"
20 endif
21 endfor
22 return "false"
23endfunction
24
25function! DoVimgrep(filetype) abort
26 if (a:filetype=='review')
27 vimgrep /^=\+ / %
28 elseif (a:filetype=='markdown')
29 vimgrep /^#\+ / %
30 endif
31endfunction
32
33nnoremap <silent> ;o :<Cmd>call CreateOutlineBuffer()<CR><CR>
Quickfix ウィンドウの存在確認は QuickfixWindowExists()
関数で行っています。
まず、tabpagebuflist()
関数で編集中のタブにあるバッファの番号リストを取得します。そうしたら、その番号リストを for
文で順番に bufwinid()
関数に渡してウィンドウID を取得し、その ID を getwininfo()
関数に渡してウィンドウ情報を辞書のリストとして取得します。ウィンドウ情報にはそのウィンドウが Quickfix/Location ウィンドウかどうかを示す項目がありますので、その項目を if
文の条件に用いています。Quickfix ウィンドウは1つしか開くことができませんので、Quickfix ウィンドウが1つ見つかった時点で "true"
を返して関数を終了します。
あとは、QuickfixWindowExists()
関数の返り値が "true"
なら Quickfix ウィンドウが存在するので winsaveview()
関数を実行してウィンドウ配置の情報を取得して変数に格納します。それからアウトライン表示の DoVimgrep()
関数を実行してアウトラインを更新し、Ex コマンドの winrestview
に先ほど格納したウィンドウ配置の情報を渡してウィンドウ配置を復元します。
なお、cbuffer
コマンドを実行してアウトラインを更新する方法も試しましたが、行頭に ||
が追加されて Enter キーを押しても該当箇所にジャンプできなくなる症状を解消できなかったため、断念して上記の方法に切り替えました。
日本語テキストでの移動の効率化
Vim の f{char}
コマンドを日本語で使う場合、f
キーをタイプしてから IME をオンにして検索文字を入力する必要があり、非常に面倒くさいです。
そこで、Vimで日本語編集時の f, t の移動や操作を楽にするプラグイン ftjpn を作りました をインストールして、f,
や f.
や fg
のキーバインドで 、
や 。
や が
に移動できるようにしました。
ソースコードを編集するときのような細やかな移動はできませんが、、
や 。
や が
に移動できるだけでも結構効率は上がりますので、このプラグインは便利なプラグインです。なお、私の設定は次のとおりです。
1let g:ftjpn_key_list = [
2 \ ['.', '。', '.'],
3 \ [',', '、', ','],
4 \ ['g', 'が'],
5 \ ['w', 'を'],
6 \ ['h', 'は'],
7 \ ['(', '(', ')'],
8 \ [';', '!', '?'],
9 \ ['[', '「', '『', '【'],
10 \ [']', '」', '』', '】'],
11 \ ]
なお、このプラグインを使い始めた当初、,
や .
は使えるのに g
や h
が使えなくて困っていましたが、作者に相談して無事に使えるようになりました。あらためて御礼申し上げます。
ddu.vim の導入
エディタ内蔵のファイラーがあると便利なので色々探したのですが、Vim 標準の Netrw は操作性が独特だったりサイドバーの幅の調整が難しかったりとイマイチ合わなかったので、思い切って 新世代のファイラー UI ddu-ui-filer を導入しました。
最初は設定方法などがさっぱり分からず悪戦苦闘の連続でしたが、何とか使えるようになるととても便利で、手放せないプラグインになりそうです。
現在はファイラーに加えて、バッファリストとコマンド履歴の絞り込み&選択にも使っています。特に、バッファリストの選択は便利な機能で、ターミナルで nvim **.vim
や nvim **.re
と入力して設定ファイルや Re:VIEW ファイルを一括して開いてバッファリストに読み込み、そのリストを ;b
キーバインドで呼び出してサクサク切り替えています。ついでに、リストから開くときに Enter
だとカレントバッファに読み込み、vo
でウィンドウを縦分割して読み込み、vs
でウィンドウを水平分割して読み込むように設定しています。ただし、ここまで来るには悪戦苦闘の連続でした。
実際の設定とその解説を書けば他の人の役に立つと思うのですが、記事が一本書けそうな気がしますので、設定の解説は別の記事にします。そのため、ここでは実際の設定のみ掲載します。
1let g:denops#deno = '/home/s-show/.deno/bin/deno'
2
3call ddu#custom#patch_global({
4\ 'ui': 'filer',
5\ 'sources': [
6\ {
7\ 'name': 'file',
8\ 'params': {},
9\ },
10\ ],
11\ 'sourceOptions': {
12\ '_': {
13\ 'columns': ['filename'],
14\ },
15\ 'command_history': {
16\ 'matchers': [ 'matcher_substring' ],
17\ },
18\ 'buffer': {
19\ 'matchers': [ 'matcher_substring' ],
20\ },
21\ },
22\ 'kindOptions': {
23\ 'file': {
24\ 'defaultAction': 'open',
25\ },
26\ 'command_history': {
27\ 'defaultAction': 'execute',
28\ },
29\ },
30\ 'uiParams': {
31\ 'filer': {
32\ 'sort': 'filename',
33\ 'split': 'floating',
34\ 'displayTree': v:true,
35\ 'previewVertical': v:true,
36\ 'previewWidth': 80,
37\ }
38\ },
39\ })
40
41autocmd FileType ddu-ff call s:ddu_my_settings()
42function! s:ddu_my_settings() abort
43 nnoremap <buffer><silent> <CR>
44 \ <Cmd>call ddu#ui#ff#do_action('itemAction')<CR>
45 nnoremap <buffer><silent> vo
46 \ <Cmd>call ddu#ui#ff#do_action('itemAction', {'name': 'open', 'params': {'command': 'vsplit'}})<CR>
47 nnoremap <buffer><silent> vs
48 \ <Cmd>call ddu#ui#ff#do_action('itemAction', {'name': 'open', 'params': {'command': 'split'}})<CR>
49 nnoremap <buffer><silent> <Space>
50 \ <Cmd>call ddu#ui#ff#do_action('toggleSelectItem')<CR>
51 nnoremap <buffer><silent> i
52 \ <Cmd>call ddu#ui#ff#do_action('openFilterWindow')<CR>
53 nnoremap <buffer><silent> q
54 \ <Cmd>call ddu#ui#ff#do_action('quit')<CR>
55endfunction
56
57autocmd FileType ddu-ff-filter call s:ddu_filter_my_settings()
58function! s:ddu_filter_my_settings() abort
59 inoremap <buffer><silent> <CR>
60 \ <Esc><Cmd>close<CR>
61 nnoremap <buffer><silent> <CR>
62 \ <Cmd>close<CR>
63 nnoremap <buffer><silent> q
64 \ <Cmd>close<CR>
65endfunction
66
67autocmd FileType ddu-filer call s:ddu_filer_my_settings()
68function! s:ddu_filer_my_settings() abort
69 nnoremap <buffer><silent><expr> <CR>
70 \ ddu#ui#filer#is_tree() ?
71 \ "<Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'narrow'})<CR>" :
72 \ "<Cmd>call ddu#ui#filer#do_action('itemAction')<CR>"
73 nnoremap <buffer><silent><expr> vo
74 \ ddu#ui#filer#is_tree() ?
75 \ "<Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'narrow'})<CR>" :
76 \ "<Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'open', 'params': {'command': 'vsplit'}})<CR>"
77 nnoremap <buffer><silent> <Space>
78 \ <Cmd>call ddu#ui#filer#do_action('toggleSelectItem')<CR>
79 nnoremap <buffer><silent> <Esc>
80 \ <Cmd>call ddu#ui#filer#do_action('quit')<CR>
81 nnoremap <buffer> o
82 \ <Cmd>call ddu#ui#filer#do_action('expandItem',
83 \ {'mode': 'toggle'})<CR>
84 nnoremap <buffer><silent> q
85 \ <Cmd>call ddu#ui#filer#do_action('quit')<CR>
86 nnoremap <buffer><silent> ..
87 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'narrow', 'params': {'path': '..'}})<CR>
88 nnoremap <buffer><silent> c
89 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'copy'})<CR>
90 nnoremap <buffer><silent> p
91 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'paste'})<CR>
92 nnoremap <buffer><silent> d
93 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'delete'})<CR>
94 nnoremap <buffer><silent> r
95 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'rename'})<CR>
96 nnoremap <buffer><silent> mv
97 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'move'})<CR>
98 nnoremap <buffer><silent> t
99 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'newFile'})<CR>
100 nnoremap <buffer><silent> mk
101 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'newDirectory'})<CR>
102 nnoremap <buffer><silent> yy
103 \ <Cmd>call ddu#ui#filer#do_action('itemAction', {'name': 'yank'})<CR>
104endfunction
105
106" `;f` でファイルリストを表示する
107nmap <silent> ;f <Cmd>call ddu#start({
108\ 'name': 'filer',
109\ 'uiParams': {
110\ 'filer': {
111\ 'search': expand('%:p')
112\ }
113\ },
114\ })<CR>
115
116" `;b` でバッファリストを表示する
117nmap <silent> ;b <Cmd>call ddu#start({
118\ 'ui': 'ff',
119\ 'sources': [{'name': 'buffer'}],
120\ 'uiParams': {
121\ 'ff': {
122\ 'split': 'floating',
123\ }
124\ },
125\ })<CR>
126
127" `;c` でコマンドリストを表示する
128nmap <silent> ;c <Cmd>call ddu#start({
129\ 'ui': 'ff',
130\ 'sources': [
131\ {
132\ 'name': 'command_history',
133\ },
134\ ],
135\ 'uiParams': {
136\ 'ff': {
137\ 'split': 'floating',
138\ },
139\ },
140\ })<CR>
ファイルリストなどの実際の表示は次のとおりです。
ddc.vim の導入
自動補完機能を強化するため、上記の ddu.vim と同じ方が作成してる自動補完プラグインの 新世代の自動補完プラグイン ddc.vim を導入しました。合わせて、自動補完をコマンドラインバッファでも有効にするため、自動補完プラグイン ddc.vim + pum.vim も導入しました。こちらも導入は悪戦苦闘の連続でしたが、何とか自動補完ができるようになりました。
過去に入力したコマンドであれば、上記の ddu.vim のコマンド履歴表示でも対応可能なのですが、新規のコマンドはコマンドラインバッファで入力しないといけないですし、コマンド履歴表示は若干タイムラグがあるので、コマンドラインバッファでの自動補完は便利です。また、通常のファイル編集でも2文字入力すれば補完機能が発動しますので、便利に使っています。ただし、プログラミングで必須の LSP 周りの設定は全くできていないため、次はここに挑戦します。
なお、こちらも ddu.vim と同じく解説を書くと記事が一本書けそうな感じなので、ここでは実際のコードのみ掲載します。
1"=======================================================================================
2" ddc.nvim の設定
3"=======================================================================================
4"
5call ddc#custom#patch_global('sources', ['around'])
6call ddc#custom#patch_global('sourceOptions', {
7 \ '_': {
8 \ 'matchers': ['matcher_head'],
9 \ 'sorters': ['sorter_rank']
10 \ },
11 \ 'around': {
12 \ 'mark': 'around'
13 \ },
14 \})
15
16
17"=======================================================================================
18" pum.nvim の設定
19"=======================================================================================
20"
21call ddc#custom#patch_global('cmdlineSources', {
22 \ ':': ['cmdline-history', 'cmdline', 'around'],
23 \ '@': ['cmdline-history', 'input', 'file', 'around'],
24 \ '>': ['cmdline-history', 'input', 'file', 'around'],
25 \ '/': ['around', 'line'],
26 \ '?': ['around', 'line'],
27 \ '-': ['around', 'line'],
28 \ '=': ['input'],
29 \ })
30
31call ddc#custom#patch_global('ui', 'pum')
32"call ddc#custom#patch_global('completionMenu', 'pum.vim')
33inoremap <silent><expr> <TAB>
34 \ pum#visible() ? '<Cmd>call pum#map#insert_relative(+1)<CR>' :
35 \ (col('.') <= 1 <Bar><Bar> getline('.')[col('.') - 2] =~# '\s') ?
36 \ '<TAB>' : ddc#manual_complete()
37inoremap <S-Tab> <Cmd>call pum#map#insert_relative(-1)<CR>
38inoremap <C-n> <Cmd>call pum#map#select_relative(+1)<CR>
39inoremap <C-p> <Cmd>call pum#map#select_relative(-1)<CR>
40inoremap <C-y> <Cmd>call pum#map#confirm()<CR>
41inoremap <C-e> <Cmd>call pum#map#cancel()<CR>
42inoremap <PageDown> <Cmd>call pum#map#insert_relative_page(+1)<CR>
43inoremap <PageUp> <Cmd>call pum#map#insert_relative_page(-1)<CR>
44
45call ddc#custom#patch_global('autoCompleteEvents', [
46 \ 'InsertEnter', 'TextChangedI', 'TextChangedP',
47 \ 'CmdlineEnter', 'CmdlineChanged',
48 \ ])
49
50nnoremap : <Cmd>call CommandlinePre()<CR>:
51nnoremap ;; <Cmd>call CommandlinePre()<CR>:
52
53function! CommandlinePre() abort
54 " Note: It disables default command line completion!
55 cnoremap <Tab> <Cmd>call pum#map#insert_relative(+1)<CR>
56 cnoremap <S-Tab> <Cmd>call pum#map#insert_relative(-1)<CR>
57 cnoremap <C-n> <Cmd>call pum#map#insert_relative(+1)<CR>
58 cnoremap <C-p> <Cmd>call pum#map#insert_relative(-1)<CR>
59 cnoremap <C-y> <Cmd>call pum#map#confirm()<CR>
60 cnoremap <C-e> <Cmd>call pum#map#cancel()<CR>
61
62 " Overwrite sources
63 if !exists('b:prev_buffer_config')
64 let b:prev_buffer_config = ddc#custom#get_buffer()
65 endif
66 call ddc#custom#patch_buffer('cmdlinesources',
67 \ ['neovim', 'around'])
68
69 autocmd User DDCCmdlineLeave ++once call CommandlinePost()
70 autocmd InsertEnter <buffer> ++once call CommandlinePost()
71
72 " Enable command line completion
73 call ddc#enable_cmdline_completion()
74endfunction
75
76function! CommandlinePost() abort
77 silent! cunmap <Tab>
78 silent! cunmap <S-Tab>
79 silent! cunmap <C-n>
80 silent! cunmap <C-p>
81 silent! cunmap <C-y>
82 silent! cunmap <C-e>
83
84 " Restore sources
85 if exists('b:prev_buffer_config')
86 call ddc#custom#set_buffer(b:prev_buffer_config)
87 unlet b:prev_buffer_config
88 else
89 call ddc#custom#set_buffer({})
90 endif
91endfunction
92
93call ddc#enable()
実際の自動補完の様子は次のとおりです。
lualine.nvim の導入
ステータスバーにもっと多くの情報を表示するため、nvim-lualine/lualine.nvim: A blazing fast and easy to configure neovim statusline plugin written in pure lua. を導入しました。
設定は公式リポジトリの「Default configuration」とほぼ同じですが、右側の表示のセパレータを
から |
に変更し、また、ファイル名の表示をフルパス表示に変更しています。
IMEの状態を右側に表示しようとしましたが、カーソルを上下移動するたびに画面がちらつくうえ、動作が明らかに重くなったため断念しました。
1lua << END
2 require('lualine').setup {
3 options = {
4 icons_enabled = true,
5 theme = 'nord',
6 component_separators = { left = '', right = '|'},
7 section_separators = { left = '', right = ''},
8 disabled_filetypes = {
9 statusline = {},
10 winbar = {},
11 },
12 ignore_focus = {},
13 always_divide_middle = true,
14 globalstatus = true,
15 refresh = {
16 statusline = 1000,
17 tabline = 1000,
18 winbar = 1000,
19 }
20 },
21 sections = {
22 lualine_a = {'mode'},
23 lualine_b = {'branch', 'diff', 'diagnostics'},
24 lualine_c = {{ 'filename', file_status = true, path = 3 }},
25 lualine_x = {'encoding', 'fileformat', 'filetype'},
26 lualine_y = {'progress'},
27 lualine_z = {'location'}
28 },
29 inactive_sections = {
30 lualine_a = {},
31 lualine_b = {},
32 lualine_c = {'filename'},
33 lualine_x = {'location'},
34 lualine_y = {},
35 lualine_z = {}
36 },
37 tabline = {},ftjpnの動作状況
38
39 winbar = {},
40 inactive_winbar = {},
41 extensions = {}
42 }
43END
44
45" IMEの状態を取得する関数。動作に支障が出るくらい遅くなるため未使用
46function! Get_ime_status()
47 let b:ime_status=system('spzenhan.exe')
48 if b:ime_status==1
49 return 'IME ON'
50 else
51 return 'IME OFF'
52 endif
53endfunction
また、lualine.nvim の導入と合わせて、画面を縦分割してもステータスバーを分割しないという設定も行っています。この設定は、設定ファイルに次の設定を追加すれば可能です。
1set laststatus=3
設定の結果は次のとおりです。
キーバインド変更
上書き保存などを少しでも簡単にできるようにするため、いくつかのキーバインドを設定しました。
1" 大文字Kでカーソル上のヘルプが見られる設定
2" 日本語ヘルプがあれば日本語版を、無ければ英語版を表示します。
3" 事前に 'vim-jp/vimdoc-ja' をインストールする必要があります。
4nnoremap <silent> K :<C-u>call <SID>show_documentation()<CR>
5function! s:show_documentation() abort
6 if index(['vim','help'], &filetype) >= 0
7 try
8 execute 'h ' . expand('<cword>') .. "@ja"
9 catch /^Vim\%((\a\+)\)\=:E661:/
10 execute 'h ' . expand('<cword>')
11 endtry
12 endif
13endfunction
14
15" qa で全てのバッファを閉じる
16nnoremap qa qall<CR>
17
18" ;w で保存
19nnoremap ;w <Cmd>update<CR>
20
21" ; 2連打でコマンドラインに移動
22nnoremap ;; :
23
24" ノーマルモードで BackSpace による削除を可能にする
25nnoremap <BS> X
設定ファイル分割
最初は init.vim
に全ての設定を書いていましたが、可読性に欠けるので次のような形で分割することにしました。
1nvim
2├── filetype.vim
3├── ftplugin
4│ └── review.vim
5├── init.vim
6├── init.vim.backup
7├── minimal.lua
8└── config_files
9 ├── init
10 │ ├── basic.vim
11 │ ├── clipboard.vim
12 │ ├── IME.vim
13 │ ├── jetpack.vim
14 │ ├── keymapping.vim
15 │ ├── lsp.vim
16 │ └── user_interface.vim
17 └── plugin
18 ├── ddc.vim
19 ├── ddu.vim
20 ├── ftpjn.vim
21 └── lualine.vim
そして、この分割したファイルを init.vim
の先頭で読み込んでいます。
1source $HOME/.config/nvim/config_files/init/jetpack.vim
2source $HOME/.config/nvim/config_files/init/basic.vim
3source $HOME/.config/nvim/config_files/init/IME.vim
4source $HOME/.config/nvim/config_files/init/clipboard.vim
5source $HOME/.config/nvim/config_files/init/user_interface.vim
6source $HOME/.config/nvim/config_files/init/keymapping.vim
7source $HOME/.config/nvim/config_files/init/lsp.vim
8source $HOME/.config/nvim/config_files/plugin/ddu.vim
9source $HOME/.config/nvim/config_files/plugin/ddc.vim
10source $HOME/.config/nvim/config_files/plugin/ftpjn.vim
11source $HOME/.config/nvim/config_files/plugin/lualine.vim
なお、runtime! config_files/init/*.vim
、runtime! config_files/plugins/*.vim
の2行を init.vim
の先頭に書いて自動的に設定ファイルを読み込む方法を紹介しているサイトがあり、私もその方法を一度採用しましたが、プラグインが機能しなかったため、上記のように愚直に設定ファイルを読み込む方法に変えました。
Re:VIEWのシンタックスハイライト
Re:VIEW は Neovim にデフォルトで登録されているファイルタイプではないため、当然シンタックスハイライトも用意されていません。そのため、シンタックスハイライトは手動で設定する必要があります。
とはいえ、シンタックスハイライトの設定を公開(tokorom/vim-review: Vim syntax for Re:VIEW)してくださっている方がいますので、その方の設定を拝借することにしました。
手順は次のとおりです。なお、Re:VIEW のファイルタイプ判定は、上記の「アウトラインバッファの作成」を参照してください。
まず、.config/nvim/ftplugin/review.vim
を作成して、次のコードを記述します。
1" .config/nvim/ftplugin/review.vim
2setl commentstring=#@#\ %s
3
4if !exists('g:vim_review#include_filetypes')
5 let g:vim_review#include_filetypes = []
6endif
7
8if !exists('g:vim_review#include_grouplists')
9 let g:vim_review#include_grouplists = {}
10 for ft in g:vim_review#include_filetypes
11 let g:vim_review#include_grouplists[ft] = 'syntax/' . ft . '.vim'
12 endfor
13endif
それから、.config/nvim/syntax/review.vim
を作成して次のコードを記述します。
1" .config/nvim/syntax/review.vim
2" Vim syntax file
3" Language: Re:VIEW
4" Maintainer: Yuta Tokoro <tokorom@gmail.com>
5
6if exists("b:current_syntax")
7 finish
8endif
9
10" ----------
11" syntax
12
13syn case match
14
15syn match reviewHeading contains=reviewInlineCommand,reviewInlineStyleCommand
16 \ "^=\+\%(\s\+\|{\|\[\).*"
17
18syn region reviewInlineCommand oneline
19 \ start="@<\w\+>{" end="}"
20syn region reviewInlineStyleCommand transparent oneline
21 \ matchgroup=reviewInlineCommand
22 \ start="@<\%\(kw\|bou\|ami\|u\|b\|i\|strong\|em\|tt\|tti\|ttb\|code\|tcy\)>{"
23 \ end="}"
24
25syn region reviewBlockCommand transparent keepend
26 \ matchgroup=reviewBlockDeclaration start="^//\w\+\[\?.*{\s*$" end="^//}\s*$"
27
28syn match reviewBlockCommandWithoutContent
29 \ "^//\w\+\[.*[^{]\s*$"
30syn match reviewControlCommand
31 \ "^//\<\%\(noindent\|blankline\|linebreak\|pagebreak\)\>\s*$"
32
33syn region reviewItemize transparent oneline
34 \ matchgroup=reviewItemizePrefix start="^\s\+\*\+\s\+" end="$"
35syn region reviewOrderedItemize transparent oneline
36 \ matchgroup=reviewItemizePrefix start="^\s\+[0-9]\+\.\s\+" end="$"
37syn region reviewDefinitionList transparent oneline
38 \ matchgroup=reviewItemizePrefix start="^\s\+\:\s\+" end="$"
39
40syn match reviewComment contains=reviewTodo
41 \ "^#@.*"
42syn region reviewCommentBlock keepend contains=reviewTodo
43 \ start="^//\<comment\>\[\?.*{\s*" end="^//}\s*$"
44syn region reviewCommentInline oneline contains=reviewTodo
45 \ start="@<comment>{" end="}"
46
47syn match reviewPreProcCommand
48 \ "^#@\<\%\(require\|provide\)\>\s\+.*"
49syn region reviewPreProcBlockCommand keepend
50 \ start="^#@\<\%\(mapfile\|maprange\|mapoutput\)\>(.*).*" end="^#@end\s*$"
51
52syn region reviewWarning oneline
53 \ matchgroup=reviewPreProcCommand start="^#@warn(" end=").*$"
54
55syn case ignore
56syn keyword reviewTodo MARK TODO FIXME contained
57syn case match
58
59" ----------
60" include other languages
61
62if exists('g:vim_review#include_grouplists')
63 let include_grouplists = g:vim_review#include_grouplists
64 let operations = '\<\%\(list\|listnum\|emlist\|emlistnum\)\>'
65
66 for ft in keys(include_grouplists)
67 let syntaxfile = include_grouplists[ft]
68 execute 'syn include @' . ft . ' ' . syntaxfile
69 let code_block_region = 'start="^//' . operations . '\[.*\[' . ft . '\]{\s*$"'
70 \ . ' end="^//}\s*$"'
71 let groupname = 'reviewCodeBlock_' . ft
72 execute 'syn region ' . groupname . ' keepend contains=@' . ft
73 \ . ' matchgroup=reviewBlockDeclaration'
74 \ . ' ' . code_block_region
75
76 if exists('b:current_syntax')
77 unlet b:current_syntax
78 endif
79 endfor
80endif
81
82" ----------
83" highlight
84
85hi def link reviewHeading Conditional
86hi def link reviewInlineCommand Function
87hi def link reviewBlockDeclaration Identifier
88hi def link reviewBlockCommandWithoutContent Identifier
89hi def link reviewControlCommand Identifier
90hi def link reviewItemizePrefix Special
91hi def link reviewComment Comment
92hi def link reviewCommentBlock Comment
93hi def link reviewCommentInline Comment
94hi def link reviewPreProcCommand PreProc
95hi def link reviewPreProcBlockCommand PreProc
96hi def link reviewWarning Underlined
97hi def link reviewTodo Todo
98
99" ----------
100
101let b:current_syntax = "review"
なお、シンタックスハイライトを設定したのに一部のキーワードがハイライトされない場合、テーマの色とバッティングしている可能性があります。私も shaunsingh/nord.nvim: Neovim theme based off of the Nord Color Palette, written in lua with tree sitter support テーマを設定していたところ、一部のキーワードがハイライトされなかったので、テーマの色が邪魔をしているのではないかと疑って別のテーマにしたら全てのキーワードがハイライトされました。
ちなみに、変更後のテーマは protesilaos/tempus-themes: [Mirror] Tempus is a collection of themes for Vim, text editors, and terminal emulators that are compliant at the very least with the WCAG AA accessibility standard for colour contrast の tempus_classic です。
noice.nvim の導入
Neovim だとコマンドラインバッファがエディタの下の端にありますが、VSCode だとコマンドパレットがエディタの上半分の区域のちょうど良い場所にポップアップするので、Neovim でも同じような機能が欲しいと思って folke/noice.nvim: 💥 Highly experimental plugin that completely replaces the UI for messages, cmdline and the popupmenu. を導入してみました。
ddc.vim も設定してコマンドライン補完もできるようにしたのですが、入力カーソルが表示されないという問題が発生し、公式リポジトリの Issues にも同じ症状が投稿(Invisible cursor in cmdline popup · Issue #251 · folke/noice.nvim)されていましたので、問題が解消されるまで様子見かなと思い、この後に紹介する VonHeikemen/fine-cmdline.nvim: Enter ex-commands in a nice floating input. に切り替えました。
ところが、作者が「その問題はカラースキーマがカーソルのカラーを提供しないことが原因だ」とコメントしたのを見て、カラースキーマを変えた今なら問題が解消されているかもと思ってプラグインを再び有効化したところ、問題なく入力カーソルが表示されました。そのため、本プラグインを引き続き利用することにしました。
現在の設定は次のとおりです。公式リポジトリの設定例などを継ぎ接ぎしたもので詳細は勉強中です。noice.vim
ファイルで設定しているため、先頭に lua << END
を追加し、末尾に END
を追加しています。
1lua << END
2require("noice").setup({
3 -- you can enable a preset for easier configuration
4 presets = {
5 bottom_search = false, -- use a classic bottom cmdline for search
6 command_palette = true, -- position the cmdline and popupmenu together
7 long_message_to_split = true, -- long messages will be sent to a split
8 inc_rename = false, -- enables an input dialog for inc-rename.nvim
9 lsp_doc_border = false, -- add a border to hover docs and signature help
10 },
11 messages = {
12 view_search = 'notify',
13 },
14 routes = {
15 {
16 view = "notify",
17 filter = { event = "msg_showmode" },
18 },
19 {
20 filter = {
21 event = "notify",
22 warning = true,
23 find = "failed to run generator.*is not executable",
24 },
25 opts = { skip = true },
26 },
27 },
28 cmdline = {
29 enabled = true,
30 menu = 'popup',
31 },
32 views = {
33 cmdline_popup = {
34 position = {
35 row = 5,
36 col = "50%",
37 },
38 size = {
39 width = 60,
40 height = "auto",
41 },
42 },
43 popupmenu = {
44 relative = "editor",
45 position = {
46 row = 8,
47 col = "50%",
48 },
49 size = {
50 width = 60,
51 height = 10,
52 },
53 border = {
54 style = "rounded",
55 padding = { 0, 1 },
56 },
57 win_options = {
58 cursorline = true,
59 cursorlineopt = 'line',
60 winhighlight = { Normal = "Normal", FloatBorder = "DiagnosticInfo" },
61 },
62 },
63 },
64})
65END
導入したものの廃止したもの
fine-cmdline.nvim
noice.nvim の使用を一度断念した後、同様の機能を持つプラグインである VonHeikemen/fine-cmdline.nvim: Enter ex-commands in a nice floating input. を使ってみました。
こちらは入力カーソルが消失するような不具合が無く、ddc.vim による補完もできるのですが、上下キーで履歴を移動する度に画面がちらつく上、動作もキビキビしているとは言い難かったため使用を断念しました。
参考にしたサイト・情報
- vimgrepとQuickfix知らないVimmerはちょっとこっち来い - Qiita
- Vimファイラの決定版「ddu-ui-filer」設定例を紹介 - アルパカログ
- Vimで技術書を執筆する環境 with Re:VIEW + RedPen + prh | Spinners Inc.
- とある PR のおかげで Neovim がもはや VSCode な件について
- Vimの自動補完プラグイン「ddc.vim」の使い方
- vim - Vim起動時にウィンドウ縦分割→右側にファイルを開く方法 - スタック・オーバーフロー
- 新世代の UI 作成プラグイン ddu.vim
- 自動補完プラグイン ddc.vim + pum.vim
- 新世代の UI 作成プラグイン ddu.vim
- 新世代のファイラー UI ddu-ui-filer
- cmdheight=0 in neovim
- Neovimの補完をddc.vim + Built-in LSP へ移行した | ntsk
- Vimファイラの決定版「ddu-ui-filer」設定例を紹介 - アルパカログ
- Neovimのコマンドラインや通知がリッチになるnoice.nvim使ってみた | DevelopersIO
- Vim のヘルプファイル