mirror of
https://github.com/cap153/nvim.git
synced 2026-04-19 03:20:27 +08:00
first commit
This commit is contained in:
commit
3b19a9c8a8
29 changed files with 2428 additions and 0 deletions
75
lua/pack/configs/blinkcmp.lua
Normal file
75
lua/pack/configs/blinkcmp.lua
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
-- === 自动补全插件 (Blink.cmp) ===
|
||||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "blink.cmp",
|
||||
module = "blink.cmp",
|
||||
deps = { "friendly-snippets" },
|
||||
-- build_cmd = "cargo build --release",
|
||||
}
|
||||
|
||||
-- PackUtils.setup_listener(P.name, P.build_cmd)
|
||||
|
||||
vim.api.nvim_create_autocmd({ "InsertEnter", "CmdlineEnter", "LspAttach" }, {
|
||||
once = true,
|
||||
callback = function()
|
||||
-- 调用引擎的 load 方法,把 setup 逻辑作为匿名函数传进去
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({
|
||||
fuzzy = {
|
||||
prebuilt_binaries = {
|
||||
force_version = 'v*',
|
||||
},
|
||||
},
|
||||
cmdline = {
|
||||
-- keymap = { ["<CR>"] = { "select_and_accept", "fallback" } },
|
||||
completion = {
|
||||
list = { selection = { preselect = false, auto_insert = true } },
|
||||
menu = { auto_show = function() return vim.fn.getcmdtype() == ":" end },
|
||||
ghost_text = { enabled = false },
|
||||
},
|
||||
},
|
||||
keymap = {
|
||||
preset = "none",
|
||||
["<C-space>"] = { "show", "show_documentation", "hide_documentation" },
|
||||
["<CR>"] = { "accept", "fallback" },
|
||||
["<S-Tab>"] = { "select_prev", "snippet_backward", "fallback" },
|
||||
["<Tab>"] = { "select_next", "snippet_forward", "fallback" },
|
||||
["<C-b>"] = { "scroll_documentation_up", "fallback" },
|
||||
["<C-f>"] = { "scroll_documentation_down", "fallback" },
|
||||
["<C-e>"] = { "snippet_forward", "select_next", "fallback" },
|
||||
["<C-u>"] = { "snippet_backward", "select_prev", "fallback" },
|
||||
},
|
||||
completion = {
|
||||
keyword = { range = "full" },
|
||||
documentation = { auto_show = true, auto_show_delay_ms = 0 },
|
||||
list = { selection = { preselect = false, auto_insert = false } },
|
||||
},
|
||||
enabled = function()
|
||||
return not vim.tbl_contains({}, vim.bo.filetype)
|
||||
and vim.bo.buftype ~= "prompt"
|
||||
and vim.b.completion ~= false
|
||||
end,
|
||||
appearance = {
|
||||
use_nvim_cmp_as_default = true,
|
||||
nerd_font_variant = "mono",
|
||||
},
|
||||
sources = {
|
||||
default = { "buffer", "lsp", "path", "snippets" },
|
||||
providers = {
|
||||
buffer = { score_offset = 5 },
|
||||
path = { score_offset = 3 },
|
||||
lsp = { score_offset = 2 },
|
||||
snippets = { score_offset = 1 },
|
||||
-- cmdline = { -- 输入超过3个及以上字母才触发补全
|
||||
-- min_keyword_length = function(ctx)
|
||||
-- if ctx.mode == "cmdline" and string.find(ctx.line, " ") == nil then return 3 end
|
||||
-- return 0
|
||||
-- end,
|
||||
-- },
|
||||
},
|
||||
},
|
||||
})
|
||||
end)
|
||||
end
|
||||
})
|
||||
49
lua/pack/configs/bufferline.lua
Normal file
49
lua/pack/configs/bufferline.lua
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
local map = require("core.keymap")
|
||||
-- 新建空缓冲区,neovim自带的
|
||||
map:cmd('tu', 'enew')
|
||||
-- 关闭当前缓冲区,neovim自带的,完整命令bdelete
|
||||
map:cmd('tq', 'bd')
|
||||
|
||||
-- 在缓冲区之间移动
|
||||
map:cmd('tn', 'BufferLineCyclePrev')
|
||||
map:cmd('ti', 'BufferLineCycleNext')
|
||||
|
||||
-- 移动缓冲区的位置
|
||||
map:cmd('tmn', 'BufferLineMovePrev')
|
||||
map:cmd('tmi', 'BufferLineMoveNext')
|
||||
|
||||
-- 关闭缓冲区
|
||||
map:cmd('tN', 'BufferLineCloseLeft')
|
||||
map:cmd('tI', 'BufferLineCloseRight')
|
||||
map:cmd('tQ', 'BufferLineCloseOthers')
|
||||
|
||||
-- === 顶部标签栏 (Bufferline) ===
|
||||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "bufferline.nvim",
|
||||
module = "bufferline",
|
||||
deps = { "nvim-web-devicons" }, -- 确保图标库先加载
|
||||
}
|
||||
|
||||
-- 懒加载触发器:打开或新建文件时触发
|
||||
vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, {
|
||||
once = true,
|
||||
callback = function()
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({
|
||||
options = {
|
||||
modified_icon = "",
|
||||
buffer_close_icon = "×",
|
||||
-- show_buffer_close_icons = false,
|
||||
max_name_length = 14,
|
||||
max_prefix_length = 13,
|
||||
tab_size = 10,
|
||||
indicator = {
|
||||
style = "none",
|
||||
},
|
||||
},
|
||||
})
|
||||
end)
|
||||
end
|
||||
})
|
||||
53
lua/pack/configs/coderunner.lua
Normal file
53
lua/pack/configs/coderunner.lua
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "code_runner.nvim",
|
||||
module = "code_runner",
|
||||
deps = {},
|
||||
}
|
||||
|
||||
-- 定义映射配置表
|
||||
local mappings = {
|
||||
{ ft = { "rust", "python" }, cmd = "RunCode", desc = "Save and Run Code" },
|
||||
{ ft = "markdown", cmd = "PeekClose;PeekOpen", desc = "Reload Markdown Preview" },
|
||||
{ ft = "dart", cmd = "Telescope flutter commands", desc = "Open Flutter Commands" },
|
||||
{ ft = "go", cmd = "set splitbelow;sp;term go run %", desc = "Run Go file" },
|
||||
}
|
||||
|
||||
-- 只有进入这些文件类型时,才会为当前 buffer 绑定 r 键
|
||||
local ft_group = vim.api.nvim_create_augroup("CodeRunnerLazy", { clear = true })
|
||||
|
||||
for _, entry in ipairs(mappings) do
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
pattern = entry.ft,
|
||||
group = ft_group,
|
||||
callback = function(args)
|
||||
-- 为当前 buffer 绑定 r 键
|
||||
vim.keymap.set("n", "r", function()
|
||||
if vim.bo.modified then vim.cmd("wall") end
|
||||
-- 只有命令中包含 "RunCode" 时,才加载代码运行器插件
|
||||
if string.find(entry.cmd, "RunCode") then
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({
|
||||
-- project = {
|
||||
-- ["~/sixsixsix"] = {
|
||||
-- name = "sixsixsix",
|
||||
-- description = "六爻网页排盘",
|
||||
-- command = "cargo run --release"
|
||||
-- },
|
||||
-- },
|
||||
filetype = {
|
||||
python = "uv run $fileName",
|
||||
rust = { "cargo run --release" },
|
||||
},
|
||||
})
|
||||
end)
|
||||
end
|
||||
-- 处理多条命令的情况 (用分号分隔)
|
||||
for c in string.gmatch(entry.cmd, "[^;]+") do
|
||||
vim.cmd(c)
|
||||
end
|
||||
end, { buffer = args.buf, desc = entry.desc, silent = true })
|
||||
end,
|
||||
})
|
||||
end
|
||||
61
lua/pack/configs/conform.lua
Normal file
61
lua/pack/configs/conform.lua
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
-- === 代码格式化 (conform.nvim) ===
|
||||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "conform.nvim",
|
||||
module = "conform",
|
||||
deps = {
|
||||
"mason.nvim",
|
||||
"mason-registry",
|
||||
},
|
||||
}
|
||||
|
||||
-- 提取出统一的配置变量
|
||||
local formatters_by_ft = {
|
||||
python = { "isort", "black" },
|
||||
rust = { "rust-analyzer", lsp_format = "fallback" },
|
||||
toml = { "templ" },
|
||||
html = { "djlint" },
|
||||
sh = { "shfmt" },
|
||||
zsh = { "shfmt" },
|
||||
}
|
||||
|
||||
|
||||
-- 辅助函数:从指定文件类型的配置中提取所有工具名称(去重)
|
||||
local function get_ensure_installed_for_ft(ft, ft_table)
|
||||
local tools = {}
|
||||
local cfg = ft_table[ft]
|
||||
if type(cfg) == "table" then
|
||||
for _, item in ipairs(cfg) do
|
||||
if type(item) == "string" then
|
||||
tools[item] = true
|
||||
end
|
||||
end
|
||||
elseif type(cfg) == "string" then
|
||||
tools[cfg] = true
|
||||
end
|
||||
local list = {}
|
||||
for tool, _ in pairs(tools) do
|
||||
table.insert(list, tool)
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
-- 快捷键纯懒加载:只在按下快捷键时激活
|
||||
vim.keymap.set({ "n", "v" }, "<leader>f", function()
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({ -- At a minimum, you will need to set up some formatters by filetype
|
||||
formatters_by_ft = formatters_by_ft
|
||||
})
|
||||
end)
|
||||
local registry = require("mason-registry")
|
||||
local ft = vim.bo.filetype
|
||||
local tools = get_ensure_installed_for_ft(ft, formatters_by_ft)
|
||||
for _, tool in ipairs(tools) do
|
||||
if not registry.is_installed(tool) then
|
||||
vim.notify("Installing formatter: " .. tool, vim.log.levels.INFO)
|
||||
registry.get_package(tool):install()
|
||||
end
|
||||
end
|
||||
require("conform").format({ async = true, lsp_fallback = true })
|
||||
end, { desc = "格式化代码(检测安装缺失工具)" })
|
||||
42
lua/pack/configs/indentblankline.lua
Normal file
42
lua/pack/configs/indentblankline.lua
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
-- === 彩虹缩进 ===
|
||||
|
||||
local P = {
|
||||
name = "indent-blankline.nvim", -- 仓库名
|
||||
module = "ibl", -- require模块名
|
||||
}
|
||||
|
||||
-- 懒加载触发器
|
||||
vim.api.nvim_create_autocmd({
|
||||
"UIEnter", -- vim.schedule(function()
|
||||
}, {
|
||||
callback = function()
|
||||
vim.schedule(function()
|
||||
PackUtils.load(P, function(plugin)
|
||||
local highlight = {
|
||||
"RainbowBlue",
|
||||
"RainbowViolet",
|
||||
"RainbowRed",
|
||||
"RainbowYellow",
|
||||
"RainbowGreen",
|
||||
"RainbowOrange",
|
||||
"RainbowCyan",
|
||||
}
|
||||
local hooks = require("ibl.hooks")
|
||||
-- create the highlight groups in the highlight setup hook, so they are reset
|
||||
-- every time the colorscheme changes
|
||||
hooks.register(hooks.type.HIGHLIGHT_SETUP, function()
|
||||
vim.api.nvim_set_hl(0, "RainbowRed", { fg = "#E06C75" })
|
||||
vim.api.nvim_set_hl(0, "RainbowYellow", { fg = "#E5C07B" })
|
||||
vim.api.nvim_set_hl(0, "RainbowBlue", { fg = "#61AFEF" })
|
||||
vim.api.nvim_set_hl(0, "RainbowOrange", { fg = "#D19A66" })
|
||||
vim.api.nvim_set_hl(0, "RainbowGreen", { fg = "#98C379" })
|
||||
vim.api.nvim_set_hl(0, "RainbowViolet", { fg = "#C678DD" })
|
||||
vim.api.nvim_set_hl(0, "RainbowCyan", { fg = "#56B6C2" })
|
||||
end)
|
||||
plugin.setup({
|
||||
indent = { highlight = highlight }
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
})
|
||||
26
lua/pack/configs/kommentary.lua
Normal file
26
lua/pack/configs/kommentary.lua
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
-- 注释快捷键
|
||||
local map = require("core.keymap")
|
||||
map:key("n", "<space>cn", "<Plug>kommentary_line_increase")
|
||||
map:key("x", "<space>cn", "<Plug>kommentary_visual_increase<esc>") -- 选择模式下注释/反注释后退出该模式
|
||||
map:key("n", "<space>cu", "<Plug>kommentary_line_decrease")
|
||||
map:key("x", "<space>cu", "<plug>kommentary_visual_decrease<esc>")
|
||||
|
||||
local P = {
|
||||
name = "b3nj5m1n/kommentary", -- 仓库名
|
||||
module = "kommentary.config", -- require模块名
|
||||
}
|
||||
|
||||
-- 懒加载触发器
|
||||
vim.api.nvim_create_autocmd({
|
||||
"UIEnter",
|
||||
}, {
|
||||
callback = function()
|
||||
vim.schedule(function()
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.configure_language("default", {
|
||||
prefer_single_line_comments = true,
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
})
|
||||
121
lua/pack/configs/lspconfig.lua
Normal file
121
lua/pack/configs/lspconfig.lua
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
-- === LSP 核心配置 (Lspconfig + Mason) ===
|
||||
if vim.g.vscode then return end
|
||||
|
||||
-- 1. 环境探测:判断 CPU 架构,决定安装哪些 LSP
|
||||
local arch = jit and jit.arch or ""
|
||||
local is_arm = arch:match("arm") or arch:match("aarch64")
|
||||
|
||||
local servers = { "lua_ls", "rust_analyzer", "pylsp" }
|
||||
if not is_arm then
|
||||
vim.list_extend(servers, { "marksman", "ts_ls", "svelte", "cssls", "html" })
|
||||
end
|
||||
|
||||
-- 2. 插件配置清单
|
||||
local P = {
|
||||
name = "nvim-lspconfig",
|
||||
module = "lspconfig",
|
||||
deps = { "mason.nvim", "mason-lspconfig.nvim", "inlay-hints.nvim" },
|
||||
}
|
||||
|
||||
-- 3. 懒加载触发器:当打开文件时触发
|
||||
vim.api.nvim_create_autocmd({ "BufReadPost", "BufNewFile" }, {
|
||||
callback = function()
|
||||
PackUtils.load(P, function()
|
||||
-- === A. 基础依赖初始化 (Mason) ===
|
||||
require("mason").setup()
|
||||
require("mason-lspconfig").setup({
|
||||
ensure_installed = servers,
|
||||
})
|
||||
require("inlay-hints").setup()
|
||||
|
||||
-- === B. 全局诊断设置 ===
|
||||
vim.diagnostic.config({
|
||||
signs = {
|
||||
text = {
|
||||
[vim.diagnostic.severity.ERROR] = "✘",
|
||||
[vim.diagnostic.severity.WARN] = "▲",
|
||||
[vim.diagnostic.severity.HINT] = "⚑",
|
||||
[vim.diagnostic.severity.INFO] = "»",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
-- === C. 全局快捷键映射 ===
|
||||
-- local opts = { noremap = true, silent = true }
|
||||
vim.keymap.set("n", "<leader>h", vim.lsp.buf.hover, opts) -- <space>h显示提示文档
|
||||
vim.keymap.set("n", "gd", vim.lsp.buf.definition, opts) -- gd跳转到定义
|
||||
vim.keymap.set('n', 'gD', vim.lsp.buf.declaration, opts) -- gD跳转到声明(例如c语言中的头文件中的原型、一个变量的extern声明)
|
||||
vim.keymap.set("n", "go", vim.lsp.buf.type_definition, opts) -- go跳转到变量类型定义的位置(例如一些自定义类型)
|
||||
vim.keymap.set("n", "<leader>rn", vim.lsp.buf.rename, opts) -- <space>rn变量重命名
|
||||
vim.keymap.set("n", "<leader>aw", vim.lsp.buf.code_action, opts) -- <space>aw可以在出现警告或错误的地方打开建议的修复方法
|
||||
vim.keymap.set("n", "<leader>d", vim.diagnostic.open_float, opts) -- <space>d浮动窗口显示所在行警告或错误信息
|
||||
vim.keymap.set("n", "<leader>-", vim.diagnostic.goto_prev, opts) -- <space>-跳转到上一处警告或错误的地方
|
||||
vim.keymap.set("n", "<leader>=", vim.diagnostic.goto_next, opts) -- <space>+跳转到下一处警告或错误的地方
|
||||
-- vim.keymap.set("n", "gr", vim.lsp.buf.references, opts) -- gr跳转到引用了对应变量或函数的位置,改用snacks
|
||||
-- vim.keymap.set({ 'n', 'x' }, '<leader>f', function() vim.lsp.buf.format({ async = true }) end, opts) -- <space>f进行代码格式化
|
||||
|
||||
-- === D. 特定 LSP 配置 (使用 Neovim 0.11+ vim.lsp.config 语法) ===
|
||||
|
||||
-- Python (pylsp) + uv 虚拟环境自适应
|
||||
vim.lsp.config("pylsp", {
|
||||
on_init = function(client)
|
||||
-- 1. 安全获取 root_dir
|
||||
local root_dir = client.config.root_dir
|
||||
-- 2. 只有当 root_dir 不为 nil 时才进行后续探测
|
||||
if root_dir then
|
||||
local venv_python = root_dir .. "/.venv/bin/python"
|
||||
-- 3. 检查虚拟环境文件是否真的存在且可读
|
||||
if vim.fn.filereadable(venv_python) == 1 then
|
||||
client.config.settings.pylsp.plugins.jedi.environment = venv_python
|
||||
-- 只有修改了设置才发送通知
|
||||
client.notify("workspace/didChangeConfiguration", {
|
||||
settings = client.config.settings
|
||||
})
|
||||
end
|
||||
end
|
||||
-- 无论是否找到虚拟环境,都返回 true 让 LSP 继续启动
|
||||
return true
|
||||
end,
|
||||
settings = {
|
||||
pylsp = {
|
||||
plugins = {
|
||||
jedi = { environment = nil }
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
-- Lua (lua_ls)
|
||||
vim.lsp.config("lua_ls", {
|
||||
settings = {
|
||||
["Lua"] = {
|
||||
hint = { enable = true },
|
||||
diagnostics = { globals = { "vim", "require", "opts", "PackUtils", "jit" } },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
-- Go (gopls)
|
||||
vim.lsp.config("gopls", {
|
||||
settings = {
|
||||
["gopls"] = {
|
||||
hints = {
|
||||
rangeVariableTypes = true,
|
||||
parameterNames = true,
|
||||
constantValues = true,
|
||||
assignVariableTypes = true,
|
||||
compositeLiteralFields = true,
|
||||
compositeLiteralTypes = true,
|
||||
functionTypeParameters = true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
-- 遍历列表并正式启用服务器
|
||||
for _, server in ipairs(servers) do
|
||||
vim.lsp.enable(server)
|
||||
end
|
||||
end)
|
||||
end
|
||||
})
|
||||
28
lua/pack/configs/noice.lua
Normal file
28
lua/pack/configs/noice.lua
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
-- === noice ===
|
||||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "noice.nvim",
|
||||
module = "noice",
|
||||
deps = { "nui.nvim" },
|
||||
}
|
||||
|
||||
-- 复刻 "VeryLazy" 策略
|
||||
vim.api.nvim_create_autocmd("UIEnter", {
|
||||
callback = function()
|
||||
-- vim.schedule 的作用是:别卡住界面的渲染,等 UI 画完了、闲下来了,再偷偷加载
|
||||
vim.schedule(function()
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({
|
||||
presets = {
|
||||
bottom_search = true, -- use a classic bottom cmdline for search
|
||||
command_palette = true, -- position the cmdline and popupmenu together
|
||||
long_message_to_split = true, -- long messages will be sent to a split
|
||||
inc_rename = false, -- enables an input dialog for inc-rename.nvim
|
||||
lsp_doc_border = false, -- add a border to hover docs and signature help
|
||||
},
|
||||
})
|
||||
end)
|
||||
end)
|
||||
end
|
||||
})
|
||||
36
lua/pack/configs/peek.lua
Normal file
36
lua/pack/configs/peek.lua
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "peek.nvim",
|
||||
module = "peek",
|
||||
deps = {},
|
||||
-- 编译命令:需要环境中安装了 deno
|
||||
build_cmd = { "deno", "task", "--quiet", "build:fast" },
|
||||
}
|
||||
|
||||
PackUtils.setup_listener(P.name, P.build_cmd)
|
||||
|
||||
-- 2. 封装加载逻辑
|
||||
local function load_peek()
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({
|
||||
port = 9000,
|
||||
app = { "zen", "-private-window" },
|
||||
-- app = { "firefox-esr", "-private-window" },
|
||||
-- app = { "google-chrome-stable", "--app=http://localhost:9000/?theme=dark", "--incognito" },
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
-- 在插件未加载时,这些命令就存在了。一旦被调用,它们会先加载插件,再执行真正的功能。
|
||||
vim.api.nvim_create_user_command("PeekOpen", function()
|
||||
load_peek() -- 触发 PackUtils.load (包含构建检查)
|
||||
require("peek").open()
|
||||
end, { desc = "Lazy load and open Peek" })
|
||||
|
||||
vim.api.nvim_create_user_command("PeekClose", function()
|
||||
-- 如果插件没加载,Close 命令通常不需要做任何事,或者也触发加载
|
||||
if PackUtils.is_initialized[P.name] then
|
||||
require("peek").close()
|
||||
end
|
||||
end, { desc = "Close Peek" })
|
||||
37
lua/pack/configs/snacks.lua
Normal file
37
lua/pack/configs/snacks.lua
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
-- === Snacks ===
|
||||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "snacks.nvim",
|
||||
module = "snacks",
|
||||
}
|
||||
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({
|
||||
image = {},
|
||||
lazygit = {},
|
||||
notifier = {}, -- 替代了folke/noice.nvim插件的rcarriga/nvim-notify依赖
|
||||
picker = {
|
||||
win = {
|
||||
input = {
|
||||
keys = { -- Esc直接关闭窗口,不进入normal模式
|
||||
["<Esc>"] = { "close", mode = { "n", "i" } },
|
||||
["<c-e>"] = { "list_down", mode = { "i", "n" } },
|
||||
["<c-u>"] = { "list_up", mode = { "i", "n" } },
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
end)
|
||||
|
||||
local map = require("core.keymap")
|
||||
map:lua("<c-g>", "Snacks.lazygit()")
|
||||
map:lua("gr", "Snacks.picker.lsp_references()")
|
||||
|
||||
-- 彻底清除 Neovim 0.10+ 自带的 LSP 默认映射,解除 `gr` 的 1秒等待延迟
|
||||
local default_lsp_keys = { "grr", "grn", "gra", "gri", "grt", "grx" }
|
||||
for _, key in ipairs(default_lsp_keys) do
|
||||
pcall(vim.keymap.del, "n", key) -- 使用 pcall 忽略找不到映射时的报错
|
||||
pcall(vim.keymap.del, "x", key)
|
||||
end
|
||||
53
lua/pack/configs/treesitter.lua
Normal file
53
lua/pack/configs/treesitter.lua
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "nvim-treesitter",
|
||||
module = "nvim-treesitter",
|
||||
build_cmd = ":TSUpdate",
|
||||
}
|
||||
|
||||
PackUtils.setup_listener(P.name, P.build_cmd)
|
||||
|
||||
local ensure_installed = {
|
||||
"json",
|
||||
"rust",
|
||||
"python",
|
||||
"lua",
|
||||
"markdown",
|
||||
"bash",
|
||||
"java",
|
||||
}
|
||||
|
||||
-- 在 FileType 确定时,检查、安装并启动
|
||||
vim.api.nvim_create_autocmd("FileType", {
|
||||
group = vim.api.nvim_create_augroup("NativeTreesitter", { clear = true }),
|
||||
callback = function(args)
|
||||
local buf = args.buf
|
||||
local ft = vim.bo[buf].filetype
|
||||
-- 过滤无效 buffer, 终端, 以及 yazi
|
||||
if ft == "" or ft == "yazi" or vim.bo[buf].buftype ~= "" then return end
|
||||
-- 过滤大型文件 (大于 100KB 不开启 Treesitter,防止卡顿)
|
||||
local max_filesize = 100 * 1024
|
||||
local ok, stats = pcall(vim.loop.fs_stat, vim.api.nvim_buf_get_name(buf))
|
||||
if ok and stats and stats.size > max_filesize then return end
|
||||
-- 获取标准化的 Parser 名称
|
||||
local lang = vim.treesitter.language.get_lang(ft) or ft
|
||||
-- 检查该语言是否在我们的配置列表中
|
||||
if vim.tbl_contains(ensure_installed, lang) then
|
||||
local no_err, is_added = pcall(vim.treesitter.language.add, lang)
|
||||
if not no_err or not is_added then
|
||||
-- 只有在需要安装时,才把 nvim-treesitter 插件加载进内存
|
||||
PackUtils.load(P, function() end)
|
||||
vim.notify("🌱 Installing " .. lang .. " parser...", vim.log.levels.INFO)
|
||||
local ts = require("nvim-treesitter")
|
||||
if ts.install then
|
||||
ts.install({ lang }):wait(60000)
|
||||
else
|
||||
vim.cmd("TSInstall " .. lang)
|
||||
end
|
||||
end
|
||||
-- 万事俱备,启动高亮
|
||||
pcall(vim.treesitter.start, buf, lang)
|
||||
end
|
||||
end,
|
||||
})
|
||||
47
lua/pack/configs/tv.lua
Normal file
47
lua/pack/configs/tv.lua
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
-- === television模糊查找 ===
|
||||
if vim.g.vscode then return end
|
||||
|
||||
local P = {
|
||||
name = "tv.nvim", -- 仓库名
|
||||
module = "tv", -- require模块名
|
||||
}
|
||||
|
||||
local function load_plugin()
|
||||
PackUtils.load(P, function(plugin)
|
||||
local h = plugin.handlers
|
||||
plugin.setup({
|
||||
channels = {
|
||||
["git-files"] = {
|
||||
handlers = {
|
||||
["<CR>"] = h.open_as_files,
|
||||
},
|
||||
},
|
||||
files = {
|
||||
handlers = {
|
||||
["<CR>"] = h.open_as_files, -- default: open selected files
|
||||
},
|
||||
},
|
||||
-- `text`: ripgrep search through file contents
|
||||
text = {
|
||||
handlers = {
|
||||
['<CR>'] = h.open_at_line, -- Jump to line:col in file
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
end)
|
||||
end
|
||||
|
||||
vim.keymap.set({ "n", "v" }, "<c-t>", function()
|
||||
load_plugin()
|
||||
if vim.fs.root(0, '.git') then
|
||||
vim.cmd('Tv git-files')
|
||||
else
|
||||
vim.cmd('Tv files')
|
||||
end
|
||||
end, { desc = "Smart Tv: git-files or files" })
|
||||
|
||||
vim.keymap.set({ "n", "v" }, "<c-f>", function()
|
||||
load_plugin()
|
||||
vim.cmd('Tv text')
|
||||
end, { desc = "模糊查找字符串" })
|
||||
32
lua/pack/configs/yazi.lua
Normal file
32
lua/pack/configs/yazi.lua
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
-- VSCode 拦截
|
||||
if vim.g.vscode then
|
||||
local vscode = require("vscode")
|
||||
vim.keymap.set("n", "tt", function()
|
||||
vscode.action("yazi-vscode.toggle")
|
||||
end, { noremap = true, silent = true, desc = "Toggle Yazi (VSCode)" })
|
||||
return
|
||||
end
|
||||
|
||||
vim.g.loaded_netrwPlugin = 1
|
||||
|
||||
local P = {
|
||||
name = "yazi.nvim",
|
||||
module = "yazi",
|
||||
deps = { "plenary.nvim" },
|
||||
}
|
||||
|
||||
-- 快捷键触发懒加载
|
||||
vim.keymap.set({ "n", "v" }, "tt", function()
|
||||
-- 核心:直接调用引擎,把配置逻辑传进去
|
||||
PackUtils.load(P, function(plugin)
|
||||
plugin.setup({
|
||||
open_for_directories = false,
|
||||
keymaps = { show_help = "<f1>" },
|
||||
})
|
||||
end)
|
||||
-- 提前触发 BufReadPost 的事件钩子,让 LSP 悄悄在后台 require 完毕
|
||||
vim.schedule(function()
|
||||
vim.api.nvim_exec_autocmds("BufReadPost", { modeline = false })
|
||||
end)
|
||||
vim.cmd("Yazi")
|
||||
end, { desc = "Open yazi" })
|
||||
318
lua/pack/plugins.lua
Normal file
318
lua/pack/plugins.lua
Normal file
|
|
@ -0,0 +1,318 @@
|
|||
-- ==============================================================
|
||||
-- 插件花名册
|
||||
-- ==============================================================
|
||||
local specs = {
|
||||
-- 公共依赖
|
||||
'https://github.com/nvim-lua/plenary.nvim',
|
||||
'https://github.com/williamboman/mason.nvim',
|
||||
'https://github.com/mason-org/mason-registry',
|
||||
'https://github.com/nvim-tree/nvim-web-devicons',
|
||||
-- blinkcmp.lua 自动补全、代码片段
|
||||
'https://github.com/saghen/blink.cmp',
|
||||
'https://github.com/rafamadriz/friendly-snippets',
|
||||
-- lspconfig.lua
|
||||
'https://github.com/neovim/nvim-lspconfig',
|
||||
'https://github.com/williamboman/mason-lspconfig.nvim',
|
||||
'https://github.com/MysticalDevil/inlay-hints.nvim',
|
||||
-- treesitter.lua 需要安装tree-sitter-cli工具包
|
||||
'https://github.com/nvim-treesitter/nvim-treesitter',
|
||||
-- indentblankline.lua 彩虹缩进
|
||||
"https://github.com/lukas-reineke/indent-blankline.nvim",
|
||||
-- conform.lua 格式化工具formatter
|
||||
"https://github.com/stevearc/conform.nvim",
|
||||
-- kommentary.lua 注释插件
|
||||
"https://github.com/b3nj5m1n/kommentary",
|
||||
-- noice.lua 取代消息、命令行和弹出菜单的 UI
|
||||
"https://github.com/folke/noice.nvim",
|
||||
"https://github.com/MunifTanjim/nui.nvim",
|
||||
-- snacks.lua 图片预览、lazygit、lsp_references模糊查找
|
||||
"https://github.com/folke/snacks.nvim",
|
||||
-- tv.lua 模糊查找television
|
||||
"https://github.com/alexpasmantier/tv.nvim",
|
||||
-- coderunner.lua 运行代码
|
||||
'https://github.com/CRAG666/code_runner.nvim',
|
||||
-- peek.lua 预览markdown
|
||||
'https://github.com/cap153/peek.nvim',
|
||||
-- bufferline.lua 顶部状态栏
|
||||
'https://github.com/akinsho/bufferline.nvim',
|
||||
-- yazi.lua 文件管理器
|
||||
'https://github.com/mikavilpas/yazi.nvim',
|
||||
-- 查看可用键位
|
||||
"https://github.com/folke/which-key.nvim",
|
||||
}
|
||||
-- 禁用插件:不会加载,不会下载(如果是新添加的),已在硬盘上不会被删除
|
||||
local disabled = {
|
||||
-- { src = 'https://github.com/saghen/blink.cmp', version = 'v1.10.2' }, -- 指定版本,暂时不想用,但想留着源码
|
||||
}
|
||||
|
||||
-- ==============================================================
|
||||
-- 快捷管理命令
|
||||
-- ==============================================================
|
||||
|
||||
-- 获取所有已安装插件的名称列表(用于 Tab 补全)
|
||||
local function get_plugin_names(arg_lead)
|
||||
local installed = vim.pack.get(nil, { info = false })
|
||||
local names = {}
|
||||
for _, p in ipairs(installed) do
|
||||
local name = p.spec.name
|
||||
-- 只添加匹配开头字符串的插件
|
||||
if name:lower():find(arg_lead:lower(), 1, true) == 1 then
|
||||
table.insert(names, name)
|
||||
end
|
||||
end
|
||||
-- 排序让补全列表更整洁
|
||||
table.sort(names)
|
||||
return names
|
||||
end
|
||||
|
||||
-- :PackUpdate 命令更新插件,不带参数更新全部
|
||||
vim.api.nvim_create_user_command("PackUpdate", function(opts)
|
||||
local targets = #opts.fargs > 0 and opts.fargs or nil
|
||||
if targets then
|
||||
vim.notify("Checking updates for: " .. table.concat(targets, ", "), vim.log.levels.INFO)
|
||||
else
|
||||
vim.notify("Checking updates for all plugins...", vim.log.levels.INFO)
|
||||
end
|
||||
vim.pack.update(targets)
|
||||
end, {
|
||||
nargs = "*", -- 支持 0 到多个参数
|
||||
complete = get_plugin_names, -- 绑定补全函数
|
||||
desc = "Update specified or all plugins",
|
||||
})
|
||||
|
||||
-- :PackStatus 命令查看插件当前状态和版本
|
||||
vim.api.nvim_create_user_command("PackStatus", function(opts)
|
||||
local targets = #opts.fargs > 0 and opts.fargs or nil
|
||||
vim.pack.update(targets, { offline = true })
|
||||
end, {
|
||||
nargs = "*",
|
||||
complete = get_plugin_names,
|
||||
desc = "Check plugin status without downloading",
|
||||
})
|
||||
|
||||
-- ==============================================================
|
||||
-- 插件管理引擎 (PackUtils) (暴露给全局,供 configs/*.lua 调用)
|
||||
-- ==============================================================
|
||||
_G.PackUtils = {
|
||||
is_building = {}, -- 记录各插件的构建状态,防止重复构建
|
||||
is_initialized = {}, -- 统一在这里管理所有插件的初始化状态
|
||||
disabled_plugins = {}, -- 专门记录被禁用的插件,供 load 拦截使用
|
||||
}
|
||||
|
||||
-- [解析插件名]
|
||||
function PackUtils.get_name(spec)
|
||||
local url = type(spec) == "table" and spec.src or spec
|
||||
return type(spec) == "table" and spec.name or url:match("([^/]+)$"):gsub("%.git$", "")
|
||||
end
|
||||
|
||||
-- [同步清理] 自动删除孤儿,并注册禁用名单
|
||||
function PackUtils.sync(active_specs, disabled_specs)
|
||||
disabled_specs = disabled_specs or {}
|
||||
local protected_names = {}
|
||||
|
||||
-- 将插件加入受保护名单
|
||||
for _, spec in ipairs(active_specs) do
|
||||
protected_names[PackUtils.get_name(spec)] = true
|
||||
end
|
||||
for _, spec in ipairs(disabled_specs) do
|
||||
local name = PackUtils.get_name(spec)
|
||||
protected_names[name] = true
|
||||
PackUtils.disabled_plugins[name] = true -- 写入字典,供 load 拦截
|
||||
end
|
||||
|
||||
-- 扫描磁盘
|
||||
local pack_dir = vim.fn.stdpath("data") .. "/site/pack"
|
||||
local installed_plugins = {}
|
||||
local packages = vim.fn.expand(pack_dir .. "/*", false, true)
|
||||
for _, pkg in ipairs(packages) do
|
||||
for _, type_dir in ipairs({ "start", "opt" }) do
|
||||
local path = pkg .. "/" .. type_dir
|
||||
if vim.fn.isdirectory(path) == 1 then
|
||||
local dirs = vim.fn.expand(path .. "/*", false, true)
|
||||
for _, dir in ipairs(dirs) do
|
||||
local name = dir:match("([^/]+)$")
|
||||
if name ~= "README.md" and name ~= "doc" then
|
||||
table.insert(installed_plugins, name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
-- 找出既不在 active 也不在 disabled 里的孤儿
|
||||
local to_delete = {}
|
||||
for _, installed in ipairs(installed_plugins) do
|
||||
if not protected_names[installed] then
|
||||
table.insert(to_delete, installed)
|
||||
end
|
||||
end
|
||||
|
||||
if #to_delete > 0 then
|
||||
vim.schedule(function()
|
||||
vim.notify("🧹 Clean Up Orphaned Plugins: " .. table.concat(to_delete, ", "), vim.log.levels.INFO)
|
||||
vim.pack.del(to_delete)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- [动态路径] 获取插件根目录
|
||||
function PackUtils.get_root(name)
|
||||
local paths = vim.api.nvim_get_runtime_file("pack/*/*/" .. name, true)
|
||||
if #paths > 0 then return paths[1] end
|
||||
local glob = vim.fn.globpath(vim.o.packpath, "pack/*/*/" .. name, 0, 1)
|
||||
return glob[1] or nil
|
||||
end
|
||||
|
||||
-- [构建执行] 执行编译任务
|
||||
function PackUtils.run_build(name, build_cmd)
|
||||
if PackUtils.disabled_plugins[name] then return end
|
||||
if not build_cmd or PackUtils.is_building[name] then return end
|
||||
local path = PackUtils.get_root(name)
|
||||
if not path then return end
|
||||
local stamp = path .. "/.build_done"
|
||||
PackUtils.is_building[name] = true
|
||||
|
||||
-- 判断是否为 Neovim 内部命令 (以 : 开头)
|
||||
local is_vim_cmd = false
|
||||
local vim_cmd_str = ""
|
||||
|
||||
if type(build_cmd) == "string" and build_cmd:sub(1, 1) == ":" then
|
||||
is_vim_cmd = true
|
||||
vim_cmd_str = build_cmd:sub(2)
|
||||
elseif type(build_cmd) == "table" and type(build_cmd[1]) == "string" and build_cmd[1]:sub(1, 1) == ":" then
|
||||
is_vim_cmd = true
|
||||
vim_cmd_str = table.concat(build_cmd, " "):sub(2)
|
||||
end
|
||||
|
||||
if is_vim_cmd then
|
||||
-- 在当前实例的空闲时执行 vim.cmd
|
||||
vim.schedule(function()
|
||||
vim.notify("⚙️ Running " .. name .. " setup command...", vim.log.levels.INFO)
|
||||
-- 确保插件在当前实例已经被加载
|
||||
pcall(vim.cmd.packadd, name)
|
||||
-- 保护执行,防止命令错误导致编辑器崩溃
|
||||
local ok, err = pcall(vim.cmd, vim_cmd_str)
|
||||
PackUtils.is_building[name] = false
|
||||
if ok then
|
||||
local f = io.open(stamp, "w")
|
||||
if f then f:close() end
|
||||
vim.notify("✅ " .. name .. " setup success.", vim.log.levels.INFO)
|
||||
else
|
||||
vim.notify("❌ " .. name .. " setup failed: " .. tostring(err), vim.log.levels.ERROR)
|
||||
end
|
||||
end)
|
||||
else
|
||||
local final_cmd = {}
|
||||
if type(build_cmd) == "string" then
|
||||
for word in build_cmd:gmatch("%S+") do
|
||||
table.insert(final_cmd, word)
|
||||
end
|
||||
else
|
||||
final_cmd = build_cmd
|
||||
end
|
||||
vim.schedule(function() vim.notify("⚙️ Building " .. name .. " (Background)...", vim.log.levels.INFO) end)
|
||||
vim.system(final_cmd, { cwd = path }, function(out)
|
||||
PackUtils.is_building[name] = false
|
||||
if out.code == 0 then
|
||||
local f = io.open(stamp, "w")
|
||||
if f then f:close() end
|
||||
vim.schedule(function() vim.notify("✅ " .. name .. " build success.", vim.log.levels.INFO) end)
|
||||
else
|
||||
vim.schedule(function()
|
||||
vim.notify("❌ " .. name .. " build failed: " .. (out.stderr or "Unknown Error"), vim.log.levels.ERROR)
|
||||
end)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- [监听器] 注册安装/更新监听
|
||||
function PackUtils.setup_listener(name, build_cmd)
|
||||
if PackUtils.disabled_plugins[name] then return end
|
||||
if not build_cmd then return end
|
||||
vim.api.nvim_create_autocmd('PackChanged', {
|
||||
pattern = '*',
|
||||
callback = function(ev)
|
||||
if ev.data.spec.name == name and (ev.data.kind == "update" or ev.data.kind == "install") then
|
||||
local stamp = ev.data.path .. "/.build_done"
|
||||
os.remove(stamp) -- 自动删除.build_done文件触发构建
|
||||
PackUtils.run_build(name, build_cmd)
|
||||
end
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
-- [健康检查] 如果没标记且有构建命令,则触发构建
|
||||
function PackUtils.check_health(name, build_cmd)
|
||||
if PackUtils.disabled_plugins[name] then return end
|
||||
if not build_cmd then return end
|
||||
local path = PackUtils.get_root(name)
|
||||
if path then
|
||||
local stamp = path .. "/.build_done"
|
||||
if vim.fn.filereadable(stamp) == 0 then
|
||||
PackUtils.run_build(name, build_cmd)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 全方位防崩加载引擎
|
||||
function PackUtils.load(P, config_fn)
|
||||
if PackUtils.disabled_plugins[P.name] then return end
|
||||
if PackUtils.is_initialized[P.name] then return end
|
||||
PackUtils.check_health(P.name, P.build_cmd)
|
||||
|
||||
-- 强制将主插件挂载到 runtimepath
|
||||
pcall(vim.cmd.packadd, P.name)
|
||||
|
||||
-- 保护依赖加载 (防止 dependencies 里的插件没下载)
|
||||
if P.deps then
|
||||
for _, dep in ipairs(P.deps) do
|
||||
local dep_ok = pcall(vim.cmd.packadd, dep)
|
||||
if not dep_ok then
|
||||
vim.notify("Warning: " .. P.name .. " dependency [" .. dep .. "] missing", vim.log.levels.WARN)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- 保护 require (防止插件文件夹还没下载完)
|
||||
local req_ok, plugin = pcall(require, P.module)
|
||||
-- 如果失败,说明插件还没下载好或者路径不对,优雅退出
|
||||
if not req_ok then
|
||||
-- 经过上面强制挂载后还是失败,且硬盘上确实有这个文件夹,那绝对是 module 填错了
|
||||
if PackUtils.get_root(P.name) then
|
||||
vim.notify("Error: Plugin [" .. P.name .. "] module not found", vim.log.levels.ERROR)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
-- 保护 Setup 执行:使用 pcall 包裹传进来的匿名函数,防止 setup 里的参数写错导致崩溃
|
||||
if config_fn then
|
||||
local setup_ok, err = pcall(config_fn, plugin)
|
||||
if not setup_ok then
|
||||
vim.notify("Error: " .. P.name .. " setup failed: " .. tostring(err), vim.log.levels.ERROR)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- 只有全部流程走通,才标记为已初始化
|
||||
PackUtils.is_initialized[P.name] = true
|
||||
end
|
||||
|
||||
-- ==============================================================
|
||||
-- 执行启动流程
|
||||
-- ==============================================================
|
||||
|
||||
-- 同步清理孤儿插件并注册禁用名单
|
||||
PackUtils.sync(specs, disabled)
|
||||
|
||||
-- 正式下载/更新插件
|
||||
vim.pack.add(specs)
|
||||
|
||||
-- 加载 configs/ 注册所有监听器
|
||||
local config_path = vim.fn.stdpath("config") .. "/lua/pack/configs"
|
||||
if vim.fn.isdirectory(config_path) == 1 then
|
||||
for name, type in vim.fs.dir(config_path) do
|
||||
if type == "file" and name:match("%.lua$") then
|
||||
pcall(require, "pack.configs." .. name:gsub("%.lua$", ""))
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Add table
Add a link
Reference in a new issue