From 077ec08dc18f1b76a0382619c9afa3913d0bf27c Mon Sep 17 00:00:00 2001 From: xfy Date: Thu, 28 May 2026 11:04:08 +0800 Subject: [PATCH] Add lazy loading for all plugins - Create lua/lazy.lua helper with load/on_event/on_keys - Rewrite pack.lua: lazy load all mini modules + fugitive - Defer treesitter.install() with vim.defer_fn - Defer mason + LSP config to VimEnter - Move mini.pick keymaps from keymaps.lua to pack.lua - Keep mini.notify and mini.cmdline eager (startup needed) Co-Authored-By: Claude Opus 4.7 (1M context) --- lua/keymaps.lua | 73 +------------- lua/lazy.lua | 34 +++++++ lua/lsp.lua | 64 ++++++------ lua/pack.lua | 240 ++++++++++++++++++++++++++++++++------------- lua/treesitter.lua | 79 ++++++++------- 5 files changed, 283 insertions(+), 207 deletions(-) create mode 100644 lua/lazy.lua diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 253ea17..149e93d 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -2,8 +2,7 @@ vim.g.mapleader = " " local map = vim.keymap.set -map("x", "p", [["_dP]], { desc = "Paste over selection without losing yanked text" }) --- map({ "n", "v" }, "d", [["_d]], { desc = "Delete without yanking" }) +map("x", "p", [[_dP]], { desc = "Paste over selection without losing yanked text" }) map("n", "", ":nohl", { desc = "Clear search highlighting", silent = true }) @@ -14,7 +13,6 @@ map("n", "J", "mzJ`z", { desc = "Join lines without moving cursor" }) map("n", "ss", [[:%s/\<\>//gI]], { desc = "Replace word cursor is on globally" }) --- search map("v", "ss", ":s/\\%V", { desc = "Search and replace in visual selection" }) -- general @@ -38,7 +36,6 @@ map("n", "tn", ":tabnew", { desc = "New tab" }) map("n", "]", ":tabnext", { desc = "Next tab" }) map("n", "[", ":tabprevious", { desc = "Previous tab" }) - -- yank path map("n", "yp", function() local path = vim.fn.expand("%") @@ -84,71 +81,3 @@ map("n", "bo", function() end end end, { desc = "Close other buffers" }) - --- mini.pick buffer picker (telescope buffers equivalent) -map("n", "", function() - local MiniPick = require("mini.pick") - - local delete_buf = function() - local matches = MiniPick.get_picker_matches() - local item = matches and matches.current - if not item or not item.bufnr then return end - local bufnr = item.bufnr - - -- 先删除 buffer(pcall 保护,避免删除当前窗口 buffer 时出错) - pcall(vim.api.nvim_buf_delete, bufnr, { force = true }) - - -- 刷新列表:只保留仍然有效的 buffer - if MiniPick.is_picker_active() then - local items = vim.tbl_filter(function(i) - return i.bufnr and vim.api.nvim_buf_is_valid(i.bufnr) - end, MiniPick.get_picker_items() or {}) - MiniPick.set_picker_items(items) - end - end - - -- 收集所有 buffer,过滤特殊类型和未列出的,按最近使用排序 - local bufs = vim.tbl_filter(function(b) - return vim.bo[b.bufnr].buftype == "" and b.listed == 1 - end, vim.fn.getbufinfo()) - table.sort(bufs, function(a, b) - return a.lastused > b.lastused - end) - - local items = {} - for _, info in ipairs(bufs) do - local name = info.name ~= "" and vim.fn.fnamemodify(info.name, ":.") or "[No Name]" - table.insert(items, { - text = name, - bufnr = info.bufnr, - }) - end - - MiniPick.start({ - source = { - name = "Buffers", - items = items, - show = function(buf_id, items_arr, query) - MiniPick.default_show(buf_id, items_arr, query, { show_icons = true }) - end, - }, - mappings = { - delete_buffer = { char = "", func = delete_buf }, - }, - }) -end, { desc = "Buffers" }) - --- mini.pick filetype picker -map("n", "ft", function() - local MiniPick = require("mini.pick") - local filetypes = vim.fn.getcompletion("", "filetype") - MiniPick.start({ - source = { - name = "Filetypes", - items = filetypes, - choose = function(item) - vim.bo.filetype = item - end, - }, - }) -end, { desc = "Change filetype" }) diff --git a/lua/lazy.lua b/lua/lazy.lua new file mode 100644 index 0000000..24cf69d --- /dev/null +++ b/lua/lazy.lua @@ -0,0 +1,34 @@ +local M = {} + +M._loaded = {} + +M.load = function(name, fn) + if M._loaded[name] then + return + end + M._loaded[name] = true + fn() +end + +M.on_event = function(name, event, pattern, fn) + vim.api.nvim_create_autocmd(event, { + pattern = pattern or "*", + once = true, + callback = function() + M.load(name, fn) + end, + }) +end + +M.on_keys = function(name, keys, mode, fn, action, opts) + mode = mode or "n" + opts = opts or {} + vim.keymap.set(mode, keys, function() + M.load(name, fn) + if action then + action() + end + end, { desc = opts.desc, buffer = opts.buffer }) +end + +return M diff --git a/lua/lsp.lua b/lua/lsp.lua index 02c3b82..14615f0 100644 --- a/lua/lsp.lua +++ b/lua/lsp.lua @@ -1,7 +1,39 @@ -require("mason").setup() +local lazy = require("lazy") -vim.keymap.set('n', 'gd', vim.lsp.buf.definition, { desc = "Go to definition" }) -vim.keymap.set('n', 'gh', vim.lsp.buf.hover, { desc = "Hover" }) +-- mason + LSP 配置延迟到 VimEnter,避免启动时加载 +lazy.on_event("lsp", "VimEnter", "*", function() + require("mason").setup() + + local capabilities = vim.lsp.protocol.make_client_capabilities() + capabilities = vim.tbl_deep_extend("force", capabilities, require("mini.completion").get_lsp_capabilities()) + + vim.lsp.config("*", { capabilities = capabilities }) + + vim.lsp.config("lua_ls", { + settings = { + Lua = { + diagnostics = { globals = { "vim" } }, + }, + }, + }) + + vim.lsp.enable({ + "html", + "cssls", + "gopls", + "vtsls", + "rust_analyzer", + "lua_ls", + "taplo", + "svelte", + "dartls", + "kotlin_lsp", + }) +end) + +-- LSP keymaps +vim.keymap.set("n", "gd", vim.lsp.buf.definition, { desc = "Go to definition" }) +vim.keymap.set("n", "gh", vim.lsp.buf.hover, { desc = "Hover" }) vim.keymap.set("n", "fm", vim.lsp.buf.format, { desc = "Format Local buffer" }) vim.keymap.set("n", "df", vim.diagnostic.open_float, { desc = "Show line diagnostics" }) vim.keymap.set("n", "ca", vim.lsp.buf.code_action, { desc = "Code action" }) @@ -24,29 +56,3 @@ vim.keymap.set("n", "]w", diagnostic_goto(true, "WARN"), { desc = "Next Warning" vim.keymap.set("n", "[w", diagnostic_goto(false, "WARN"), { desc = "Prev Warning" }) vim.diagnostic.config({ virtual_text = true }) - -local capabilities = vim.lsp.protocol.make_client_capabilities() -capabilities = vim.tbl_deep_extend("force", capabilities, require("mini.completion").get_lsp_capabilities()) - -vim.lsp.config("*", { capabilities = capabilities }) - -vim.lsp.config("lua_ls", { - settings = { - Lua = { - diagnostics = { globals = { "vim" } }, - }, - }, -}) - -vim.lsp.enable({ - "html", - "cssls", - "gopls", - "vtsls", - "rust_analyzer", - "lua_ls", - "taplo", - "svelte", - "dartls", - "kotlin_lsp" -}) diff --git a/lua/pack.lua b/lua/pack.lua index 3acf0e8..5d4ccff 100644 --- a/lua/pack.lua +++ b/lua/pack.lua @@ -1,3 +1,5 @@ +local lazy = require("lazy") + vim.pack.add({ "https://github.com/nvim-mini/mini.nvim", "https://github.com/rafamadriz/friendly-snippets", @@ -7,85 +9,187 @@ vim.pack.add({ "https://github.com/tpope/vim-fugitive", }) --- mini files ---- -local MiniFiles = require("mini.files") -MiniFiles.setup({ - mappings = { - go_in = "", - go_in_plus = "L", - go_out = "_", - go_out_plus = "H", - }, -}) +-- mini.files - 按键触发 +lazy.on_keys("files", "-", "n", function() + require("mini.files").setup({ + mappings = { + go_in = "", + go_in_plus = "L", + go_out = "_", + go_out_plus = "H", + }, + }) +end, function() + require("mini.files").open() +end, { desc = "Toggle mini file explorer" }) -vim.keymap.set("n", "-", "lua MiniFiles.open()", { desc = "Toggle mini file explorer" }) -vim.keymap.set("n", "-", function() +lazy.on_keys("files", "-", "n", function() + require("mini.files").setup({ + mappings = { + go_in = "", + go_in_plus = "L", + go_out = "_", + go_out_plus = "H", + }, + }) +end, function() + local MiniFiles = require("mini.files") MiniFiles.open(vim.api.nvim_buf_get_name(0), false) MiniFiles.reveal_cwd() end, { desc = "Toggle into currently opened file" }) ----- mini notify ---- -require("mini.notify").setup({ - -- only show messages - content = { - format = function(notif) - return notif.msg - end, - }, -}) +-- mini.notify - 启动时加载 +lazy.load("notify", function() + require("mini.notify").setup({ + content = { + format = function(notif) + return notif.msg + end, + }, + }) +end) ---- mini cmdline completion --- -require("mini.cmdline").setup({ - autocorrect = { enable = false } -}) +-- mini.cmdline - 启动时加载 +lazy.load("cmdline", function() + require("mini.cmdline").setup({ + autocorrect = { enable = false }, + }) +end) ---- mini surround --- -require("mini.surround").setup() --- Default Keymaps --- | `sa` | Add surrounding or Direct with 'saiw' | --- | `sd` | Delete surrounding | --- | `sr` | Replace surrounding | --- | `sf` | Find surrounding (right) | --- | `sF` | Find surrounding (left) | --- | `sh` | Highlight surrounding | --- | `sn` | Update n_lines | --- | `l` / `n` | as suffix for prev/next | +-- mini.surround - BufReadPost +lazy.on_event("surround", "BufReadPost", "*", function() + require("mini.surround").setup() +end) ---- mini picker --- -local MiniPick = require("mini.pick") -local MiniExtra = require("mini.extra") -MiniPick.setup() -MiniExtra.setup() +-- mini.pick + mini.extra - 按键触发 +local load_pick = function() + lazy.load("pick", function() + require("mini.pick").setup() + end) + lazy.load("extra", function() + require("mini.extra").setup() + end) +end --- keymaps -vim.keymap.set("n", "ff", function() MiniPick.builtin.files() end, { desc = "Mini File Picker" }) -vim.keymap.set("n", "fw", function() MiniPick.builtin.grep({ pattern = vim.fn.expand("") }) end, { desc = "Grep word/Search word" }) -vim.keymap.set("n", "vh", function() MiniPick.builtin.help() end, { desc = "Mini Help" }) +lazy.on_keys("pick", "ff", "n", load_pick, function() + require("mini.pick").builtin.files() +end, { desc = "Mini File Picker" }) -vim.keymap.set("n", "ds", function() MiniExtra.pickers.diagnostic() end, { desc = "Mini Picker Diagnostics" }) -vim.keymap.set("n", "sk", function() MiniExtra.pickers.keymaps() end, { desc = 'Search keymaps' }) +lazy.on_keys("pick", "fw", "n", load_pick, function() + require("mini.pick").builtin.grep({ pattern = vim.fn.expand("") }) +end, { desc = "Grep word/Search word" }) ---- mini completions --- -require("mini.completion").setup({ - lsp_completion = { - auto_setup = true, - } -}) +lazy.on_keys("pick", "vh", "n", load_pick, function() + require("mini.pick").builtin.help() +end, { desc = "Mini Help" }) ---- mini snippets --- -local MiniSnippets = require("mini.snippets") -MiniSnippets.setup({ - snippets = { - MiniSnippets.gen_loader.from_lang(), -- loads friendly-snippets - }, -}) -MiniSnippets.start_lsp_server({ match = false }) +lazy.on_keys("pick", "ds", "n", load_pick, function() + require("mini.extra").pickers.diagnostic() +end, { desc = "Mini Picker Diagnostics" }) ---- mini diff and fugitive --- -local MiniDiff = require("mini.diff") -MiniDiff.setup({ - source = MiniDiff.gen_source.git({ index = false }), -}) +lazy.on_keys("pick", "sk", "n", load_pick, function() + require("mini.extra").pickers.keymaps() +end, { desc = "Search keymaps" }) -vim.keymap.set("n", "gg", "tabnew | Git | only", { desc = "Fugitive Full Page New Tab" }) -vim.keymap.set("n", "gd", "Gvdiffsplit", { desc = "Git diff split", }) +-- mini.completion - InsertEnter +lazy.on_event("completion", "InsertEnter", "*", function() + require("mini.completion").setup({ + lsp_completion = { + auto_setup = true, + }, + }) +end) + +-- mini.snippets - InsertEnter +lazy.on_event("snippets", "InsertEnter", "*", function() + local MiniSnippets = require("mini.snippets") + MiniSnippets.setup({ + snippets = { + MiniSnippets.gen_loader.from_lang(), + }, + }) + MiniSnippets.start_lsp_server({ match = false }) +end) + +-- mini.diff - BufReadPost +lazy.on_event("diff", "BufReadPost", "*", function() + require("mini.diff").setup({ + source = require("mini.diff").gen_source.git({ index = false }), + }) +end) + +-- vim-fugitive - 按键触发 +lazy.on_keys("fugitive", "gg", "n", nil, function() + vim.cmd("tabnew | Git | only") +end, { desc = "Fugitive Full Page New Tab" }) + +lazy.on_keys("fugitive", "gd", "n", nil, function() + vim.cmd("Gvdiffsplit") +end, { desc = "Git diff split" }) + +-- mini.pick buffer picker (telescope buffers equivalent) +lazy.on_keys("pick", "", "n", load_pick, function() + local MiniPick = require("mini.pick") + + local delete_buf = function() + local matches = MiniPick.get_picker_matches() + local item = matches and matches.current + if not item or not item.bufnr then + return + end + local bufnr = item.bufnr + + pcall(vim.api.nvim_buf_delete, bufnr, { force = true }) + + if MiniPick.is_picker_active() then + local items = vim.tbl_filter(function(i) + return i.bufnr and vim.api.nvim_buf_is_valid(i.bufnr) + end, MiniPick.get_picker_items() or {}) + MiniPick.set_picker_items(items) + end + end + + local bufs = vim.tbl_filter(function(b) + return vim.bo[b.bufnr].buftype == "" and b.listed == 1 + end, vim.fn.getbufinfo()) + table.sort(bufs, function(a, b) + return a.lastused > b.lastused + end) + + local items = {} + for _, info in ipairs(bufs) do + local name = info.name ~= "" and vim.fn.fnamemodify(info.name, ":.") or "[No Name]" + table.insert(items, { + text = name, + bufnr = info.bufnr, + }) + end + + MiniPick.start({ + source = { + name = "Buffers", + items = items, + show = function(buf_id, items_arr, query) + MiniPick.default_show(buf_id, items_arr, query, { show_icons = true }) + end, + }, + mappings = { + delete_buffer = { char = "", func = delete_buf }, + }, + }) +end, { desc = "Buffers" }) + +-- mini.pick filetype picker +lazy.on_keys("pick", "ft", "n", load_pick, function() + local MiniPick = require("mini.pick") + local filetypes = vim.fn.getcompletion("", "filetype") + MiniPick.start({ + source = { + name = "Filetypes", + items = filetypes, + choose = function(item) + vim.bo.filetype = item + end, + }, + }) +end, { desc = "Change filetype" }) diff --git a/lua/treesitter.lua b/lua/treesitter.lua index b307872..cf6dc2e 100644 --- a/lua/treesitter.lua +++ b/lua/treesitter.lua @@ -1,49 +1,52 @@ local treesitter = require("nvim-treesitter") local ensure_installed = { - "lua", - "vim", - "vimdoc", - -- Web - "javascript", - "typescript", - "tsx", - "jsdoc", - "json", - "html", - "css", - "yaml", - -- Backend - "c", - "rust", - "toml", - "go", - "gomod", - "gosum", - "gowork", - -- Infra - "dockerfile", - "make", + "lua", + "vim", + "vimdoc", + -- Web + "javascript", + "typescript", + "tsx", + "jsdoc", + "json", + "html", + "css", + "yaml", + -- Backend + "c", + "rust", + "toml", + "go", + "gomod", + "gosum", + "gowork", + -- Infra + "dockerfile", + "make", } -treesitter.install(ensure_installed) +-- 延迟安装 parser,避免启动时阻塞 +vim.defer_fn(function() + treesitter.install(ensure_installed) +end, 100) vim.api.nvim_create_autocmd("FileType", { - pattern = "*", - callback = function(args) - local buf = args.buf - local ft = vim.bo[buf].filetype + pattern = "*", + callback = function(args) + local buf = args.buf + local ft = vim.bo[buf].filetype - local lang = vim.treesitter.language.get_lang(ft) - if not lang then - return - end + local lang = vim.treesitter.language.get_lang(ft) + if not lang then + return + end - local ok_add = pcall(vim.treesitter.language.add, lang) - if not ok_add then - return - end + local ok_add = pcall(vim.treesitter.language.add, lang) + if not ok_add then + return + end - pcall(vim.treesitter.start, buf, lang) - end, + pcall(vim.treesitter.start, buf, lang) + end, })