プラットフォーム

Firenvimとは?ブラウザのテキストエリアでNeovimを使う設定と切り分け手順

Firenvimとは?ブラウザのテキストエリアでNeovimを使う設定と切り分け手順

Firenvimは、ブラウザ上のテキストエリアをローカルのNeovimに置き換えるブラウザ拡張です。GitHubのIssue欄やブログの入力欄をクリックすると、その場に普段の設定を読み込んだNeovimが立ち上がります。ここでは公式リポジトリ glacambre/firenvim(GPL-3.0)の最新リリース0.2.17と、2026年9月18日時点のmasterのソース・付属ドキュメントを基に、導入手順・設定項目・動かないときの切り分けを整理します。リリース版とmasterで記述が分かれる箇所は、その都度どちらを指すか明記します。

まとめ:Firenvim導入の要点

  • Neovimプラグインとブラウザ拡張の2点セットで動く。片方だけでは起動しない。
  • Neovimは0.6.0以上が必須。firenvim#install() が冒頭で has('nvim-0.6.0') を判定し、満たさなければ中断する。
  • 拡張の導入先はChromeウェブストアとFirefox Add-ons。GitHub Releasesにも chrome.zip と firefox-latest.xpi が置かれている。最新は0.2.17(2026年5月8日公開)。
  • 設定はすべて vim.g.firenvim_config に集約され、サイト別の localSettings と全体の globalSettings に分かれる。
  • takeover の値は always、once、empty、nonempty、never の5種類。
  • 起動しないときの確認対象は、ネイティブマニフェストの未作成(No config detected)と、Snap/Flatpak・Local Network Accessによる通信の遮断。

Firenvimの仕組みと類似ツールとの使い分け

ローカルNeovimをネイティブメッセージングで起動する構成

Firenvimはブラウザ内でNeovimを再実装しているわけではありません。拡張がWebExtensionsのネイティブメッセージング経由でローカルのNeovimプロセスを起動し、編集用のiframeが ws://127.0.0.1:<ランダムなポート> のWebSocketでそのNeovimに接続します。この構成のため、普段の init.lua・プラグイン・カラースキームがそのまま効きます。

編集内容の反映は BufWrite 自動コマンドで検知しており、:w を押した時点で隠されたテキストエリアへ書き戻されます。:q でフレームを閉じるとブラウザのテキストエリアに戻ります。バッファ名は domainname_page_selector.txt の形式で付くため、github.com_*.txt のようなパターンで BufEnter を張ればサイトごとにfiletypeを切り替えられます。

GhostText・Tridactyl・Texternとの選び分け

READMEが挙げる類似ツールは4つで、それぞれ前提が違います。エディタ側にプラグインを入れたくないならTextern、ブラウザ操作全体をVim化したいならTridactyl、複数エディタで使い回すならGhostTextが向きます。Firenvimの違いは、テキストエリアがあった位置にNeovimの画面そのものが出る点です。別ウィンドウへ視線とフォーカスを移さず、ページ内で編集を完結させたい場合に向きます。

ツール エディタ側プラグイン 編集画面の出る場所 対応エディタ
Firenvim 必要 ページ内(テキストエリア上) Neovimのみ
GhostText 必要 エディタ側の別ウィンドウ 複数
Textern 不要 エディタ側の別ウィンドウ 任意
Tridactyl 不要 エディタ側の別ウィンドウ 任意

Vim本体(Neovimではない方)だけを使っている場合、Firenvimは選択肢に入りません。Vim 9.2で開発者が押さえるべき主要変更点とリリース背景の全体像で扱ったVim 9系にはNeovimのUI接続プロトコルがなく、FirenvimはNeovimのUIクライアントとして実装されているためです。

導入手順:Neovim側の要件とfirenvim#installの実処理

前提となるNeovimのバージョンとLuaランタイム

autoload/firenvim.vim の firenvim#install() は、最初に2つの前提を検査します。ひとつは has('nvim-0.6.0') で、満たさないと「Error: nvim version >= 0.6.0 required. Aborting.」を出して終了します。もうひとつはLuaの bit モジュールで、読み込めない場合はLua package "bit" unavailable. Install it or switch to LuaJIT.となります。標準ビルドのNeovimはLuaJIT同梱なので後者は通常問題になりませんが、PUC-Luaでビルドした環境では引っかかります。

