Neovim on FreeBSD: Modern Text Editor Review
Neovim is Vim's most successful fork. It took Vim's modal editing model, ripped out the legacy code, added Lua scripting, built-in LSP support, and a proper API for GUIs and plugins. On FreeBSD, Neovim runs perfectly -- it is a terminal application with no platform-specific dependencies, so the experience is identical to Linux.
This review covers installation, configuring Neovim with Lua, setting up the Language Server Protocol for real IDE-like features, plugin management, and a comparison with both classic Vim and the newer Helix editor. For a broader comparison of text editors on FreeBSD, see our guide to the best text editors for FreeBSD.
Why Neovim Over Vim
The practical differences that matter:
- Lua configuration: Vim uses VimScript, a language nobody enjoys. Neovim supports Lua natively, which is faster, better documented, and actually pleasant to write.
- Built-in LSP client: Neovim has a native Language Server Protocol client. You get autocompletion, go-to-definition, rename refactoring, and diagnostics without heavyweight plugins.
- Treesitter integration: Syntax highlighting powered by proper parsing instead of regex patterns. The result is more accurate highlighting and structural text objects.
- Async architecture: Plugin operations run asynchronously. No more freezing the editor while a plugin does background work.
- Active development: Neovim has a faster release cadence and more contributors than Vim.
What Neovim does NOT change: the modal editing model, core keybindings, and Vim compatibility layer all work the same. If you know Vim, you know Neovim.
Installation
sh# Install Neovim pkg install neovim # Verify nvim --version
The FreeBSD package typically tracks the latest stable release. As of early 2026, this is Neovim 0.10.x.
Optional but recommended:
sh# Install dependencies for full functionality pkg install git # Required by most plugin managers pkg install ripgrep # Fast search (used by telescope.nvim) pkg install fd-find # Fast file finder (used by telescope.nvim) pkg install node npm # Required by some LSP servers pkg install python3 # Python provider for some plugins pkg install py311-pynvim # Python-Neovim bridge pkg install xclip # Clipboard support (X11) pkg install wl-clipboard # Clipboard support (Wayland)
Directory Structure
Neovim configuration lives in ~/.config/nvim/:
shmkdir -p ~/.config/nvim/lua
The main entry point is ~/.config/nvim/init.lua. Unlike Vim's ~/.vimrc, Neovim uses Lua by default (though it still supports VimScript via init.vim if you prefer).
A clean directory structure:
shell~/.config/nvim/ init.lua -- Entry point lua/ options.lua -- Editor settings keymaps.lua -- Key mappings plugins/ init.lua -- Plugin manager setup lsp.lua -- LSP configuration treesitter.lua -- Treesitter configuration telescope.lua -- Fuzzy finder completion.lua -- Autocompletion
Basic Configuration
Create ~/.config/nvim/init.lua:
shcat > ~/.config/nvim/init.lua << 'EOF' -- Load configuration modules require("options") require("keymaps") require("plugins") EOF
Create ~/.config/nvim/lua/options.lua:
shcat > ~/.config/nvim/lua/options.lua << 'EOF' local opt = vim.opt -- Line numbers opt.number = true opt.relativenumber = true -- Indentation opt.tabstop = 4 opt.shiftwidth = 4 opt.expandtab = true opt.smartindent = true -- Search opt.ignorecase = true opt.smartcase = true opt.hlsearch = true opt.incsearch = true -- Appearance opt.termguicolors = true opt.signcolumn = "yes" opt.cursorline = true opt.scrolloff = 8 opt.sidescrolloff = 8 -- Behavior opt.splitright = true opt.splitbelow = true opt.undofile = true opt.swapfile = false opt.updatetime = 250 opt.timeoutlen = 300 opt.clipboard = "unnamedplus" opt.completeopt = "menuone,noselect" -- Disable netrw (if using a file explorer plugin) vim.g.loaded_netrw = 1 vim.g.loaded_netrwPlugin = 1 EOF
Create ~/.config/nvim/lua/keymaps.lua:
shcat > ~/.config/nvim/lua/keymaps.lua << 'EOF' local map = vim.keymap.set -- Leader key vim.g.mapleader = " " vim.g.maplocalleader = " " -- Better window navigation map("n", "<C-h>", "<C-w>h", { desc = "Move to left window" }) map("n", "<C-j>", "<C-w>j", { desc = "Move to lower window" }) map("n", "<C-k>", "<C-w>k", { desc = "Move to upper window" }) map("n", "<C-l>", "<C-w>l", { desc = "Move to right window" }) -- Buffer navigation map("n", "<S-l>", ":bnext<CR>", { desc = "Next buffer" }) map("n", "<S-h>", ":bprevious<CR>", { desc = "Previous buffer" }) map("n", "<leader>bd", ":bdelete<CR>", { desc = "Delete buffer" }) -- Move lines map("v", "J", ":m '>+1<CR>gv=gv", { desc = "Move line down" }) map("v", "K", ":m '<-2<CR>gv=gv", { desc = "Move line up" }) -- Keep cursor centered map("n", "<C-d>", "<C-d>zz") map("n", "<C-u>", "<C-u>zz") map("n", "n", "nzzzv") map("n", "N", "Nzzzv") -- Clear search highlight map("n", "<Esc>", ":nohlsearch<CR>", { desc = "Clear search highlight" }) -- Quick save map("n", "<leader>w", ":w<CR>", { desc = "Save file" }) map("n", "<leader>q", ":q<CR>", { desc = "Quit" }) EOF
Plugin Management with lazy.nvim
lazy.nvim is the standard plugin manager for modern Neovim. It is fast, supports lazy loading, and has a clean Lua API.
Create ~/.config/nvim/lua/plugins/init.lua:
shmkdir -p ~/.config/nvim/lua/plugins cat > ~/.config/nvim/lua/plugins/init.lua << 'EOF' -- Bootstrap lazy.nvim local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" if not 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) -- Plugin specifications require("lazy").setup({ -- Color scheme { "catppuccin/nvim", name = "catppuccin", priority = 1000, config = function() vim.cmd.colorscheme("catppuccin-mocha") end, }, -- Treesitter { "nvim-treesitter/nvim-treesitter", build = ":TSUpdate", config = function() require("plugins.treesitter") end, }, -- LSP { "neovim/nvim-lspconfig", dependencies = { "williamboman/mason.nvim", "williamboman/mason-lspconfig.nvim", }, config = function() require("plugins.lsp") end, }, -- Autocompletion { "hrsh7th/nvim-cmp", dependencies = { "hrsh7th/cmp-nvim-lsp", "hrsh7th/cmp-buffer", "hrsh7th/cmp-path", "L3MON4D3/LuaSnip", "saadparwaiz1/cmp_luasnip", }, config = function() require("plugins.completion") end, }, -- Fuzzy finder { "nvim-telescope/telescope.nvim", dependencies = { "nvim-lua/plenary.nvim" }, config = function() require("plugins.telescope") end, }, -- File explorer { "nvim-tree/nvim-tree.lua", dependencies = { "nvim-tree/nvim-web-devicons" }, config = function() require("nvim-tree").setup() vim.keymap.set("n", "<leader>e", ":NvimTreeToggle<CR>", { desc = "Toggle file explorer" }) end, }, -- Status line { "nvim-lualine/lualine.nvim", config = function() require("lualine").setup({ options = { theme = "catppuccin" }, }) end, }, -- Git signs { "lewis6991/gitsigns.nvim", config = function() require("gitsigns").setup() end, }, -- Autopairs { "windwp/nvim-autopairs", event = "InsertEnter", config = true, }, -- Comment toggling { "numToStr/Comment.nvim", config = true, }, }) EOF
LSP Configuration
The Language Server Protocol gives Neovim IDE-like features. Create ~/.config/nvim/lua/plugins/lsp.lua:
shcat > ~/.config/nvim/lua/plugins/lsp.lua << 'EOF' -- Mason manages LSP server installations require("mason").setup() require("mason-lspconfig").setup({ ensure_installed = { "lua_ls", -- Lua "clangd", -- C/C++ "pyright", -- Python "rust_analyzer", -- Rust }, }) -- LSP keybindings (active when an LSP server attaches) vim.api.nvim_create_autocmd("LspAttach", { callback = function(event) local map = function(keys, func, desc) vim.keymap.set("n", keys, func, { buffer = event.buf, desc = "LSP: " .. desc }) end map("gd", vim.lsp.buf.definition, "Go to definition") map("gD", vim.lsp.buf.declaration, "Go to declaration") map("gr", vim.lsp.buf.references, "Go to references") map("gi", vim.lsp.buf.implementation, "Go to implementation") map("K", vim.lsp.buf.hover, "Hover documentation") map("<leader>ca", vim.lsp.buf.code_action, "Code action") map("<leader>rn", vim.lsp.buf.rename, "Rename symbol") map("<leader>ds", vim.lsp.buf.document_symbol, "Document symbols") map("[d", vim.diagnostic.goto_prev, "Previous diagnostic") map("]d", vim.diagnostic.goto_next, "Next diagnostic") end, }) -- LSP server configurations local lspconfig = require("lspconfig") local capabilities = require("cmp_nvim_lsp").default_capabilities() lspconfig.lua_ls.setup({ capabilities = capabilities, settings = { Lua = { diagnostics = { globals = { "vim" } }, }, }, }) lspconfig.clangd.setup({ capabilities = capabilities }) lspconfig.pyright.setup({ capabilities = capabilities }) lspconfig.rust_analyzer.setup({ capabilities = capabilities }) EOF
Note on Mason: Mason downloads prebuilt LSP server binaries. Most work on FreeBSD, but some may not have FreeBSD builds. For those, install the LSP server from ports:
sh# If Mason cannot install clangd (it may already be in base) # clangd is part of LLVM, which ships in FreeBSD base which clangd # /usr/bin/clangd on FreeBSD 14.x # For language servers not in Mason, install from ports pkg install rust-analyzer # Rust pkg install py311-python-lsp-server # Python (alternative to pyright)
Treesitter Configuration
Create ~/.config/nvim/lua/plugins/treesitter.lua:
shcat > ~/.config/nvim/lua/plugins/treesitter.lua << 'EOF' require("nvim-treesitter.configs").setup({ ensure_installed = { "c", "cpp", "lua", "python", "rust", "go", "javascript", "typescript", "html", "css", "json", "yaml", "toml", "markdown", "bash", "vim", "vimdoc", "query", }, highlight = { enable = true }, indent = { enable = true }, incremental_selection = { enable = true, keymaps = { init_selection = "<C-space>", node_incremental = "<C-space>", scope_incremental = false, node_decremental = "<bs>", }, }, }) EOF
Treesitter compiles parsers from C source code. This requires a C compiler, which FreeBSD provides in base (cc). No additional setup is needed.
Autocompletion
Create ~/.config/nvim/lua/plugins/completion.lua:
shcat > ~/.config/nvim/lua/plugins/completion.lua << 'EOF' local cmp = require("cmp") local luasnip = require("luasnip") cmp.setup({ snippet = { expand = function(args) luasnip.lsp_expand(args.body) end, }, mapping = cmp.mapping.preset.insert({ ["<C-n>"] = cmp.mapping.select_next_item(), ["<C-p>"] = cmp.mapping.select_prev_item(), ["<C-d>"] = cmp.mapping.scroll_docs(-4), ["<C-f>"] = cmp.mapping.scroll_docs(4), ["<C-Space>"] = cmp.mapping.complete(), ["<CR>"] = cmp.mapping.confirm({ select = true }), ["<Tab>"] = cmp.mapping(function(fallback) if cmp.visible() then cmp.select_next_item() elseif luasnip.expand_or_jumpable() then luasnip.expand_or_jump() else fallback() end end, { "i", "s" }), }), sources = { { name = "nvim_lsp" }, { name = "luasnip" }, { name = "buffer" }, { name = "path" }, }, }) EOF
Telescope (Fuzzy Finder)
Create ~/.config/nvim/lua/plugins/telescope.lua:
shcat > ~/.config/nvim/lua/plugins/telescope.lua << 'EOF' local telescope = require("telescope") local builtin = require("telescope.builtin") telescope.setup({ defaults = { file_ignore_patterns = { "node_modules", ".git/", "*.o", "*.a" }, }, }) -- Keybindings vim.keymap.set("n", "<leader>ff", builtin.find_files, { desc = "Find files" }) vim.keymap.set("n", "<leader>fg", builtin.live_grep, { desc = "Live grep" }) vim.keymap.set("n", "<leader>fb", builtin.buffers, { desc = "Find buffers" }) vim.keymap.set("n", "<leader>fh", builtin.help_tags, { desc = "Help tags" }) vim.keymap.set("n", "<leader>fo", builtin.oldfiles, { desc = "Recent files" }) vim.keymap.set("n", "<leader>fd", builtin.diagnostics, { desc = "Diagnostics" }) EOF
Telescope uses ripgrep and fd for fast searching:
sh# Already installed above, but verify: which rg which fd
FreeBSD-Specific Notes
Clipboard Integration
On FreeBSD, clipboard integration depends on your display server:
sh# X11: install xclip or xsel pkg install xclip # Wayland: install wl-clipboard pkg install wl-clipboard
Neovim detects the clipboard provider automatically. Verify:
shnvim --headless -c "echo has('clipboard')" -c "q" # Or inside Neovim: :checkhealth
Terminal Emulator
For the best Neovim experience on FreeBSD:
- foot: Wayland-native, fast, supports true color
- alacritty: GPU-accelerated, cross-platform
- kitty: Feature-rich, supports Neovim's advanced UI features
shpkg install foot # or alacritty, or kitty
Performance
Neovim on FreeBSD performs identically to Linux. Startup time with a full plugin configuration (lazy-loaded) is typically under 100ms. Treesitter parsing and LSP operations are CPU-bound and show no platform-specific overhead.
sh# Benchmark startup time nvim --startuptime /tmp/nvim-startup.log tail -1 /tmp/nvim-startup.log
Neovim vs Vim on FreeBSD
| Feature | Neovim | Vim |
|---------|--------|-----|
| Configuration language | Lua (and VimScript) | VimScript |
| LSP support | Built-in | Plugin required (CoC, etc.) |
| Treesitter | Built-in | Not available |
| Async operations | Native | Partial (Vim 8+) |
| Plugin ecosystem | Growing rapidly | Massive, established |
| Startup time | Fast (~50-100ms with plugins) | Fast (~30-50ms) |
| FreeBSD availability | pkg install neovim | In base system (vi), full Vim via pkg install vim |
| Terminal emulator | Built-in (:terminal) | Built-in (Vim 8+) |
Choose Neovim if: You want LSP, treesitter, Lua configuration, and a modern plugin ecosystem.
Choose Vim if: Your existing Vim configuration works perfectly and you see no reason to change.
Neovim vs Helix on FreeBSD
Helix is a newer modal editor inspired by Kakoune. It ships with LSP, treesitter, and sensible defaults out of the box -- no plugins needed.
| Feature | Neovim | Helix |
|---------|--------|-------|
| Configuration | Lua, highly extensible | TOML, limited customization |
| Plugin system | Extensive (lazy.nvim, etc.) | None (by design) |
| LSP | Built-in, configurable | Built-in, zero-config |
| Treesitter | Built-in | Built-in |
| Selection model | Verb-object (d + motion) | Selection-first (select then act) |
| Learning curve | Vim keybindings | Different from Vim |
| Customization | Nearly unlimited | Intentionally limited |
| FreeBSD | pkg install neovim | pkg install helix |
Choose Neovim if: You want maximum customization, an extensive plugin ecosystem, or are already proficient with Vim keybindings.
Choose Helix if: You want a batteries-included editor with no configuration needed, and are willing to learn a different keybinding paradigm.
Maintenance
Updating Plugins
sh# Inside Neovim, open lazy.nvim UI: # Press: :Lazy # Then press U to update all plugins
Or from the command line:
shnvim --headless "+Lazy! sync" +qa
Updating Treesitter Parsers
sh# Inside Neovim: # :TSUpdate
Health Check
Run Neovim's built-in health checker to diagnose issues:
shnvim -c ":checkhealth"
This checks clipboard support, Python/Node providers, LSP status, and treesitter parser availability.
FAQ
Does Neovim work on FreeBSD?
Yes. Neovim is available as a binary package (pkg install neovim) and works identically to its Linux version. All features including LSP, treesitter, and the full plugin ecosystem work on FreeBSD.
Can I use my existing Vim configuration with Neovim?
Neovim can read ~/.vimrc and most VimScript plugins work unchanged. However, to use Neovim's full capabilities (Lua API, built-in LSP, treesitter), you should migrate to a Lua-based configuration. The two can coexist during migration.
Do LSP servers work on FreeBSD?
Most LSP servers work on FreeBSD. Servers written in interpreted languages (Python, JavaScript, Lua) work everywhere. Servers distributed as prebuilt binaries (via Mason) may not always have FreeBSD builds. In those cases, install the LSP server from the FreeBSD ports tree.
How much RAM does Neovim use?
Base Neovim uses about 20-30 MB. With a full plugin configuration (LSP, treesitter, telescope, completion), expect 80-150 MB depending on the project size and number of active LSP servers.
Should I use Mason on FreeBSD?
Mason is convenient but not all its binaries work on FreeBSD. A practical approach: use Mason where it works (most Lua, JavaScript, and Python-based servers), and fall back to pkg install for servers that need FreeBSD-native binaries (like clangd, rust-analyzer).
How do I configure Neovim for C/C++ development on FreeBSD?
Install clangd (usually already present in FreeBSD base as part of LLVM), configure it in your LSP setup, and ensure treesitter has the c and cpp parsers installed. The combination of clangd + treesitter provides excellent C/C++ editing with autocompletion, diagnostics, and go-to-definition.
sh# Verify clangd is available which clangd # /usr/bin/clangd (from base system LLVM)