jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

Neovim · Part 5 — Plugin architecture với lazy.nvim

Cài lazy.nvim, chia plugin specs, hiểu lazy-loading, dependencies, opts/config, lockfile, update và cách không biến config thành mớ rối.

Plugin manager là bước biến Neovim từ editor tối giản thành môi trường làm việc hiện đại.

Năm 2025-2026 có hai hướng đáng biết:

  • vim.pack: package manager built-in mới của Neovim, nhưng vẫn được ghi là experimental.
  • lazy.nvim: plugin manager thực dụng, ổn định, có UI, lockfile, lazy-loading, profile.

Series này dùng lazy.nvim vì người chuyển từ VSCode cần một hệ sinh thái dễ học, dễ debug.


Bootstrap lazy.nvim

init.lua:

require("config.options")
require("config.keymaps")
require("config.autocmds")
require("config.lazy")

Tạo lua/config/lazy.lua:

local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"

if not (vim.uv or vim.loop).fs_stat(lazypath) then
  local lazyrepo = "https://github.com/folke/lazy.nvim.git"
  local out = vim.fn.system({
    "git",
    "clone",
    "--filter=blob:none",
    "--branch=stable",
    lazyrepo,
    lazypath,
  })

  if vim.v.shell_error ~= 0 then
    vim.api.nvim_echo({
      { "Failed to clone lazy.nvim:\n", "ErrorMsg" },
      { out, "WarningMsg" },
      { "\nPress any key to exit..." },
    }, true, {})
    vim.fn.getchar()
    os.exit(1)
  end
end

vim.opt.rtp:prepend(lazypath)

require("lazy").setup({
  spec = {
    { import = "plugins" },
  },
  install = { colorscheme = { "habamax" } },
  checker = { enabled = true },
  change_detection = { notify = false },
})

Tạo thư mục:

lua/
├─ config/
│  └─ lazy.lua
└─ plugins/
   ├─ ui.lua
   ├─ editor.lua
   ├─ lsp.lua
   └─ coding.lua

Plugin spec là gì?

Một plugin spec là một table Lua mô tả plugin:

return {
  {
    "folke/which-key.nvim",
    event = "VeryLazy",
    opts = {},
  },
}

Các field thường gặp:

FieldNghĩa
string đầurepo GitHub
eventload khi event xảy ra
cmdload khi gọi command
keysload khi bấm key
ftload theo filetype
dependenciesplugin phụ thuộc
optsoptions truyền vào setup()
configfunction tự cấu hình
buildlệnh chạy sau install/update
versionpin semver/tag

Quy tắc: dùng opts khi plugin có setup đơn giản; dùng config khi cần logic.


Plugin đầu tiên: which-key

Tạo lua/plugins/editor.lua:

return {
  {
    "folke/which-key.nvim",
    event = "VeryLazy",
    opts = {
      preset = "modern",
    },
  },
}

Mở Nvim, chạy:

:Lazy

lazy.nvim sẽ cài plugin thiếu. Sau đó bấm <leader> và chờ một chút, which-key sẽ hiện các keymap có desc.


Theme và UI tối thiểu

Tạo lua/plugins/ui.lua:

return {
  {
    "folke/tokyonight.nvim",
    lazy = false,
    priority = 1000,
    opts = {
      style = "night",
    },
    config = function(_, opts)
      require("tokyonight").setup(opts)
      vim.cmd.colorscheme("tokyonight")
    end,
  },

  {
    "nvim-lualine/lualine.nvim",
    event = "VeryLazy",
    opts = {
      options = {
        theme = "auto",
        globalstatus = true,
      },
    },
  },
}

Theme dùng lazy = falsepriority cao để load sớm. Statusline có thể load trễ.


Lazy-loading không phải trò ảo thuật

Lazy-loading tốt khi plugin không cần chạy ngay. Nhưng đừng lazy-load mọi thứ.

PluginCó nên lazy-load?Lý do
colorschemeKhôngcần trước khi UI render
mason.nvimKhông nên defersetup PATH/tooling sớm
Treesitter parser layerCẩn thậnnhiều thay đổi, không nên load quá muộn
pickerchỉ cần khi tìm file/grep
git signsCó thể theo BufReadPrechỉ cần khi mở file
which-keyUI helper

Nếu plugin không hoạt động, thử bỏ lazy-loading trước khi debug sâu.


Lockfile

lazy.nvim tạo:

lazy-lock.json

Commit file này vào Git. Nó giúp máy khác cài đúng revision plugin.

Workflow update:

:Lazy

Sau đó:

  • bấm U để update;
  • đọc diff trong UI;
  • restart Nvim;
  • nếu hỏng, revert lazy-lock.json.

Đừng update plugin trong lúc deadline trừ khi bạn thích cảm giác tim đập nhanh.


Commands nên biết

LệnhÝ nghĩa
:Lazymở UI
:Lazy syncinstall/update/clean
:Lazy updateupdate plugin
:Lazy cleanxóa plugin không còn dùng
:Lazy profilexem startup/plugin cost
:Lazy healthkiểm tra lazy.nvim

Anti-pattern

Đừng làm thế này:

return {
  "plugin/a",
  "plugin/b",
  "plugin/c",
  "plugin/d",
}

Không event, không opts, không desc, không lý do. Sau 3 tháng bạn sẽ không biết plugin nào làm gì.

Nên làm:

return {
  {
    "lewis6991/gitsigns.nvim",
    event = { "BufReadPre", "BufNewFile" },
    opts = {},
  },
}

Mỗi plugin nên có:

  • điều kiện load rõ;
  • config tối thiểu;
  • keymap có desc;
  • lý do tồn tại.

Bài tập

  1. Cài lazy.nvim theo cấu trúc trên.
  2. Thêm which-key.nvim.
  3. Thêm một colorscheme bạn thích.
  4. Chạy :Lazy profile, ghi lại startup time.
  5. Commit lazy-lock.json vào dotfiles.
Checklist lỗi thường gặp
  • module 'lazy' not found: kiểm tra vim.opt.rtp:prepend(lazypath).
  • Plugin không cài: kiểm tra Git/network.
  • Keymap không hiện trong which-key: thiếu desc hoặc plugin chưa load.
  • Theme chớp màu khi mở: colorscheme bị lazy-load quá muộn.

Điều cốt lõi

lazy.nvim không chỉ để cài plugin. Nó là kiến trúc config: chia module, kiểm soát load, giữ lockfile, update có kiểm soát. Từ đây Neovim bắt đầu thành editor của bạn.