Neovim補完プラグインblink.cmpの使い方とカスタマイズ
Neovimの補完プラグインblink.cmpの使い方や設定方法を解説します。

blink.cmpとは?
blink.cmpはNeovimの補完プラグインで、補完の表示や絞り込みが速いです。
追加のプラグイン無しで「LSP」「バッファ」「パス」「コマンドライン」「スニペット」に対する補完ができます。
プラグインを入れれば辞書やAIによる補完ソースを追加できます。さらに、nvim-cmpの補完ソースも使えます。
キーマップの設定も簡潔に書けますし、カスタマイズも可能です。
導入方法
saghen/blink.cmp
インストール方法は公式ドキュメントに書いてあります。以下、日本人向けに情報を整理しました。
依存関係
blink.cmpではfuzzy mathcerのためにバイナリを使います。バイナリはダウンロードするか自分でビルドするかを選択できます。
そんなわけで必要なものは次のとおりです。
- Neovim(0.10以上)
- バイナリをダウンロードする場合
- curl
- Git
- 自分でビルドする場合
- Rustかrustup
インストール
ここではlazy.nvimの例を載せますが、ビルドコマンドなどは他のプラグインマネージャーでも参考になるはずです。
return { 'saghen/blink.cmp', version = '*', -- バイナリをダウンロードする場合 opts = { -- 次の項目で紹介 },}
以下、ビルドする場合です。
return { 'saghen/blink.cmp', version = '*', build = 'cargo build --release', -- build = 'nix run .#build-plugin', -- ...}
return { 'saghen/blink.cmp', version = '*', build = 'nix run .#build-plugin', -- ...}
最低限の設定
最低限のプラグインの設定はsetup
を呼び出すだけで完了です。
require('blink.cmp').setup({})
lazy.nvimの場合、setup
の中身はopts
に書くためこの記述は不要です。この書き方について知りたい人は別の記事「lazy.nvimの使い方から起動を爆速にする方法までを解説」を読んでください。
LSPの設定
Neovimのバージョンが0.11よりも低い場合、nvim-lspconfigに次のように設定してあげます。
nvim-lspconfig
の依存関係にblink.cmp
を追加- 各Language serverの
capabilities
にrequire('blink.cmp').get_lsp_capabilities()
を追加
1ステップずつ解説します。まずは依存関係へ追加。
return { "neovim/nvim-lspconfig", dependencies = { "saghen/blink.cmp", }, -- ...}
続いて、各Language serverのcapabilitiesにblink.cmp
のcapabilitiesを渡します。
以下、公式ドキュメントに書かれているものとは別に、筆者の例です。
local lspconfig = require("lspconfig")local function server_register(server_name) local opts = {} local success, req_opts = pcall(require, "plugins.lsp.servers." .. server_name) if success then opts = req_opts end opts.capabilities = require("blink.cmp").get_lsp_capabilities(opts.capabilities) lspconfig[server_name].setup(opts)end
require("mason").setup()local mason_lspconfig = require("mason-lspconfig")mason_lspconfig.setup_handlers({ server_register })
local other_lsp = { "biome", "eslint", "jsonls", "markdown-language-server",}for _, server_name in pairs(other_lsp) do server_register(server_name)end
lazy.nvimでの遅延読み込み
lazy.nvimでの遅延読み込みの例も載せておきます。
return { "saghen/blink.cmp", event = { "InsertEnter", "CmdLineEnter" }, -- ...}
使い方
Neovim初心者・デフォルトの設定にしている人向けに使い方を説明します。
呼吸するようにカスタマイズする皆さんは、この項目は読み飛ばしてください。
挿入モードやコマンドラインモードで文字を入力後、次のデフォルトキーマップを使って候補を選択・決定します。Neovimのデフォルトの補完に似てますね(:help popupmenu-completion
のやつ)。
キーマップ | 内容 |
---|---|
<C-y> | 補完候補を決定 |
<C-e> | キャンセル |
<Up> | 前の補完候補を選択 |
<Down> | 次の補完候補を選択 |
<C-p> | 前の補完候補を選択 |
<C-n> | 次の補完候補を選択 |
他のキーマップについてはKeymapの”default”プリセットを読みましょう。
設定の書き方について補足
ここまでは最低限の設定のみでした。でも設定したいですよね。
ここで、lazy.nvimでの設定する場合に1点補足です。次のようにopts_extend
を書いてあげましょう。
return { 'saghen/blink.cmp', version = '*', ---@module 'blink.cmp' ---@type blink.cmp.Config opts = { -- ここに設定を書く }, opts_extend = { "sources.default" },}
opts_extend
はlazy.nvimの機能で、opts_extendに指定したもの(プラグインで定義されているsources.default)をopts
内で拡張できます。おまじないだと思って書いておきましょう。
余談:lazy.nvimの実装をgrepしてもopts_extend
は出てきません。〇〇_extend
という汎用的なマージ機能として実装されているからです。実装探検隊の方は注意しましょう。
設定例
公式ドキュメントに各設定の挙動が画像つきで載っています。
ここではよく設定されるであろう項目を解説します。opts = {}
の形式で書きますが、lazy.nvim以外の人はrequire('blink.cmp').setup({})
に読み替えてください。
キーマップ
キーマップのプリセットは4つ用意されています。
プリセット名 | 特徴 |
---|---|
none | キーマップは全部自分でセットする |
default | <C-y> で候補を決定 |
super-tab | <Tab> で候補を決定かスニペットジャンプ |
enter | <CR> で候補を決定 |
プリセットの中身は公式ドキュメントのKeymapを読みましょう。
キーマップの書き方
筆者はプリセットをsuper-tab
にして、ところどころ上書きしています。
opts = { keymap = { preset = "super-tab", ["<C-u>"] = { "scroll_documentation_up", "fallback" }, ["<C-d>"] = { "scroll_documentation_down", "fallback" }, ["<C-b>"] = {}, ["<C-f>"] = {}, },},
各キーマップは{ "command-foo", "command-bar" }
のようにテーブルで定義します。テーブルの最初の要素が発動しなかったら次の要素へ、といった具合です。
使用できるコマンドの一覧はCommandsに書いてあります。
単純に無効化したいだけであれば{}
を渡します。
キーマップに関数を渡す
キーマップの定義テーブルには、コマンド名だけではなく関数も渡せます。
たとえば、プリセットsuper-tab
の<Tab>
は次のように定義されています。
['<Tab>'] = { function(cmp) if cmp.snippet_active() then return cmp.accept() else return cmp.select_and_accept() end end, 'snippet_forward', 'fallback',},
コマンドラインだけ個別設定
コマンドラインやターミナルだけ個別に設定したいなら、cmdline.keymap
のような場所に定義します。
筆者はコマンドラインで「<CR>
で補完候補の決定とエンター」を設定しています。
opts = { cmdline = { keymap = { preset = "super-tab", ["<CR>"] = { "accept_and_enter", "fallback" }, }, },},
accept_and_enter
は「補完候補の決定」である<CR>
と「実行コマンドの決定」である<CR>
を一括で入力できる、という補完コマンドです。
一方、「:LspRestart tailwindcss
の:LspRestart
のような部分」の入力中は「補完候補の決定」だけしたくなります。そんなときはsuper-tab
の<Tab>
を使っています。
補完ソースの設定
補完ソースはsources
に書きます。
単純な例
まずはビルトインの補完ソースのみを使っている例を見てみましょう。
使いたいソースはdefault
に書きます。ファイルタイプ毎にソースを変えるならper_filetype
に書きます。
opts = { sources = { default = { "snippets", "lsp", "path", "buffer" }, per_filetype = { markdown = { "snippets", "lsp", "path" }, mdx = { "snippets", "lsp", "path" }, }, }}
筆者は上記のようにmarkdown
・mdx
のときだけ「バッファ補完」を無効化しています。日本語の文章だと煩わしいからです。
スニペット
vim.snippet
を使ったスニペット、LuaSnip、mini.snippetsがビルトインでサポートされています。
これらのスニペットエンジンを使う場合、snippets.preset
にdefault
・luasnip
・mini_snippets
のいずれかを指定します。
opts = { snippets = { preset = "luasnip" },}
この1行でキーマップをよしなに設定してくれます。
LuaSnipの使い方の記事も書いたので良ければどうぞ。
プラグインマネージャーでインストールや依存関係に追加するのも忘れずに。
return { "saghen/blink.cmp", dependencies = { "rafamadriz/friendly-snippets", -- スニペット集 "L3MON4D3/LuaSnip", -- スニペットエンジン }, -- ...}
サポートされていないスニペットエンジン
サポートされていなスニペットエンジンでも、nvim-cmpでのプラグインがあればblink.compat経由で使えそうです。
ただし、スニペットの「展開」「アクティブ判定」「ジャンプ」の関数を自分で定義する必要があります。
opts = { snippets = { expand = function(snippet) foo.expand(snippet) end, active = function(filter) return foo.available(filter) end, jump = function(direction) foo.jump(direction) end, }}
公式ドキュメント:Snippets | Blink Completion (blink.cmp)
ビルトインの補完ソースをカスタマイズ
ビルトインの補完ソースをカスタマイズする場合、providers.〇〇
のような場所に書きます。
以下は、「パス補完」をカスタマイズしています。基準となるディレクトリを変更する調整です。
opts = { sources = { -- ... providers = { path = { opts = { get_cwd = function(context) -- NOTE: ZLEのedit-command-lineで使いやすいようにcwdを変更 local dir_name = vim.fn.expand(("#%d:p:h"):format(context.bufnr)) if dir_name == "/tmp" then return vim.fn.getcwd() end return dir_name end, }, }, } }}
別の記事で触れているZLEのedit-command-line
機能での例です。edit-command-line
ではカレントディレクトリが/tmp
になってしまうため、変更しています。
プラグインを使う例
続いて、プラグインによる補完ソースを使う設定方法の解説です。ここではblink-cmp-dictionaryを例にします。
まずはプラグインのインストールや依存関係を設定します。
return { "saghen/blink.cmp", dependencies = { { "Kaiser-Yang/blink-cmp-dictionary", dependencies = { "nvim-lua/plenary.nvim" }, }, -- ... }, -- ...}
次に、sources.providers
に定義を追加します。そして、sources.default
やsources.per_filetype.〇〇
のテーブルにそのキー名を追加します。
opts = { sources = { default = { "snippets", "lsp", "path", "buffer", "dictionary" }, providers = { dictionary = { module = "blink-cmp-dictionary", -- 必須 name = "Dict", -- 必須 min_keyword_length = 3, async = true, score_offset = -1000, max_items = 5, opts = { dictionary_files = { "/usr/share/dict/words" }, }, }, } }}
module
が読み込まれるモジュールです。プラグインのREADMEに載っているものを記載しましょう。
その他はオプションです。
補完ソースのオプション
補完ソースのオプションには2つあります。opts
がそれぞれの補完ソース固有、それ以外は共通のオプションです。
providers = { dictionary = { module = "blink-cmp-dictionary", -- 必須 name = "Dict", -- 必須 min_keyword_length = 3, async = true, score_offset = -1000, max_items = 5, opts = { dictionary_files = { "/usr/share/dict/words" }, }, },}
公式ドキュメント:Sources | Blink Completion (blink.cmp)
見た目の設定
見た目も細かく設定できます。よくありそうなやつをピックアップします。
ボーダー
よくありそうなのは「ウィンドウにボーダーを付けたい」といったところでしょうか。
次の設定は公式ドキュメントRecipesより引用です。
opts = { completion = { menu = { border = 'single' }, documentation = { window = { border = 'single' } }, }, signature = { window = { border = 'single' } },}
ドキュメントには記載されていませんがボーダーは現時点(2025年2月)では7種類あります。
インストール後ならlazydev.nvimでの補完が効くのでそれで見てください。あるいは、実装でblink.cmp.WindowBorder
型を検索してください。
ドキュメントを表示
デフォルトでは、ドキュメントはキーマップで表示・非表示を切り替えます。
最初からドキュメントを表示したいなら次のように設定します。
次の設定は公式ドキュメントGeneralより引用です。
opts = { completion = { documentation = { auto_show = true, auto_show_delay_ms = 500 }, },}