付属の TROUBLESHOOTING.md には「nvim version >= 0.4.0 required」と書かれていますが、これは実装が0.6.0へ引き上げられた後も更新されていない記述です。判断はコード側の0.6.0を採ってください。ただしFirenvim側の下限が0.6.0でも、本記事とREADMEが載せるLua版の設定例は vim.api.nvim_create_autocmd を使うため0.7.0以上が必要です(この関数はNeovim 0.6.1のAPIドキュメントには存在せず、0.7.0で追加されています)。なお現行のNeovim 0.12系でも動作要件は変わりません(Neovim 0.12系への移行手順:vim.packと内蔵LSP・自動補完の実装)。

プラグインマネージャー別の記述とインストールスクリプト

プラグイン本体を入れたあと、ポストインストールスクリプトを必ず走らせます。lazy.nvimなら次のとおりです。

{ 'glacambre/firenvim', build = ":call firenvim#install(0)" }

プラグインマネージャーがビルドフックを持たない場合は、シェルから直接実行しても同じ結果になります。

nvim --headless "+call firenvim#install(0) | q"

引数の0は「設定ディレクトリが存在するブラウザにだけ入れる」、1は「存在を確認せず全ブラウザに強制インストールする」という意味です。この違いが後述の切り分けで効いてきます。

firenvim#installが実際に書き込むもの

firenvim#install() はNeovim側の設定を書き換える処理ではありません。実際に作られるのは次の2つです。

  • Neovimを起動するためのシェルスクリプト(またはバッチ)を $XDG_DATA_HOME/firenvim に作成し、パーミッションを rwx------ に設定する。Linux・macOSでは通常 $HOME/.local/share/firenvim、Windowsでは %LOCALAPPDATA%\firenvim です。
  • 検出したブラウザに応じた配置先へ、上記スクリプトを指すネイティブメッセージングマニフェストを rw------- で作成する。ファイル名はLinux・macOSでは firenvim.json、Windowsではデータディレクトリ内の firenvim-<ブラウザ名>.json です。Windowsではあわせて、このファイルを指すレジストリキーをPowerShell経由で作成します。

対象となるブラウザは実装上15種類あります。arc、brave、chrome、chrome-canary、chrome-dev、chromium、edge、firefox、librewolf、waterfox、opera、ungoogled-chromium、vivaldi、zen、heliumです。ただしこれは「マニフェストの置き場所を知っている」という意味にとどまり、動作を保証するものではありません。拡張そのものの導入先はChromeウェブストアとFirefox Add-onsで、GitHub Releasesにも chrome.zip と firefox-latest.xpi が公開されています。Chromium系ブラウザで使う場合はChromeウェブストア版を導入することになります。SafariについてはREADMEが対象外としています(READMEはWebExtensions非対応を理由に挙げていますが、Safariは現在Web Extensionsに対応しており、この記述は古いものです)。

WSL環境では処理が2回走ります。WSL側でインストールしたあと s:is_wsl を再判定し、真ならWindows側のパスに対してもう一度マニフェストとレジストリキーを作ります。WSL内のブラウザとWindows側のブラウザのどちらからでも使えるよう、両方の環境へ配置する作りです。

Chrome版とFirefox版のマニフェストバージョンの違い

リポジトリの src/manifest.json はManifest V2の形で書かれていますが、webpack.config.js のビルド時にChrome向けだけV3へ変換されます。具体的には manifest_version を3に書き換え、background.scripts を background.service_worker に、browser_action を action に差し替え、host_permissions を分離しています。Firefox版はV2のままです。挙動の違いを追う必要が出た場合、この分岐を知らないとソースとストア版の食い違いに見えるので注意してください。

vim.g.firenvim_configで変えられる設定項目

localSettings:URLパターンごとの挙動

