← Vim研究所

goda.vimとは denops.vimとの違いと、Goでプラグインを動かすまで

2026-11-12 公開 ・

goda.vimは、ddc.vimやdpp.vimの作者であるShougoさんが2026年10月に公開した、Goでプラグインを書くための基盤です。READMEには「denops.vimの精神的な後継で、Denoに依存しない」とあり、同じバイナリがVimでもNeovimでも動きます。

Deno版よりどれだけ軽いのかはREADMEに書かれていないので、同じ中身のhelloプラグインをdenops.vimとgoda.vimで用意し、起動の速さと常駐メモリを測りました。補完のgoda-cmp.vimも最小構成で動かしています。

検証環境:macOS 27.0(Apple M1 Max)、Vim 9.1.1752、NVIM v0.12.6、go1.27.3、Deno 2.9.8。goda.vimはコミットca93d5a(2026-11-11、Goモジュールとしてはv0.8.0)、denops.vimはコミット121d530(2026-10-31)、goda-cmp.vimはv0.4.1(コミット5c8327a、2026-11-12)で確かめました。どれも作られて1か月前後で毎日のように更新されており、関数名や手順は今後変わる可能性があります。掲載している出力と数値は、すべて手元で実行した結果です。

目次(9項目)

goda.vimとは何か

goda.vim(読みは「ゴダ」)は、エディタとプラグインのバイナリの間でメッセージを運ぶことだけを受け持ちます。補完のような機能は持たず、上に作られた別のプラグインが担当します。コマンドは無く、goda#start()やgoda#request()のような関数だけです。

プラグインのバイナリはそれぞれが独立したプロセスで、1つが落ちても他は巻き込まれません。Vimとの間はJSONのチャネル、Neovimとの間はmsgpack-rpcで、どちらも標準入出力を通るので、プラグイン側はつながっている先を気にせずに書けます。

denops.vimとの違い

いちばん大きな違いは、実行時にランタイムが要らないことです。denops.vimは使う人の環境にDenoが要りますが、goda.vimのプラグインはGoでビルドしたバイナリなので、動かすときには何も要りません。

項目denops.vimgoda.vim
言語TypeScript(Deno)Go
実行に要るものDenoなし(ビルド済みのバイナリ)
プロセスDenoのサーバー1つに全プラグインが載るバイナリごとに1プロセス
起動のきっかけVimの起動時(VimEnter)関数を呼んだとき
拡張の組み込み実行時に読み込むビルド時にGoのパッケージとして取り込む
対応版Vim 9.1.1646以降、Neovim 0.11.3以降Vim 9.1以降(+job +channel)、Neovim 0.11以降

起動のきっかけの違いはplugin/に表れています。denops.vimはplugin/denops.vimでautocmd VimEnter * call denops#server#connect_or_start()を登録し、立ち上がるとすぐDenoのサーバーを起動します。goda.vimにはplugin/が無く、goda#...()の関数を初めて呼んだときに読まれます。

拡張の扱いも逆です。ddc.vimではソースやフィルタを設定で名前指定し、Deno側が実行時に読み込みます。goda-cmp.vimでは、自分の設定用プログラムのmain.goがGoのパッケージとしてimportしてビルドします。版はgo.modで固定され、組み合わせの間違いはコンパイルで見つかる代わりに、設定を変えるたびにビルドし直すことになります。

Goでhelloプラグインを書いて呼び出す

READMEの「Try it」をそのまま追いました。helloプラグインのgreetメソッドが、渡された名前と、エディタに問い合わせたカレントディレクトリを返します。プラグインの側からエディタの関数(ここではgetcwd)を呼び返せます。

mkdir hello && cd hello
go mod init hello
# main.go を置いてから
go get github.com/Shougo/goda.vim@latest
go mod tidy
go build -o hello .
package main

import (
	"context"
	"fmt"

	"github.com/Shougo/goda.vim/host"
	"github.com/Shougo/goda.vim/sdk"
)

type hello struct{}

func (hello) Name() string { return "hello" }

func (hello) Handlers() map[string]sdk.Handler {
	return map[string]sdk.Handler{
		"greet": func(ctx context.Context, h sdk.Host, params []any) (any, error) {
			var name string
			if err := sdk.DecodeArgs(params, &name); err != nil {
				return nil, err
			}
			cwd, err := h.Call(ctx, "getcwd")
			if err != nil {
				return nil, err
			}
			return fmt.Sprintf("hello, %s, from %s (cwd %v)", name, h.Meta().Host, cwd), nil
		},
	}
}

func main() { host.Main(hello{}) }

