Neovim Lua設定覚え書き&LSP入門
もともとVimを使っていたのですが、LSPまわりの利便性のためNeovimに乗り換え、ついでに設定ファイルを全てLuaで記述しました。その時に調べた諸々の情報含めた設定の覚え書きです。説明のため分割していますが、全て .config/nvim/init.lua
1つにまとめています。
未完成の部分はありますが、開発に必要な一通りの機能は使えるようになりましたのでまとめて公開します。
- fuzzy finder
- Git連携
- LSP(補完、定義ジャンプ、Error/Warning表示 ...)
OS : ArchLinux
Nvim : v0.7.2
2022.9.14
ご指摘いただいたコメントを元にautocmdをlua APIで設定する内容を追記しました。
2022.9.28
mason.nvimを中心にいくつか内容を更新しました
1. 要件
Packer(プラグインマネージャ)とfzf(あいまい検索ツール)を入れる必要があります。適宜インストールしてください。
この2つさえ入れればあとはコピペでだいたい動きます。
2. 共通設定
-
:help lua-vim-options
( https://neovim.io/doc/user/lua.html#lua-vim-options )
VimScriptでset number
とかやるやつです。vim.bo
, vim.go
, vim.wo
でバッファ単位などの設定ができるようですが、必要性がなければ全部vim.opt
で設定して良いと思います。
autocmdなどの一部の機能はAPIが実装されていないので vim.cmd
でVimScriptをそのまま書きます。
autocmdもAPI経由で設定することができます。VimScriptでの設定方法も併記しました。詳細は :help nvim_create_autocmd()
:help nvim_create_augroup()
などを見てください。
vim.opt.clipboard = "unnamedplus"
vim.opt.whichwrap = "b,s,[,],<,>"
vim.opt.backspace = "indent,eol,start"
vim.opt.ambiwidth = "single"
vim.opt.wildmenu = true
vim.opt.cmdheight = 1
vim.opt.laststatus = 2
vim.opt.showcmd = true
vim.opt.hlsearch = true
vim.opt.hidden = true
vim.opt.backup = true
vim.opt.backupdir = os.getenv("HOME") .. '/.vim/backup'
vim.opt.winblend = 20
vim.opt.pumblend = 20
vim.opt.termguicolors = true
vim.opt.shiftwidth = 4
vim.opt.tabstop = 4
vim.opt.expandtab = true
vim.opt.autoindent = true
vim.opt.smartindent = true
vim.opt.number = true
vim.opt.wrap = false
vim.opt.nrformats = "bin,hex"
vim.opt.swapfile = false
vim.opt.formatoptions:remove('t')
vim.opt.formatoptions:append('mM')
vim.api.nvim_create_autocmd({ 'TermOpen' }, {
pattern = '*',
command = 'startinsert',
})
-- eqaul to below setting
vim.cmd 'autocmd TermOpen * startinsert'
vim.cmd [[
if executable('fcitx5')
let g:fcitx_state = 1
augroup fcitx_savestate
autocmd!
autocmd InsertLeave * let g:fcitx_state = str2nr(system('fcitx5-remote'))
autocmd InsertLeave * call system('fcitx5-remote -c')
autocmd InsertEnter * call system(g:fcitx_state == 1 ? 'fcitx5-remote -c': 'fcitx5-remote -o')
augroup END
endif
]]
一部機能について少し追記します
vim.opt.ambiwidth = "single"
East Asian Ambigous Widthの問題で、Vimではset ambiwidth=double
としないと曖昧幅の表示が崩れてしまうのですが、なぜかNeovimだとsingle
の方が綺麗に表示できます。LSPの補完などに影響が出ます。
vim.opt.winblend = 20
vim.opt.pumblend = 20
vim.opt.termguicolors=true
fzfなどの仮想端末を開いた時に後ろの文字を透過してくれるようになります。単純に格好いいだけですが、オススメです。なお、これを有効にするとテーマ設定でcterm
ではなくguiterm
が使用されるのでご注意ください。
vim.cmd [[
if executable('fcitx5')
let g:fcitx_state = 1
augroup fcitx_savestate
autocmd!
autocmd InsertLeave * let g:fcitx_state = str2nr(system('fcitx5-remote'))
autocmd InsertLeave * call system('fcitx5-remote -c')
autocmd InsertEnter * call system(g:fcitx_state == 1 ? 'fcitx5-remote -c': 'fcitx5-remote -o')
augroup END
endif
]]
Fcitx5の設定です。入力モード解除時に全角/半角の状態を保存して、再度入力モードに入る時に自動で変更してくれます。私は日本語を書くことも多いので地味に重宝してます。コピペすればVimでも動きます。
3. プラグイン
-
:help packer.txt
( https://github.com/wbthomason/packer.nvim )
遅延読み込みなどの複雑なことはしていません。編集してNeovimを立ち上げて :PackerSync
でインストールされます。
require("packer").startup(function()
use 'wbthomason/packer.nvim'
use 'neovim/nvim-lspconfig'
-- use 'williamboman/nvim-lsp-installer'
use 'williamboman/mason.nvim'
use 'williamboman/mason-lspconfig.nvim'
use 'vim-airline/vim-airline'
use 'vim-airline/vim-airline-themes'
use 'tpope/vim-fugitive'
use 'airblade/vim-gitgutter'
use 'cocopon/iceberg.vim'
use 'obaland/vfiler.vim'
use 'obaland/vfiler-fzf'
use 'ibhagwan/fzf-lua'
-- nvim-cmp
use "hrsh7th/nvim-cmp"
use "hrsh7th/cmp-path"
use "hrsh7th/cmp-buffer"
use "hrsh7th/cmp-cmdline"
use "hrsh7th/cmp-nvim-lsp"
use "hrsh7th/vim-vsnip"
end)
4. キーボードショートカット
-
:help api.txt
( https://neovim.io/doc/user/api.html )-
:help nvim_set_keymap
( https://neovim.io/doc/user/api.html#nvim_set_keymap() ) -
:help nvim_buf_set_keymap
( https://neovim.io/doc/user/api.html#nvim_buf_set_keymap() )
-
お好みのキーマップに割り当ててください。スペースキーを<leader>
に設定するのがお気に入りです。
Git(fugitive)やfzf-luaはたくさんのコマンドがあるのですが、使いこなせてないです。要改善ですね。
vim.g.mapleader = " "
vim.api.nvim_set_keymap('n', '<leader><leader>', ':<C-u>cd %:h<CR>', {noremap = true})
vim.api.nvim_set_keymap('n', '<leader>w', ':<C-u>w<CR>', {noremap = true})
vim.api.nvim_set_keymap('n', '<leader>q', ':<C-u>bd<CR>', {noremap = true})
vim.api.nvim_set_keymap('n', '<C-l>', ':<C-u>bnext<CR>', {noremap = true, silent = true})
vim.api.nvim_set_keymap('n', '<C-h>', ':<C-u>bprevious<CR>', {noremap = true, silent = true})
vim.api.nvim_set_keymap('n', 'j', 'gj', {noremap = true})
vim.api.nvim_set_keymap('n', 'k', 'gk', {noremap = true})
vim.api.nvim_set_keymap('i', 'jj', '<ESC>', {silent=true})
vim.api.nvim_set_keymap('n', '<C-W>+', ':<C-u>resize +5<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<C-W>-', ':<C-u>resize -5<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<C-W>>', ':<C-u>vertical resize +10<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<C-W><', ':<C-u>vertical resize -10<CR>', { silent = true })
vim.api.nvim_set_keymap('n', '<ESC><ESC>', ':nohlsearch<CR>', {silent=true})
vim.api.nvim_set_keymap('t', '<ESC>', '<C-\\><C-n>', {silent=true})
vim.api.nvim_set_keymap('t', '<C-W>j', '<CMD>wincmd j<CR>', {silent=true})
vim.api.nvim_set_keymap('t', '<C-W>k', '<CMD>wincmd k<CR>', {silent=true})
vim.api.nvim_set_keymap('t', '<C-W>h', '<CMD>wincmd h<CR>', {silent=true})
vim.api.nvim_set_keymap('t', '<C-W>l', '<CMD>wincmd l<CR>', {silent=true})
-- 'ibhagwan/fzf-lua' ----------------------------------------------------------
opt = { noremap = true, silent = true }
vim.api.nvim_set_keymap('n', '<leader>e', "<cmd>lua require('fzf-lua').files()<CR>", opt)
vim.api.nvim_set_keymap('n', '<leader>g', "<cmd>lua require('fzf-lua').git_status()<CR>", opt)
vim.api.nvim_set_keymap('n', '<leader>p', "<cmd>lua require('fzf-lua').live_grep()<CR>", opt)
vim.api.nvim_set_keymap('n', '<leader>h', "<cmd>lua require('fzf-lua').oldfiles()<CR>", opt)
vim.api.nvim_set_keymap('n', '<leader>b', "<cmd>lua require('fzf-lua').buffers()<CR>", opt)
-- 'tpope/vim-fugitive' --------------------------------------------------------
vim.api.nvim_set_keymap('n', '<leader>GG', ':<C-u>Git<CR>', {noremap = true})
vim.api.nvim_set_keymap('n', '<leader>GC', ':<C-u>Git commit<CR>', {noremap = true})
vim.api.nvim_set_keymap('n', '<leader>GP', ':<C-u>Git push<CR>', {noremap = true})
vim.api.nvim_set_keymap('n', '<leader>GL', ':<C-u>Git log --oneline<CR>', {noremap = true})
vim.api.nvim_set_keymap('n', '<leader>GD', ':<C-u>vert Gdiffsplit !~1', {noremap = true})
5. LSP
-
:help lsp.txt
( https://neovim.io/doc/user/lsp.html )-
:help lsp-handers
( https://neovim.io/doc/user/lsp.html#lsp-handlers )
-
-
:help diagnostic.txt
( https://neovim.io/doc/user/diagnostic.html ) -
:help lspconfig.txt
( https://github.com/neovim/nvim-lspconfig ) :help nvim-lsp-installer.txt
( https://github.com/williamboman/nvim-lsp-installer )-
:help mason.nvim
(https://github.com/williamboman/mason.nvim)
使用可能なコマンドは neovim/nvim-lspconfig を確認ください。キーマップはサーバーが有効になった時にのみ読み込まれるようにしています。定義ジャンプ、ヘルプ、リネームなどたくさん機能がありますが、こちらも使いこなせてないです。
require('mason').setup()
require('mason-lspconfig').setup_handlers({ function(server)
local opt = {
on_attach = function(client, bufnr)
-- vim.api.nvim_buf_set_option(bufnr, 'omnifunc', 'v:lua.vim.lsp.omnifunc')
local opts = { noremap=true, silent=true }
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'K', '<cmd>lua vim.lsp.buf.hover()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gd', '<cmd>lua vim.lsp.buf.definition()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "<cmd>lua vim.lsp.buf.declaration()<CR>", opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'gr', '<cmd>lua vim.lsp.buf.references()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<C-k>', '<cmd>lua vim.lsp.buf.signature_help()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '<space>f', '<cmd>lua vim.lsp.buf.formatting()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', 'ge', '<cmd>lua vim.diagnostic.open_float()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', '[d', '<cmd>lua vim.diagnostic.goto_prev()<CR>', opts)
vim.api.nvim_buf_set_keymap(bufnr, 'n', ']d', '<cmd>lua vim.diagnostic.goto_next()<CR>', opts)
end,
capabilities = require('cmp_nvim_lsp').update_capabilities(
vim.lsp.protocol.make_client_capabilities()
)
}
require('lspconfig')[server].setup(opt)
end)
vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
vim.lsp.diagnostic.on_publish_diagnostics, { virtual_text = false }
)
vim.lsp.handlers["textDocument/hover"] = vim.lsp.with(
vim.lsp.handlers.hover, { separator = true }
)
vim.lsp.handlers["textDocument/signatureHelp"] = vim.lsp.with(
vim.lsp.handlers.signature_help, { separator = true }
)
サーバーのインストールは williamboman/nvim-lsp-installer に一任しています。:LspInstallInfo
を実行すればインストール可能なサーバーが一覧表示されるのでそれほど難しいことはありません。
nvim-lsp-installerの開発終了に合わせてwilliamboman/mason.nvimを使うよう設定を変更しました。使用感はだいたい同じです。Linter, Formatter, DAPも管理できるようになったようです。
6. 補完
-
:help cmp
( https://github.com/hrsh7th/nvim-cmp ) -
:help cmp-nvim-lsp
( https://github.com/hrsh7th/cmp-nvim-lsp )
主にLSPとの連携のために入れましたが、コマンドやファイルパスも補完してくれるのでとても便利です。著者は使用していませんが、スニペットとの連携もできるようです。
local cmp = require("cmp")
cmp.setup({
snippet = {
expand = function(args)
vim.fn["vsnip#anonymous"](args.body)
end,
},
sources = {
{ name = "nvim_lsp" },
-- { name = "vsnip" },
{ name = "buffer" },
{ name = "path" },
},
mapping = cmp.mapping.preset.insert({
['<C-n>'] = cmp.mapping.select_next_item(),
['<C-p>'] = cmp.mapping.select_prev_item(),
['<C-f>'] = cmp.mapping.scroll_docs(4),
['<C-b>'] = cmp.mapping.scroll_docs(-4),
['<C-l>'] = cmp.mapping.complete(),
['<C-e>'] = cmp.mapping.abort(),
['<CR>'] = cmp.mapping.confirm { select = true },
}),
experimental = {
ghost_text = true,
},
})
cmp.setup.cmdline('/', {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = 'buffer' }
}
})
cmp.setup.cmdline(":", {
mapping = cmp.mapping.preset.cmdline(),
sources = {
{ name = "path" },
{ name = "cmdline" },
},
})
7. その他
Airline, gitgutter, fzfなどの見た目を設定しています。カラースキーマは iceberg がオススメです。ターミナルで背景を透過にしてるので ... ctermbg=none guibg=none
を設定しています
-- colorscheme -----------------------------------------------------------------
vim.cmd 'autocmd ColorScheme * highlight Normal ctermbg=none guibg=none'
vim.cmd 'autocmd ColorScheme * highlight NonText ctermbg=none guibg=none'
vim.cmd 'autocmd ColorScheme * highlight LineNr ctermbg=none guibg=none'
vim.cmd 'autocmd ColorScheme * highlight Folded ctermbg=none guibg=none'
vim.cmd 'autocmd ColorScheme * highlight EndOfBuffer ctermbg=none guibg=none'
vim.cmd 'colorscheme iceberg'
-- 'vim-airline/vim-airline' ---------------------------------------------------
vim.cmd 'let g:airline_symbols_ascii = 1'
vim.cmd 'let g:airline#extensions#tabline#enabled = 1'
vim.cmd 'let g:airline#extensions#whitespace#mixed_indent_algo = 1'
-- 'vim-airline/vim-airline-themes' --------------------------------------------
vim.cmd 'let g:airline_theme = "iceberg"'
-- 'airblade/vim-gitgutter' ----------------------------------------------------
vim.cmd 'let g:gitgutter_sign_added = "+"'
vim.cmd 'let g:gitgutter_sign_modified = "^"'
vim.cmd 'let g:gitgutter_sign_removed = "-"'
vim.cmd 'highlight GitGutterAdd guifg=#009900 ctermfg=2'
vim.cmd 'highlight GitGutterChange guifg=#bbbb00 ctermfg=3'
vim.cmd 'highlight GitGutterDelete guifg=#ff2222 ctermfg=1'
vim.cmd 'highlight GitGutterAddLine ctermbg=2'
vim.cmd 'highlight GitGutterChangeLine ctermbg=3'
vim.cmd 'highlight GitGutterDeleteLine ctermbg=1'
-- 'junegunn/fzf.vim' ----------------------------------------------------------
vim.cmd "let g:fzf_preview_window = ['right:70%', 'ctrl-/']"
以上です。
あまり複雑な設定は自分でもわからなくなってしまうので200行ちょっとのファイル1つにまとまる量に抑えていますが、充分使えるのではないでしょうか。他にもフォーマッタ、ファイラなどのプラグインを追加すれば更に使いやすくなるのではと思います。
Discussion
詳細な記事をありがとうございます。参考になります。
いくつか気になったのでコメントさせてください。
vim.api.nvim_set_keymap
ではなく、vim.keymap.set
を使うとデフォルトでnoremap = true
状態なので便利です:vim.g
を使って設定できます:教えていただきどうもありがとうございます。すごく助かります
参考にさせていただきます