localSettings のキーはURL全体に対するJavaScriptの正規表現パターンで、値がそのパターンに一致したページで使う設定です。READMEは「一致したパターンのうち priority が最大のものが使われる」と書いていますが、実装はやや違います。一致した設定を priority の昇順に並べ、Object.assign で順に統合するため、同じ項目は優先度の高い側で上書きされ、低い側にしかない項目はそのまま残ります。部分的に上書きする書き方ができる、と読むのが正確です。

vim.g.firenvim_config = {
    globalSettings = { alt = "all" },
    localSettings = {
        [".*"] = {
            cmdline  = "neovim",
            content  = "text",
            priority = 0,
            selector = "textarea",
            takeover = "always"
        }
    }
}
キー 取りうる値 既定値
selector 任意のCSSセレクタ textarea と div[role=textbox]
takeover always / once / empty / nonempty / never always
cmdline neovim / firenvim / none firenvim
content text / html text
renderer canvas / html canvas
priority 数値 0
filename 書式文字列 下の本文を参照

既定値は公式実装の src/utils/configuration.ts で定義されています。selector の既定は textarea:not([readonly], [aria-readonly]), div[role="textbox"]、filename の既定は {hostname%32}_{pathname%32}_{selector%32}_{timestamp%32}.{extension} です。ここで注意したいのが cmdline で、READMEの設定例は neovim を書いていますが実装上の既定は firenvim、つまり外部コマンドラインです。狭いテキストエリアで領域を節約するための選択で、READMEの例をそのまま写すと既定から挙動が変わります。

既定のセレクタが div[role="textbox"] を含む点も把握しておく価値があります。Gmail・Outlook・Slackのようなリッチテキストエディタまで奪われるのが煩わしければ、selector = 'textarea' に絞るのが最短の対処です。

takeover は5値です。always は常に奪う、empty は空の要素だけ、nonempty は空でない要素だけ、never は自動では出さずショートカット操作を必須にする、once は最初に選択したときだけ奪い :q の後は再びショートカットが必要になる、という区分です。empty と nonempty を使うと、書きかけの本文は奪わせず新規入力欄だけNeovimに任せる、といった振り分けができます。

globalSettings:キー入力とブラウザショートカットの扱い

globalSettings は全サイト共通の設定です。以下のコード例は単体で完結した形なので、複数の例をそのまま順に書き足すと vim.g.firenvim_config が上書きされ、先に書いた localSettings が消えます。実際の設定では1つのテーブルにまとめて一度だけ代入してください。実務で使う頻度が高いのは次の3つです。

  • ignoreKeys:指定したキーをFirenvimが握らず、ブラウザに渡します。キーがNeovimのモード名(all も可)、値が無視するキーの配列です。修飾キーを重ねる場合の順序はShift、Alt、Control、OS/Metaの順で、Ctrl+Alt+Shift+1 は <SAC-1> と書きます。
  • cmdlineTimeout:外部コマンドラインが消えずに残る問題への回避策で、カーソル移動から指定ミリ秒後に隠します。既定は3000ミリ秒です。
  • alt:macOSでoptionキーが特殊文字やDead keyを生む問題への設定です。既定値がOSで分かれており、macOSでは alphanum、それ以外では all になります。alphanum の場合、非英数字(/[a-zA-Z0-9]/ に一致しない文字)に付いたalt修飾を落とすため、option+oはNeovimに ø として届きます。alt = "all" にすると修飾を落とさず <M-ø> として送りますが、これは <M-o> を復活させる設定ではありません。macOSのキーボード配列がoption+oで ø を生成する以上、<M-o> のマッピングはブラウザ拡張の側では取り戻せない、というのがREADMEの説明です。
vim.g.firenvim_config = {
    globalSettings = {
        ignoreKeys = {
            all = { '<C-->' },
            normal = { '<C-1>', '<C-2>' }
        },
        cmdlineTimeout = 3000
    }
}

サイト別の有効・無効とキー競合の調整

許可リスト・拒否リストの組み方

