Neovimタスクランナーoverseer.nvimの使い方
Neovimのタスクランナープラグインoverseer.nvimの使い方・独自のタスクの追加方法などを解説します。
かなりボリュームのある記事です。
overseer.nvimとは
overseer.nvimはタスクランナーです。プロジェクトに応じたタスクを実行できます。
たとえばpnpmを使っているプロジェクトであればpackage.jsonのscriptsがpnpmで実行できます。

もちろん自分で設定したタスクも実行できます。
非同期に実行できる
:lua vim.fn.system({"sleep","5s"})のように外部コマンドを実行すると、コマンドが終了するまで操作できません。
overseer.nvimを使えば、UIがブロックされずに非同期で実行できます。
ステータスや履歴が分かる
実行中・実行済みのタスクの履歴を表示できます。

導入方法
stevearc/overseer.nvim各々が使っているプラグインマネージャーでインストールします。
インストール
lazy.nvimだと次のように書いてインストールできます。
return { 'stevearc/overseer.nvim', opts = {},}依存プラグイン
必須のプラグインはありません。
次のプラグインを入れると、そのプラグインが提供するUIに変化します。
- telescope.nvim:選択系のUIを変更
- nvim-notify:通知のUIを変更
最低限の設定
最低限のプラグインの設定はsetupを呼び出すだけで完了です。
require('overseer').setup()lazy.nvimの場合、setupの中身はoptsに書くためこの記述は不要です。この書き方について知りたい人は別の記事「lazy.nvimの使い方から起動を爆速にする方法までを解説」を読んでください。
lazy.nvimでの遅延読み込み
lazy.nvimでの遅延読み込みの例も載せておきます。
return { "stevearc/overseer.nvim", keys = { { "<space>r", "<CMD>OverseerRun<CR>" }, { "<space>R", "<CMD>OverseerToggle<CR>" }, }, opts = {},}筆者は2つのコマンドに遅延読み込みを設定しています。各コマンドの詳細は後で解説します。
ビルトインのタスク
デフォルトの設定でもさまざまなビルドツール・タスクランナーを実行できます。
2025年1月時点で対応されているビルトインのツールは次のとおりです。
- make
- shell
- JavaScript関係:npm、pnpm、yarn、bun、deno
- Composer
- cargo
- cargo-make
- Rake
- tox
- mix
- Mage
- task
- just
.vscode/tasks.jsonのタスクも実行できます。
pnpmやyarn、bunなどパッケージマネージャーはプロジェクトに合わせて表示してくれます。

タスクの実行方法
:OverseerRunでプロジェクトに応じたタスクの一覧が出ます。

タスクを選んで<CR>を押すとバックグラウンドで実行してくれます。
完了すると通知がきます。
実行結果の確認
実行完了後、成功ならSUCCESS、失敗ならFAILUREのような通知が来ます。
コマンドの出力結果を見たい場合:OverseerToggleまたは:OverseerOpenを実行します。
すると、次のようにタスクの実行履歴が表示されます。

左がタスクの履歴一覧、右が現在カーソルのある行のタスクの実行画面(後述)です。
タスクに依存関係があればネストで表示されます。
ステータスは次の6つです。
- RUNNING:実行中
- PENDING:保留中
- SUCCESS:成功
- FAILURE:失敗
- CANCELED:キャンセル済み
- DISPOSED:廃棄済み
タスク履歴の操作
タスクの履歴画面ではデフォルトでキーマップが設定されており、?で確認できます。
次の表はよく使いそうなキーマップの一覧です。
| key | |
|---|---|
?/g? | デフォルトのキーバインドを確認 |
q | ウィンドウを閉じる |
<CR> | アクションメニューを開く |
<C-F> | タスクをfloating windowで開く |
p | タスクのプレビューをトグル |
[ | ウィンドウを狭くする |
] | ウィンドウを広くする |
バッファなのでjやkなどで普通に移動できます。
アクションメニューの使い方
アクションメニューはターゲットとなるタスクに対して選んだアクションが実行されます。