goda.vimのモジュールの取得からビルドまでで約20秒かかりました。入ったのはgoda.vim v0.8.0で、helloは4.5MBです。エディタなしで動作を見る開発用の入口もあります。

echo '{"method":"goda.plugins"}' | ./hello --transport=jsonl
# {"result":{"hello":["greet"]}}

エディタからはruntimepathにgoda.vimを足して呼びます。

set runtimepath+=/path/to/goda.vim
call goda#start('/path/to/hello/hello')
echo goda#request('hello', 'greet', 'world')

Vim 9.1.1752とNeovim 0.12.6で、同じバイナリから次のように返ってきました(カレントディレクトリは省いています)。goda#start()の戻り値は、そのバイナリが受け持つプラグイン名の一覧['hello']です。

hello, world, from vim (cwd ...)
hello, world, from nvim (cwd ...)

psで見ると、Vimからは--transport=vim、Neovimからは--transport=nvimの引数で起動されていて、エディタごとのビルドは要りません。なお-uで設定ファイルを指定して試すときにset nocompatibleを忘れると、autoload/goda.vimの行継続が読めずE10で止まります。

goda#registerで最初の呼び出し時に起動させる

goda#start()で起動したバイナリをgoda#stop()で止めると、そのあとのgoda#request()は次のエラーになり、起こし直してはくれませんでした。