全体に広いパターンを敷き、個別サイトに優先度の高いパターンを重ねるのが基本形です。ここで先に押さえておきたい落とし穴があります。READMEのLua例は vim.g.firenvim_config.localSettings[...] = {...} のように入れ子のフィールドへ直接代入していますが、Neovimのマニュアルが明記しているとおり、vim.g 経由で取り出したテーブルのフィールドを書き換えても g: 変数には反映されません。設定が効かないときの原因になります。

いったんローカル変数へ受け、書き換えてから代入し直すのが確実です。次の例は全サイトで自動起動しつつ、co.ukドメインでのみ自動起動を止めます。

local cfg = vim.g.firenvim_config
cfg.localSettings["https?://[^/]+\\.co\\.uk/"] = { takeover = 'never', priority = 1 }
vim.g.firenvim_config = cfg

逆に「基本は無効、特定サイトだけ有効」にしたいなら、.* に takeover = 'never' を置き、使いたいサイトのパターンに高い priority と takeover = 'always' を与えます。判断としては、業務でWebフォームを多用する環境ほど後者を勧めます。既定の always はセレクタに一致する入力欄をすべて自動で奪うため、社内ツールのコメント欄のようにブラウザ側で直接打ちたい場所でも編集画面へ切り替わるからです。

ブラウザが握っているキーの取り返し方

<C-n>、<C-t>、<C-w> は通常の設定では上書きできません。Firefoxは about:addons のショートカットメニュー、Chromeは chrome://extensions/shortcuts から割り当てます。拡張が公開しているコマンドIDは nvimify(既定で Ctrl+E、フォーカス中の要素をNeovimフレーム化)、send_C-n、send_C-t、send_C-w、toggle_firenvim(タブ単位の有効・無効切り替え)の5つです。

フレームの外でこれらのキーを押したときにブラウザ本来の動作を真似るかどうかは、globalSettings で個別に指定できます。

vim.g.firenvim_config = {
    globalSettings = {
      ['<C-w>'] = 'noop',
      ['<C-n>'] = 'default'
    }
}

Firenvim専用の表示設定と設定プロファイルの分離

起動元の判定とUIの簡略化

Firenvimから起動されたNeovimには g:started_by_firenvim が設定されます。テキストエリアの高さは数行しかないことが多く、ステータスラインを畳めばその分だけ編集に使える行が増えます。

if vim.g.started_by_firenvim == true then
  vim.o.laststatus = 0
else
  vim.o.laststatus = 2
end

常駐しているNeovimに後からFirenvimが接続する構成では、この変数では判定できません。その場合は UIEnter 自動コマンドでクライアント名を見ます。切断側は UILeave です。

vim.api.nvim_create_autocmd({'UIEnter'}, {
    callback = function(event)
        local client = vim.api.nvim_get_chan_info(vim.v.event.chan).client
        if client ~= nil and client.name == "Firenvim" then
            vim.o.laststatus = 0
        end
    end
})

NVIM_APPNAMEによる設定の完全分離

分岐が増えて init.lua が読みづらくなってきたら、設定ツリーごと分ける手があります。2023年2月17日以降のNeovim nightlyビルドで使えるようになった NVIM_APPNAME を設定した状態で firenvim#install() を実行すると、Firenvim専用の設定ディレクトリを持たせられます。環境変数の設定方法そのものは環境変数とは?Linux・Windowsの設定方法とDocker・CIでの受け渡しで扱っています。

ページのフォーカス操作とJavaScript評価

Neovim側から呼べる関数がいくつか用意されています。firenvim#focus_page() と firenvim#focus_input() はフォーカスをページ側へ戻し、firenvim#hide_frame() はフレームを一時的に隠します。firenvim#eval_js() はページ上でJavaScript式を評価しますが、CSPで評価を禁じているページでは回避できません。チャットアプリで <CR> を送信キーとして扱わせる firenvim#press_keys() は、Slackのように反応しないサイトがあるとREADMEに明記されています。

起動しないときの切り分け手順

「テキストエリアをクリックしても何も起きない」場合は、付属の TROUBLESHOOTING.md を基に、マニフェストの作成、起動スクリプトの単体動作、サンドボックスとWebSocketの通信制限、環境変数の順で確認します。これで切り分かないときは、selector や takeover の設定が対象要素に合っているかも見直してください。

