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:

  1. Lazy Specs: Declarative plugin configuration

  2. LSP Setup: Server configuration with on_attach

  3. Safe Require: Handle missing modules gracefully

  4. Keymap Helpers: Consistent mapping patterns

  5. Buffer Utils: Common buffer operations

  6. Toggle Features: Enable/disable functionality

  7. Floating Windows: Custom UI elements

  8. Autocommands: Event-driven behavior

  9. State Management: Persistent configuration

Apply these patterns to build maintainable Neovim configurations.