18 人が閲覧(直近 30 日) 開発

Neovim 0.12系への移行手順:vim.packと内蔵LSP・自動補完の実装【0.12.6対応】

Neovim 0.12系への移行手順:vim.packと内蔵LSP・自動補完の実装【0.12.6対応】

Neovim 0.12系では、組み込みプラグイン管理のvim.packと挿入モードの自動補完が加わり、既存の内蔵LSPの制御機能も拡張されました。2026年10月9日には最新パッチの0.12.6が公開されています。この記事では、2026年10月18日にmacOS x86_64向け0.12.6公式バイナリを使い、空のXDGディレクトリでvim.packの導入・バージョン指定・遅延読み込み・削除された関数の挙動を確かめました。その結果をもとに、既存のinit.luaを0.12系へ移す手順をまとめます。Neovimのインストールや基本操作から始めたい場合は、Neovim(nvim)の使い方:インストールから基本操作・init.lua設定・lazy.nvimとLSP導入までを先に読むと流れがつかめます。

まとめ:Neovim 0.12系へ移行する前に押さえる要点

  • 最新は0.12.6(2026年10月9日公開)。0.12.5からの31コミットは不具合修正が中心で、機能変更は実験的APIvim.pos・vim.rangeのフィールドを書き換え可能にした1件だけです。statuscolumnのヒープバッファオーバーフローや、netrwのコード注入(Vimパッチ9.2.0495由来)の修正を含むため、0.12系を使っているなら上げておきます。
  • vim.packはadd()・update()・del()・get()の4関数で動き、状態はnvim-pack-lock.jsonに記録されます。公式には「実験的だが日常利用には十分安定」と書かれています。
  • vim.version.range('1.0')は1.0.0以上1.1.0未満という意味です。条件に合うタグが無いとadd()全体がエラーで止まります。1.xのタグが無いnvim-treesitterにrange('1.0')を書くと入りません。
  • lazy.nvimの遅延読み込みは、load関数と:packaddで再現できます。依存関係はadd()に渡す配列の並び順で表し、ビルド処理はPackChangedイベントで受けます。
  • 破壊的変更は3種類に分けて確認します。呼ぶとエラーになる削除(vim.diagnostic.disable())、エラーは出ないのに効かない設定(sign_define()による診断サイン)、警告も出ずに動く非推奨(vim.diff)です。
  • 0.13開発版ではvim.packの更新・削除が:packupdate・:packdelコマンドになっています。チームでは、本体を0.12.6へ上げることと、vim.packへ移ることを分けて判断します。

Neovim 0.12系の最新パッチ0.12.6とリリース履歴

0.12.0から0.12.6までの公開日と各版の位置付け

0.12.0は2026年3月29日に公開され、0.12.6までのパッチ公開間隔は8〜49日です。公開日はGitHubのリリース情報(published_at・UTC)で確認しました。

版 公開日(UTC) 前版からの間隔
0.12.0 2026-03-29 –
0.12.1 2026-04-06 8日
0.12.2 2026-04-22 16日
0.12.3 2026-06-10 49日
0.12.4 2026-07-05 25日
0.12.5 2026-08-23 49日
0.12.6 2026-10-09 47日

v0.12.5とv0.12.6の差分は31コミットです(GitHub APIのcompareで2026年10月18日に確認)。内容は端末のtruecolor表示、スニペットのEsc処理、ヘルスチェック、statuscolumnのヒープバッファオーバーフロー、msgpackの範囲外読み込み、TUIのAlt判定といった修正で、リリースのChangelogで機能変更(FEATURES)に挙がるのは、実験的APIvim.pos・vim.rangeのフィールドを書き換え可能にした1件です。セキュリティ関連では、netrwのNetrwBookHistSave()経由のコード注入を塞ぐVimパッチ9.2.0495も取り込まれています。設定項目の追加や既定値の変更はありません。手元の版は次のコマンドで確認できます。