手順1:ネイティブマニフェストの作成確認

Neovimを引数なしで起動し、call firenvim#install(0) を実行して出力を読みます。「Installed native manifest for <ブラウザ名>」が出れば正常です。「No config detected for <ブラウザ名>. Skipping.」が出た場合は、そのブラウザの設定ディレクトリを検出できていません。インストールしていないブラウザについても出る通知なので、まず使いたいブラウザ名が含まれているかを確認します。含まれていれば、設定ディレクトリが標準の場所にないのが原因です。対処は標準の場所へシンボリックリンクを張るか、firenvim#install(1) で強制インストールしてから既定ディレクトリの内容を実際のディレクトリへコピーします。

「Unknown function: firenvim#install」が出るならプラグイン自体が読み込まれていません。この場合はプラグインマネージャーの設定を疑います。

手順2:起動スクリプトの単体実行

インストールで作られたスクリプトが単体で動くかを確認します。

echo 'abcde{}' | ${XDG_DATA_HOME:-${HOME}/.local/share}/firenvim/firenvim

Firenvimプラグインのバージョンを含むJSONが返れば正常です。何も返らない場合はスクリプトの存在と実行権限に加えて、そのスクリプトから起動されるNeovimがエラーで落ちていないかも確認します。パーミッションエラーなら実行権限の問題です。あわせて、ブラウザ設定ディレクトリに置かれたマニフェストの path がこのスクリプトを指しているかも確認します。

手順3:Snap・Flatpakによる起動制限の確認

ブラウザをSnapやFlatpakで入れている場合、サンドボックスの制限でNeovimの起動がブロックされることがあります。次のコマンドで権限を確認できます。

flatpak permissions webextensions

ブロックされていれば flatpak permission-set webextensions firenvim snap.firefox yes で許可します。ディストリビューションの標準がSnap版Firefoxになっている環境では、この一手だけで解決することがあります。

手順4:Local Network AccessによるWebSocket遮断の確認

これは比較的新しい症状です。最近のFirefoxとLibreWolfはWebSocketにもLocal Network Access(LNA)のチェックを効かせるため、Firenvimが使う ws://127.0.0.1 への接続が通りません。見え方が独特で、Firenvimのフレームが一瞬だけ表示されてすぐ閉じます。ブラウザコンソールにはcan't establish a connection to the server at ws://127.0.0.1:<port>が出ます。

