Neovim の設定集(2022年12月30時点)

前置き

WSL2 + Neovim + VSCode で使っていく予定だったのですが、Neovim をあれこれカスタマイズしていると Neovim が使いやすくなってきてメインエディタになりそうなので、これまでの設定をまとめてみようと思います。

順不同で色々なネタを扱っていて記事が長いですので、上記の目次で必要な個所を拾い読みしてください。

アウトラインバッファの作成

2022年1月10日追記

このブログは、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 を次のとおり組み合わせます。

  1. Markdown と Re:VIEW の見出し部分を検索条件に設定した Vimgrep を実行
  2. Vimgrep の結果を copen コマンドで表示
  3. wincmd L コマンドで結果を右側に表示
  4. 上記の処理を関数にまとめた上で ;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 次のスクリーンショットのようにアウトラインバッファを作成することができます。

アウトラインバッファを表示

実際の動作は次の動画のとおりです。

ちなみに、このアウトラインバッファは、今は開発が終了した Github 製エディタのプラグインの [t9md/atom-narrow: narrow something](https://github.com/t9md/atom-narrow) の見た目を模して設定しました。私はこのプラグインがお気に入りでしたので、見た目だけでも同じようにしたかったものです。

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    \ ]

なお、このプラグインを使い始めた当初、,. は使えるのに gh が使えなくて困っていましたが、作者に相談して無事に使えるようになりました。あらためて御礼申し上げます。

ddu.vim の導入

エディタ内蔵のファイラーがあると便利なので色々探したのですが、Vim 標準の Netrw は操作性が独特だったりサイドバーの幅の調整が難しかったりとイマイチ合わなかったので、思い切って 新世代のファイラー UI ddu-ui-filer を導入しました。

最初は設定方法などがさっぱり分からず悪戦苦闘の連続でしたが、何とか使えるようになるととても便利で、手放せないプラグインになりそうです。

現在はファイラーに加えて、バッファリストとコマンド履歴の絞り込み&選択にも使っています。特に、バッファリストの選択は便利な機能で、ターミナルで nvim **.vimnvim **.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

設定の結果は次のとおりです。

設定後の lualine.nvim の表示

キーバインド変更

上書き保存などを少しでも簡単にできるようにするため、いくつかのキーバインドを設定しました。

 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/*.vimruntime! 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 による補完もできるのですが、上下キーで履歴を移動する度に画面がちらつく上、動作もキビキビしているとは言い難かったため使用を断念しました。

参考にしたサイト・情報