Practical Patterns
Production patterns extracted from domus-nvim. Copy these for your own configurations.
Configuration Patterns
Lazy Plugin Spec
-- lua/plugins/telescope.lua (lazy.nvim format)
return {
"nvim-telescope/telescope.nvim",
branch = "0.1.x",
dependencies = {
"nvim-lua/plenary.nvim",
{ "nvim-telescope/telescope-fzf-native.nvim", build = "make" },
},
cmd = "Telescope", -- Lazy load on command
keys = {
{ "<leader>ff", "<cmd>Telescope find_files<cr>", desc = "Find Files" },
{ "<leader>fg", "<cmd>Telescope live_grep<cr>", desc = "Live Grep" },
{ "<leader>fb", "<cmd>Telescope buffers<cr>", desc = "Buffers" },
{ "<leader>fh", "<cmd>Telescope help_tags<cr>", desc = "Help" },
},
opts = {
defaults = {
prompt_prefix = " ",
selection_caret = " ",
path_display = { "truncate" },
file_ignore_patterns = { "node_modules", ".git/" },
},
pickers = {
find_files = {
hidden = true,
},
},
},
config = function(_, opts)
local telescope = require("telescope")
telescope.setup(opts)
telescope.load_extension("fzf")
end,
}
LSP Configuration
-- lua/plugins/lsp.lua
return {
"neovim/nvim-lspconfig",
dependencies = {
"williamboman/mason.nvim",
"williamboman/mason-lspconfig.nvim",
},
config = function()
-- Setup mason first
require("mason").setup()
require("mason-lspconfig").setup({
ensure_installed = { "lua_ls", "pyright", "rust_analyzer" },
})
local lspconfig = require("lspconfig")
local capabilities = require("cmp_nvim_lsp").default_capabilities()
-- On attach function for all LSPs
local on_attach = function(client, bufnr)
local map = function(mode, lhs, rhs, desc)
vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, desc = desc })
end
map("n", "gd", vim.lsp.buf.definition, "Go to definition")
map("n", "gr", vim.lsp.buf.references, "References")
map("n", "K", vim.lsp.buf.hover, "Hover")
map("n", "<leader>ca", vim.lsp.buf.code_action, "Code action")
map("n", "<leader>rn", vim.lsp.buf.rename, "Rename")
map("n", "<leader>f", function()
vim.lsp.buf.format({ async = true })
end, "Format")
end
-- Configure servers
lspconfig.lua_ls.setup({
capabilities = capabilities,
on_attach = on_attach,
settings = {
Lua = {
diagnostics = { globals = { "vim" } },
workspace = { checkThirdParty = false },
},
},
})
lspconfig.pyright.setup({
capabilities = capabilities,
on_attach = on_attach,
})
end,
}
Utility Functions
Safe Require
-- Safely require module, return nil on failure
local function safe_require(module)
local ok, result = pcall(require, module)
if ok then
return result
end
vim.notify("Failed to load: " .. module, vim.log.levels.WARN)
return nil
end
local telescope = safe_require("telescope")
if telescope then
telescope.setup({})
end
Keymap Helper
-- lua/utils/keymaps.lua
local M = {}
function M.map(mode, lhs, rhs, opts)
opts = opts or {}
opts.noremap = opts.noremap ~= false
opts.silent = opts.silent ~= false
vim.keymap.set(mode, lhs, rhs, opts)
end
function M.nmap(lhs, rhs, opts)
M.map("n", lhs, rhs, opts)
end
function M.vmap(lhs, rhs, opts)
M.map("v", lhs, rhs, opts)
end
function M.imap(lhs, rhs, opts)
M.map("i", lhs, rhs, opts)
end
-- Leader map helper
function M.leader(key, rhs, desc)
M.nmap("<leader>" .. key, rhs, { desc = desc })
end
return M
-- Usage:
-- local map = require("utils.keymaps")
-- map.leader("ff", "<cmd>Telescope find_files<cr>", "Find Files")
Buffer Utilities
-- lua/utils/buffer.lua
local M = {}
function M.is_valid(bufnr)
return vim.api.nvim_buf_is_valid(bufnr)
and vim.api.nvim_buf_is_loaded(bufnr)
end
function M.is_file_buffer(bufnr)
bufnr = bufnr or 0
local buftype = vim.bo[bufnr].buftype
return buftype == ""
end
function M.get_visible_buffers()
local bufs = {}
for _, win in ipairs(vim.api.nvim_list_wins()) do
local buf = vim.api.nvim_win_get_buf(win)
if M.is_valid(buf) then
bufs[buf] = true
end
end
return vim.tbl_keys(bufs)
end
function M.close_others()
local current = vim.api.nvim_get_current_buf()
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
if buf ~= current and M.is_valid(buf) and M.is_file_buffer(buf) then
vim.api.nvim_buf_delete(buf, { force = false })
end
end
end
return M
Plugin Patterns
Toggle Feature
local M = {}
M.enabled = false
function M.toggle()
M.enabled = not M.enabled
if M.enabled then
M._enable()
vim.notify("Feature enabled", vim.log.levels.INFO)
else
M._disable()
vim.notify("Feature disabled", vim.log.levels.INFO)
end
end
function M._enable()
-- Setup keymaps, autocmds, etc.
vim.keymap.set("n", "<leader>x", M.action, { desc = "Feature action" })
end
function M._disable()
-- Cleanup
pcall(vim.keymap.del, "n", "<leader>x")
end
function M.action()
if not M.enabled then return end
-- Do something
end
return M
Event System
local M = {}
M._callbacks = {}
function M.on(event, callback)
if not M._callbacks[event] then
M._callbacks[event] = {}
end
table.insert(M._callbacks[event], callback)
end
function M.emit(event, ...)
if not M._callbacks[event] then return end
for _, callback in ipairs(M._callbacks[event]) do
callback(...)
end
end
function M.off(event, callback)
if not M._callbacks[event] then return end
for i, cb in ipairs(M._callbacks[event]) do
if cb == callback then
table.remove(M._callbacks[event], i)
break
end
end
end
return M
-- Usage:
-- local events = require("utils.events")
-- events.on("file_saved", function(filename)
-- print("Saved: " .. filename)
-- end)
-- events.emit("file_saved", "init.lua")
State Manager
local M = {}
M._state = {}
function M.get(key, default)
if M._state[key] == nil then
return default
end
return M._state[key]
end
function M.set(key, value)
M._state[key] = value
end
function M.toggle(key)
M._state[key] = not M._state[key]
return M._state[key]
end
function M.increment(key, amount)
amount = amount or 1
M._state[key] = (M._state[key] or 0) + amount
return M._state[key]
end
function M.save()
-- Persist to file
local path = vim.fn.stdpath("data") .. "/mystate.json"
local file = io.open(path, "w")
if file then
file:write(vim.json.encode(M._state))
file:close()
end
end
function M.load()
local path = vim.fn.stdpath("data") .. "/mystate.json"
local file = io.open(path, "r")
if file then
local content = file:read("*a")
file:close()
M._state = vim.json.decode(content) or {}
end
end
return M
UI Patterns
Floating Window
local M = {}
function M.create_float(lines, opts)
opts = opts or {}
-- Calculate dimensions
local width = opts.width or 60
local height = opts.height or #lines
-- Create buffer
local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
vim.bo[buf].modifiable = false
-- Calculate position
local win_width = vim.api.nvim_get_option("columns")
local win_height = vim.api.nvim_get_option("lines")
local col = math.floor((win_width - width) / 2)
local row = math.floor((win_height - height) / 2)
-- Create window
local win = vim.api.nvim_open_win(buf, true, {
relative = "editor",
width = width,
height = height,
col = col,
row = row,
style = "minimal",
border = opts.border or "rounded",
title = opts.title,
title_pos = "center",
})
-- Keymaps to close
vim.keymap.set("n", "q", function()
vim.api.nvim_win_close(win, true)
end, { buffer = buf })
vim.keymap.set("n", "<Esc>", function()
vim.api.nvim_win_close(win, true)
end, { buffer = buf })
return buf, win
end
return M
-- Usage:
-- require("utils.float").create_float({
-- "Line 1",
-- "Line 2",
-- "Line 3",
-- }, { title = " My Window ", border = "rounded" })
Input Prompt
local function input_prompt(prompt, callback, opts)
opts = opts or {}
vim.ui.input({
prompt = prompt,
default = opts.default or "",
}, function(input)
if input and input ~= "" then
callback(input)
end
end)
end
-- Usage:
-- input_prompt("Enter filename: ", function(name)
-- vim.cmd("edit " .. name)
-- end)
-- Select prompt
local function select_prompt(items, prompt, callback)
vim.ui.select(items, {
prompt = prompt,
format_item = function(item)
return item
end,
}, function(choice)
if choice then
callback(choice)
end
end)
end
-- Usage:
-- select_prompt({ "Option 1", "Option 2", "Option 3" }, "Choose:", function(choice)
-- print("Selected: " .. choice)
-- end)
Autocommand Patterns
Filetype Setup
local function setup_filetype(ft, callback)
vim.api.nvim_create_autocmd("FileType", {
pattern = ft,
callback = function(args)
callback(args.buf)
end,
})
end
-- Usage
setup_filetype("python", function(buf)
vim.bo[buf].tabstop = 4
vim.bo[buf].shiftwidth = 4
vim.keymap.set("n", "<leader>r", function()
vim.cmd("!python3 %")
end, { buffer = buf, desc = "Run Python" })
end)
setup_filetype("lua", function(buf)
vim.bo[buf].tabstop = 2
vim.bo[buf].shiftwidth = 2
end)
Auto Save
local function setup_autosave(delay)
delay = delay or 5000 -- 5 seconds
local timer = nil
local augroup = vim.api.nvim_create_augroup("AutoSave", { clear = true })
vim.api.nvim_create_autocmd({ "TextChanged", "TextChangedI" }, {
group = augroup,
callback = function()
if timer then
timer:stop()
end
timer = vim.defer_fn(function()
if vim.bo.modified and vim.bo.buftype == "" then
vim.cmd("silent! write")
vim.notify("Auto-saved", vim.log.levels.INFO)
end
end, delay)
end,
})
end
LSP Patterns
Format on Save
local function setup_format_on_save()
vim.api.nvim_create_autocmd("BufWritePre", {
callback = function(args)
-- Check if LSP client supports formatting
local clients = vim.lsp.get_active_clients({ bufnr = args.buf })
for _, client in ipairs(clients) do
if client.supports_method("textDocument/formatting") then
vim.lsp.buf.format({
bufnr = args.buf,
timeout_ms = 2000,
})
return
end
end
end,
})
end
Diagnostic Config
vim.diagnostic.config({
virtual_text = {
prefix = "●",
spacing = 4,
},
signs = {
text = {
[vim.diagnostic.severity.ERROR] = " ",
[vim.diagnostic.severity.WARN] = " ",
[vim.diagnostic.severity.INFO] = " ",
[vim.diagnostic.severity.HINT] = " ",
},
},
underline = true,
update_in_insert = false,
severity_sort = true,
float = {
border = "rounded",
source = "always",
header = "",
prefix = "",
},
})
Summary
These patterns form the foundation of production Neovim configurations:
-
Lazy Specs: Declarative plugin configuration
-
LSP Setup: Server configuration with on_attach
-
Safe Require: Handle missing modules gracefully
-
Keymap Helpers: Consistent mapping patterns
-
Buffer Utils: Common buffer operations
-
Toggle Features: Enable/disable functionality
-
Floating Windows: Custom UI elements
-
Autocommands: Event-driven behavior
-
State Management: Persistent configuration
Apply these patterns to build maintainable Neovim configurations.