network.lna.local-network-to-localhost.skip-checks では解決しません。拡張のページ(moz-extension://)がプライベートネットワークのオリジンとして扱われないためです。現状の回避策は about:config で network.lna.websocket.enabled を false にする方法だけですが、これは全WebSocket接続からLNA保護を外す操作です。任意のサイトがWebSocket経由で 127.0.0.1 やプライベートIPを走査できるようになるため、業務端末で恒常的に無効化するのは勧めません。Firenvimを使うプロファイルを分けるほうが現実的です。

手順5:macOSでのシェルとの$PATH差異の確認

起動はするのに外部コマンドに依存するプラグインだけが動かない場合、ブラウザの $PATH が空にされている可能性があります。シェルでの echo $PATH とFirenvim内での :!echo $PATH を比べ、違っていればプロローグ付きでインストールし直します。

nvim --headless -c "call firenvim#install(0, 'export PATH=\"$PATH\"')" -c quit

なお、Firenvimが初期化を終える前の init.vim では echo も echom も使えません。デバッグ出力を見たいときは UIEnter 後にまとめて表示するか、echoerr に切り替えて標準エラー出力をファイルへリダイレクトします。

日本語入力とChrome・Firefoxの実装差

Chrome・FirefoxのIME合成イベント処理の違い

日本語入力が絡む不具合は、Firenvimのキー処理がブラウザの合成イベント(composition event)に依存していることが背景にあります。src/KeyHandler.ts のコメントは、中国語のPinyin入力で漢字1文字を確定させたときの観測結果を記録しています。Firefoxでは compositionstart、input(変換中の文字)、compositionend、input(確定結果)の順にイベントが届きます。ところがChromeでは compositionstart、input、input、compositionend の順になり、確定結果のinputイベントでも isComposing が真のままになります。

そのためFirenvimはChromeでのみ compositionend にリスナーを追加して辻褄を合わせています。日本語入力まわりの処理はこの分岐を通るため、ブラウザによって再現するかどうかが変わり得ます。バグ報告や切り分けの際は、必ずブラウザ名とバージョンをセットで扱ってください。

NeovimのSKKプラグインへ寄せる選択肢

OSのIMEを介さず、Neovim側で日本語入力を完結させる構成もとれます。代表例が skkeleton で、SKK方式の入力をVim/Neovim上で実装しています。denops.vimに依存しており、skkeletonのREADMEはDeno 1.42.0以上を要件としています。依存先のdenops.vimにも要件があり、現行mainは起動時に has('nvim-0.11.3') を検査してNeovim 0.11.3以上(Vimなら9.1.1646以上)を求めます。Firenvim単体の下限である0.6.0では足りない点に注意してください。Firenvimと組み合わせると、ブラウザのどの入力欄でも同じ辞書・同じキーバインドで日本語を打てる状態になります。

ただし導入コストは小さくありません。SKKの入力方式自体に慣れが要るうえ、Denoランタイムの管理も増えます。既存のIMEで支障が出ていないなら移行する理由は薄く、複数マシンで辞書を統一したい、あるいはIMEの変換候補ウィンドウがFirenvimのフレームと干渉するといった具体的な不満があるときに検討する類のものです。denops.vimの仕組みはdenops.vimとは|DenoでVim/Neovimプラグインを書く仕組みと始め方で解説しています。

よくある質問

Firenvimはどのブラウザで使えますか?

公式にテストされているのはFirefoxとChromeです。Brave、Vivaldi、Opera、ArcなどのChromium系ブラウザについて、READMEは動作するはずだとしつつ個別にはテストしていないと明記しています。Microsoft Edgeも同様で、インストールスクリプトにEdge向けのマニフェスト配置とレジストリキー作成の処理はありますが、READMEの表現は「動くかもしれない」にとどまります。SafariはFirenvimの対象外です。

Neovimのバージョンはいくつ必要ですか?

0.6.0以上です。firenvim#install() が has('nvim-0.6.0') で判定し、満たさない場合はエラーを出して処理を中断します。付属ドキュメントには0.4.0と書かれた箇所がありますが、これは実装の変更に追随していない古い記述です。

Chromeウェブストアで拡張を入れただけでは動かないのはなぜですか?

拡張はローカルのNeovimの起動と編集画面の描画を担いますが、起動方法を記したネイティブメッセージングマニフェストはNeovim側の firenvim#install() が作成するためです。プラグイン導入後にこのスクリプトを一度も実行していないと、拡張は接続先を見つけられません。

特定のサイトだけFirenvimを無効にできますか?

localSettings に対象サイトのURL正規表現を追加し、takeover = 'never' と、既定パターンより大きい priority を指定します。一時的に止めるだけなら、URLバー横のFirenvimボタンを押すか toggle_firenvim コマンドにショートカットを割り当てる方法もあります。

編集内容を自動でページへ反映できますか?

TextChanged と TextChangedI で write を呼べば可能です。ただしバッファが大きいと遅くなるため、READMEはタイマーで書き込みを間引く実装も併記しています。

関連記事

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

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

資料請求

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

  1. 2026.09.28 テックブログ タイムズカーの不正アクセスと約660万件の流出|免許証画像を退会者まで残さない保管設計
  2. 2026.09.25 コラム 最低賃金引き上げ【令和8年度】47都道府県の改定額・発効日と企業の対応手順
  3. 2026.09.25 コラム 障害者雇用の助成金一覧:月いくら・支給要件と申請書類を勤怠データで揃える方法
  4. 2026.09.28 テックブログ anthropic skillsとは?公式19スキルの中身とClaude Code・APIでの導入手順
  5. 2026.09.05 コラム 犯罪収益移転防止法の本人確認:2027年4月の対面IC読み取り義務化と改修要件

RELATED POSTS 関連記事

目次