検索時には補完をオフにしたい
/
や?
による検索のときには補完の表示をオフにすることも設定できます。
これも公式ドキュメントRecipesより引用です。
opts = { completion = { menu = { auto_show = function(ctx) return ctx.mode ~= "cmdline" or not vim.tbl_contains({ '/', '?' }, vim.fn.getcmdtype()) end, }, }}
この設定ではあくまでも「補完の自動開始をしない」という挙動です。補完したくなったら<C-Space>
を入力すれば補完できます(キーマップのプリセットを使っている場合)。
カスタマイズ例
ここまではよくありそうな設定方法の解説でした。ここからは実際に筆者がやっているカスタマイズの紹介です。
キーマップのカスタマイズ
キーマップで関数を使ってカスタマイズしているものを挙げます。
スニペット変換の優先
補完を無視して「一番優先度が高いスニペット」を展開するキーマップです。
補完候補を見るまでもない、よく使うスニペットを展開するときに便利です。
opts = { keymap = { -- ... ["<C-y>"] = { function(cmp) if require("luasnip").expandable() then cmp.hide() vim.schedule(function() require("luasnip").expand() end) return true end return false end, "fallback", }, },},
false
を返すとテーブルの次の要素(今回はfallback
)の実行に移ります。
補完ワードの先頭の大文字・小文字を切り替え
候補を挿入しつつ、先頭を大文字にしたいことがあります。
例:foo
という候補をFoo
にしたい。
そんなときに次のキーマップを使っています。
["<C-v>"] = { function(cmp) cmp.accept({ callback = function() vim.api.nvim_feedkeys( vim.api.nvim_replace_termcodes("<Esc>mzBvg~`za", true, false, true), "in", false ) end, }) end,},
Bvg~
で先頭の文字へ移動して大文字・小文字を切り替えています。
直前の空白削除
getFoo
やdefault_foo
のような連続する単語を書くとき、Foo
やfoo
の部分だけ補完を効かせたいことがあります。
そんなときに使うのが次のキーマップです。
-- 直前に入れた空白を削除する-- `get Foo` -> `getFoo`-- getFoo, default_foo のようなprefixがあるときに使う["<C-g>"] = { function() vim.api.nvim_feedkeys( vim.api.nvim_replace_termcodes('<Esc>mzF<Space>"_x`za', true, false, true), "in", false ) end,},
このキーマップは次のステップ4の部分を実現します。
get_
を入力get_
:最後に空白を入れるget_ foo
:補完するget_foo
:空白を削除する
getFoo
のような場合は前述の<C-v>
のキーマップと合わせて使ったりします。
別にblink.cmp経由で登録する必要はありませんが、近いタイミングで使うものなので。
コマンドラインの補完開始の条件
コマンドラインにて、:wq
のようなコマンドまで補完候補に入ると煩わしいです。
公式ドキュメントでは、「補完開始の文字数条件をコマンドラインだけ2にする方法」を紹介しています。
ただこれだと:L
のときに:Lazy
のようなコマンドが表示されません。
そこで筆者は次のように補完の開始条件を設定しました。
opts = { sources = { min_keyword_length = function(ctx) -- :wq, :qa -> menu doesn't popup -- :Lazy, :wqa -> menu popup if ctx.mode == "cmdline" and ctx.line:find("^%l+$") ~= nil then return 3 end return 0 end, }}
小文字アルファベット2文字までは補完がオフになります。
特定のときに補完をオフにしたい
補完のオンオフ条件は、補完自体ならenabled
、補完ソースごとならproviders.〇〇.enabled
で指定できます。
以下は「markdown
・mdx
でコードブロックの言語名を書くとき」に補完を無効化する例です。
opts ={ enabled = function() return not ( vim.tbl_contains({ "markdown", "mdx" }, vim.bo.filetype) and vim.api.nvim_get_current_line():match("^```[%a]*`") ) end,}
ファイルを分けてぇんだ
カスタマイズ大好き人間の場合、設定ファイルが長くて分割したくなるかもしれません。lazy.nvimの場合は以前書いた記事が参考になるでしょう。
ここではblink.cmpの筆者のディレクトリ構成と、設定を分割したい場合に使える型情報を記載しておきます。
.config/nvim/lua/plugins├── completion│ └── index.lua│ └── luasnip.lua│ └── mappings.lua│ └── sources.lua├── ...
---@module "lazy"---@type LazyPluginSpecreturn { "saghen/blink.cmp", dependencies = { { import = "plugins.completion.luasnip" }, -- ... }, ---@module "blink.cmp" ---@type blink.cmp.Config opts = { keymap = require("plugins.completion.mappings"), sources = require("plugins.completion.sources"), -- ... }, opts_extend = { "sources.default" },}
---@module "lazy"---@type LazyPluginSpecreturn { "L3MON4D3/LuaSnip", dependencies = { "rafamadriz/friendly-snippets" }, -- ...}
---@module "blink.cmp"---@type blink.cmp.KeymapConfigreturn { preset = "super-tab", ["<C-u>"] = { "scroll_documentation_up", "fallback" }, -- ...}
---@module "blink.cmp"---@type blink.cmp.SourceConfigPartialreturn { default = { "snippets", "lsp", "path", "buffer", "dictionary" }, -- ...}
sourcesの部分の分割で使う型はblink.cmp.SourceConfigPartial
ですので注意を。
トラブルシューティング
筆者が遭遇したトラブルの解決方法です。
スニペットが表示されない
「公式ドキュメントどおりに設定しているのにスニペットが表示されない」
このトラブルは、単に補完候補の下の方に表示されているだけでした。優先度が低かったわけです。score_offset
で優先度を設定できるようです。
providers = { snippets = { score_offset = 10, }, },
筆者は上記の代わりに次のようにdefault
でスニペットを最初に追加することで対応しました。この方がスニペットの主張が強すぎないです。
opts = { sources = { default = { "snippets", "lsp", "path", "buffer" }, per_filetype = { markdown = { "snippets", "lsp", "path" }, mdx = { "snippets", "lsp", "path" }, }, }}
コマンドラインやターミナルだけキーマップがおかしい
コマンドライン・ターミナルだけ個別にキーマップを設定して、プリセットを追加し忘れていませんか?
opts = { cmdline = { keymap = { preset = "super-tab", ["<CR>"] = { "accept_and_enter", "fallback" }, }, },},
任意のタイミングで補完を開始したい
<C-space>
を入力すれば、任意のタイミングで補完候補を表示できます。
['<C-space>'] = { 'show', 'show_documentation', 'hide_documentation' },
筆者の自作LSPでは、[[]]
のような「括弧の中にカーソルがあるとき」に補完候補を返すように実装しています。
ですが、blimk.cmpでは表示されませんでした。nvim-cmpやVSCodeでは表示されたので、どうしてなのか調査中です。
ひとまずはこのキーマップで対応しています。
以上、blink.cmpの解説でした。この記事を書いている時点(2025年2月22日)ではまだβ版なのですが、細かくカスタマイズできて大変便利です。