🎊
選抜!Neovimプラグイン2025
環境
- Neovim v0.10.2
- Deno 2.0.6
紹介のしかた
編集を強化するもの、UIを追加するものなど分野に分けて紹介します。カスタマイズ性が高いものについては自分の設定や入門記事を併記するかも。各プラグインの筆者の設定は下記dotfilesを参照してください。
plugin manager
Shougo/dpp.vim
Shougo/暗黒美無王さんが開発しているdenops製プラグインマネージャー。暗黒の力を使っているのでプラグインが100個近くあっても起動時間が30msを切る可能性がある。所謂プラグインアーキテクチャのような設計思想で、標準的な機能を実現するには以下の補助プラグインが必要。
筆者がインストールしているdppプラグイン
開発者の記事
オススメ入門記事
bypass
あるとちょっと便利なプラグイン集。
tpope/eunuch.vim
Unix系のコマンドのラッパー。Vim側でエラーを出すことなく現在開いているファイルを削除するみたいなことができる。
jghauser/mkdir.nvim
:edit
したときに存在しないディレクトリを指定すると勝手にmkdir
してくれる。地味に重宝している。
nacro90/numb.nvim
コマンドラインで行番号を入力してその行をチラ見、そのままEnterで移動できる。
tani/vim-artemis
hoge#fuga()
みたいな関数をLuaでhoge.fuga()
と呼び出せる。当然シグネチャが取得できるわけではないが補完が出るし書きやすくなる。
colorscheme
maxmx03/fluoromachine.nvim
紫がベースの3種類から選べるかっこいいcolorscheme。lualineにも対応している。
depends
いろんなプラグインが依存しているのでインストールしているものたち。
nvim-tree/nvim-web-devicons
いろんなアイコンが揃っている。必須。
nvim-lua/plenary.nvim
lua用のユーティリティライブラリ。
vim-denops/denops.vim
denops製プラグインを動かすためのプラグイン。現代の暗黒の力の主な供給源。
開発者の記事
nvim-treesitter/nvim-treesitter
Neovimとtreesitterを連携させるためのプラグイン。必須。
ddc.vim
Shougo/ddc.vim
Shougo/暗黒美無王さんが開発しているdenops製補完プラグイン。暗黒の力を使っているのでCPUに負担をかけず高速に動作する。極限まで自分好みにカスタマイズが可能だが、納得がいく機能を実現するにはそれなりの補助プラグインと設定が必要。設定をTypeScriptで書くと型の恩恵を受けられる。
筆者がインストールしているddcプラグイン
ui
source
filter
postfilter
筆者の設定(TypeScript部分)
import {
BaseConfig,
type ConfigArguments,
} from "jsr:@shougo/ddc-vim@~9.1.0/config";
export class Config extends BaseConfig {
override async config(args: ConfigArguments): Promise<void> {
const commonSources = ["around", "rg", "file", "skkeleton"];
const commonLangSources = ["lsp", "denippet"].concat(commonSources);
const headMatchers = ["matcher_head", "matcher_prefix"];
const commonConverters = [
"converter_truncate_abbr",
"converter_remove_overlap",
];
const fuzzyMatchers = ["matcher_fuzzy"];
const fuzzySorters = ["sorter_fuzzy"];
const fuzzyConverters = ["converter_fuzzy"].concat(commonConverters);
args.contextBuilder.patchGlobal({
ui: "pum",
sources: commonSources,
autoCompleteEvents: [
"InsertEnter",
"TextChangedI",
"TextChangedP",
"InsertEnter",
"CmdlineEnter",
"CmdlineChanged",
],
cmdlineSources: {
":": ["file", "cmdline", "cmdline-history", "around"],
"/": commonSources,
},
sourceOptions: {
_: {
matchers: headMatchers,
sorters: ["sorter_rank"],
converters: commonConverters,
minAutoCompleteLength: 3,
ignoreCase: true,
enabledIf: "!skkeleton#is_enabled()",
},
around: {
mark: "[around]",
matchers: fuzzyMatchers,
sorters: fuzzySorters,
converters: fuzzyConverters,
minAutoCompleteLength: 1,
},
buffer: {
mark: "[buf]",
matchers: fuzzyMatchers,
sorters: fuzzySorters,
converters: fuzzyConverters,
},
cmdline: {
mark: "[>_]",
forceCompletionPattern: "\\S/\\S*|\\.\\w*",
minAutoCompleteLength: 1,
},
"cmdline-history": {
mark: "[>_] his",
sorters: [],
minAutoCompleteLength: 1,
},
denippet: {
mark: "[snip]",
dup: "keep",
matchers: headMatchers,
sorters: ["sorter_rank"],
converters: [],
},
file: {
mark: "[file]",
forceCompletionPattern: "S/S*",
isVolatile: true,
},
line: {
mark: "[line]",
matchers: fuzzyMatchers,
sorters: fuzzySorters,
converters: fuzzyConverters,
},
lsp: {
mark: "[LSP]",
matchers: fuzzyMatchers.concat(["matcher_prefix"]),
sorters: ["sorter_lsp-kind"],
converters: ["converter_kind_labels"].concat(fuzzyConverters),
forceCompletionPattern: "\\.\\w*|::\\w*|->\\w*",
dup: "force",
},
"nvim-lua": {
mark: "[lua]",
matchers: fuzzyMatchers,
sorters: fuzzySorters,
converters: fuzzyConverters,
forceCompletionPattern: "/w*",
},
rg: {
mark: "[rg]",
matchers: fuzzyMatchers,
sorters: fuzzySorters,
converters: fuzzyConverters,
minAutoCompleteLength: 6,
},
"shell-native": {
mark: "[sh]",
matchers: headMatchers,
sorters: ["sorter_rank"],
converters: commonConverters,
isVolatile: true,
forceCompletionPattern: "\\S/\\S*",
},
skkeleton: {
mark: "[SKK]",
matchers: [],
sorters: [],
converters: [],
isVolatile: true,
minAutoCompleteLength: 1,
},
treesitter: {
mark: "[TS]",
matchers: fuzzyMatchers,
sorters: fuzzySorters,
converters: fuzzyConverters,
},
},
sourceParams: {
buffer: {
limitBytes: 5000000,
forceCollect: true,
},
lsp: {
enableAdditionalTextEdit: true,
enableDisplayDetail: true,
enableMatchLabel: true,
enableResolveItem: true,
lspEngine: "nvim-lsp",
snippetEngine: async (body: string) => {
await args.denops.call("denippet#anonymous", body);
},
},
"shell-native": {
shell: "zsh",
},
},
postFilters: ["postfilter_score"],
filterParams: {
converter_fuzzy: {
hlGroup: "Title",
},
postfilter_score: {
hlGroup: "",
},
converter_kind_labels: {
kindLabels: {
Text: " text",
Method: " method",
Function: " function",
Constructor: " constructor",
Field: " field",
Variable: " variable",
Class: " class",
Interface: " interface",
Module: " module",
Property: " property",
Unit: " unit",
Value: " value",
Enum: " enum",
Keyword: " keyword",
Snippet: " snippet",
Color: " color",
File: " file",
Reference: " reference",
Folder: " folder",
EnumMember: " enum member",
Constant: " constant",
Struct: " struct",
Event: " event",
Operator: " operator",
TypeParameter: " type parameter",
},
kindHlGroups: {
Method: "Function",
Function: "Function",
Constructor: "Function",
Field: "Identifier",
Variable: "Identifier",
Class: "Structure",
Interface: "Structure",
},
},
},
uiParams: {
"ui-pum": {
insert: false,
},
},
backspaceCompletion: true,
});
const enabledFiletypes = [
"elm",
"lua",
"nim",
"nix",
"python",
"scala",
"sql",
"mysql",
"svelte",
"toml",
"typescript",
"typescriptreact",
"javascript",
"kotlin",
"v",
"vsh",
"vv",
"zig",
"zir",
];
for (const ft of enabledFiletypes) {
args.contextBuilder.patchFiletype(ft, { sources: commonLangSources });
}
args.contextBuilder.patchFiletype("deol", {
specialBufferCompletion: true,
sources: ["shell-native"].concat(commonSources),
sourceOptions: {
_: {
keywordPattern: "[0-9a-zA-Z_./#:-]*",
},
},
});
await Promise.resolve();
}
}
開発者の記事
オススメ入門記事
Shougo/pum.vim
独自の補完ポップアップを作成するためのプラグイン。今のところddc.vimにしか使われていないっぽい。
開発者の記事
uga-rosa/ddc-source-lsp-setup
ddc.vimが関連するnvim-lspconfigの設定を自動で設定してくれる。
ddu.vim
Shougo/ddu.vim
Shougo/暗黒美無王さんが開発しているdenops製プラグイン。何かしらの対象から文字列のリストを取得して並べ替えて変換して表示して選択することができるので、ファイラーやファジーファインダーなどを自在に作ることができる。暗黒の力を使っているのでCPUに負担をかけず高速に動作する。ddc.vimと同じく極限まで自分好みにカスタマイズが可能で、設定次第で本当にいろんなUIが作成できる。設定をTypeScriptで書くと型の恩恵を受けられる。
筆者がインストールしているdduプラグイン
ui
source
filter
kind
column
筆者の設定(TypeScript部分)
ddu-ff.ts
import {
BaseConfig,
type ConfigArguments,
} from "jsr:@shougo/ddu-vim@~9.4.0/config";
export class Config extends BaseConfig {
override config(args: ConfigArguments) {
args.contextBuilder.patchLocal("floating_finder", {
ui: "ff",
uiParams: {
ff: {
startAutoAction: true,
autoAction: {
delay: 0,
name: "preview",
},
split: "floating",
statusline: false,
floatingBorder: "rounded",
prompt: "Search: ",
winRow: "(&lines - &lines % 2) / 2 - 9",
previewFloating: true,
previewFloatingBorder: "rounded",
previewFloatingTitle: "Preview",
previewWidth: "(&columns - &columns % 2) / 2",
},
},
sources: ["file_rec"],
sourceOptions: {
["_"]: {
matchers: ["matcher_substring", "matcher_ignore_files"],
sorters: ["sorter_alpha"],
converters: [],
columns: ["icon_filename"],
},
},
sourceParams: {
file_rec: {
ignoredDirectories: [".git", "nimcache", "testresults"],
},
},
filterParams: {
matcher_ignore_files: {
ignoreGlobs: ["testresults.html", ".DS_Store"],
},
},
kindOptions: {
ui_select: {
defaultAction: "select",
},
},
});
}
}
ddu-filer.ts
import {
BaseConfig,
type ConfigArguments,
} from "jsr:@shougo/ddu-vim@~9.4.0/config";
export class Config extends BaseConfig {
override config(args: ConfigArguments): Promise<void> {
args.contextBuilder.patchLocal("side_filer", {
ui: "filer",
uiParams: {
filer: {
startAutoAction: true,
autoAction: {
delay: 0,
name: "updatePreview",
},
displayRoot: false,
sortTreesFirst: true,
split: "vertical",
splitDirection: "topleft",
statusline: false,
winWidth: 25,
previewSplit: "no",
},
},
sources: ["file"],
sourceOptions: {
["_"]: {
matchers: [],
sorters: ["sorter_alpha"],
converters: [],
},
file: {
columns: ["icon_filename"],
},
},
filterParams: {
matcher_ignore_files: {
ignoreGlobs: [".DS_Store"],
},
},
kindOptions: {
file: {
defaultAction: "open",
},
},
actionOptions: {
open: {
quit: false,
},
},
});
return Promise.resolve();
}
}
開発者の記事
オススメ入門記事
editing
uga-rosa/ccc.nvim
シンプルなUIでカラーコードを視覚的に編集できる。バッファ上のカラーコードの背景色をその色にしてくれる機能もある。
開発者の記事
ysmb-wtsg/in-and-out.nvim
<C-CR>
で任意の囲い文字を抜けられる。以前は<C-O>a
でやっていたが今は白目を剥きながらこれを連打するだけで済む。
cohama/lexima.vim
{.pragma.}
の中のドットとかも閉じてくれる。
kylechui/nvim-surround
s
を上書きしないのでこれを選んだ。
echasnovski/mini.comment
コメントアウト用。
JoosepAlviste/nvim-ts-context-commentstring
コメントアウトが便利になる。
オススメ入門記事
windwp/nvim-ts-autotag
htmlタグを入力すると自動で閉じてくれる。
git
tpope/vim-fugitive
VimにGitコマンドが追加される。gitは基本的にコマンドからしか操作しないのでこれで十分。
lewis6991/gitsigns.nvim
差分表示がありがたい。必須。
lsp
neovim/nvim-lspconfig
必須。efm-language-serverもこれで設定している。
筆者の設定
local lspconfig = require("lspconfig")
require("lsp-format").setup({})
---@param client vim.lsp.Client
---@param bufnr? number
local lspformat_on_attach = function(client, bufnr)
require("lsp-format").on_attach(client, bufnr)
end
local efm_languages = {}
local filetype_config = {
elm = {
efm = {
{
formatCommand = "elm-format --stdin",
formatStdin = true,
},
},
},
-- go = {
-- efm = {
-- {
-- formatCommand = "gofmt",
-- formatStdin = true,
-- lintCommand = "golangci-lint run",
-- lintStdin = true,
-- },
-- },
-- },
-- haskell = {
-- efm = {
-- {
-- formatCommand = "stack exec fourmolu --stdin-input-file",
-- formatStdin = true,
-- },
-- },
-- },
lua = {
efm = {
{
formatCommand = "stylua --indent-type Spaces --indent-width 2 -",
formatStdin = true,
},
},
},
nim = {
-- lsp = {
-- name = "nim_langserver",
-- config = {
-- root_dir = lspconfig.util.root_pattern("*.nimble", ".git"),
-- settings = {
-- nim = {
-- projectMapping = {
-- projectFile = currentDir .. "src/" .. vim.fs.basename(currentDir) .. ".nim",
-- fileRegex = ".*\\.nim",
-- },
-- },
-- },
-- },
-- },
efm = {
{
formatCommand = "nph -",
formatStdin = true,
},
},
},
nix = {
efm = {
{
formatCommand = "nixfmt -",
formatStdin = true,
},
},
},
python = {
efm = {
{
formatCommand = "ruff format -",
formatStdin = true,
},
},
},
scala = {
efm = {
{
formatCommand = "scalafmt --stdin --non-interactive",
formatCanRange = true,
formatStdin = true,
},
},
},
sql = {
filetypes = { "sql", "mysql" },
efm = {
{
formatCommand = "sql-formatter",
formatCanRange = true,
formatStdin = true,
},
},
},
svelte = {},
toml = {
efm = {
{
formatCommand = "taplo format -",
formatStdin = true,
},
},
},
typescript = {
filetypes = { "typescript", "typescriptreact", "javascript" },
efm = {
{
formatCommand = "biome check --apply --stdin-file-path '${INPUT}'",
formatStdin = true,
rootMarkers = { "biome.json", "package.json" },
},
},
},
kotlin = {
filetypes = { "kotlin", "kotlin.kts" },
efm = {
{
formatCommand = "ktfmt -",
formatStdin = true,
},
},
},
v = {
filetypes = { "v", "vsh", "vv" },
efm = {
{
formatCommand = "v fmt",
formatStdin = true,
},
},
},
zig = {
filetypes = { "zig", "zir" },
efm = {
{
formatCommand = "zig fmt --stdin",
formatStdin = true,
},
},
},
}
---@param ft string
---@param config { efm: table, extraSources: string[] }
local register_language = function(ft, config)
efm_languages[ft] = config.efm or {}
end
for ft, config in pairs(filetype_config) do
if config.filetypes ~= nil then
for _, ft2 in ipairs(config.filetypes) do
register_language(ft2, config)
end
else
register_language(ft, config)
end
end
require("ddc_source_lsp_setup").setup({
override_capabilities = true,
respect_trigger = true,
})
local servers = {
efm = {
on_attach = lspformat_on_attach,
init_options = {
documentFormatting = true,
documentRangeFormatting = true,
},
single_file_support = true,
settings = {
rootMarkers = {
".git/",
},
languages = efm_languages,
},
},
denols = {
single_file_support = true,
root_dir = lspconfig.util.root_pattern("deno.json"),
settings = {
deno = {
inlayHints = {
parameterNames = { enabled = "all", suppressWhenArgumentMatchesName = true },
parameterTypes = { enabled = true },
variableTypes = { enabled = true, suppressWhenTypeMatchesName = true },
propertyDeclarationTypes = { enabled = true },
functionLikeReturnTypes = { enable = true },
enumMemberValues = { enabled = true },
},
},
},
init_options = {
lint = true,
unstable = true,
suggest = {
imports = {
hosts = {
["https://deno.land"] = true,
["https://cdn.nest.land"] = true,
["https://crux.land"] = true,
},
},
},
},
},
elmls = {
root_dir = lspconfig.util.root_pattern("elm.json"),
},
gopls = {},
-- hls = {},
kotlin_language_server = {
settings = {
kotlin = {
compiler = {
jvm = {
target = "21",
},
},
hints = {
typeHints = true,
parameterHints = true,
chaineHints = true,
},
},
},
},
lua_ls = {
settings = {
Lua = {
runtime = {
version = "LuaJIT",
pathStrict = true,
path = { "?.lua", "?/init.lua" },
},
workspace = {
library = vim.list_extend(vim.api.nvim_get_runtime_file("lua", true), {
"${3rd}/luv/library",
"${3rd}/busted/library",
"${3rd}/luassert/library",
}),
checkThirdParty = false,
},
hint = {
enable = true,
},
},
},
},
marksman = {},
metals = {},
nil_ls = {
settings = {
flake = {
autoArchive = true,
},
},
},
pylyzer = {
settings = {
python = {
inlayHints = true,
},
},
},
sqlls = {},
svelte = {
on_attach = lspformat_on_attach,
settings = {
typescript = {
inlayHints = {
parameterNames = { enabled = "all" },
parameterTypes = { enabled = true },
variableTypes = { enabled = true },
propertyDeclarationTypes = { enabled = true },
functionLikeReturnTypes = { enabled = true },
enumMemberValues = { enabled = true },
},
},
},
},
taplo = {},
ts_ls = {
root_dir = lspconfig.util.root_pattern("package.json"),
single_file_support = false,
settings = {
typescript = {
includeInlayParameterNameHints = "all",
includeInlayParameterNameHintsWhenArgumentMatchesName = false,
includeInlayFunctionParameterTypeHints = true,
includeInlayVariableTypeHints = true,
includeInlayVariableTypeHintsWhenTypeMatchesName = false,
includeInlayPropertyDeclarationTypeHints = true,
includeInlayFunctionLikeReturnTypeHints = true,
includeInlayEnumMemberValueHints = true,
},
},
},
vimls = {},
v_analyzer = {},
zls = {
settings = {
zls = {
enable_inlay_hints = true,
inlay_hints_show_builtin = true,
inlay_hints_exclude_single_argument = true,
inlay_hints_hide_redundant_param_names = false,
inlay_hints_hide_redundant_param_names_last_token = false,
},
},
},
}
for lsp, config in pairs(servers) do
lspconfig[lsp].setup(config)
end
lukas-reineke/lsp-format.nvim
一部のLanguage Serverについているformat機能を呼び出す設定を簡単に書ける。
motion
rhysd/clever-f.vim
f/tモーションを強化してくれる。
lambdalisue/vim-kensaku
ローマ字入力のまま日本語の検索をしてくれる。記事を書くときも重宝している。denops製。
オススメ入門記事
lambdalisue/vim-kensaku-search
/
とかでの検索に対応してくれる。kensaku.vimは基本的にここから呼び出している。
yuki-yano/fuzzy-motion.vim
画面上をあいまい検索できる。kensaku.vimと連携がすばらしい。denops製。
haya14busa/vim-edgemotion
if文や関数のブロックの間を移動できる。
haya14busa/vim-asterisk
アスタリスク検索を強化してくれる。
skanehira/jumpcursor.vim
少なめの打鍵数で画面上のどこへでも移動できる。結局これがいちばん早いという場面が往々にしてある。
開発者の記事
skk
vim-skk/skkeleton
SKKをVimで扱えるようになる。この記事もこれで書いている。ddc.vimとの連携するためのsourceも提供している。
delphinus/skkeleton_indicator.nvim
SKKで入力を切り替えるときに現在の状態を示してくれる。
snippet
uga-rosa/denippet.vim
denops製のスニペットプラグイン。TypeScriptを含むさまざまな形式でスニペットを作成できる。ddc.vimのsourceを同梱している。
開発者の記事
rafamadriz/friendly-snippets
いろんな言語のスニペット集。
statusline
nvim-lualine/lualine.nvim
カスタマイズ性の高いステータスライン。gitsigns.nvimやLSPとの連携ができる。
SmiteshP/nvim-navic
lualine上にLSPを用いたパンくずを表示してくれる。
pnx/lualine-lsp-status
lualine上に現在のバッファに適用されているLanguage Serverの数を表示してくれる。筆者の環境では基本的にefmと言語固有のLSPが常駐しているので、どれか停止していてもすぐに把握できる。
akinsho/bufferline.nvim
これはステータスラインというか開いているバッファを上に表示してくれる。カスタマイズ性もなかなか。
terminal
Shougo/deol.nvim
Vim上でターミナルを使える。これも一応暗黒の力を使っている。
ui
rcarriga/nvim-notify
高級感のある通知を出せるようになる。
MunifTanjim/nui.nvim
いくつかのUIプラグインが依存してるので入れておく。直接の起動はしない。
folke/noice.nvim
いろんなUIを改善してくれる全部入り系プラグインだが、コマンドラインを中央に持ってくる機能とnvim-notifyと連携する機能だけ使っている。
j-hui/fidget.nvim
LSPの起動時に右下にかっこいいプログレスバーが出る。遅延読み込みはLspAttachイベントがちょうどいい。
matsui54/denops-popup-preview.vim
補完候補の定義を表示できる。denops製。
matsui54/denops-signature_help
入力中の関数のシグネチャを表示できる。denops製。
visibility
shellRaining/hlchunk.nvim
インデントを分かりやすく示す4つの機能を提供してくれる。
gen740/SmoothCursor.nvim
縦方向のカーソルの移動をちっちゃな矢印で可視化してくれる。Vim標準のcursorlineより目に優しいが同じ領域を使うgitsignsとかに消されがち。
unblevable/quick-scope
各単語へ最短で移動できる文字をハイライトしてくれる。有名。
MeanderingProgrammer/render-markdown.nvim
Normal modeでMarkdownをいい感じに描画してくれる。これがあるのでpreviewはあまり使わない。
m-demare/hlargs.nvim
treesitterの力で関数の引数をハイライトしてくれる。効果があると信じて使っている。
chentoast/marks.nvim
使ったマークを可視化してくれる。
andersevenrud/nvim_context_vt
treesitterの力で閉じかっこに対応する開きかっこを可視化してくれる。とても優秀。
以上
以上です!詳しい設定は筆者のdotfilesからどうぞ。
Discussion