# 1行目が NVIM v0.12.6 なら最新
nvim --version | head -n 3
# 設定ファイルとプラグインの置き場所を確認する
nvim --headless -c 'echo stdpath("config") .. "\n" .. stdpath("data")' -c 'quit'

0.11系から0.12系で増えた機能の中心

公式のnews-0.12で、設定に直接関わる追加は3つです。組み込みプラグインマネージャのvim.pack、LSPクライアントの制御整理(vim.lsp.enable()が対象外のクライアントを切り離すようになり、対話用の:lspコマンドも追加)、挿入モードの自動補完'autocomplete'です。ほかに:restart(ZR)による再起動、実験的な新メッセージUI(require('vim._core.ui2').enable()で有効化し、「Press ENTER」の待ちをなくす)、uv tool install pynvimだけで済むPythonプロバイダ設定が入りました。

:DiffToolと:Undotreeは同梱プラグインで、既定では読み込まれません。0.12.6を--cleanで起動すると、どちらのコマンドも未定義でした。使う前に:packadd nvim.difftool、:packadd nvim.undotreeを実行するか、init.luaに書いておきます。

vim.packでプラグインを導入・更新・削除する手順

vim.pack.add()の書き方とバージョン範囲の実際の意味

vim.packが管理するのは、データ用標準パス配下のsite/pack/core/optだけです。プラグインの取得にはgitコマンドが要り、バージョン範囲で選別されるタグはv1.2.0や1.2.0のようなsemver形式に限られます(1.2やv1は対象外)。仕様はv0.12.6のpack.txtにあります。

-- init.lua
vim.pack.add({
  -- 既定ブランチ(main か master)を追う
  'https://github.com/nvim-lua/plenary.nvim',
  -- 0.16.0 以上 0.17.0 未満のうち最新のタグを入れる
  { src = 'https://github.com/nvim-mini/mini.nvim', version = vim.version.range('0.16') },
  -- ブランチ・タグ・コミットハッシュを文字列で直接指定する
  -- 実在するリポジトリに置き換えて使用する指定例:
  -- { src = 'https://github.com/user/plugin4', version = 'main' },
})
-- add() の直後からプラグインのコードを呼べる
require('mini.pairs').setup()

vim.version.range()の範囲は、0.12.6でtostring()を使うと文字列で確認できます。実行するとrange('0.16')は「0.16.0 – 0.17.0」、range('1.0')は「1.0.0 – 1.1.0」、range('*')は「>=0.0.0」と表示されました。つまりrange('1.0')は「1系の最新」ではなく「1.0.x」の意味です。

範囲に合うタグが無い場合の挙動も確かめました。タグがv0.1.4までしかないplenary.nvimにrange('1.0')を指定すると、No versions fit constraint. Relax it or switch to branch.というエラーでadd()が止まります。エラーには選べるタグとブランチの一覧も出ます。同じ呼び出しに並べた別のプラグインはインストールされますが、エラーの後にあるinit.luaの行は実行されません。初回のインストールでは確認プロンプトが出るので、CIやヘッドレスで環境を作るときは第2引数に{ confirm = false }を渡します。

vim.pack.update()とvim.pack.del()による更新・削除の操作