たとえばタスクを停止するならstopを選びます。すると、そのタスクのステータスがCANCELEDになります。
disposeとは
disposeはタスクを停止して履歴から消すというアクションです。依存関係のあるタスクが失敗したとき、その後に実行予定だったタスクはステータスPENDING(保留)になります。これを消すときに使います。
アクションメニューの呼び出し方
アクションメニューの呼び出しは次のとおりです。
- タスク履歴画面で
<CR> :OverseerQuickAction:最新のタスクのアクションメニューを開く:OverseerTaskAction:タスクを選んでからアクションメニューを開く
タスクを開いて何ができるというんだ
タスクを〇〇で開くというアクションがちょいちょい出てきます。これらはタスクの実行画面を指定した種類のウィンドウで開くアクションです。
タスクの実行画面はNeovimの:terminalと同じように操作できます。つまり、入力や実行結果のコピーができます。
入力をしたい場合、最初はノーマルモードなのでaやIなどで挿入モードに切り替えてから入力します。
動画の例ではvertical(左右分割)で開いています。aで挿入モード切り替え後、1 2<CR>を入力すると、3が出力されています。
overseer.nvimの主要な概念
自作タスクを定義するときに登場する概念を紹介します。
タスクとは
タスクとは、実行される処理のことです。
たとえば、npm buildやmake deployなどの外部コマンドです。
コンポーネントとは
コンポーネントはフックっぽいやつです。タスクに対し、依存関係の定義や出力・通知などを定義します。
たとえば、「実行結果はquickfixで表示するぞ」といったことを定義するのがコンポーネントの役割です。
テンプレートとは
テンプレートはタスクを組み立てるときに使われるものです。
つまり文字どおりタスクのテンプレートです。
タスクの内容はもちろん、タスクが有効になる条件やリスト化されたときの優先度を定義します。
また、外部コマンドの実行前にLuaの処理を挟むこともできます。
独自のタスクを作成する方法
自分でカスタマイズしたタスクを作成したい場合、いくつか手段があります。
- テンプレートから作成:ほぼこれ
:OverseerBuild:その場限りのタスクで使う- APIの実行
この記事では一番よく使うであろう「テンプレートから作成」を解説します。
テンプレートの置き場所
自作タスクの元となる「テンプレート」はlua/overseer/templateディレクトリ以下に置きます。
.config/nvim/lua/overseer└── template ├── blog │ └── open-preview.lua ├── cpp │ ├── gpp-build-only.lua │ └── gpp-build-run.lua └── test.luaそして、setupのtemplatesに次のようにディレクトリ.ファイル名またはファイル名を書いて登録します。
require("overseer").setup({ templates = { "builtin", "blog.open-preview", "cpp.gpp-build-only", "cpp.gpp-build-run", "test", },})ちなみにファイルに分けずAPIでテンプレートを作成する方法もありますが、結局処理が長くなってファイルを分けると思うので割愛します。
テンプレートの書き方
いよいよテンプレートの書き方です。まずは簡単な例をお見せしましょう。nameはタスクの名前です。
---@type overseer.TemplateDefinitionreturn { name = "First Template", builder = function() local file = vim.fn.expand("%") ---@type overseer.TaskDefinition return { cmd = { "echo" }, args = { "hello", file }, } end,}テンプレートに必須なのはnameとbuilderだけです。
-- templatesでの登録も忘れずに!require("overseer").setup({ templates = { "builtin", "first-template", },})builderの書き方
builderはそのタスクが呼び出されたときの処理です。実行したい外部コマンドをreturnします。returnされるのはoverseer.TaskDefinitionという型です。
cmdとargs
cmdは「文字列のテーブル」か「文字列」を渡します。引数はargsに渡します。
---@type overseer.TaskDefinitionreturn { cmd = { "echo" }, args = { "hello", file },}echo helloのように引数を直接cmdに書きたい場合や、|といったshellの機能を使ってまとめてコマンドを書きたいケースが出てきます。
そんなときはcmdを文字列にします。
---@type overseer.TaskDefinitionreturn { cmd = "echo foo | sed 's/f/o/'", -- cmd = { "echo foo | sed 's/f/o/'" }, これはエラーになる}複数のコマンドを使う場合、別々にタスク化して順番に実行させる方法もあります(後述)。
cppのビルドの例
ここからは実際に使える例をお見せします。まずはC++のビルドです。
---@type overseer.TemplateDefinitionreturn { name = "g++ build only", builder = function() local file = vim.fn.expand("%:p") local outfile = vim.fn.expand("%:p:r") .. ".out" ---@type overseer.TaskDefinition return { cmd = { "g++" }, args = { file, "-o", outfile }, components = { { "on_output_quickfix", open_on_exit = "failure" }, "default", }, } end, condition = { filetype = { "cpp" }, },}builderでNeovimのAPIを呼び出してファル名を取得していますね。conditionで呼び出せるかどうかの条件を決めています。
quickfixで開く
コンパイルエラーになったらどの行が問題かを知りたいところです。
そこで、タスクが失敗したらquickfixで開くというコンポーネントを追加します。
components = { { "on_output_quickfix", open_on_exit = "failure" }, "default",},
ヘルプ::help on_output_quickfix
defaultコンポーネントとは?
defaultはデフォルトのコンポーネント集です。タスク完了後の通知など、細かい設定をユーザーがやらなくてもいいように短くしてくれています。componentsがない場合はdefaultになります。
components = { { "on_output_quickfix", open_on_exit = "failure" }, "default",},componentsをカスタマイズするときはおまじないだと思ってセットで書いておくのが吉です。
詳しく知りたい人向けに後で解説します。
依存関係のある例
C++でビルドした実行ファイルを実行する例です。先ほど定義した「g++でビルドするタスク」を先に実行させます。
---@type overseer.TemplateDefinitionreturn { name = "g++ build and run", builder = function() local outfile = vim.fn.expand("%:p:r") .. ".out" ---@type overseer.TaskDefinition return { cmd = { outfile }, components = { { "dependencies", task_names = { "g++ build only" }, }, { "open_output", focus = true, direction = "vertical" }, "default", }, } end, condition = { filetype = { "cpp" }, },}dependencies
依存関係のあるタスクではdependenciesコンポーネントを使います。task_namesのテーブルに他のタスクのnameを書きます。
{ "dependencies", task_names = { "g++ build only" },},あるいは、task_namesの中に直接タスク内容を書いてもかまいません。
{ "dependencies", task_names = { { cmd = { "g++" }, args = { file, "-o", outfile }, -- 省略 }, },},依存タスクを順番に実行したい
task_namesは複数のタスクを登録できます。通常だとテーブル内のタスクを一斉に実行します。
{ "dependencies", task_names = { { cmd = "sleep 5" }, { cmd = "sleep 3" }, { cmd = "sleep 2" }, },},上記のタスクだとsleep 2、slee 3、sleep 5の順で実行が完了するということです。
これをリストどおりに順番に実行したい場合、次のように引数が必要です。
{ "dependencies", task_names = { { cmd = "sleep 5" }, { cmd = "sleep 3" }, { cmd = "sleep 2" }, }, sequential = true,},上記のタスクだと「sleep 5が完了後にsleep 3を実行開始」というように順番に実行してくれます。
ヘルプ::help dependencies(名前被りありそうなので:help overseer-componentsのほうがいいかも?)
open_output
open_outputコンポーネントを使うと、実行画面を開けます。quickfixと違ってファイルの行に基づいた出力が必要なければopen_outputを使いましょう。
components = { { "dependencies", task_names = { "g++ build only" }, }, { "open_output", focus = true, direction = "vertical" }, "default",},すでに説明しましたが、実行画面はNeovimの:terminalと同じように操作できます。
ヘルプ::help open_output
Luaの処理だけをタスクとして追加する方法
ここまでは全部外部コマンドでした。ここではLuaの処理をテンプレートとして定義します。
---@type overseer.TemplateDefinitionreturn { name = "open blog preview", builder = function() local basename = vim.fn.expand("%:t:r") vim.ui.open("http://localhost:4321/blog/" .. basename) return { cmd = { "echo" }, args = { basename }, } end, priority = 1, condition = { dir = "~/ghq/github.com/eetann/example", },}筆者がブログを書くときに使っているテンプレートです。
Luaだけの処理をするときのbuilder
Luaの処理をbuilderのreturn前に書きます。
builder = function() local basename = vim.fn.expand("%:t:r") vim.ui.open("http://localhost:4321/blog/" .. basename) return { cmd = { "echo" }, args = { basename }, }end,cmdは必須パラメータであるため適当に書きます。空文字だと履歴が分かりづらくなるため、筆者は上記のように登場した変数を表示させています。
優先度の設定
:OverseerRunで表示されるタスクのリストはpriorityで並び替えられます。数字の小さいほうが優先されます。
priority = 1,特定のディレクトリだけで使うタスク
タスクが特定のディレクトリだけで使う場合、テンプレートのconditionのdirで指定します。
condition = { dir = "~/ghq/github.com/eetann/example",},他のプラグインとの連携
他のプラグインとの連携例を紹介します。
ステータスプラグインとの連携
lualine.nvimにタスクの実行状況を表示できます。

