Neovimの初期設定(LSP, Treesitterなど)
これまでVimを使ってきたが、最近Neovimへ移行した。
きっかけは、VS Codeを完全に使わなくなったことだ。
VS Codeは便利だが、常に起動しておくにはそれなりにメモリを使う。自分のMacはメモリに余裕があるわけではないので、思い切ってアンインストールしてしまった。
もともとVimは使っており、特に不満はなかった。ただ、VS Codeを使わなくなったことで、もう少し機能を充実させたくなった。
そこで、Vimの操作感や軽さはそのままに、Neovimで必要な機能だけを追加することにした。
この記事では、その環境を作っていった過程をまとめる。
目次
環境
今回の環境はM1 Macbook Airで、NeovimはHomebrewから0.12.5をインストールした。
brew install neovim
今回はNeovimの設定を一から組み直すことにした。
設定ファイルを分ける
Neovimの設定は ~/.config/nvim/ に置く。
最終的には以下のような構成にした。
~/.config/nvim/
├── init.lua
└── lua/
├── config/
│ ├── options.lua
│ ├── keymaps.lua //キーマップ
│ └── autocmds.lua //自動コマンド
└── plugins/ //プラグイン
├── treesitter.lua
├── lsp.lua
├── completion.lua
├── conform.lua
├── lint.lua
├── autotag.lua
├── lualine.lua
└── colorscheme.lua
init.lua にすべてを書く方法もあるが、設定が増えてくると何がどこにあるのか分かりにくくなるので、AIの勧めに従って適宜分割した。
プラグイン管理にlazy.nvimを使う
Neovimのプラグイン管理には lazy.nvim を使う。
プラグイン管理にはいくつか選択肢があるが、今回はNeovimの設定を自分で組み立てることにしたので、シンプルに使えるlazy.nvimを選んだ。
インストール処理を init.lua に書いている。
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not vim.uv.fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable",
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
require("config.options")
require("config.keymaps")
require("config.autocmds")
require("lazy").setup("plugins", {
rocks = {
enabled = false,
},
})
プラグインの設定は lua/plugins/ に置いていく。
プラグインごとにファイルを分けられるので、後から設定を変更するときにも探しやすくなっている。
使わないLuaRocksは、lazy.nvim側でも無効にしている。
基本設定
行番号、検索、インデント、クリップボードなど、普段使う設定をここに置いている。
local opt = vim.opt
opt.number = true
opt.title = true
opt.swapfile = false
opt.showmode = false
opt.cursorline = true
opt.mouse = "a"
opt.showmatch = true
opt.backspace = { "indent", "eol", "start" }
opt.expandtab = true
opt.shiftwidth = 2
opt.softtabstop = 2
opt.tabstop = 2
opt.ignorecase = true
opt.smartcase = true
opt.incsearch = true
opt.hlsearch = true
-- クリップボードをNeovimの外と共有
opt.clipboard:append("unnamed")
opt.fileencodings = { "ucs-boms", "utf-8", "euc-jp", "cp932" }
opt.fileformats = { "unix", "dos", "mac" }
opt.ambiwidth = "double"
-- TypeScriptなどでの正規表現エンジン問題を避ける
opt.regexpengine = 0
Treesitterで構文解析を行う
コードを書く上で、まず導入したのがTreesitterである。
Treesitterはソースコードを構文として解析できるため、シンタックスハイライトだけでなく、折りたたみなどにも利用できる。
私は普段使うPythonなどの言語のパーサーをインストールしているが、適宜利用する言語に置き換えて欲しい。
サポート言語はここから確認できる。
return {
{
"nvim-treesitter/nvim-treesitter",
lazy = false,
build = ":TSUpdate",
config = function()
require("nvim-treesitter").install({
"python",
"javascript",
"typescript",
"tsx",
"html",
"css",
"astro",
"markdown",
"markdown_inline",
"json",
"lua",
})
vim.api.nvim_create_autocmd("FileType", {
pattern = {
"python",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"html",
"css",
"astro",
"markdown",
"json",
"lua",
},
callback = function()
vim.treesitter.start()
vim.wo.foldmethod = "expr"
vim.wo.foldexpr = "v:lua.vim.treesitter.foldexpr()"
end,
})
end,
},
}
LSPを使う
次にLSPを導入する。
LSPを使うことで、定義への移動、参照の検索、ホバーによる情報表示、名前変更などができる。
ここでは、
- Mason
- mason-lspconfig
- nvim-lspconfig
を組み合わせる。
MasonはLSPなどの外部ツールを管理するためのもの、nvim-lspconfigはNeovim側でLSPを設定するためのもの、その間をつなぐのがmason-lspconfigである。
return {
{
"mason-org/mason.nvim",
opts = {},
},
{
"neovim/nvim-lspconfig",
},
{
"mason-org/mason-lspconfig.nvim",
opts = {
ensure_installed = {
-- basedpyrightはPython
"basedpyright",
"ts_ls",
"astro",
"html",
"cssls",
},
},
dependencies = {
"mason-org/mason.nvim",
"neovim/nvim-lspconfig",
},
},
}
LSPを使うためのキーマップも設定する。
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local opts = { buffer = args.buf }
-- 定義元へ移動
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
-- 使用箇所の一覧を表示
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
-- 説明をポップアップウィンドウで表示
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
-- 名前変更
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
-- 利用可能なコードアクションのメニュー表示
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
end,
})
例えば gd で定義へ移動したあと、元の場所に戻るときは Ctrl-o を使う。
<leader>はデフォルトではSpaceになっている。
補完にblink.cmpを使う
補完には blink.cmp を使う。
LSPから取得した候補だけでなく、ファイルパスやスニペット、現在のバッファの内容も補完候補として扱える。
return {
{
"saghen/blink.cmp",
version = "1.*",
dependencies = {
"rafamadriz/friendly-snippets",
},
opts = {
keymap = {
preset = "default",
},
completion = {
documentation = {
auto_show = false,
},
},
sources = {
default = {
"lsp",
"path",
"snippets",
"buffer",
},
},
fuzzy = {
implementation = "rust",
},
},
},
}
デフォルトでのキーバインドは次のようになっている。
| キー | 操作 |
| -------------- | ---------------- |
| Ctrl-Space | 補完メニューを表示(LSPで自動で表示される) |
| Ctrl-n / ↓ | 次の候補へ移動 |
| Ctrl-p / ↑ | 前の候補へ移動 |
| Ctrl-y | 選択中の候補を確定 |
| Ctrl-e | 補完メニューを閉じる |
| Ctrl-k | シグネチャヘルプを表示/切り替え |
| Ctrl-b | ドキュメントを上へスクロール |
| Ctrl-f | ドキュメントを下へスクロール |
| Tab | スニペットの次のプレースホルダへ |
| Shift-Tab | スニペットの前のプレースホルダへ |
フォーマットはconform.nvimに任せる
コードの整形には conform.nvim を使う。
フォーマッターそのものをNeovimに内蔵するのではなく、PrettierやRuffといった外部ツールを呼び出す構成にしている。
return {
{
"stevearc/conform.nvim",
event = { "BufWritePre" },
cmd = { "ConformInfo" },
keys = {
{
"<leader>f",
function()
require("conform").format({
async = true,
lsp_format = "fallback",
})
end,
mode = "",
desc = "Format buffer",
},
},
opts = {
formatters_by_ft = {
javascript = { "prettier" },
javascriptreact = { "prettier" },
typescript = { "prettier" },
typescriptreact = { "prettier" },
astro = { "prettier" },
html = { "prettier" },
css = { "prettier" },
json = { "prettier" },
markdown = { "prettier" },
python = { "ruff_format" },
},
},
},
}
Prettierはプロジェクト側にnpmなどでインストールして使う。
PythonについてはRuffを使う。こちらはHomebrewでインストールした。
Neovimのプラグインと、実際にコードを整形する外部ツールは別物であることに注意。
リントにはnvim-lintを使う
フォーマットとは別に、コードの問題を検出するために nvim-lint を使う。
フォーマットとLintを同じものとして扱わず、それぞれ別の役割にしている。
return {
{
"mfussenegger/nvim-lint",
event = {
"BufReadPre",
"BufNewFile",
},
config = function()
local lint = require("lint")
lint.linters_by_ft = {
javascript = { "eslint" },
javascriptreact = { "eslint" },
typescript = { "eslint" },
typescriptreact = { "eslint" },
astro = { "eslint" },
python = { "ruff" },
}
vim.api.nvim_create_autocmd(
{ "BufEnter", "BufWritePost", "InsertLeave" },
{
callback = function()
lint.try_lint()
end,
}
)
end,
},
}
これで、
- LSP:コードの意味を解析する
- 補完:入力を支援する
- Formatter:コードを整形する
- Linter:問題を検出する
という形になった。
HTMLタグの補完
HTMLやJSXなどを書くときのタグ操作には nvim-ts-autotag を使う。
Treesitterを利用してタグを自動的に閉じたり、変更したりできる。
return {
{
"windwp/nvim-ts-autotag",
dependencies = {
"nvim-treesitter/nvim-treesitter",
},
event = "VeryLazy",
config = function()
require("nvim-ts-autotag").setup({
enable = true,
filetypes = {
"html",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro",
"xml",
},
})
end,
},
}
ステータスライン
画面下部のステータスラインには lualine.nvim を使う。
ファイル名、カーソル位置など最低限の内容を表示している。
return {
{
"nvim-lualine/lualine.nvim",
event = "VeryLazy",
opts = {
options = {
theme = "nord",
globalstatus = true,
component_separators = "",
section_separators = "",
},
sections = {
lualine_a = {},
lualine_b = {},
lualine_c = {
"filename",
},
lualine_x = {},
lualine_y = {
"location",
},
lualine_z = {
"progress",
},
},
},
},
}
カラースキーム
カラースキームには no-clown-fiesta を使う。これは好みのものを使って欲しい。
return {
{
"aktersnurra/no-clown-fiesta.nvim",
lazy = false,
priority = 1000,
config = function()
vim.cmd("colorscheme no-clown-fiesta")
end,
},
}
キーマップ
基本的なキーマップは lua/config/keymaps.lua にまとめている。
普段の入力でよく使うものと、LSP関連の操作だけを追加している。
local map = vim.keymap.set
-- jを2回連打でインサートモード終了
map("i", "jj", "<Esc>")
-- インサートモード中の移動
map("i", "<C-j>", "<Down>")
map("i", "<C-k>", "<Up>")
map("i", "<C-h>", "<Left>")
map("i", "<C-l>", "<Right>")
-- かっこなどの自動補完
map("i", "(", "()<Left>")
map("i", "{", "{}<Left>")
map("i", "[", "[]<Left>")
map("i", "'", "''<Left>")
map("i", '"', '""<Left>')
vim.api.nvim_create_autocmd("FileType", {
pattern = "markdown",
callback = function(args)
local opts = { buffer = args.buf }
map("i", "url", "[]()<Left>", opts)
map("i", "h1", "#", opts)
map("i", "h2", "##", opts)
map("i", "h3", "###", opts)
map("i", "h4", "####", opts)
map("i", "h5", "#####", opts)
map("i", "h6", "######", opts)
map("i", "```", "``````<Left><Left><Left>", opts)
map("i", "code", "``<Left>", opts)
end,
})
vim.api.nvim_create_autocmd("FileType", {
pattern = "python",
callback = function(args)
map("i", "{%", "{%%}<Left><Left>", { buffer = args.buf })
end,
})
vim.api.nvim_create_autocmd("FileType", {
pattern = "css",
callback = function(args)
local opts = { buffer = args.buf }
map("i", ":", ":;<Left>", opts)
map("i", "/*", "/**/<Left>", opts)
end,
})
vim.api.nvim_create_autocmd("LspAttach", {
callback = function(args)
local opts = { buffer = args.buf }
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts)
vim.keymap.set("n", "gr", vim.lsp.buf.references, opts)
vim.keymap.set("n", "K", vim.lsp.buf.hover, opts)
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts)
vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts)
end,
})
ファイルタイプごとの設定
インデントなど、ファイルタイプによって変えたい設定は自動コマンドにまとめた。
vim.api.nvim_create_autocmd("FileType", {
pattern = {
"css",
"html",
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"astro",
"c",
"dart",
"markdown",
},
callback = function()
vim.opt_local.shiftwidth = 2
vim.opt_local.softtabstop = 2
vim.opt_local.tabstop = 2
vim.opt_local.expandtab = true
end,
})
vim.api.nvim_create_autocmd("FileType", {
pattern = {
"python",
"zsh",
},
callback = function()
vim.opt_local.shiftwidth = 4
vim.opt_local.softtabstop = 4
vim.opt_local.tabstop = 4
vim.opt_local.expandtab = true
end,
})
vim.api.nvim_create_autocmd("BufReadPost", {
callback = function(args)
local mark = vim.api.nvim_buf_get_mark(args.buf, '"')
local line_count = vim.api.nvim_buf_line_count(args.buf)
if mark[1] > 0 and mark[1] <= line_count then
vim.cmd('normal! g`"')
end
end,
})
使ってみての感想
一通り設定してみると、Vimの操作感を大きく変えずに、コードを書くときに必要だった機能がかなり揃った。
特にLSP、補完、Treesitter、Formatter、Linterなどを揃えることで、VS CodeのようなIDEには敵わずとも、かなり快適な環境が作れたと思う。
ただ、利便性を求めてプラグインをむやみやたらに増やすと管理コストがかかったり、起動に時間がかかったりする可能性があるので、必要な機能だけに厳選している。
最後に
VS Codeを使わなくなったことで、Vimにもう少し高機能さを求めるようになった。
その結果としてNeovimを使うことになったが、目的はVimを別のエディタに置き換えることではなかった。
Vimの軽さや操作感を残したまま、必要なところだけを現代的にしている。
この備忘録が誰かの役に立てば嬉しい。
Mentions
Neovimの初期設定(LSP, Treesitterなど) これまでVimを使ってきたが、最近Neovimへ移行した。 きっかけは、VS Codeを完全に使わなくなったことだ。 VS Codeは便利だが、常に起動しておくにはそれなりにメモリを使う。自分のMacはメモリに余裕があるわけではないので、思い切ってアンインストールしてしまった。 もともとVimは使っており、特に不満はなかった。ただ、VS Codeを使わなくなったことで、もう少し機能を充実させたくなった。 そこで、Vimの操作感や軽さはそのままに、Neovimで必要な機能だけを追加することにした。 この記事では、その環境を作っていった過程をまとめる。 目次 環境 今回の環境はM1 Macbook Airで、NeovimはHomebrewから0.12.5をインストールした。 brew install neovim 今回はNeovimの設定を一から組み直すことにした。 設定ファイルを分ける Neovimの設定は ~/.config/nvim/ に置く。 最終的には以下のような構成にした。 ~/.config/nvim/ ├── init.lua └── lua/ ├── config/ │ ├── options.lua │ ├── keymaps.lua //キーマップ │ └── autocmds.lua //自動コマンド └── plugins/ //プラグイン ├── treesitter.lua ├── lsp.lua ├── completion.lua ├── conform.lua ├── lint.lua ├── autotag.lua ├── lualine.lua └── colorscheme.lua init.lua にすべてを書く方法もあるが、設定が増えてくると何がどこにあるのか分かりにくくなるので、AIの勧めに従って適宜分割した。 プラグイン管理にlazy.nvimを使う Neovimのプラグイン管理には lazy.nvim を使う。 プラグイン管理にはいくつか選択肢があるが、今回はNeovimの設定を自分で組み立てることにしたので、シンプルに使えるlazy.nvimを選んだ。 インストール処理を init.lua に書いている。 local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not vim.uv.fs_stat(lazypath) then vim.fn.system({ "git", "clone", "--filter=blob:none", "https://github.com/folke/lazy.nvim.git", "--branch=stable", lazypath, }) end vim.opt.rtp:prepend(lazypath) require("config.options") require("config.keymaps") require("config.autocmds") require("lazy").setup("plugins", { rocks = { enabled = false, }, }) プラグインの設定は lua/plugins/ に置いていく。 プラグインごとにファイルを分けられるので、後から設定を変更するときにも探しやすくなっている。 使わないLuaRocksは、lazy.nvim側でも無効にしている。 基本設定 行番号、検索、インデント、クリップボードなど、普段使う設定をここに置いている。 local opt = vim.opt opt.number = true opt.title = true opt.swapfile = false opt.showmode = false opt.cursorline = true opt.mouse = "a" opt.showmatch = true opt.backspace = { "indent", "eol", "start" } opt.expandtab = true opt.shiftwidth = 2 opt.softtabstop = 2 opt.tabstop = 2 opt.ignorecase = true opt.smartcase = true opt.incsearch = true opt.hlsearch = true -- クリップボードをNeovimの外と共有 opt.clipboard:append("unnamed") opt.fileencodings = { "ucs-boms", "utf-8", "euc-jp", "cp932" } opt.fileformats = { "unix", "dos", "mac" } opt.ambiwidth = "double" -- TypeScriptなどでの正規表現エンジン問題を避ける opt.regexpengine = 0 Treesitterで構文解析を行う コードを書く上で、まず導入したのがTreesitterである。 Treesitterはソースコードを構文として解析できるため、シンタックスハイライトだけでなく、折りたたみなどにも利用できる。 私は普段使うPythonなどの言語のパーサーをインストールしているが、適宜利用する言語に置き換えて欲しい。 サポート言語はここから確認できる。 return { { "nvim-treesitter/nvim-treesitter", lazy = false, build = ":TSUpdate", config = function() require("nvim-treesitter").install({ "python", "javascript", "typescript", "tsx", "html", "css", "astro", "markdown", "markdown_inline", "json", "lua", }) vim.api.nvim_create_autocmd("FileType", { pattern = { "python", "javascript", "javascriptreact", "typescript", "typescriptreact", "html", "css", "astro", "markdown", "json", "lua", }, callback = function() vim.treesitter.start() vim.wo.foldmethod = "expr" vim.wo.foldexpr = "v:lua.vim.treesitter.foldexpr()" end, }) end, }, } LSPを使う 次にLSPを導入する。 LSPを使うことで、定義への移動、参照の検索、ホバーによる情報表示、名前変更などができる。 ここでは、 - Mason - mason-lspconfig - nvim-lspconfig を組み合わせる。 MasonはLSPなどの外部ツールを管理するためのもの、nvim-lspconfigはNeovim側でLSPを設定するためのもの、その間をつなぐのがmason-lspconfigである。 return { { "mason-org/mason.nvim", opts = {}, }, { "neovim/nvim-lspconfig", }, { "mason-org/mason-lspconfig.nvim", opts = { ensure_installed = { -- basedpyrightはPython "basedpyright", "ts_ls", "astro", "html", "cssls", }, }, dependencies = { "mason-org/mason.nvim", "neovim/nvim-lspconfig", }, }, } LSPを使うためのキーマップも設定する。 vim.api.nvim_create_autocmd("LspAttach", { callback = function(args) local opts = { buffer = args.buf } -- 定義元へ移動 vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts) -- 使用箇所の一覧を表示 vim.keymap.set("n", "gr", vim.lsp.buf.references, opts) -- 説明をポップアップウィンドウで表示 vim.keymap.set("n", "K", vim.lsp.buf.hover, opts) -- 名前変更 vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts) -- 利用可能なコードアクションのメニュー表示 vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts) end, }) 例えば gd で定義へ移動したあと、元の場所に戻るときは Ctrl-o を使う。 <leader>はデフォルトではSpaceになっている。 補完にblink.cmpを使う 補完には blink.cmp を使う。 LSPから取得した候補だけでなく、ファイルパスやスニペット、現在のバッファの内容も補完候補として扱える。 return { { "saghen/blink.cmp", version = "1.*", dependencies = { "rafamadriz/friendly-snippets", }, opts = { keymap = { preset = "default", }, completion = { documentation = { auto_show = false, }, }, sources = { default = { "lsp", "path", "snippets", "buffer", }, }, fuzzy = { implementation = "rust", }, }, }, } デフォルトでのキーバインドは次のようになっている。 | キー | 操作 | | -------------- | ---------------- | | Ctrl-Space | 補完メニューを表示(LSPで自動で表示される) | | Ctrl-n / ↓ | 次の候補へ移動 | | Ctrl-p / ↑ | 前の候補へ移動 | | Ctrl-y | 選択中の候補を確定 | | Ctrl-e | 補完メニューを閉じる | | Ctrl-k | シグネチャヘルプを表示/切り替え | | Ctrl-b | ドキュメントを上へスクロール | | Ctrl-f | ドキュメントを下へスクロール | | Tab | スニペットの次のプレースホルダへ | | Shift-Tab | スニペットの前のプレースホルダへ | フォーマットはconform.nvimに任せる コードの整形には conform.nvim を使う。 フォーマッターそのものをNeovimに内蔵するのではなく、PrettierやRuffといった外部ツールを呼び出す構成にしている。 return { { "stevearc/conform.nvim", event = { "BufWritePre" }, cmd = { "ConformInfo" }, keys = { { "<leader>f", function() require("conform").format({ async = true, lsp_format = "fallback", }) end, mode = "", desc = "Format buffer", }, }, opts = { formatters_by_ft = { javascript = { "prettier" }, javascriptreact = { "prettier" }, typescript = { "prettier" }, typescriptreact = { "prettier" }, astro = { "prettier" }, html = { "prettier" }, css = { "prettier" }, json = { "prettier" }, markdown = { "prettier" }, python = { "ruff_format" }, }, }, }, } Prettierはプロジェクト側にnpmなどでインストールして使う。 PythonについてはRuffを使う。こちらはHomebrewでインストールした。 Neovimのプラグインと、実際にコードを整形する外部ツールは別物であることに注意。 リントにはnvim-lintを使う フォーマットとは別に、コードの問題を検出するために nvim-lint を使う。 フォーマットとLintを同じものとして扱わず、それぞれ別の役割にしている。 return { { "mfussenegger/nvim-lint", event = { "BufReadPre", "BufNewFile", }, config = function() local lint = require("lint") lint.linters_by_ft = { javascript = { "eslint" }, javascriptreact = { "eslint" }, typescript = { "eslint" }, typescriptreact = { "eslint" }, astro = { "eslint" }, python = { "ruff" }, } vim.api.nvim_create_autocmd( { "BufEnter", "BufWritePost", "InsertLeave" }, { callback = function() lint.try_lint() end, } ) end, }, } これで、 - LSP:コードの意味を解析する - 補完:入力を支援する - Formatter:コードを整形する - Linter:問題を検出する という形になった。 HTMLタグの補完 HTMLやJSXなどを書くときのタグ操作には nvim-ts-autotag を使う。 Treesitterを利用してタグを自動的に閉じたり、変更したりできる。 return { { "windwp/nvim-ts-autotag", dependencies = { "nvim-treesitter/nvim-treesitter", }, event = "VeryLazy", config = function() require("nvim-ts-autotag").setup({ enable = true, filetypes = { "html", "javascript", "javascriptreact", "typescript", "typescriptreact", "astro", "xml", }, }) end, }, } ステータスライン 画面下部のステータスラインには lualine.nvim を使う。 ファイル名、カーソル位置など最低限の内容を表示している。 return { { "nvim-lualine/lualine.nvim", event = "VeryLazy", opts = { options = { theme = "nord", globalstatus = true, component_separators = "", section_separators = "", }, sections = { lualine_a = {}, lualine_b = {}, lualine_c = { "filename", }, lualine_x = {}, lualine_y = { "location", }, lualine_z = { "progress", }, }, }, }, } カラースキーム カラースキームには no-clown-fiesta を使う。これは好みのものを使って欲しい。 return { { "aktersnurra/no-clown-fiesta.nvim", lazy = false, priority = 1000, config = function() vim.cmd("colorscheme no-clown-fiesta") end, }, } キーマップ 基本的なキーマップは lua/config/keymaps.lua にまとめている。 普段の入力でよく使うものと、LSP関連の操作だけを追加している。 local map = vim.keymap.set -- jを2回連打でインサートモード終了 map("i", "jj", "<Esc>") -- インサートモード中の移動 map("i", "<C-j>", "<Down>") map("i", "<C-k>", "<Up>") map("i", "<C-h>", "<Left>") map("i", "<C-l>", "<Right>") -- かっこなどの自動補完 map("i", "(", "()<Left>") map("i", "{", "{}<Left>") map("i", "[", "[]<Left>") map("i", "'", "''<Left>") map("i", '"', '""<Left>') vim.api.nvim_create_autocmd("FileType", { pattern = "markdown", callback = function(args) local opts = { buffer = args.buf } map("i", "url", "[]()<Left>", opts) map("i", "h1", "#", opts) map("i", "h2", "##", opts) map("i", "h3", "###", opts) map("i", "h4", "####", opts) map("i", "h5", "#####", opts) map("i", "h6", "######", opts) map("i", "```", "``````<Left><Left><Left>", opts) map("i", "code", "``<Left>", opts) end, }) vim.api.nvim_create_autocmd("FileType", { pattern = "python", callback = function(args) map("i", "{%", "{%%}<Left><Left>", { buffer = args.buf }) end, }) vim.api.nvim_create_autocmd("FileType", { pattern = "css", callback = function(args) local opts = { buffer = args.buf } map("i", ":", ":;<Left>", opts) map("i", "/*", "/**/<Left>", opts) end, }) vim.api.nvim_create_autocmd("LspAttach", { callback = function(args) local opts = { buffer = args.buf } vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts) vim.keymap.set("n", "gr", vim.lsp.buf.references, opts) vim.keymap.set("n", "K", vim.lsp.buf.hover, opts) vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts) vim.keymap.set("n", "<leader>ca", vim.lsp.buf.code_action, opts) end, }) ファイルタイプごとの設定 インデントなど、ファイルタイプによって変えたい設定は自動コマンドにまとめた。 vim.api.nvim_create_autocmd("FileType", { pattern = { "css", "html", "javascript", "javascriptreact", "typescript", "typescriptreact", "astro", "c", "dart", "markdown", }, callback = function() vim.opt_local.shiftwidth = 2 vim.opt_local.softtabstop = 2 vim.opt_local.tabstop = 2 vim.opt_local.expandtab = true end, }) vim.api.nvim_create_autocmd("FileType", { pattern = { "python", "zsh", }, callback = function() vim.opt_local.shiftwidth = 4 vim.opt_local.softtabstop = 4 vim.opt_local.tabstop = 4 vim.opt_local.expandtab = true end, }) vim.api.nvim_create_autocmd("BufReadPost", { callback = function(args) local mark = vim.api.nvim_buf_get_mark(args.buf, '"') local line_count = vim.api.nvim_buf_line_count(args.buf) if mark[1] > 0 and mark[1] <= line_count then vim.cmd('normal! g`"') end end, }) 使ってみての感想 一通り設定してみると、Vimの操作感を大きく変えずに、コードを書くときに必要だった機能がかなり揃った。 特にLSP、補完、Treesitter、Formatter、Linterなどを揃えることで、VS CodeのようなIDEには敵わずとも、かなり快適な環境が作れたと思う。 ただ、利便性を求めてプラグインをむやみやたらに増やすと管理コストがかかったり、起動に時間がかかったりする可能性があるので、必要な機能だけに厳選している。 最後に VS Codeを使わなくなったことで、Vimにもう少し高機能さを求めるようになった。 その結果としてNeovimを使うことになったが、目的はVimを別のエディタに置き換えることではなかった。 Vimの軽さや操作感を残したまま、必要なところだけを現代的にしている。 この備忘録が誰かの役に立てば嬉しい。