vim.pack.update()を実行すると、変更点が別タブの確認用バッファに表示されます。:writeで確定し、:quitで破棄します。このバッファでは]]と[[でプラグイン間を移動でき、Kで詳細、graで「更新しない」「削除」などのコードアクションを選べます。実際に行った更新は、logの標準パスにあるnvim-pack.logに残ります。

-- 通信せず、手元の状態だけを確認用バッファに出す
vim.pack.update(nil, { offline = true })
-- 確認を飛ばしてすぐ更新する
vim.pack.update({ 'mini.nvim' }, { force = true })
-- 削除する(このセッションで add() 済みなら force が必要)
vim.pack.del({ 'plenary.nvim' })
vim.pack.del({ 'plenary.nvim' }, { force = true })
-- このセッションで vim.pack.add() されていないプラグイン名を集める
local unused = vim.iter(vim.pack.get())
  :filter(function(p) return not p.active end)
  :map(function(p) return p.spec.name end)
  :totable()

削除するときは、先にinit.luaのvim.pack.add()から該当行を消して再起動します。行を残したままdel()すると、次の起動で再インストールされます。

nvim-pack-lock.jsonの版管理と別マシンへの同期

0.12系では、ロックファイルはstdpath('config')配下のnvim-pack-lock.jsonです。通常のUnix環境では$XDG_CONFIG_HOME/nvim配下ですが、WindowsやNVIM_APPNAME指定時は設定ディレクトリが異なります。空の設定で2本を入れたところ、プラグインごとにrev(コミット)、src、versionが記録されました。versionには、範囲指定なら「0.16.0 – 0.17.0」、ブランチなら「’main’」がそのまま入ります。ロックファイルは手で編集しない前提です。

# 設定リポジトリにロックファイルを含める
cd ~/.config/nvim
git add init.lua nvim-pack-lock.json
git commit -m "pin plugins"
# 別マシン:pull して再起動すると未導入のプラグインがロックの rev で入る
# 既存のプラグインをロックの rev に合わせる(確認用バッファで :write)
nvim -c "lua vim.pack.update(nil, { target = 'lockfile' })"

更新後に戻したいときは、git checkout HEAD -- nvim-pack-lock.jsonでロックファイルを戻して再起動し、vim.pack.update({ '名前' }, { offline = true, target = 'lockfile' })を実行します。ロックファイルを版管理していない場合は、nvim-pack.logから更新前のrevを探すことになります。

lazy.nvimからvim.packへ書き換える対応表と遅延読み込み

lazy.nvimの設定キーとvim.packの対応表

vim.packには、依存関係の自動解決と、イベントごとの遅延読み込みの宣言がありません。lazy.nvimの各キーは次のように読み替えます。

lazy.nvimのキー vim.packでの書き方
‘user/repo’ src に完全なURL
version = ‘*’ version = vim.version.range(‘*’)
branch / tag / commit version に文字列で指定
pin = true version に現在のrev
dependencies add() の配列で先に並べる
opts / config add() の後に setup() を書く
build PackChanged の autocmd
event / ft / cmd / keys load 関数 + :packadd
-- lazy.nvim(移行前)
require('lazy').setup({
  { 'nvim-telescope/telescope.nvim', version = '*',
    dependencies = { 'nvim-lua/plenary.nvim' }, opts = {} },
})
-- vim.pack(移行後)
vim.pack.add({
  'https://github.com/nvim-lua/plenary.nvim',
  { src = 'https://github.com/nvim-telescope/telescope.nvim',
    version = vim.version.range('*') },
})
require('telescope').setup({})

load関数と:packaddで遅延読み込みを組む方法

vim.pack.add()のloadに何もしない関数を渡すと、ディスクへの導入だけを行い、runtimepathには追加しません。必要になった時点で:packaddすれば読み込まれます。0.12.6でplenary.nvimを使って確かめたところ、add()の直後はrequire('plenary.path')が失敗し、InsertEnterでpackaddした後は成功しました。

-- 導入だけしておき、読み込みは遅らせる
vim.pack.add({ 'https://github.com/nvim-lua/plenary.nvim' },
  { load = function() end })

-- 最初に挿入モードへ入ったときに一度だけ読み込む
vim.api.nvim_create_autocmd('InsertEnter', {
  once = true,
  callback = function() vim.cmd.packadd('plenary.nvim') end,
})

lazy.nvimでイベント・ファイルタイプ・キー単位の条件を十数個積んでいる設定は、この形で1つずつ書き直すことになります。数が多いなら、vim.packへの移行は見送ってもかまいません。

vim.packによるnvim-treesitterのmainブランチ導入

nvim-treesitterの既定ブランチは2026年10月時点でmainです。READMEには互換性のない全面的な書き直しであることと、Neovim 0.12.0以降、tree-sitter-cli0.26.1以降(npm以外で導入)が必要なことが書かれています。加えて、PATH上にtar・curl・Cコンパイラが必要です。サポート対象はNeovimの最新安定版と最新nightlyだけです。遅延読み込みには対応せず、プラグインを更新したら:TSUpdateでパーサーも揃えるよう求めています。公開タグはv0.10.0までしかないので、バージョン範囲ではなくブランチ名で指定します。

-- フックは add() より前に登録する(初回インストールでも発火させるため)
vim.api.nvim_create_autocmd('PackChanged', {
  callback = function(ev)
    local name, kind = ev.data.spec.name, ev.data.kind
    if name == 'nvim-treesitter' and (kind == 'install' or kind == 'update') then
      if not ev.data.active then vim.cmd.packadd('nvim-treesitter') end
      vim.cmd('TSUpdate')
    end
  end,
})
vim.pack.add({
  { src = 'https://github.com/nvim-treesitter/nvim-treesitter', version = 'main' },
})

0.12.6で確認すると、インストール時のPackChangedはactive=falseで発火しました。読み込み前に呼ばれるので、プラグインのコマンドを使うには先にpackaddが要ります。mainにはnvim-treesitter.configsモジュールが無く、masterブランチ向けのrequire('nvim-treesitter.configs').setupはエラーになります。初回は:TSInstall luaなどで使用言語のパーサーを導入します。ハイライトは本体のvim.treesitter.start()をFileTypeのautocmdから呼んで有効にします。

lazy.nvimからの移行で起きやすい失敗

1つ目は、lazy.nvimが作ったディレクトリの流用です。vim.packはsite/pack/core/optの中身をすべて自分の管理物とみなすので、旧マネージャの置き場は退避し、空の状態から入れ直します。2つ目は、ディスク上にあるプラグインの版をadd()が確認しないことです。versionを書き換えたらvim.pack.update()で同期します。3つ目は、上で見た範囲指定の不一致による停止です。init.lua全体が途中で止まるので、移行直後に:messagesを確認します。ターミナル側の描画やキー入力を見直す場合は、WezTerm(ウェズターム)とは?Windows/Macの導入と設定・日本語入力・WSL2/SSHで設定を揃えておくと、移行後の不具合を切り分けやすくなります。

プラグイン不要の内蔵LSP設定と挿入モード自動補完

vim.lsp.configによるサーバ定義とvim.lsp.enableによる有効化

公式のLSPドキュメントどおり、言語サーバ本体は各自でインストールします。Neovim側では、vim.lsp.configに定義を書いてvim.lsp.enable()で有効化するだけです。root_markersは入れ子のリストで優先度を表せます。

vim.lsp.config('lua_ls', {
  cmd = { 'lua-language-server' },
  filetypes = { 'lua' },
  root_markers = { { '.luarc.json', '.luarc.jsonc' }, '.git' },
  settings = { Lua = { runtime = { version = 'LuaJIT' } } },
})
vim.lsp.enable('lua_ls')
-- 接続状況の確認::checkhealth vim.lsp
-- 対話操作::lsp restart / :lsp stop / :lsp disable lua_ls

0.12系ではvim.lsp.is_enabled()とvim.lsp.get_configs()が加わり、既定のキーマップにgrt(型定義へ移動)とgrx(コードレンズ実行)が増えました。0.11のvim.lsp.configとenable()で書いた設定は、そのまま動きます。

‘autocomplete’の遅延と件数を調整する基準

'autocomplete'は既定オフの真偽値です。表示までの遅延は'autocompletedelay'(既定0ミリ秒)、補完元を順に後回しにする初期タイムアウトは'autocompletetimeout'(既定80ミリ秒)で調整します。options.txtには「80以上のみ有効」とあります。ただし0.12.6でautocompletetimeout=10を設定してもエラーは出ず、値も10のまま入りました。値の誤りに気付けないので、自分で80以上を書くようにします。

vim.o.autocomplete = true
-- 打鍵中に開きすぎないよう少し遅らせる
vim.o.autocompletedelay = 120
-- バッファ・ウィンドウ・omnifunc(LSP)を補完元にし、LSPは5件まで
vim.o.complete = '.,w,b,o^5'
-- 現在のバッファ由来の候補をカーソルに近い順に並べる(fuzzy指定時は無効)
vim.opt.completeopt:append('nearest')

'complete'にoやF(関数の補完元)が入っていると、LSPのような遅い補完元には最大1秒程度の長いタイムアウトが使われます。'autocompletetimeout'を上げなくても、LSPの候補は表示されます。

0.12系の破壊的変更で既存のinit.luaが壊れる箇所

既存設定の互換性:削除・無視・非推奨の3区分

news-0.12の破壊的変更とdeprecated.txtを、0.12.6で1つずつ呼んで確かめると、壊れ方が3種類に分かれます。

記述 0.12.6での挙動 置き換え先
vim.diagnostic.disable() 削除(nil・呼ぶとエラー) vim.diagnostic.enable(false)
vim.diagnostic.is_disabled() 削除(nil・呼ぶとエラー) not vim.diagnostic.is_enabled()
sign_define(‘DiagnosticSign…’) 成功するが診断サインに効かない vim.diagnostic.config の signs
vim.diff() 非推奨・警告なしで動く vim.text.diff()
semantic_tokens.start/stop 非推奨・動く semantic_tokens.enable()
keymap.set の buffer 非推奨・動く buf

最も気付きにくいのは診断サインです。sign_define()はエラーを返さず成功しますが、0.12系の診断表示はvim.diagnostic.config({ signs = { text = { ... } } })の設定しか読みません。サインの文字が既定のE・Wに戻っていたら、この記述を探します。非推奨の3つは今は動きますが、deprecated.txtの「DEPRECATED IN 0.12」に載っており将来削除されるので、移行のついでに書き換えます。

# 影響を受ける記述を洗い出す
cd ~/.config/nvim
grep -rn "diagnostic.disable\|is_disabled\|sign_define" .
grep -rn "vim.diff(\|semantic_tokens.st\|buffer *=" .
# 非推奨APIの呼び出しを本体に検出させる
nvim -c "checkhealth vim.deprecated"

既定値と同梱プラグインの変更点

オプション'shelltemp'の既定がfalseになり、外部コマンドとのやり取りが一時ファイルからパイプに変わりました。HTML変換のtohtmlは:packadd nvim.tohtmlで有効にする方式になっています。LSPメッセージのJSONのnullはnilではなくvim.NILで届くので、ハンドラを自作している場合は分岐を見直します。TreesitterのQuery:iter_matches()からはallオプションが削除されました。Windowsでは、外部コマンドを探すときにカレントディレクトリを見なくなっています。'exrc'の確認では「(a)llow」が無くなり、「(v)iew」で中身を見てから:trustを実行する手順に変わりました。

本体の更新とvim.pack移行を分けて進める作業順序

最初に、現在の設定をgitにコミットして戻せる状態にします。次に上のgrepで削除済みの記述を直し、プラグインマネージャはそのままにして、本体だけを0.12.6へ上げて起動を確認します。vim.packへの移行はその後に行います。2つを同時に進めると、起動に失敗したときにどちらが原因か分からなくなります。

Neovim 0.13開発版で変わるvim.packの仕様

開発版(0.13.0-dev)のvim.packドキュメントでは、更新と削除がコマンドになっています。:packupdate[!] [++offline] [++lockfile] [name]と、:packdel[!] {name}・:packdel ++allです。ロックファイルの位置はオプション'packlockfile'で変えられ、プラグインがpkg.jsonマニフェストにinstallやupdateのスクリプトを書けるようになりました。PackChangedでの手書きのビルドフックは、プラグイン側へ移っていく見込みです。

GitHubの0.13マイルストーンの期日は2026年10月31日で、10月18日にGitHub APIで確認した時点では未完了の課題が79件残っていました。期日どおりに出るとは限りません。0.12.6で書いたvim.pack.update()やvim.pack.del()の呼び出しは、プラグイン定義と一緒に1つのLuaモジュール(例:lua/plugins.lua)へまとめておくと、0.13で書き換える範囲が小さく済みます。

チームでNeovim 0.12系とvim.packを採用する条件

vim.packへ移してよい条件と見送る場面

移してよいのは、設定リポジトリを版管理していて、nvim-pack-lock.jsonの差分をレビューできる場合です。新しいメンバーは、プラグインマネージャを入れる手順を飛ばしてロックファイルどおりの版を再現できます。遅延読み込みの条件が数個に収まる構成なら、移行の手間も小さく済みます。

見送るのは、lazy.nvimの遅延読み込みやdependenciesの自動解決に大きく頼っている構成です。案件ごとに開発環境を固定して納品する受託開発でも、実験的と明記された機能を土台に据えるのは避けます。0.13で操作の入口が変わる以上、納品後の保守で追随の作業が発生するからです。個人環境なら0.12.6でvim.packを試し、チーム環境では本体だけを0.12.6へ上げてvim.packは0.13の仕様が固まってから移す、という二段構えにします。エディタを含む開発環境の保守を社内で抱えきれない場合は、保守運用 / 内製化支援のように、運用の引き取りと内製化を合わせて相談できる窓口を決めておくと、選定だけが宙に浮く事態を防げます。

Vim script資産をそのまま使い続けたいなら、NeovimではなくVim 9.2のほうが手戻りは少なくなります。DenoでVim・Neovimの両方に対応するプラグインを使う場合は、denops.vimがNeovim 0.11.3以上を求める点も確認しておきます。ブラウザの入力欄でNeovimを使う場合は、Firenvimの設定と切り分け手順も参考になります。

Neovim 0.12系への移行でよくある質問

Neovim 0.12.6では何が変わりましたか?

0.12.5から0.12.6への変更は31コミットで、不具合修正が中心です。機能変更は実験的APIvim.pos・vim.rangeに関する1件だけで、設定項目の変更はありません。statuscolumnのヒープバッファオーバーフロー、msgpackの範囲外読み込み、netrwのコード注入の修正を含むため、0.12系を使っているなら更新します。

vim.packに移ればlazy.nvimは不要になりますか?

プラグインの取得・更新・版の固定は置き換えられます。依存関係の自動解決と宣言的な遅延読み込みはないため、load関数と:packaddで自分で書きます。同じディレクトリで2つのマネージャを併用する運用は想定されていません。

vim.packで遅延読み込み(lazy loading)はできますか?

できます。vim.pack.add(specs, { load = function() end })で導入だけを行い、autocmdやキーマップの中でvim.cmd.packadd('名前')を呼びます。0.12.6で、packaddの前はrequire()が失敗し、後は成功することを確認しました。

vim.pack.del()でプラグインが消えないのはなぜですか?

理由は2つ考えられます。現在のセッションでvim.pack.add()したプラグインは、読み込みを遅延させていても{ force = true }を付けないと削除できません。また、init.luaのvim.pack.add()に行が残っていると、次の起動で再インストールされます。先に行を消して再起動し、それからdel()します。

VimとNeovim 0.12系はどちらを選ぶべきですか?

Luaで設定とプラグインを組み、LSPと補完を本体で済ませたいならNeovim 0.12系です。既存のVim script資産やvimrcをそのまま持ち込みたいならVim 9.2が向いています。チーム内でどちらの資産が多いかで選ぶと、手戻りが少なくなります。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.23 テックブログ Renta!不正アクセスでサービス停止:再開未定の現状と運営側の停止判断・証拠保全・段階的再開
  2. 2026.10.21 テックブログ ふるさとチョイス不正アクセスで445万件流出:対象確認・パスワード変更と再開時期
  3. 2026.10.24 テックブログ カプコン不正アクセス:CAPCOM ID 1,177万件漏えいと196件の乗っ取りへの対処
  4. 2026.10.24 テックブログ ブラストメール不正アクセス:配信先288件の漏えいと利用企業の報告・通知の手順
  5. 2026.10.17 コラム カスハラ奨励金とは?東京都40万円の第2回要件と11月6日までに間に合う取組

RELATED POSTS 関連記事

目次