require("lualine").setup({ sections = { lualine_x = { "overseer" }, },})テンプレートの補完を効かせる
この記事では、---@typeのようなLua Language Serverのアノテーション機能がちょっと登場しました。
今回のreturn {}のような「require()を使わないけど補完させたい」というときに便利です。
具体的にはプラグインlazydev.nvimを使って次のように設定しています。
return { "folke/lazydev.nvim", ft = "lua", opts = { library = { { path = "${3rd}/luv/library", words = { "vim%.uv" } }, { path = "overseer.nvim", words = { "overseer" } }, }, },}よくある問題とその解決
エンカウント率高めな問題と解決方法もまとめました。
登録したテンプレートがリストに表示されない
setupのtemplatesに追加しているか、名前が正しいかを確認してください。
require("overseer").setup({ templates = { "builtin", "cpp.build-only", },})あるいはテンプレートのconditionとマッチしているか確認してください。
dependenciesのタスクが順番に実行されない
依存タスクを順番に実行したいで説明したとおり、sequential=trueを追加しましょう。
{ "dependencies", task_names = { { cmd = "sleep 5" }, { cmd = "sleep 3" }, { cmd = "sleep 2" }, }, sequential = true,},挿入モードになってしまう
タスク実行時に挿入モードへ切り替わってしまう人がいるかもしれません。
次のようにstartinsertを定義しているとそうなります。
vim.api.nvim_create_autocmd({ "TermOpen" }, { group = "my_nvim_rc", command = "startinsert",})この設定を削除するか、overseer.nvimのときだけオフにします。
ここで、プラグイン作者stevearcさんの設定を見てみましょう。
vim.api.nvim_create_autocmd("TermOpen", { desc = "Auto enter insert mode when opening a terminal", pattern = "*", group = aug, callback = function() -- Wait briefly just in case we immediately switch out of the buffer vim.defer_fn(function() if vim.bo.buftype == "terminal" and not vim.b.overseer_task then vim.cmd.startinsert() end end, 100) end,})変数vim.b.overseer_taskを使ってoverseerかどうか判定していますね。
あえてタスクを失敗させたい
あえてタスクを失敗させるときはfalseコマンドを使いましょう。Luaの処理で分岐させたいときに便利です。
たとえば筆者は「dependenciesを機械的に作成し、存在しなければタスクを失敗とする」というテンプレートを作りました。
builder = function() local dependencies = create_tasks_before_foo() if next(dependencies) == nil then return { cmd = "echo 'FAILED: foo' && false", } end ---@type overseer.TaskDefinition return { cmd = "echo 'SUCCESS: foo'", components = { { "dependencies", task_names = dependencies, sequential = true, }, "default", }, }記事を書いているときの画像変換のテンプレートで使っています。
on_exit_set_statusに関するエラーが出る
componentsをいじっていると次のようなエラーになることがあります。
[ERROR] Task false did not finalize during exit. Is it missing the on_exit_set_status component?これはおそらくdefaultコンポーネントを使っていないからでしょう。次の項目に出てくるon_exit_set_statusを読んでください。
結局default componentとは?
途中で説明を省いたdefaultコンポーネントについて筆者が調べた内容を解説します。
defaultコンポーネントは次の5つのコンポーネントの集まりです。
default = { { "display_duration", detail_level = 2 }, "on_output_summarize", "on_exit_set_status", "on_complete_notify", { "on_complete_dispose", require_view = { "SUCCESS", "FAILURE" } },},設定を変更する機会が一番ありそうなon_complete_notifyから解説します。
on_complete_notify
on_complete_notifyはタスク終了時の通知設定です。
「defaultコンポーネントを使いたいけど通知はいらない」という場合はstatusesを空にします。
components = { { "on_complete_notify", statuses = {} }, "default",},ヘルプ:;help on_complete_notify
display_duration
display_durationはタスクの実行時間をどの詳細レベルで表示するか、という設定です。
タスク履歴でLを押すと詳細レベルが上がります。
たとえば次がデフォルト設定での詳細レベル1です。

