今更だけどvim-fernを本気で設定する(on Neovim)
10年来、NeovimとVSCodeを併用していましたが、ここのところNeovimの設定欲が高まっており、あれこれ設定した結果Neovimに片寄せできる目処が立ってきました。残りの設定すべき事項を整理したところ、ファイルツリーの設定がまだだったことに気が付いたため、前々から入れるだけ入れて設定してこなかったvim-fernを本気で設定してみました。

一通り設定した筆者のvim-fern(フォーカスによって背景色やカーソルラインの色が変わる)
vim-fernとは
vim-fernはdenops,fall.vim,vim-ginでおなじみのありすえさんによるファイラプラグインです。Vimscriptのみで書かれており、Vim/Neovimに両対応しているのが特徴です。豊富な機能を有していますが、ドキュメントやREADMEでの解説、Tipsなどが非常に充実しており、設定可能性と設定難易度を両立している非常に使い勝手のいいファイラです。
ありすえさんによる各ファイラの比較記事は以下のとおりです。5年前の記事ですが、今でも参考になります。
LuaからVimscript製プラグインを設定するには
vim-fernはVimscript製なのでLuaによる設定APIを持っていません。というわけで今回は「Luaからどのようにvim-fernを設定するか」という部分に焦点を当てて解説しています。
ちなみに今回の設定を作成するに当たって、以下のycinoさんの設定を参考にさせていただきました(かなり手を加えてしまったので、別物になってしまいましたが...)。
autocmd,keymapなどはLuaAPIが生えているため、覚えておくべき知識はVimscriptの変数と関数にアクセスする記法だけです。どちらもテーブル経由でアクセスできます。
-- 変数アクセス
vim.g["foo"] = true
-- 関数呼び出し
vim.fn["bar"]("baz")
vim-fernはどのように設定するか
vim-fernではautocmdベースでイベントを設定していきます。一見素朴なアプローチですが、autocmdとvim-fernのAPIが非常に強力なため、実際のところかなり深い部分まで挙動をカスタムできます。本記事ではautocmdを以下のように使い分けます。
-
FileType: ft=fernである場合に絞ることでfernが起動されるたびに呼び出される。on_initのように使える -
User FernHighlight: ハイライト関連の設定。Fernウィンドウが初期化されたり、hover-popup(後述)が表示されるたびに呼び出される -
BufEnter:vim.bo.filetype="fern"で条件分岐することでvim-fernのファイルツリーにウィンドウのフォーカスが入ったことを検知する -
BufLeave:vim.bo.filetype="fern"で条件分岐することでvim-fernのファイルツリーから出たことを検知する
vim-fern独自のautocmdとしてFernHighlightというハイライト用のautocmdがあります。
このautocmdは以前まではhover-popupと呼ばれる、ファイル長がオーバーフローしたときにファイル名をプレビューするためのウィンドウでは呼ばれなかったのですが、今日(2025/11/24)のアップデートでhover-popupでも呼ばれるように挙動が変更されました。本記事ではこの最新版を前提として設定していきます。
vim-fernに対する設定
下準備
autocmdを多用するのであらかじめaugroupを定義しておきます。
また、色を使った設定をするため、お好きなカラースキームのパレットを読み込んでおいてください。私はtokyonight派なので、それを使います。加えて、今回の設定ではtokyonightをベースにカスタムしたサブパレットも使います。サブパレットについても各自のカラースキームと相談して、いい感じに設定してください。
さらにハイライトについて名前空間を設定するのでこちらも定義しておきます。
-- augroup
vim.api.nvim_create_augroup('FernMyConf', {})
-- 名前空間
local ns = vim.api.nvim_create_namespace("fern-colors")
-- カラースキーム
local colors = require('tokyonight.colors').setup()
-- 以下のパレットはtokyonightベースなので各自カスタムが必要
local dark_colors = {
blue = "#6180d5",
fg = colors.fg_dark,
bg = "#141621"
}
local light_colors = {
bg = "#212640",
blue = "#9ebbf7"
}
下準備もできたので、いよいよ設定をしていきます!以下では項目ごとに設定の解説を行っていきます。
デフォルトで隠しファイルを表示する
グローバル変数の設定のみです。プラグインマネージャーのconfigとかに記述するのが良いと思います。
vim.g['fern#default_hidden'] = true
fernでvimのカーソルを隠す
隠しファイルと同じように設定できます。個人的には必須です。
vim.g['fern#hide_cursor'] = true
行番号、foldcolumnとsigncolumnを隠す
行番号とfoldcolumn(折りたたみ関連の情報を出すための列)は基本的に使わないので、BufEnter時に色を変更して、注意喚起するみたいな特殊な使い方をしない限り[1]切ってしまったほうがよいです。signcolumn(エラーとか警告とかを出すための列)については、フォルダ・ファイルを開くときにSpinnerが表示されるのですが、なくても困らなかったので切ってしまっています。
これらはbufferローカルな設定が望ましいのでFileType autocmd中でvim.opt_localで切ります。
vim.api.nvim_create_autocmd('FileType', {
pattern = 'fern',
callback = function(args)
-- 相対と絶対の両方の行番号をオフにする
vim.opt_local.relativenumber = false
vim.opt_local.number = false
vim.opt_local.signcolumn = 'no'
vim.opt_local.foldcolumn = "0"
end
})
vim-fernを開いているウィンドウの上書きを禁止する
ファイルツリーを開いているウィンドウで誤って別のファイルを開いてしまうと、面倒です。そこでファイルツリーのウィンドウのバッファを固定してしまいます。winfixbufは副作用も強い設定(ヘルプファイルとかも開きづらくなる)なのでお好みですが、個人的には設定してあったほうが便利です。
vim.api.nvim_create_autocmd('FileType', {
pattern = 'fern',
callback = function(args)
-- 別のバッファに切り替えない
vim.opt_local.winfixbuf = true
end
})
nvim-web-deviconsを使う
fernの公式の方法だとvimプラグインを使う方法しか載っていませんが、実はnvim-web-deviconsを使えるようにするためのfern-renderer-web-deviconsが存在しています。
これを依存プラグインに入れて、変数を設定してあげるとnvim-web-deviconsを使うことができます。
以下はvim-jetpackを使っている場合の設定です。
{ 'lambdalisue/fern.vim',
requires = {
{ 'lambdalisue/vim-glyph-palette' },
{ 'TheLeoP/fern-renderer-web-devicons.nvim' },
},
config = function()
vim.g['fern#renderer'] = 'nvim-web-devicons'
vim.g['glyph_palette#palette'] = require 'fr-web-icons'.palette()
vim.fn['glyph_palette#apply']()
vim.api.nvim_create_autocmd('FileType', {
pattern = 'fern',
callback = function(args)
-- 中略
-- FileTypeで再度applyする(再起動時に反映させるため)
vim.fn['glyph_palette#apply']()
end
})
end
}
フローティングウィンドウでプレビューする
デフォルトでもプレビュー機能がありますがフローティングウィンドウを使った方がコンパクトで見やすく感じます。fern-preview.vimを使うことでこの機能を実現できます。
下記の設定ではpキーを押すとプレビューをトグルできます。私は設定していないですが、プレビュー内でスクロールすることもできます。
{ 'lambdalisue/fern.vim',
requires = {
{ 'lambdalisue/vim-glyph-palette' },
{ 'TheLeoP/fern-renderer-web-devicons.nvim' },
{ 'lambdalisue/vim-fern-git-status' },
{ 'yuki-yano/fern-preview.vim' } -- フローティングウィンドウには必須
},
config = function ()
-- (中略)
vim.api.nvim_create_autocmd('FileType', {
pattern = 'fern',
callback = function(args)
-- (中略)
vim.keymap.set('n', 'p', '<Plug>(fern-action-preview:auto:toggle)', { buffer = true })
end
})
end
}
ツリースタイルを便利にするキーバインド
fernの設定からはやや脱線しますが、本記事で扱っているようなツリースタイル(サイドバースタイル、drawerスタイル)のファイラだと、垂直分割されたウィンドウを素早く移動できるキーバインドがあるとめちゃくちゃ便利です。筆者はNormalモードのインデント調整機能をほとんど使っていないため(Visualモードで調整してしまうため)<,>にそれぞれ<C-W>h,<C-W>lを割り当ててしまっています。
Normalのインデント調整を普段使いするのであれば、<leader>をプリフィックスにする、修飾キーで設定する、などの工夫が必要ですが、いずれにしても<C-W>プリフィックスより簡潔な垂直分割移動キーマップを設定することで操作感を大幅に改善できます。
vim.keymap.set("n", ">", "<C-W>l")
vim.keymap.set("n", "<", "<C-W>h")
--- もしくは
vim.keymap.set("n", "<leader>l", "<C-W>l")
vim.keymap.set("n", "<leader>h", "<C-W>h")
vim.keymap.set("n", "<leader>>", "<C-W>l")
vim.keymap.set("n", "<leader><", "<C-W>h")
--- など
フォルダの色を他のファイラと合わせる
oil.nvimなど、多くのファイラではフォルダにおいてはDirectoryハイライトを参照します。ところが、vim-fernではFernBranchSymbol,FernBranchTextという独自ハイライトになっています。複数のファイラを使い分ける場合には統一されていたほうが見やすいと思うので、ハイライトグループをリンクします。hover-popupでも機能してほしいので、UserautocmdのFernHighlightを使います。
最近のNeovimではローカルなハイライトを定義するために、名前空間を経由する洒落た方法が使えるため今回はそちらを使っています。
vim.api.nvim_create_autocmd('User', {
group = 'FernMyConf',
pattern = "FernHighlight",
callback = function()
-- 0,すなわちカレントバッファのみに名前空間を適用する
-- hover-popup作成時にも呼び出されるためうまく適用される
vim.api.nvim_win_set_hl_ns(0, ns)
vim.api.nvim_set_hl(ns, 'FernBranchSymbol', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernBranchText', { link = 'Directory' })
end
})
fernのカーソルラインの色を変更する
FernHighlightで通常画面もhover-popupも起動時に反映されるため、単に名前空間で対象を限定してCursorLineにハイライトを当てるだけです。
vim.api.nvim_create_autocmd('User', {
group = 'FernMyConf',
pattern = "FernHighlight",
callback = function()
vim.api.nvim_win_set_hl_ns(0, ns)
-- fernのみカーソルを青色にする
vim.api.nvim_set_hl(ns, 'CursorLine', {
fg = colors.bg,
bg = colors.blue,
})
end
})
Neovimの起動時にvim-fernも自動起動する
VimEnterで起動してあげるだけでいけます。指定したファイルがない場合にはvim-fernに自動でフォーカスがあたり、ファイルが渡された場合にはファイルにフォーカスが当たった状態でスタートします。ちなみにnestedを設定しないとfernが正常に起動されないので、必ずnestedを設定してください。
-- -stayによってfernではなくいきなりファイルにフォーカスが当たった状態になる
vim.api.nvim_create_autocmd('VimEnter', {
group = 'FernMyConf',
nested = true, -- 必須
callback = function(args)
if vim.fn.argc() > 0 then
vim.cmd [[Fern . -reveal=% -drawer -toggle -stay]]
else
vim.cmd [[Fern . -reveal=% -drawer -toggle]]
end
end
})
編集しているファイルが常にfernでフォーカスされるようにする
VSCodeだと開き方にかかわらず、新しいファイルを開くたびにツリーのフォーカスが現在開いているファイルに移動します。今編集しているファイルの位置を直感的に把握できるため非常に便利な機能です。vim-fernでも設定することでこの機能を実装することができます。
動作としては新しいBufferが読まれるたびにrevealでフォーカス位置を変更しているだけです。-stayを加えることで開いたファイルから移動しないようにしています。ちなみに-stayをつけない場合にはLSP関連でエラーが発生する可能性があるので、vim.scheduleで囲う必要があります。(もっともその場合編集しているウィンドウがfernに移ってしまうため、あまり意味がない)
vim.api.nvim_create_autocmd('BufRead', {
group = 'FernMyConf',
nested = true, -- 必須
callback = function()
if vim.bo.filetype ~= "fern" and vim.bo.buftype == "" then
vim.cmd [[Fern . -reveal=% -drawer -stay]]
end
end
})
外部のファイル操作をfernに自動で反映する
ファイルツリーとしてはvim-fernを使っていますが、ディレクトリ・ファイルのリネーム・移動・削除などにはoil.nvimを使っています。oil.nvimは
- バッファを編集する感覚でディレクトリを編集できる
- LSPと連携して自動でインポートを調整してくれる
というメリットがあるためvim-fernと使い分ける形になっています。またこれ以外にgitで変更をdiscardする、ターミナルでファイルを削除する、といったことも考えられます。このようにfernの外部でファイル操作することも多々あるわけですが、ここで問題となるのがfernの表示と実際の状態の同期です。<F5>でreloadすればよいのですが、外部で操作するたびに<F5>を押すのも面倒です。
そこでBufEnterを使って、fernに戻ってくるたびに自動で<F5>キーを送信するようにします。
バッファに入るたびに毎回<F5>を送信しても問題はないのですが、自分の場合は画面がちらつくのが気になったので、グローバル変数should_reload_fernを定義し、gitやoil.nvimなどのファイル操作が行われる可能性が高い操作のときだけ、リロードするようにしています。
vim.api.nvim_create_autocmd('BufEnter', {
group = 'FernMyConf',
callback = function(args)
if vim.bo.filetype == "fern" then
if vim.g["should_reload_fern"] then
vim.g["should_reload_fern"] = false
vim.api.nvim_input("<F5>")
end
end
end
})
-- oil.nvimの例
vim.keymap.set('n', 'O', function()
vim.g['should_reload_fern'] = true
vim.cmd([[Oil --float ]])
end, { buffer = true })
フォーカスしているウィンドウが編集中のコードかfernかをわかりやすくする
デフォルトだと、今触っているウィンドウが編集中のコードなのか、それともfernなのか見た目的にわかりづらいです(これはtree系全般に言えることですが)。色々試してみた結果、
- 背景色と文字色をfernを触っているときは明るめに、それ以外を触っているときは暗めにする
- ルートテキストをfernを触っているときだけ黄色にする
という設定を行うことで視覚的にわかりやすくなるように感じました。また、CursorLineについてもfernの外側を触っているときには半透明っぽくしています。
操作感が上がる上に、見栄えもいいため個人的には気に入っている設定です。
vim.api.nvim_create_autocmd('BufEnter', {
group = 'FernMyConf',
callback = function(args)
if vim.bo.filetype == "fern" then
vim.api.nvim_win_set_hl_ns(0, ns)
vim.api.nvim_set_hl(ns, 'FernRootSymbol', { fg = colors.yellow })
vim.api.nvim_set_hl(ns, 'FernRootText', { fg = colors.yellow })
vim.api.nvim_set_hl(ns, 'FernBranchSymbol', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernBranchText', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernLeafText', { fg = colors.fg })
vim.api.nvim_set_hl(ns, 'Normal', {
bg = light_colors.bg,
})
vim.api.nvim_set_hl(ns, 'FoldColumn', {
bg = colors.blue,
})
vim.api.nvim_set_hl(ns, 'EndOfBuffer', {
bg = light_colors.bg,
fg = light_colors.bg
})
vim.api.nvim_set_hl(ns, 'CursorLine', {
fg = colors.bg,
bg = colors.blue,
})
end
end
})
vim.api.nvim_create_autocmd('BufLeave', {
group = 'FernMyConf',
callback = function(args)
if vim.bo.filetype == "fern" then
vim.api.nvim_set_hl(ns, 'FernRootSymbol', { fg = colors.fg_dark })
vim.api.nvim_set_hl(ns, 'FernRootText', { fg = colors.fg_dark })
vim.api.nvim_set_hl(ns, 'FernBranchSymbol', { fg = dark_colors.blue })
vim.api.nvim_set_hl(ns, 'FernBranchText', { fg = dark_colors.blue })
vim.api.nvim_set_hl(ns, 'FernLeafText', { fg = dark_colors.fg })
vim.api.nvim_win_set_hl_ns(0, ns)
vim.api.nvim_set_hl(ns, 'Normal', {
bg = dark_colors.bg,
})
vim.api.nvim_set_hl(ns, 'EndOfBuffer', {
bg = dark_colors.bg,
fg = dark_colors.bg
})
vim.api.nvim_set_hl(ns, 'CursorLine', {
bg = colors.comment,
fg = colors.fg
})
end
end
})
vim.api.nvim_create_autocmd('User', {
group = 'FernMyConf',
pattern = "FernHighlight",
callback = function()
vim.api.nvim_win_set_hl_ns(0, ns)
vim.api.nvim_set_hl(ns, 'CursorLine', {
fg = colors.bg,
bg = colors.blue,
})
vim.api.nvim_set_hl(ns, 'FernBranchSymbol', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernBranchText', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernRootSymbol', { fg = colors.fg_dark })
vim.api.nvim_set_hl(ns, 'FernRootText', { fg = colors.fg_dark })
end
})
まとめ
今回はvim-fernの設定をいくつか紹介してみました。vim-fernはデフォルトでも使えてしまうので、あんまり設定していないという人も多いかも知れませんが、設定を加えることでさらに便利になります。設定可能性が非常に高いプラグインですので、みなさんも極限まで設定してみて、自分好みのvim-fernを作ってみてください。
最後に筆者の現在の設定を一通り掲載しておきます。
現状での筆者の設定(vim-jetpackベース)
{ 'lambdalisue/fern.vim',
requires = {
{ 'lambdalisue/vim-glyph-palette' },
{ 'TheLeoP/fern-renderer-web-devicons.nvim' },
{ 'lambdalisue/vim-fern-git-status' },
{ 'sirasagi62/toggle-cheatsheet.nvim' }
},
config = function()
local colors = require('tokyonight.colors').setup()
vim.g['fern#renderer'] = 'nvim-web-devicons'
vim.g["glyph_palette#palette"] = require 'fr-web-icons'.palette()
vim.cmd [[
call glyph_palette#apply()
]]
vim.g['fern#hide_cursor'] = true
vim.g['fern#default_hidden'] = true
vim.keymap.set("n", ">", "<C-W>l")
vim.keymap.set("n", "<", "<C-W>h")
-- autocmd to enter fern
vim.api.nvim_create_autocmd('FileType', {
pattern = 'fern',
callback = function(args)
vim.opt_local.relativenumber = false
vim.opt_local.number = false
vim.opt_local.signcolumn = 'no'
vim.opt_local.foldcolumn = "0"
-- 別のバッファに切り替えない
vim.opt_local.winfixbuf = true
local toggle_help = function()
local tcs = require('toggle-cheatsheet').setup(true)
local raw_help = vim.fn['fern#action#list']()
local help = {}
for _, h in ipairs(raw_help) do
if type(h[1]) == "string" and h[1] ~= "" and h[1]:match("<Plug>") == nil then
table.insert(help, h)
end
end
local cs = tcs.createCheatSheetFromSubmodeKeymap(
tcs.conf(help)
)
tcs.toggle(cs)
end
vim.keymap.set('n', 'p', '<Plug>(fern-action-preview:toggle)', { buffer = true })
vim.keymap.set('n', '??', toggle_help, { buffer = true, remap = true })
vim.keymap.set('n', 'l', '<Plug>(fern-action-open-or-expand)', { buffer = true })
vim.keymap.set('n', '<Enter>', '<Plug>(fern-action-open-or-expand)', { buffer = true })
vim.keymap.set('n', '<left>', '<Plug>(fern-action-collapse)', { buffer = true })
vim.keymap.set('n', '<right>', '<Plug>(fern-action-open-or-expand)', { buffer = true })
end
})
vim.api.nvim_create_augroup('FernMyConf', {})
local ns = vim.api.nvim_create_namespace("fern-colors")
local dark_colors = {
blue = "#6180d5",
fg = colors.fg_dark,
bg = "#141621"
}
local light_colors = {
bg = "#212640",
blue = "#9ebbf7"
}
vim.api.nvim_create_autocmd('BufEnter', {
group = 'FernMyConf',
callback = function(args)
if vim.bo.filetype == "fern" then
vim.api.nvim_win_set_hl_ns(0, ns)
vim.api.nvim_set_hl(ns, 'FernRootSymbol', { fg = colors.yellow })
vim.api.nvim_set_hl(ns, 'FernRootText', { fg = colors.yellow })
vim.api.nvim_set_hl(ns, 'FernBranchSymbol', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernBranchText', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernLeafText', { fg = colors.fg })
vim.api.nvim_set_hl(ns, 'Normal', {
bg = light_colors.bg,
})
vim.api.nvim_set_hl(ns, 'FoldColumn', {
bg = colors.blue,
})
vim.api.nvim_set_hl(ns, 'EndOfBuffer', {
bg = light_colors.bg,
fg = light_colors.bg
})
vim.api.nvim_set_hl(ns, 'CursorLine', {
fg = colors.bg,
bg = colors.blue,
})
is_fern_enable = "NORMAL(VIM-FERN)"
fern_statusline_color = light_colors.blue
require("lualine").refresh()
end
end
})
vim.api.nvim_create_autocmd('BufLeave', {
group = 'FernMyConf',
callback = function(args)
if vim.bo.filetype == "fern" then
vim.api.nvim_set_hl(ns, 'FernRootSymbol', { fg = colors.fg_dark })
vim.api.nvim_set_hl(ns, 'FernRootText', { fg = colors.fg_dark })
vim.api.nvim_set_hl(ns, 'FernBranchSymbol', { fg = dark_colors.blue })
vim.api.nvim_set_hl(ns, 'FernBranchText', { fg = dark_colors.blue })
vim.api.nvim_set_hl(ns, 'FernLeafText', { fg = dark_colors.fg })
vim.api.nvim_win_set_hl_ns(0, ns)
vim.api.nvim_set_hl(ns, 'Normal', {
bg = dark_colors.bg,
})
vim.api.nvim_set_hl(ns, 'EndOfBuffer', {
bg = dark_colors.bg,
fg = dark_colors.bg
})
vim.api.nvim_set_hl(ns, 'CursorLine', {
bg = colors.comment,
fg = colors.fg
})
is_fern_enable = nil
fern_statusline_color = nil
require("lualine").refresh()
require("toggle-cheatsheet").closeCheatSheetWin()
end
end
})
vim.api.nvim_create_autocmd('BufRead', {
group = 'FernMyConf',
nested = true,
callback = function()
if vim.bo.filetype ~= "fern" and vim.bo.buftype == "" then
vim.cmd [[Fern . -reveal=% -drawer -stay]]
end
end
})
vim.api.nvim_create_autocmd('User', {
group = 'FernMyConf',
pattern = "FernHighlight",
callback = function()
vim.api.nvim_win_set_hl_ns(0, ns)
vim.api.nvim_set_hl(ns, 'CursorLine', {
fg = colors.bg,
bg = colors.blue,
})
vim.api.nvim_set_hl(ns, 'FernBranchSymbol', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernBranchText', { link = 'Directory' })
vim.api.nvim_set_hl(ns, 'FernRootSymbol', { fg = colors.fg_dark })
vim.api.nvim_set_hl(ns, 'FernRootText', { fg = colors.fg_dark })
end
})
vim.api.nvim_create_autocmd('VimEnter', {
group = 'FernMyConf',
nested = true,
callback = function(args)
if vim.fn.argc() > 0 then
vim.cmd [[Fern . -reveal=% -drawer -toggle -stay]]
else
vim.cmd [[Fern . -reveal=% -drawer -toggle]]
end
end
})
end
},
-
1回試しましたが、ピンとこなかったので見送りました。人によってはアリかもです。 ↩︎
Discussion