本文记录一套 FreeBSD 15 专用的 Neovim 生产力环境部署流程:先用 FreeBSD 的 pkg 准备编译环境,再从源码安装 Neovim 0.10+,最后写入一份基于 lazy.nvim 的 Lua 配置。
这套配置适合 FreeBSD 服务器、虚拟机和桌面环境使用,重点解决日常编辑里最常见的几件事:文件树、主题、远程 SSH 复制、保存时格式化、JSON/YAML/HTML/CSS/JavaScript/Markdown/Lua 格式化。
一、安装系统依赖并编译 Neovim
FreeBSD 使用 pkg 管理二进制包。编译 Neovim 时建议使用 gmake,不要直接用系统自带的 make。
下面命令需要 root 权限。如果当前不是 root 用户,请在命令前加 doas 或 sudo。
# 强制刷新仓库索引,并升级已安装软件包
pkg update -f
pkg upgrade -y
# 安装源码编译 Neovim 所需工具
pkg install -y ninja cmake gettext-tools gmake unzip curl git gcc然后拉取 Neovim 源码并切换到稳定分支:
git clone https://github.com/neovim/neovim.git ~/neovim-src
cd ~/neovim-src
git checkout stable开始编译并安装:
gmake CMAKE_BUILD_TYPE=Release -j$(sysctl -n hw.ncpu)
gmake install安装后刷新 shell 命令缓存并验证版本:
hash -r
which nvim
nvim -v如果之前通过 pkg 安装过旧版 Neovim,重点看 which nvim 的输出,确认当前优先调用的是刚编译安装的新版本。
二、安装格式化工具
这份配置使用 conform.nvim 统一调度外部格式化工具。FreeBSD 上可以直接安装 jq 和 stylua,Node.js 与 npm 使用带版本号的包名。
pkg install -y node20 npm-node20 jq stylua
npm install -g prettier安装完成后统一验证:
node -v
npm -v
jq --version
stylua --version
prettier -v其中:
jq用于格式化 JSON。stylua用于格式化 Lua。prettier用于格式化 YAML、HTML、CSS、JavaScript、TypeScript 和 Markdown。
三、写入统一 Lua 配置
Neovim 在 FreeBSD 下仍然使用 ~/.config/nvim/init.lua 作为用户配置文件。
远程 SSH 终端里不要直接粘贴超长 cat << EOF 脚本,容易出现粘贴错乱。建议先运行短命令清理旧配置并打开文件:
# 清理旧环境
rm -rf ~/.config/nvim ~/.local/share/nvim ~/.local/state/nvim ~/.cache/nvim
# 创建配置目录并打开 init.lua
mkdir -p ~/.config/nvim
nvim ~/.config/nvim/init.lua进入空文件后按 i 进入插入模式,复制下面这段纯 Lua 配置粘贴进去,然后按 Esc,输入 :wq 保存退出。
-- Basic settings
vim.g.mapleader = ";"
local opt = vim.opt
opt.number = true
opt.relativenumber = true
opt.cursorline = true
opt.termguicolors = true
opt.encoding = "utf-8"
opt.mouse = "a"
opt.timeoutlen = 800
opt.splitright = true
opt.splitbelow = true
-- Install lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
if not (vim.uv or vim.loop).fs_stat(lazypath) then
vim.fn.system({
"git",
"clone",
"--filter=blob:none",
"https://github.com/folke/lazy.nvim.git",
"--branch=stable",
lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
-- Plugins
require("lazy").setup({
spec = {
-- 1. Theme
{
"navarasu/onedark.nvim",
lazy = false,
priority = 1000,
config = function()
require("onedark").setup({ style = "dark" })
require("onedark").load()
local hl = vim.api.nvim_set_hl
hl(0, "Normal", { bg = "#1e2127", fg = "#abb2bf" })
hl(0, "NonText", { bg = "#1e2127", fg = "#3b4048" })
hl(0, "CursorLine", { bg = "#2c313a" })
hl(0, "Visual", { bg = "#3e4452" })
hl(0, "Search", { bg = "#61afef", fg = "#1e2127" })
hl(0, "IncSearch", { bg = "#98c379", fg = "#1e2127" })
end,
},
-- 2. File tree
{
"nvim-tree/nvim-tree.lua",
dependencies = { "nvim-tree/nvim-web-devicons" },
config = function()
require("nvim-tree").setup({
view = { width = 30, side = "left" },
renderer = { indent_markers = { enable = true } },
})
end,
},
-- 3. Remote clipboard over SSH
{
"ojroques/nvim-osc52",
config = function()
require("osc52").setup()
vim.api.nvim_create_autocmd("TextYankPost", {
callback = function()
if vim.v.event.operator == "y" and vim.v.event.regname == "" then
require("osc52").copy_register("")
end
end,
})
end,
},
-- 4. Formatting
{
"stevearc/conform.nvim",
lazy = false,
config = function()
require("conform").setup({
formatters_by_ft = {
lua = { "stylua" },
json = { "jq" },
yaml = { "prettier" },
html = { "prettier" },
css = { "prettier" },
javascript = { "prettier" },
typescript = { "prettier" },
markdown = { "prettier" },
},
formatters = {
jq = {
command = "jq",
args = { "--indent", "2", "." },
stdin = true,
},
},
format_on_save = {
timeout_ms = 3000,
lsp_format = "fallback",
},
notify_on_error = true,
})
end,
},
},
})
-- Keymaps
local map = vim.keymap.set
map("n", "<leader>w", ":w<CR>", { silent = true })
map("n", "<leader>q", ":q<CR>", { silent = true })
map("n", "<CR>", ":noh<CR><CR>", { silent = true })
-- Toggle or focus file tree: ;t
map("n", "<leader>t", function()
local view = require("nvim-tree.view")
if view.is_visible() then
if vim.api.nvim_get_current_win() == view.get_winnr() then
vim.cmd("wincmd p")
else
vim.cmd("NvimTreeFocus")
end
else
vim.cmd("NvimTreeToggle")
end
end, { silent = true })
-- Format manually: ;f
map({ "n", "v" }, "<leader>f", function()
require("conform").format({
async = false,
timeout_ms = 3000,
lsp_format = "fallback",
}, function(err)
if err then
vim.notify("Format failed: " .. err, vim.log.levels.ERROR)
else
vim.notify("Format done", vim.log.levels.INFO)
end
end)
end, { silent = true })
-- Direct format shortcut: F3
map({ "n", "v" }, "<F3>", function()
require("conform").format({
async = false,
timeout_ms = 3000,
lsp_format = "fallback",
}, function(err)
if err then
vim.notify("Format failed: " .. err, vim.log.levels.ERROR)
else
vim.notify("Format done", vim.log.levels.INFO)
end
end)
end, { silent = true })
-- Window navigation
map("n", "<C-h>", "<C-w>h")
map("n", "<C-l>", "<C-w>l")
map("n", "<C-j>", "<C-w>j")
map("n", "<C-k>", "<C-w>k")四、首次启动 Neovim
执行:
nvim首次启动时,lazy.nvim 会自动拉取并安装插件。配置正常时,插件管理界面里应该能看到这些插件处于可用状态:
conform.nvimnvim-osc52nvim-tree.luanvim-web-deviconsonedark.nvim
如果 lazy.nvim 提示有更新,可以在插件管理界面按 U 更新。确认插件安装完成后,按 q 退出管理界面,开始正常使用 Neovim。
如果 GitHub 访问不稳定,插件下载可能失败。确认网络后重新打开 Neovim,或者在 Neovim 内执行:
:Lazy sync五、验证格式化功能
先测试 JSON 格式化:
nvim test.json写入一行未格式化内容:
{"name":"freebsd","version":"15","editor":"neovim"}回到普通模式后输入:
;f如果配置正常,文件会被 jq 展开成多行:
{
"name": "freebsd",
"version": "15",
"editor": "neovim"
}也可以输入:
:w保存时 conform.nvim 会自动执行格式化。如果 JSON 本身有语法错误,例如多了逗号或少了引号,jq 会拒绝格式化,这也正好能顺手检查配置文件是否写坏。
查看当前 buffer 可用 formatter:
:ConformInfo注意::ConformInfo 只负责查看当前 buffer 的 formatter 状态,不会执行格式化。看到类似下面的状态,说明 jq 已经可用:
jq ready (json) /usr/local/bin/jq真正执行格式化需要在普通模式下按 ;f、按 F3,或者保存文件:
:w如果想绕过快捷键直接测试 jq,也可以执行:
:lua require("conform").format({ formatters = { "jq" }, async = false, timeout_ms = 3000 })如果 JSON 格式化没有反应,先检查 jq 是否可用:
which jq
jq --version如果 Lua 格式化没有反应,检查 stylua:
which stylua
stylua --version如果 YAML、HTML、CSS、JavaScript、TypeScript 或 Markdown 格式化没有反应,再检查 prettier:
which prettier
prettier -v六、常用快捷键
;t:文件树与代码窗口智能切换。;f:格式化当前文件或选中范围。F3:格式化当前文件或选中范围。;w:保存当前文件,并触发保存时格式化。;q:退出当前窗口。Ctrl+h/j/k/l:在窗口之间移动。Enter:清除搜索高亮。
七、FreeBSD 15 使用注意事项
FreeBSD 的第三方软件一般安装在 /usr/local 下,所以 jq、stylua、prettier、nvim 等命令通常会出现在 /usr/local/bin。如果 Neovim 找不到 formatter,先检查当前 shell 的 PATH 是否包含 /usr/local/bin。
如果编译或运行时遇到 ld-elf.so.1 相关动态库错误,优先执行 pkg update -f 和 pkg upgrade -y,确保基础系统、ports 包和依赖库版本一致。
如果是在远程 SSH 里使用复制功能,本地终端需要支持 OSC 52。iTerm2、WezTerm、Kitty、Windows Terminal 等现代终端通常支持,部分终端需要手动开启剪贴板访问。
图标能否正常显示,取决于本地终端字体是否安装 Nerd Fonts,例如 JetBrainsMono Nerd Font。如果没有 Nerd Font,nvim-web-devicons 仍能运行,只是图标可能显示成方块或乱码。
至此,FreeBSD 15 下的 Neovim 现代化编辑环境就部署完成了。后续只需要维护 ~/.config/nvim/init.lua,整套插件和格式化体验就能保持一致。