次が詳細レベル2です。2秒のタスクでした。

デフォルトコンポーネントでは{ "display_duration", detail_level = 2 }と設定されているため、実行時間はレベル2以上で開示されます。
詳細レベル3だと、どんなコンポーネント設定だったのかも確認できます。

ヘルプ::help display_duration
on_output_summarize
on_output_summarizeは「詳細レベル2・3のときに表示する出力の行数」を指定します。デフォルトだと4行です。

ヘルプ::help on_output_summarize
on_exit_set_status
on_exit_set_statusは「タスクが成功したと見なす終了コード」を指定します。終了コード0は指定しなくても成功扱いです。
たとえば、次のタスクは終了コード1が返って失敗します。
---@type overseer.TaskDefinitionreturn { cmd = "false", components = { "default", },}次のように設定すると0だけでなく1も成功扱いにしてくれます。
---@type overseer.TaskDefinitionreturn { cmd = "false", components = { { "on_exit_set_status", success_codes = { 1 } }, "default", },}defaultコンポーネントを使わない場合は必ずon_exit_set_statusを入れましょう。そうしないと次のようにエラーになります。
[ERROR] Task false did not finalize during exit. Is it missing the on_exit_set_status component?---@type overseer.TaskDefinitioncomponents = { { "on_exit_set_status" }, -- defaultを使わないなら必須},ヘルプ::help on_exit_set_status
on_complete_dispose
on_complete_disposeは「タスクが完了して何秒後に履歴を消すか」という設定です。
通常であれば「タスクを実行してから5分経過」「Neovimを終了する」のどちらかで履歴から消えます。
この設定を変えたい場合に活用します。
次の例では、タスク完了の1分後に履歴から消えるように設定しています。
components = { { "on_complete_dispose", timeout = 60 }, "default",},ヘルプ::help on_complete_dispose
公式ドキュメント
まだ紹介しきれていない機能やオプションがたくさんあります。詳しくは公式ドキュメントへどうぞ。
- ヘルプ:
:help overseer - GitHubリポジトリ:stevearc/overseer.nvim
- ドキュメント集:overseer.nvim/doc
Neovimから離れずにいろいろ作業できるようになり、スーパーキノコを食べた気分です。
ちなみにoverseerは監督者って意味らしいです。