goda: unknown plugin "hello" (is its binary started? see goda#plugins())

プラグインとして配るときはgoda#register()を使います。起動のしかたを登録しておくと、最初のリクエストがバイナリを起動し、止まったあとも呼べばまた起動します。

" autoload/hello.vim
call goda#register('hello', '/path/to/hello/hello')

function! hello#greet(name) abort
  return goda#request('hello', 'greet', a:name)
endfunction

手元では、goda#register()の直後のgoda#is_running('hello')は0、最初のgoda#request()が返った時点でプロセスが1つ立ち、goda#stop('hello')で0に戻り、もう一度呼ぶと答えが返って1になりました。VimでもNeovimでも同じです。

Neovim 0.12.6では、起動のたびにバイナリが2回実行されていました。実行を記録するシェルスクリプトを前に挟むと、Vimは1回、Neovimは2回です。:help goda#start()によると、Neovim 0.13より前はrpcrequest()にタイムアウトを付けられないので、最初の握手を普通のジョブで試してから本番を起動するためです。残るプロセスは1つでした。

起動の速さとメモリをdenops.vimと比べる

denops.vim側にも、同じgreetを返すdenops/hello/main.tsを用意しました。設定ファイルを読み始めてから最初の答えが返るまでをreltime()で取り、goda.vimはVimEnterのあとにgoda#register()とgoda#request()、denops.vimはDenopsPluginPost:helloを待ってdenops#request()を呼んでいます。VimとNeovimで5回ずつ測りました。

計測エディタgoda.vimdenops.vim
最初の答えまで(中央値)Vim0.10秒1.08秒
最初の答えまで(中央値)Neovim0.18秒2.04秒
最初の答えまで(範囲)両方0.02〜0.34秒0.75〜4.0秒
2回目以降の呼び出し両方0.3〜26ミリ秒0.8〜8ミリ秒
常駐プロセスのRSS両方5.8〜6.4MB84〜86MB

最初の答えまでは、goda.vimがおよそ10分の1でした。Neovimでdenops.vimのイベント時刻を記録した回では、プロセス起動が0.24秒、待ち受け開始が0.46秒、DenopsReadyが2.62秒で、プラグインの読み込み自体は10ミリ秒ほどです。時間のほとんどはDenoの準備に使われていました。

メモリはps -o rssで、goda.vimのhelloが6MB前後、Denoのサーバーが85MB前後です。ただしdenops.vimはDenoのサーバー1つに全プラグインを載せ、goda.vimはバイナリごとにプロセスが立つので、使うプラグインが増えるとgoda.vim側はその数だけプロセスが並びます。

初回はさらに別です。Denoのキャッシュが空の状態では、jsrからの取得で準備ができるまで29秒かかりました。goda.vimではこの取得がビルドの側に寄っています。

計測中はほかの作業でマシンの負荷が高く(ロードアベレージ150前後)、同じ条件でも回によって数倍ぶれたので、差の大きさの目安として読んでください。vim --startuptimeでは、goda.vimをruntimepathに足しても読まれるファイルは増えず、denops.vimはplugin/denops.vimとautoloadで0.87ミリ秒でした。どちらもエディタの起動はほとんど遅らせず、差が出るのはプラグインが使えるようになるまでです。

goda-cmp.vim・goda-fi.vim・goda-pm.vimなどの周辺プラグイン

2026-11-12の時点で、goda.vimの上に作られたShougoさんのプラグインは次のとおりです(役割はREADMEとリポジトリの説明から)。

プラグイン役割作成日
goda-cmp.vim補完(ddc.vimの後継)2026-10-15
goda-fi.vimファインダー2026-10-28
goda-skk.vimSKKの実装2026-10-28
goda-pm.vimプラグインマネージャ(dpp.vimの後継)2026-10-31
goda-term.vim端末2026-10-31

このうちgoda-cmp.vimをREADMEの手順で動かしました。設定は自分で持つGoのモジュールで、examples/configのmain.goとconfig.goを写し、pum.vim用の2行を消して標準のポップアップだけにしました。go mod tidyでgoda-cmp-ext v0.1.2とgoda-cmp-ui-native.vim v0.1.0が入ります。

autocmd VimEnter * call goda_cmp#enable()
" 初回とconfigを変えたとき
call goda_cmp#build()

goda_cmp#build()で5.0MBのバイナリができましたが、挿入モードでfooと打っても候補が出ません。goda#log()には次の行がありました。

cmp: ui native: vim: Vim(return):E117: Unknown function: goda_cmp#ui#native#show

READMEが挙げているのはgoda.vimとgoda-cmp.vim(とpum.vim)だけですが、goda-cmp-ui-native.vimもruntimepathに入れる必要がありました。Goのモジュールとしてビルドに入るだけでは、autoload/goda_cmp/のVim scriptが読めません。入れ直すと、VimでもNeovimでもfoobar foobaz footballが印A付きで出ました。設定の置き場所も違い、Vimは~/.vim/goda-cmp、Neovimはstdpath('config')の下のgoda-cmpで、Neovim側に無いとno config directoryで止まります。

goda-cmp.vimが置き換えるのはプラグインとしての補完で、エディタに入っているVim/Neovimの標準自動補完(autocomplete)とは別物です。ソースを複数まとめたい、LSPと辞書を並べたいといった用途でなければ、標準の機能で足りることも多いです。

いま乗り換えるべきか

goda.vimもgoda-cmp.vimもREADMEに「Status: early」とあり、APIが変わりうると書かれています。自動テストの対象はLinux上のVim 9.1とNeovim 0.12で、macOSとWindowsは含みません。今回のmacOSでは最小構成まで動きましたが、手順書どおりに進まない箇所もありました。denops.vimの環境で困っていないなら、急いで移る理由は薄いと考えています。

向いているのは、Denoを入れたくない環境でShougoさんのプラグインを使いたい人です。READMEによればcgoを使わない純粋なGoで、動かすのにGoは要りません(別のマシンへ持ち出しての確認はしていません)。SKKだけが目当てなら、Denoとskkeletonで組む方法をVimで日本語入力をEscでオフにする記事にまとめています。goda-skk.vimは今回試していません。

名前にも注意が要ります。Goにはloov/godaという2018年からある依存関係の解析ツール(スター1,700超)があり、go install github.com/loov/goda@latestでgodaというコマンドが入ります。goda.vimのREADMEが「プラグインのバイナリをgodaと名付けないこと」と注意しているのはこのためです。

入れ方はdpp.vimのTOMLを使えるgoda-pm.vimが推奨されていますが、goda.vim自体はruntimepathに入れば動くので(今回もset runtimepath+=で試しました)、Vim標準のパッケージやvim-plugでも、Neovimの標準のプラグイン管理vim.packでも構いません。

使ってみて

両者の違いをいちばん感じたのは計測のスクリプトを書くときでした。denops.vimの準備をdenops#plugin#wait()で待とうとタイマーの中から呼んだら、ヘッドレスのNeovimが指定した30秒のあいだ固まり、Failed to wait for "hello" to startで終わりました。DenopsPluginPostを待つ形に書き直して、ようやく測れています。goda.vimはgoda#request()が起動から答えまでその場で返すので、上から順に書くだけで済みました。

まとめ

goda.vimは、denops.vimが担ってきた「VimとNeovimの両方で動く外部プロセスのプラグイン」を、Denoの代わりにGoのバイナリでやる基盤です。手元の計測では、最初の答えまでがVimで0.10秒対1.08秒、常駐メモリが6MB前後対85MB前後でした。プラグインは関数を呼んだときに起動し、goda#register()を使えば止まったあとも呼べば起き直します。

その代わり、設定を変えるたびにGoでビルドし直し、プラグインの数だけプロセスが並びます。どれも公開から1か月ほどの早期版なので、困ったときはgoda#log()から見るのが近道です。

関連記事

← Vim研究所 トップへ戻る