Reorganize config modules into lua/plugins/ and add comprehensive docs

- Move lsp.lua, pick.lua, treesitter.lua, git.lua to lua/plugins/
- Update all require() paths in pack.lua and init.lua
- Unify code style: tab indentation, line length control, empty braces
- Add detailed Chinese module documentation to all config files
- Add grug-far.nvim plugin with <leader>sr binding
- Enable vim._core.ui2 for Neovim 0.12+ UI enhancements
- Disable additional built-in plugins to reduce startup overhead

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-28 16:42:02 +08:00
parent 79af59f4f2
commit 9c8deeafc8
18 changed files with 872 additions and 885 deletions

View File

@ -1,5 +1,7 @@
vim.o.termguicolors = true vim.o.termguicolors = true
if vim.g.colors_name then vim.cmd("hi clear") end if vim.g.colors_name then
vim.cmd("hi clear")
end
vim.g.colors_name = "mytheme-frappe" vim.g.colors_name = "mytheme-frappe"
local h = vim.api.nvim_set_hl local h = vim.api.nvim_set_hl
h(0, "DashboardFooter", { fg = "#e5c890", italic = true }) h(0, "DashboardFooter", { fg = "#e5c890", italic = true })
@ -366,7 +368,11 @@ h(0, "MiniStatuslineModeOther", { fg = "#303446", bold = true, bg = "#81c8be" })
h(0, "MiniStatuslineModeReplace", { fg = "#303446", bold = true, bg = "#e78284" }) h(0, "MiniStatuslineModeReplace", { fg = "#303446", bold = true, bg = "#e78284" })
h(0, "MiniStatuslineModeVisual", { fg = "#303446", bold = true, bg = "#ca9ee6" }) h(0, "MiniStatuslineModeVisual", { fg = "#303446", bold = true, bg = "#ca9ee6" })
h(0, "MiniSurround", { fg = "#51576d", bg = "#f4b8e4" }) h(0, "MiniSurround", { fg = "#51576d", bg = "#f4b8e4" })
h(0, "MiniTablineCurrent", { italic = true, sp = "#e78284", fg = "#c6d0f5", bold = true, underline = true, bg = "#303446" }) h(
0,
"MiniTablineCurrent",
{ italic = true, sp = "#e78284", fg = "#c6d0f5", bold = true, underline = true, bg = "#303446" }
)
h(0, "MiniTablineFill", { bg = "NONE" }) h(0, "MiniTablineFill", { bg = "NONE" })
h(0, "MiniTablineHidden", { fg = "#c6d0f5", bg = "NONE" }) h(0, "MiniTablineHidden", { fg = "#c6d0f5", bg = "NONE" })
h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#e78284", bold = true }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#e78284", bold = true })

View File

@ -1,5 +1,7 @@
vim.o.termguicolors = true vim.o.termguicolors = true
if vim.g.colors_name then vim.cmd("hi clear") end if vim.g.colors_name then
vim.cmd("hi clear")
end
vim.g.colors_name = "mytheme-latte" vim.g.colors_name = "mytheme-latte"
local h = vim.api.nvim_set_hl local h = vim.api.nvim_set_hl
h(0, "DashboardFooter", { fg = "#df8e1d", italic = true }) h(0, "DashboardFooter", { fg = "#df8e1d", italic = true })
@ -366,7 +368,11 @@ h(0, "MiniStatuslineModeOther", { fg = "#eff1f5", bold = true, bg = "#179299" })
h(0, "MiniStatuslineModeReplace", { fg = "#eff1f5", bold = true, bg = "#d20f39" }) h(0, "MiniStatuslineModeReplace", { fg = "#eff1f5", bold = true, bg = "#d20f39" })
h(0, "MiniStatuslineModeVisual", { fg = "#eff1f5", bold = true, bg = "#8839ef" }) h(0, "MiniStatuslineModeVisual", { fg = "#eff1f5", bold = true, bg = "#8839ef" })
h(0, "MiniSurround", { fg = "#bcc0cc", bg = "#ea76cb" }) h(0, "MiniSurround", { fg = "#bcc0cc", bg = "#ea76cb" })
h(0, "MiniTablineCurrent", { italic = true, sp = "#d20f39", fg = "#4c4f69", bold = true, underline = true, bg = "#eff1f5" }) h(
0,
"MiniTablineCurrent",
{ italic = true, sp = "#d20f39", fg = "#4c4f69", bold = true, underline = true, bg = "#eff1f5" }
)
h(0, "MiniTablineFill", { bg = "NONE" }) h(0, "MiniTablineFill", { bg = "NONE" })
h(0, "MiniTablineHidden", { fg = "#4c4f69", bg = "NONE" }) h(0, "MiniTablineHidden", { fg = "#4c4f69", bg = "NONE" })
h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#d20f39", bold = true }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#d20f39", bold = true })

View File

@ -1,5 +1,7 @@
vim.o.termguicolors = true vim.o.termguicolors = true
if vim.g.colors_name then vim.cmd("hi clear") end if vim.g.colors_name then
vim.cmd("hi clear")
end
vim.g.colors_name = "mytheme-macchiato" vim.g.colors_name = "mytheme-macchiato"
local h = vim.api.nvim_set_hl local h = vim.api.nvim_set_hl
h(0, "DashboardFooter", { fg = "#eed49f", italic = true }) h(0, "DashboardFooter", { fg = "#eed49f", italic = true })
@ -366,7 +368,11 @@ h(0, "MiniStatuslineModeOther", { fg = "#24273a", bold = true, bg = "#8bd5ca" })
h(0, "MiniStatuslineModeReplace", { fg = "#24273a", bold = true, bg = "#ed8796" }) h(0, "MiniStatuslineModeReplace", { fg = "#24273a", bold = true, bg = "#ed8796" })
h(0, "MiniStatuslineModeVisual", { fg = "#24273a", bold = true, bg = "#c6a0f6" }) h(0, "MiniStatuslineModeVisual", { fg = "#24273a", bold = true, bg = "#c6a0f6" })
h(0, "MiniSurround", { fg = "#494d64", bg = "#f5bde6" }) h(0, "MiniSurround", { fg = "#494d64", bg = "#f5bde6" })
h(0, "MiniTablineCurrent", { italic = true, sp = "#ed8796", fg = "#cad3f5", bold = true, underline = true, bg = "#24273a" }) h(
0,
"MiniTablineCurrent",
{ italic = true, sp = "#ed8796", fg = "#cad3f5", bold = true, underline = true, bg = "#24273a" }
)
h(0, "MiniTablineFill", { bg = "NONE" }) h(0, "MiniTablineFill", { bg = "NONE" })
h(0, "MiniTablineHidden", { fg = "#cad3f5", bg = "NONE" }) h(0, "MiniTablineHidden", { fg = "#cad3f5", bg = "NONE" })
h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#ed8796", bold = true }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#ed8796", bold = true })

View File

@ -1,5 +1,7 @@
vim.o.termguicolors = true vim.o.termguicolors = true
if vim.g.colors_name then vim.cmd("hi clear") end if vim.g.colors_name then
vim.cmd("hi clear")
end
vim.g.colors_name = "mytheme-mocha" vim.g.colors_name = "mytheme-mocha"
local h = vim.api.nvim_set_hl local h = vim.api.nvim_set_hl
h(0, "DashboardFooter", { fg = "#f9e2af", italic = true }) h(0, "DashboardFooter", { fg = "#f9e2af", italic = true })
@ -370,7 +372,11 @@ h(0, "MiniStatuslineModeOther", { fg = "#1e1e2e", bold = true, bg = "#94e2d5" })
h(0, "MiniStatuslineModeReplace", { fg = "#1e1e2e", bold = true, bg = "#f38ba8" }) h(0, "MiniStatuslineModeReplace", { fg = "#1e1e2e", bold = true, bg = "#f38ba8" })
h(0, "MiniStatuslineModeVisual", { fg = "#1e1e2e", bold = true, bg = "#cba6f7" }) h(0, "MiniStatuslineModeVisual", { fg = "#1e1e2e", bold = true, bg = "#cba6f7" })
h(0, "MiniSurround", { fg = "#45475a", bg = "#f5c2e7" }) h(0, "MiniSurround", { fg = "#45475a", bg = "#f5c2e7" })
h(0, "MiniTablineCurrent", { italic = true, sp = "#f38ba8", fg = "#cdd6f4", bold = true, underline = true, bg = "#1e1e2e" }) h(
0,
"MiniTablineCurrent",
{ italic = true, sp = "#f38ba8", fg = "#cdd6f4", bold = true, underline = true, bg = "#1e1e2e" }
)
h(0, "MiniTablineFill", { bg = "NONE" }) h(0, "MiniTablineFill", { bg = "NONE" })
h(0, "MiniTablineHidden", { fg = "#cdd6f4", bg = "NONE" }) h(0, "MiniTablineHidden", { fg = "#cdd6f4", bg = "NONE" })
h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#f38ba8", bold = true }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#f38ba8", bold = true })

View File

@ -27,7 +27,6 @@ _G.nvim_start_time = (vim.uv or vim.loop).hrtime()
-- --
-- 注意loaded_syntax 设为 1 是为了避免旧版 Vim 兼容的 syntax 加载路径; -- 注意loaded_syntax 设为 1 是为了避免旧版 Vim 兼容的 syntax 加载路径;
-- 实际语法高亮由 treesitter 接管,不受影响。 -- 实际语法高亮由 treesitter 接管,不受影响。
vim.g.loaded_2html_plugin = 1 -- :TOhtml 命令(将 buffer 导出为 HTML vim.g.loaded_2html_plugin = 1 -- :TOhtml 命令(将 buffer 导出为 HTML
vim.g.loaded_getscript = 1 -- GetScript 插件(自动下载脚本) vim.g.loaded_getscript = 1 -- GetScript 插件(自动下载脚本)
vim.g.loaded_getscriptPlugin = 1 -- GetScript 插件主体 vim.g.loaded_getscriptPlugin = 1 -- GetScript 插件主体
@ -69,7 +68,6 @@ require("vim._core.ui2").enable({})
-- ============================================================================= -- =============================================================================
-- 这些模块按顺序加载,后续模块可以依赖前面模块的设置。 -- 这些模块按顺序加载,后续模块可以依赖前面模块的设置。
-- 例如 pack.lua 中使用的键位映射依赖于 keymaps.lua 中设置的 leader 键。 -- 例如 pack.lua 中使用的键位映射依赖于 keymaps.lua 中设置的 leader 键。
require("options") -- 全局 vim 选项设置 require("options") -- 全局 vim 选项设置
require("keymaps") -- 键位映射定义 require("keymaps") -- 键位映射定义
require("autocmds") -- 自动命令autocmd require("autocmds") -- 自动命令autocmd

View File

@ -23,7 +23,6 @@ local group = vim.api.nvim_create_augroup("UserAutocmds", { clear = true })
-- - commit / gitcommitGit 提交消息文件,总是从头开始编辑 -- - commit / gitcommitGit 提交消息文件,总是从头开始编辑
-- - xxd十六进制编辑模式 -- - xxd十六进制编辑模式
-- - gitrebaseGit rebase 交互式编辑 -- - gitrebaseGit rebase 交互式编辑
vim.api.nvim_create_autocmd("BufReadPost", { vim.api.nvim_create_autocmd("BufReadPost", {
group = group, group = group,
callback = function() callback = function()
@ -52,7 +51,6 @@ vim.api.nvim_create_autocmd("BufReadPost", {
-- r - 按回车时延续注释保留insert 模式下回车延续注释符) -- r - 按回车时延续注释保留insert 模式下回车延续注释符)
-- --
-- 注意此设置对自动格式化conform.nvim的行为也有影响。 -- 注意此设置对自动格式化conform.nvim的行为也有影响。
vim.api.nvim_create_autocmd("FileType", { vim.api.nvim_create_autocmd("FileType", {
group = group, group = group,
callback = function() callback = function()
@ -73,7 +71,6 @@ vim.api.nvim_create_autocmd("FileType", {
-- foldmethod = "expr" 表示使用表达式foldexpr计算折叠范围。 -- foldmethod = "expr" 表示使用表达式foldexpr计算折叠范围。
-- foldexpr = "v:lua.vim.treesitter.foldexpr()" 是 Neovim 0.10+ 的内置函数, -- foldexpr = "v:lua.vim.treesitter.foldexpr()" 是 Neovim 0.10+ 的内置函数,
-- 基于 treesitter 语法树计算折叠边界。 -- 基于 treesitter 语法树计算折叠边界。
vim.api.nvim_create_autocmd("BufEnter", { vim.api.nvim_create_autocmd("BufEnter", {
group = group, group = group,
once = true, once = true,

View File

@ -1,202 +0,0 @@
-- =============================================================================
-- Git 工具封装 (lua/git.lua)
-- =============================================================================
-- 本模块提供基于 mini.diff 和原生 git 命令的 Git 相关功能。
--
-- 功能列表:
-- 1. Hunk Preview<leader>ghp— 预览当前光标处 git diff hunk
-- 2. Blame Line<leader>ghb— 查看当前行的 git blame 信息
-- 3. Blame Buffer<leader>gB— 整文件 blamevia fugitive
-- 4. File History<leader>gD— 查看当前文件的 git log
--
-- 依赖:
-- - mini.diff已在 pack.lua 中通过 BufReadPost 懒加载)
-- - vim-fugitive已在 pack.lua 中通过按键触发懒加载)
-- =============================================================================
-- ---------------------------------------------------------------------------
-- 辅助函数:获取光标所在行的 hunk
-- ---------------------------------------------------------------------------
-- 遍历当前 buffer 的所有 hunks找到包含光标所在行的那个。
--
-- 返回值:
-- hunk - hunk 对象 { type, buf_start, buf_count, ref_start, ref_count }
-- ref_text - 参考文本HEAD 版本的内容)
-- nil - 光标不在任何 hunk 上
local function get_cursor_hunk()
local buf = vim.api.nvim_get_current_buf()
-- 从 mini.diff 获取当前 buffer 的 diff 数据
local data = require("mini.diff").get_buf_data(buf)
if not data or not data.hunks or #data.hunks == 0 then
return nil
end
local cursor_line = vim.api.nvim_win_get_cursor(0)[1]
for _, h in ipairs(data.hunks) do
-- hunk 在 buffer 中的起始行1-based
local start_line = math.max(h.buf_start, 1)
-- hunk 在 buffer 中的结束行
local end_line = h.buf_start + h.buf_count - 1
-- 空删除0 行)时特殊处理
if h.buf_count == 0 then
end_line = start_line
end
-- 检查光标是否在此 hunk 范围内
if cursor_line >= start_line and cursor_line <= end_line then
return h, data.ref_text
end
end
return nil
end
-- ---------------------------------------------------------------------------
-- Hunk Preview — 浮动窗口预览当前 hunk
-- ---------------------------------------------------------------------------
-- 创建一个居中的浮动窗口,显示光标所在 hunk 的详细 diff 信息。
-- 支持三种 hunk 类型:
-- add — 显示新增的行
-- delete — 显示被删除的行(从参考文本中恢复)
-- change — 并排显示修改前后的内容
local function preview_hunk()
local hunk, ref_text = get_cursor_hunk()
if not hunk then
vim.notify("光标不在变更区域", vim.log.levels.WARN)
return
end
-- 构建预览内容
local lines = {}
table.insert(lines, "Hunk 类型: " .. hunk.type)
table.insert(lines, string.format("Buffer 行: %d-%d", hunk.buf_start, hunk.buf_start + hunk.buf_count - 1))
table.insert(lines, string.format("参考行: %d-%d", hunk.ref_start, hunk.ref_start + hunk.ref_count - 1))
table.insert(lines, "---")
local buf = vim.api.nvim_get_current_buf()
local ref_lines = vim.split(ref_text or "", "\n")
if hunk.type == "delete" then
-- 删除型 hunk显示被删除的内容来自参考文本
table.insert(lines, "被删除的行(来自参考版本):")
for i = hunk.ref_start, hunk.ref_start + hunk.ref_count - 1 do
table.insert(lines, "- " .. (ref_lines[i] or ""))
end
elseif hunk.type == "add" then
-- 新增型 hunk显示 buffer 中新增的内容
table.insert(lines, "新增的行:")
for i = hunk.buf_start, hunk.buf_start + hunk.buf_count - 1 do
table.insert(lines, "+ " .. (vim.api.nvim_buf_get_lines(buf, i - 1, i, false)[1] or ""))
end
else
-- 修改型 hunk显示修改前后的对比
table.insert(lines, "修改前(参考版本):")
for i = hunk.ref_start, hunk.ref_start + hunk.ref_count - 1 do
table.insert(lines, "- " .. (ref_lines[i] or ""))
end
table.insert(lines, "修改后(当前版本):")
for i = hunk.buf_start, hunk.buf_start + hunk.buf_count - 1 do
table.insert(lines, "+ " .. (vim.api.nvim_buf_get_lines(buf, i - 1, i, false)[1] or ""))
end
end
-- 计算浮动窗口尺寸
local width = math.min(80, vim.o.columns - 4)
local height = math.min(#lines + 2, vim.o.lines - 4)
-- 创建预览 buffer
local preview_buf = vim.api.nvim_create_buf(false, true) -- 无文件、可编辑
vim.api.nvim_buf_set_lines(preview_buf, 0, -1, false, lines)
vim.api.nvim_set_option_value("modifiable", false, { buf = preview_buf })
vim.api.nvim_set_option_value("filetype", "diff", { buf = preview_buf }) -- diff 语法高亮
-- 打开浮动窗口
local win = vim.api.nvim_open_win(preview_buf, true, {
relative = "editor",
row = math.floor((vim.o.lines - height) / 2),
col = math.floor((vim.o.columns - width) / 2),
width = width,
height = height,
style = "minimal",
border = "rounded",
title = " Hunk Preview ",
title_pos = "center",
})
-- q / Esc 关闭窗口
vim.keymap.set("n", "q", function()
vim.api.nvim_win_close(win, true)
end, { buffer = preview_buf })
vim.keymap.set("n", "<Esc>", function()
vim.api.nvim_win_close(win, true)
end, { buffer = preview_buf })
end
-- ---------------------------------------------------------------------------
-- Blame Line — 查看当前行的 Git blame 信息
-- ---------------------------------------------------------------------------
-- 执行 git blame --porcelain 获取当前行的详细提交信息,
-- 以通知消息的形式展示提交哈希、作者、时间和提交摘要。
--
-- --porcelain 格式是机器可读的 blame 输出,包含以下字段:
-- <hash> <orig-line> <final-line> [<num-lines>]
-- author <name>
-- author-mail <email>
-- author-time <timestamp>
-- author-tz <timezone>
-- summary <commit-message>
-- ...
local function blame_line()
local file = vim.api.nvim_buf_get_name(0)
if file == "" then
vim.notify("当前 buffer 无文件名", vim.log.levels.WARN)
return
end
local line = vim.api.nvim_win_get_cursor(0)[1]
-- -L 限制 blame 范围为单行,提升性能
local cmd = { "git", "blame", "-L", line .. "," .. line, "--porcelain", file }
local output = vim.fn.system(cmd)
if vim.v.shell_error ~= 0 then
vim.notify("git blame 执行失败", vim.log.levels.ERROR)
return
end
-- 解析 --porcelain 输出
local hash = output:match("^(%x+)%s") or "?"
local author = output:match("author ([^\n]+)") or "?"
local email = output:match("author%-mail ([^\n]+)") or "?"
local time = output:match("author%-time (%d+)")
local summary = output:match("summary ([^\n]+)") or "?"
local time_str = time and os.date("%Y-%m-%d %H:%M", tonumber(time)) or "?"
vim.notify(
string.format("%s | %s %s | %s\n%s", hash:sub(1, 8), author, email, time_str, summary),
vim.log.levels.INFO
)
end
-- ---------------------------------------------------------------------------
-- 键位映射
-- ---------------------------------------------------------------------------
-- <leader>ghp — 预览当前 hunk
vim.keymap.set("n", "<leader>ghp", preview_hunk, { desc = "预览 hunk" })
-- <leader>ghb — 查看当前行 blame
vim.keymap.set("n", "<leader>ghb", blame_line, { desc = "Blame 当前行" })
-- <leader>gB — 整文件 blame使用 fugitive 的 :Git blame
vim.keymap.set("n", "<leader>gB", function()
vim.cmd("Git blame")
end, { desc = "Blame 整个文件" })
-- <leader>gD — 查看当前文件的 git 历史
vim.keymap.set("n", "<leader>gD", function()
vim.cmd("Git log -p -- %")
end, { desc = "查看文件 Git 历史" })

View File

@ -76,7 +76,6 @@ map("v", "<", "<gv")
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Neovim 0.12+ 内置了 undotree 插件nvim.undotree -- Neovim 0.12+ 内置了 undotree 插件nvim.undotree
-- 这里通过 packadd 按需加载并打开。 -- 这里通过 packadd 按需加载并打开。
vim.keymap.set("n", "<leader>u", function() vim.keymap.set("n", "<leader>u", function()
vim.cmd.packadd("nvim.undotree") vim.cmd.packadd("nvim.undotree")
require("undotree").open() require("undotree").open()
@ -107,7 +106,6 @@ map("n", "<leader>tt", ":term<CR>", { desc = "打开新终端" })
-- 窗口导航 -- 窗口导航
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 使用 <C-h/j/k/l> 在窗口间快速跳转,替代 <C-w> h/j/k/l 的繁琐操作。 -- 使用 <C-h/j/k/l> 在窗口间快速跳转,替代 <C-w> h/j/k/l 的繁琐操作。
map("n", "<C-h>", "<C-w>h", { desc = "切换到左侧窗口" }) map("n", "<C-h>", "<C-w>h", { desc = "切换到左侧窗口" })
map("n", "<C-j>", "<C-w>j", { desc = "切换到下方窗口" }) map("n", "<C-j>", "<C-w>j", { desc = "切换到下方窗口" })
map("n", "<C-k>", "<C-w>k", { desc = "切换到上方窗口" }) map("n", "<C-k>", "<C-w>k", { desc = "切换到上方窗口" })
@ -116,7 +114,6 @@ map("n", "<C-l>", "<C-w>l", { desc = "切换到右侧窗口" })
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Tab 管理 -- Tab 管理
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
map("n", "<leader>tc", ":tabclose<CR>", { desc = "关闭当前标签页" }) map("n", "<leader>tc", ":tabclose<CR>", { desc = "关闭当前标签页" })
map("n", "<leader>tn", ":tabnew<CR>", { desc = "新建标签页" }) map("n", "<leader>tn", ":tabnew<CR>", { desc = "新建标签页" })
map("n", "<leader>]", ":tabnext<CR>", { desc = "下一个标签页" }) map("n", "<leader>]", ":tabnext<CR>", { desc = "下一个标签页" })
@ -125,7 +122,6 @@ map("n", "<leader>[", ":tabprevious<CR>", { desc = "上一个标签页" })
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 复制文件路径 -- 复制文件路径
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 复制当前文件的相对路径到系统剪贴板 -- 复制当前文件的相对路径到系统剪贴板
map("n", "<leader>yp", function() map("n", "<leader>yp", function()
local path = vim.fn.expand("%") local path = vim.fn.expand("%")
@ -143,7 +139,6 @@ end, { desc = "复制绝对文件路径" })
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Buffer 管理 -- Buffer 管理
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 关闭当前 Buffer有未保存更改时提示确认。 -- 关闭当前 Buffer有未保存更改时提示确认。
-- 使用 vim.ui.select 提供交互式选择对话框。 -- 使用 vim.ui.select 提供交互式选择对话框。
map("n", "<leader>x", function() map("n", "<leader>x", function()

View File

@ -19,7 +19,6 @@ vim.g.netrw_banner = 0
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 1. 界面显示 -- 1. 界面显示
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
vim.opt.nu = true -- 显示绝对行号 vim.opt.nu = true -- 显示绝对行号
vim.opt.relativenumber = true -- 显示相对行号(便于配合数字 + j/k 跳转) vim.opt.relativenumber = true -- 显示相对行号(便于配合数字 + j/k 跳转)
vim.opt.cursorline = true -- 高亮当前行 vim.opt.cursorline = true -- 高亮当前行
@ -31,7 +30,6 @@ vim.opt.autoread = true -- 当文件被外部修改时自动重新
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 使用 4 空格缩进,适用于大多数语言。 -- 使用 4 空格缩进,适用于大多数语言。
-- 特定语言的缩进设置(如 JS/TS 的 2 空格)应在 ftplugin 中覆盖。 -- 特定语言的缩进设置(如 JS/TS 的 2 空格)应在 ftplugin 中覆盖。
vim.opt.tabstop = 4 -- Tab 键显示的宽度(字符数) vim.opt.tabstop = 4 -- Tab 键显示的宽度(字符数)
vim.opt.softtabstop = 4 -- 插入模式下 Tab/BS 的行为宽度 vim.opt.softtabstop = 4 -- 插入模式下 Tab/BS 的行为宽度
vim.opt.shiftwidth = 4 -- 自动缩进和 >/< 操作的宽度 vim.opt.shiftwidth = 4 -- 自动缩进和 >/< 操作的宽度
@ -43,14 +41,12 @@ vim.opt.inccommand = "split" -- 实时预览替换效果(:s 命令在
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 3. 窗口与分割 -- 3. 窗口与分割
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
vim.opt.splitbelow = true -- 水平分割时新窗口在下方 vim.opt.splitbelow = true -- 水平分割时新窗口在下方
vim.opt.splitright = true -- 垂直分割时新窗口在右侧 vim.opt.splitright = true -- 垂直分割时新窗口在右侧
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 4. 搜索行为 -- 4. 搜索行为
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
vim.opt.ignorecase = true -- 搜索时忽略大小写 vim.opt.ignorecase = true -- 搜索时忽略大小写
vim.opt.smartcase = true -- 如果搜索包含大写字母,则区分大小写 vim.opt.smartcase = true -- 如果搜索包含大写字母,则区分大小写
-- 与 ignorecase 配合:全小写时忽略大小写, -- 与 ignorecase 配合:全小写时忽略大小写,
@ -59,7 +55,6 @@ vim.opt.smartcase = true -- 如果搜索包含大写字母,则区
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 5. 状态栏 -- 5. 状态栏
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
vim.opt.laststatus = 3 -- 全局状态栏(所有窗口共享一个状态栏) vim.opt.laststatus = 3 -- 全局状态栏(所有窗口共享一个状态栏)
-- 值为 2 时每个窗口有独立状态栏 -- 值为 2 时每个窗口有独立状态栏
@ -67,7 +62,6 @@ vim.opt.laststatus = 3 -- 全局状态栏(所有窗口共享一
-- 6. 文件持久化 -- 6. 文件持久化
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 禁用 swap 和 backup 文件,使用 undofile 实现跨会话的撤销历史。 -- 禁用 swap 和 backup 文件,使用 undofile 实现跨会话的撤销历史。
vim.opt.swapfile = false -- 禁用交换文件(.swp vim.opt.swapfile = false -- 禁用交换文件(.swp
vim.opt.backup = false -- 禁用备份文件(~ 后缀) vim.opt.backup = false -- 禁用备份文件(~ 后缀)
vim.opt.undodir = vim.fn.stdpath("data") .. "/undodir" -- 撤销历史存放目录 vim.opt.undodir = vim.fn.stdpath("data") .. "/undodir" -- 撤销历史存放目录
@ -76,7 +70,6 @@ vim.opt.undofile = true -- 启用持久化撤销(关闭文件后
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 7. 补全与消息 -- 7. 补全与消息
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 补全选项: -- 补全选项:
-- menuone - 即使只有一个匹配项也显示菜单 -- menuone - 即使只有一个匹配项也显示菜单
-- noselect - 不自动选择第一项(需手动选择) -- noselect - 不自动选择第一项(需手动选择)
@ -102,7 +95,6 @@ end)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 9. 其他杂项 -- 9. 其他杂项
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 将 @ 和 - 视为文件名的一部分(用于 gf/gx 等命令) -- 将 @ 和 - 视为文件名的一部分(用于 gf/gx 等命令)
vim.opt.isfname:append("@-@") vim.opt.isfname:append("@-@")
@ -123,7 +115,6 @@ vim.opt.termguicolors = true
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 将 mini.diff 的符号高亮组链接到标准 diff 高亮组, -- 将 mini.diff 的符号高亮组链接到标准 diff 高亮组,
-- 使 git diff 的添加/修改/删除标记使用 colorscheme 定义的配色。 -- 使 git diff 的添加/修改/删除标记使用 colorscheme 定义的配色。
vim.api.nvim_set_hl(0, "MiniDiffSignAdd", { link = "DiffAdd" }) -- 添加的行 vim.api.nvim_set_hl(0, "MiniDiffSignAdd", { link = "DiffAdd" }) -- 添加的行
vim.api.nvim_set_hl(0, "MiniDiffSignChange", { link = "DiffChange" }) -- 修改的行 vim.api.nvim_set_hl(0, "MiniDiffSignChange", { link = "DiffChange" }) -- 修改的行
vim.api.nvim_set_hl(0, "MiniDiffSignDelete", { link = "DiffDelete" }) -- 删除的行 vim.api.nvim_set_hl(0, "MiniDiffSignDelete", { link = "DiffDelete" }) -- 删除的行
@ -134,7 +125,6 @@ vim.api.nvim_set_hl(0, "MiniDiffSignDelete", { link = "DiffDelete" }) -- 删除
-- 复制文本时短暂高亮被复制的区域,提供视觉反馈。 -- 复制文本时短暂高亮被复制的区域,提供视觉反馈。
-- vim.hl.on_yank() 是 Neovim 0.11+ 的内置函数, -- vim.hl.on_yank() 是 Neovim 0.11+ 的内置函数,
-- 旧版使用 vim.highlight.on_yank()。 -- 旧版使用 vim.highlight.on_yank()。
vim.api.nvim_create_autocmd("TextYankPost", { vim.api.nvim_create_autocmd("TextYankPost", {
desc = "复制文本时高亮被复制的区域", desc = "复制文本时高亮被复制的区域",
callback = function() callback = function()

View File

@ -24,8 +24,8 @@
-- ============================================================================= -- =============================================================================
local lazy = require("lazy") local lazy = require("lazy")
local pick = require("pick") local pick = require("plugins.pick")
require("git") require("plugins.git")
-- 在 VimEnter 时记录完整启动时间(此时 runtime 插件、ShaDa 等已加载完毕), -- 在 VimEnter 时记录完整启动时间(此时 runtime 插件、ShaDa 等已加载完毕),
-- 使启动页显示的耗时更接近 --startuptime 的口径。 -- 使启动页显示的耗时更接近 --startuptime 的口径。
@ -43,7 +43,6 @@ vim.api.nvim_create_autocmd("VimEnter", {
-- vim.pack.add(urls, { load = false }) 将插件下载到 pack 目录, -- vim.pack.add(urls, { load = false }) 将插件下载到 pack 目录,
-- 但不自动加载load = false。后续通过 packadd 或 require 按需加载。 -- 但不自动加载load = false。后续通过 packadd 或 require 按需加载。
-- 所有插件的状态锁定在 nvim-pack-lock.json 中。 -- 所有插件的状态锁定在 nvim-pack-lock.json 中。
vim.pack.add({ vim.pack.add({
"https://github.com/nvim-mini/mini.nvim", -- 核心 UI/功能插件集 "https://github.com/nvim-mini/mini.nvim", -- 核心 UI/功能插件集
"https://github.com/rafamadriz/friendly-snippets", -- 代码片段库 "https://github.com/rafamadriz/friendly-snippets", -- 代码片段库
@ -168,13 +167,23 @@ local starter = require("mini.starter")
-- 口径与 lazy._loaded 一致:每个独立初始化的功能模块算一个, -- 口径与 lazy._loaded 一致:每个独立初始化的功能模块算一个,
-- 不再按 vim.pack 的插件包统计(如 mini.nvim 包含多个模块)。 -- 不再按 vim.pack 的插件包统计(如 mini.nvim 包含多个模块)。
local all_modules = { local all_modules = {
"starter", "notify", "cmdline", "icons", "starter",
"pick", "files", "notify",
"diff", "surround", "cmdline",
"completion", "snippets", "icons",
"treesitter", "lsp", "pick",
"conform", "lspconfig", "mason", "files",
"fugitive", "grugfar", "diff",
"surround",
"completion",
"snippets",
"treesitter",
"lsp",
"conform",
"lspconfig",
"mason",
"fugitive",
"grugfar",
} }
-- 计算 Logo 的最大显示宽度,用于页脚居中计算 -- 计算 Logo 的最大显示宽度,用于页脚居中计算
@ -217,7 +226,6 @@ lazy.track("starter")
-- mini.files — 文件浏览器(按键触发懒加载) -- mini.files — 文件浏览器(按键触发懒加载)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 提供悬浮文件浏览器,支持目录导航和文件操作。 -- 提供悬浮文件浏览器,支持目录导航和文件操作。
-- mini.files setup 配置(复用) -- mini.files setup 配置(复用)
local function setup_mini_files() local function setup_mini_files()
require("mini.files").setup({ require("mini.files").setup({
@ -267,7 +275,6 @@ vim.api.nvim_create_autocmd("User", {
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 提供文件类型图标,供 mini.pick、mini.files 等使用。 -- 提供文件类型图标,供 mini.pick、mini.files 等使用。
-- 在 VimEnter 后 setup确保 UI 已初始化。 -- 在 VimEnter 后 setup确保 UI 已初始化。
vim.api.nvim_create_autocmd("VimEnter", { vim.api.nvim_create_autocmd("VimEnter", {
once = true, once = true,
callback = function() callback = function()
@ -281,7 +288,6 @@ vim.api.nvim_create_autocmd("VimEnter", {
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 替换默认的 vim.notify提供美观的浮动通知窗口。 -- 替换默认的 vim.notify提供美观的浮动通知窗口。
-- 直接初始化(轻量,不阻塞启动)。 -- 直接初始化(轻量,不阻塞启动)。
require("mini.notify").setup({ require("mini.notify").setup({
content = { content = {
-- 简化格式:只显示消息内容,不附加时间戳等元信息 -- 简化格式:只显示消息内容,不附加时间戳等元信息
@ -299,7 +305,6 @@ lazy.track("notify")
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 提供美观的命令行界面,首次按 : 时加载。 -- 提供美观的命令行界面,首次按 : 时加载。
-- 使用 expr 映射:返回 ":" 让 Vim 继续处理命令行输入。 -- 使用 expr 映射:返回 ":" 让 Vim 继续处理命令行输入。
vim.keymap.set("n", ":", function() vim.keymap.set("n", ":", function()
-- 首次按下 : 时删除此映射,避免后续重复触发 setup -- 首次按下 : 时删除此映射,避免后续重复触发 setup
vim.keymap.del("n", ":") vim.keymap.del("n", ":")
@ -320,7 +325,6 @@ end, { desc = "诊断位置列表" })
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 所有 pick 相关映射共用 pick.load_pick() 初始化,确保 mini.pick 和 mini.extra -- 所有 pick 相关映射共用 pick.load_pick() 初始化,确保 mini.pick 和 mini.extra
-- 只在首次使用时 setup 一次。 -- 只在首次使用时 setup 一次。
-- <leader>ff :文件查找 -- <leader>ff :文件查找
lazy.on_keys("pick", "<leader>ff", "n", pick.load_pick, function() lazy.on_keys("pick", "<leader>ff", "n", pick.load_pick, function()
require("mini.pick").builtin.files() require("mini.pick").builtin.files()
@ -339,7 +343,7 @@ end, { desc = "搜索键位映射" })
-- <leader>fa :查找所有文件(含隐藏和忽略的文件) -- <leader>fa :查找所有文件(含隐藏和忽略的文件)
lazy.on_keys("pick", "<leader>fa", "n", pick.load_pick, function() lazy.on_keys("pick", "<leader>fa", "n", pick.load_pick, function()
require("mini.pick").builtin.cli({ require("mini.pick").builtin.cli({
command = { "rg", "--files", "--hidden", "--no-ignore", "--color=never" } command = { "rg", "--files", "--hidden", "--no-ignore", "--color=never" },
}) })
end, { desc = "查找所有文件(含隐藏/忽略)" }) end, { desc = "查找所有文件(含隐藏/忽略)" })
@ -362,7 +366,6 @@ end, { desc = "当前缓冲区行内搜索" })
-- vim-fugitive — Git 集成(按键触发懒加载) -- vim-fugitive — Git 集成(按键触发懒加载)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- tpope 的 Git 包装插件,提供 :Git 等命令。 -- tpope 的 Git 包装插件,提供 :Git 等命令。
local function load_fugitive() local function load_fugitive()
vim.cmd.packadd("vim-fugitive") vim.cmd.packadd("vim-fugitive")
end end
@ -381,7 +384,6 @@ end, { desc = "Git diff 分割" })
-- mini.completion — 自动补全InsertEnter 懒加载) -- mini.completion — 自动补全InsertEnter 懒加载)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 轻量级补全引擎,支持 LSP 补全、buffer 单词、路径补全。 -- 轻量级补全引擎,支持 LSP 补全、buffer 单词、路径补全。
lazy.on_event("completion", "InsertEnter", "*", function() lazy.on_event("completion", "InsertEnter", "*", function()
require("mini.completion").setup({ require("mini.completion").setup({
lsp_completion = { lsp_completion = {
@ -394,7 +396,6 @@ end)
-- mini.snippets — 代码片段InsertEnter 懒加载) -- mini.snippets — 代码片段InsertEnter 懒加载)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 代码片段引擎,支持 LSP 片段扩展。 -- 代码片段引擎,支持 LSP 片段扩展。
lazy.on_event("snippets", "InsertEnter", "*", function() lazy.on_event("snippets", "InsertEnter", "*", function()
local MiniSnippets = require("mini.snippets") local MiniSnippets = require("mini.snippets")
MiniSnippets.setup({ MiniSnippets.setup({
@ -411,7 +412,6 @@ end)
-- mini.diff — Git diff 标记BufReadPost 懒加载) -- mini.diff — Git diff 标记BufReadPost 懒加载)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 在 signcolumn 中显示当前 buffer 相对于 git HEAD 的变更标记。 -- 在 signcolumn 中显示当前 buffer 相对于 git HEAD 的变更标记。
lazy.on_event("diff", "BufReadPost", "*", function() lazy.on_event("diff", "BufReadPost", "*", function()
require("mini.diff").setup({ require("mini.diff").setup({
-- 使用 git 作为 diff 源index = false 表示对比工作区 vs HEAD不含暂存区 -- 使用 git 作为 diff 源index = false 表示对比工作区 vs HEAD不含暂存区
@ -431,7 +431,6 @@ end)
-- mini.surround — 环绕文本操作BufReadPost 懒加载) -- mini.surround — 环绕文本操作BufReadPost 懒加载)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 快速添加、删除、修改包围符号如括号、引号、HTML 标签等)。 -- 快速添加、删除、修改包围符号如括号、引号、HTML 标签等)。
lazy.on_event("surround", "BufReadPost", "*", function() lazy.on_event("surround", "BufReadPost", "*", function()
require("mini.surround").setup() require("mini.surround").setup()
end) end)
@ -441,20 +440,18 @@ end)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- treesitter 和 lsp 是启动时最耗时的模块, -- treesitter 和 lsp 是启动时最耗时的模块,
-- 延迟到 VimEnter 事件触发后加载,让编辑器界面先渲染出来。 -- 延迟到 VimEnter 事件触发后加载,让编辑器界面先渲染出来。
lazy.on_event("treesitter", "VimEnter", "*", function() lazy.on_event("treesitter", "VimEnter", "*", function()
require("treesitter").setup() require("plugins.treesitter").setup()
end) end)
lazy.on_event("lsp", "VimEnter", "*", function() lazy.on_event("lsp", "VimEnter", "*", function()
require("lsp") require("plugins.lsp")
end) end)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- grug-far.nvim — 搜索与替换(按键触发懒加载) -- grug-far.nvim — 搜索与替换(按键触发懒加载)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- 提供类似 VS Code 的查找替换界面,支持正则和文件过滤。 -- 提供类似 VS Code 的查找替换界面,支持正则和文件过滤。
local function load_grug_far() local function load_grug_far()
vim.cmd.packadd("grug-far.nvim") vim.cmd.packadd("grug-far.nvim")
require("grug-far").setup({ headerMaxWidth = 80 }) require("grug-far").setup({ headerMaxWidth = 80 })

View File

@ -1,125 +0,0 @@
-- =============================================================================
-- mini.pick 封装 (lua/pick.lua)
-- =============================================================================
-- 本模块封装 mini.pick 和 mini.extra 的初始化逻辑,
-- 提供统一的懒加载入口和自定义 picker。
--
-- 主要功能:
-- 1. load_pick() — 懒加载 mini.pick 和 mini.extra
-- 2. Buffer picker<leader><leader>)— 自定义 buffer 列表,支持 <C-d> 删除
-- 3. Filetype picker<leader>ft— 快速切换文件类型
-- =============================================================================
local lazy = require("lazy")
local M = {}
-- ---------------------------------------------------------------------------
-- 懒加载入口
-- ---------------------------------------------------------------------------
-- 通过 lazy.load 确保 mini.pick 和 mini.extra 只初始化一次。
-- 此函数被 pack.lua 中所有 pick 相关的 on_keys 映射共用。
M.load_pick = function()
lazy.load("pick", function()
require("mini.pick").setup()
end)
lazy.load("extra", function()
require("mini.extra").setup()
end)
end
-- ---------------------------------------------------------------------------
-- Buffer Picker<leader><leader>
-- ---------------------------------------------------------------------------
-- 显示当前打开的 buffer 列表,按最近使用时间排序。
-- 特殊功能:
-- - <C-d>:删除当前选中的 buffer带图标过滤
-- - 支持 mini.icons 图标显示
vim.keymap.set("n", "<leader><leader>", function()
M.load_pick()
local MiniPick = require("mini.pick")
-- 删除当前选中 buffer 的回调函数
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
-- 安全删除 bufferforce = true 跳过未保存确认)
pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
-- 如果 picker 仍活跃,刷新列表以移除已删除的 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排除特殊 buffer 如终端、quickfix
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)
-- 构建 picker items
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
-- 启动 picker
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 = "<C-d>", func = delete_buf },
},
})
end, { desc = "Buffer 列表" })
-- ---------------------------------------------------------------------------
-- Filetype Picker<leader>ft
-- ---------------------------------------------------------------------------
-- 显示所有可用的文件类型列表,选择后设置当前 buffer 的 filetype。
-- 常用于打开无扩展名文件或纠正错误的文件类型检测。
vim.keymap.set("n", "<leader>ft", function()
M.load_pick()
local MiniPick = require("mini.pick")
-- 获取所有可用的文件类型名称
local filetypes = vim.fn.getcompletion("", "filetype")
MiniPick.start({
source = {
name = "Filetypes",
items = filetypes,
choose = function(item)
-- 选择后将当前 buffer 的 filetype 设为选中值
vim.bo.filetype = item
end,
},
})
end, { desc = "切换文件类型" })
return M

199
lua/plugins/git.lua Normal file
View File

@ -0,0 +1,199 @@
-- =============================================================================
-- Git 工具封装 (lua/git.lua)
-- =============================================================================
-- 本模块提供基于 mini.diff 和原生 git 命令的 Git 相关功能。
--
-- 功能列表:
-- 1. Hunk Preview<leader>ghp— 预览当前光标处 git diff hunk
-- 2. Blame Line<leader>ghb— 查看当前行的 git blame 信息
-- 3. Blame Buffer<leader>gB— 整文件 blamevia fugitive
-- 4. File History<leader>gD— 查看当前文件的 git log
--
-- 依赖:
-- - mini.diff已在 pack.lua 中通过 BufReadPost 懒加载)
-- - vim-fugitive已在 pack.lua 中通过按键触发懒加载)
-- =============================================================================
-- ---------------------------------------------------------------------------
-- 辅助函数:获取光标所在行的 hunk
-- ---------------------------------------------------------------------------
-- 遍历当前 buffer 的所有 hunks找到包含光标所在行的那个。
--
-- 返回值:
-- hunk - hunk 对象 { type, buf_start, buf_count, ref_start, ref_count }
-- ref_text - 参考文本HEAD 版本的内容)
-- nil - 光标不在任何 hunk 上
local function get_cursor_hunk()
local buf = vim.api.nvim_get_current_buf()
-- 从 mini.diff 获取当前 buffer 的 diff 数据
local data = require("mini.diff").get_buf_data(buf)
if not data or not data.hunks or #data.hunks == 0 then
return nil
end
local cursor_line = vim.api.nvim_win_get_cursor(0)[1]
for _, h in ipairs(data.hunks) do
-- hunk 在 buffer 中的起始行1-based
local start_line = math.max(h.buf_start, 1)
-- hunk 在 buffer 中的结束行
local end_line = h.buf_start + h.buf_count - 1
-- 空删除0 行)时特殊处理
if h.buf_count == 0 then
end_line = start_line
end
-- 检查光标是否在此 hunk 范围内
if cursor_line >= start_line and cursor_line <= end_line then
return h, data.ref_text
end
end
return nil
end
-- ---------------------------------------------------------------------------
-- Hunk Preview — 浮动窗口预览当前 hunk
-- ---------------------------------------------------------------------------
-- 创建一个居中的浮动窗口,显示光标所在 hunk 的详细 diff 信息。
-- 支持三种 hunk 类型:
-- add — 显示新增的行
-- delete — 显示被删除的行(从参考文本中恢复)
-- change — 并排显示修改前后的内容
local function preview_hunk()
local hunk, ref_text = get_cursor_hunk()
if not hunk then
vim.notify("光标不在变更区域", vim.log.levels.WARN)
return
end
-- 构建预览内容
local lines = {}
table.insert(lines, "Hunk 类型: " .. hunk.type)
table.insert(lines, string.format("Buffer 行: %d-%d", hunk.buf_start, hunk.buf_start + hunk.buf_count - 1))
table.insert(lines, string.format("参考行: %d-%d", hunk.ref_start, hunk.ref_start + hunk.ref_count - 1))
table.insert(lines, "---")
local buf = vim.api.nvim_get_current_buf()
local ref_lines = vim.split(ref_text or "", "\n")
if hunk.type == "delete" then
-- 删除型 hunk显示被删除的内容来自参考文本
table.insert(lines, "被删除的行(来自参考版本):")
for i = hunk.ref_start, hunk.ref_start + hunk.ref_count - 1 do
table.insert(lines, "- " .. (ref_lines[i] or ""))
end
elseif hunk.type == "add" then
-- 新增型 hunk显示 buffer 中新增的内容
table.insert(lines, "新增的行:")
for i = hunk.buf_start, hunk.buf_start + hunk.buf_count - 1 do
table.insert(lines, "+ " .. (vim.api.nvim_buf_get_lines(buf, i - 1, i, false)[1] or ""))
end
else
-- 修改型 hunk显示修改前后的对比
table.insert(lines, "修改前(参考版本):")
for i = hunk.ref_start, hunk.ref_start + hunk.ref_count - 1 do
table.insert(lines, "- " .. (ref_lines[i] or ""))
end
table.insert(lines, "修改后(当前版本):")
for i = hunk.buf_start, hunk.buf_start + hunk.buf_count - 1 do
table.insert(lines, "+ " .. (vim.api.nvim_buf_get_lines(buf, i - 1, i, false)[1] or ""))
end
end
-- 计算浮动窗口尺寸
local width = math.min(80, vim.o.columns - 4)
local height = math.min(#lines + 2, vim.o.lines - 4)
-- 创建预览 buffer
local preview_buf = vim.api.nvim_create_buf(false, true) -- 无文件、可编辑
vim.api.nvim_buf_set_lines(preview_buf, 0, -1, false, lines)
vim.api.nvim_set_option_value("modifiable", false, { buf = preview_buf })
vim.api.nvim_set_option_value("filetype", "diff", { buf = preview_buf }) -- diff 语法高亮
-- 打开浮动窗口
local win = vim.api.nvim_open_win(preview_buf, true, {
relative = "editor",
row = math.floor((vim.o.lines - height) / 2),
col = math.floor((vim.o.columns - width) / 2),
width = width,
height = height,
style = "minimal",
border = "rounded",
title = " Hunk Preview ",
title_pos = "center",
})
-- q / Esc 关闭窗口
vim.keymap.set("n", "q", function()
vim.api.nvim_win_close(win, true)
end, { buffer = preview_buf })
vim.keymap.set("n", "<Esc>", function()
vim.api.nvim_win_close(win, true)
end, { buffer = preview_buf })
end
-- ---------------------------------------------------------------------------
-- Blame Line — 查看当前行的 Git blame 信息
-- ---------------------------------------------------------------------------
-- 执行 git blame --porcelain 获取当前行的详细提交信息,
-- 以通知消息的形式展示提交哈希、作者、时间和提交摘要。
--
-- --porcelain 格式是机器可读的 blame 输出,包含以下字段:
-- <hash> <orig-line> <final-line> [<num-lines>]
-- author <name>
-- author-mail <email>
-- author-time <timestamp>
-- author-tz <timezone>
-- summary <commit-message>
-- ...
local function blame_line()
local file = vim.api.nvim_buf_get_name(0)
if file == "" then
vim.notify("当前 buffer 无文件名", vim.log.levels.WARN)
return
end
local line = vim.api.nvim_win_get_cursor(0)[1]
-- -L 限制 blame 范围为单行,提升性能
local cmd = { "git", "blame", "-L", line .. "," .. line, "--porcelain", file }
local output = vim.fn.system(cmd)
if vim.v.shell_error ~= 0 then
vim.notify("git blame 执行失败", vim.log.levels.ERROR)
return
end
-- 解析 --porcelain 输出
local hash = output:match("^(%x+)%s") or "?"
local author = output:match("author ([^\n]+)") or "?"
local email = output:match("author%-mail ([^\n]+)") or "?"
local time = output:match("author%-time (%d+)")
local summary = output:match("summary ([^\n]+)") or "?"
local time_str = time and os.date("%Y-%m-%d %H:%M", tonumber(time)) or "?"
vim.notify(
string.format("%s | %s %s | %s\n%s", hash:sub(1, 8), author, email, time_str, summary),
vim.log.levels.INFO
)
end
-- ---------------------------------------------------------------------------
-- 键位映射
-- ---------------------------------------------------------------------------
-- <leader>ghp — 预览当前 hunk
vim.keymap.set("n", "<leader>ghp", preview_hunk, { desc = "预览 hunk" })
-- <leader>ghb — 查看当前行 blame
vim.keymap.set("n", "<leader>ghb", blame_line, { desc = "Blame 当前行" })
-- <leader>gB — 整文件 blame使用 fugitive 的 :Git blame
vim.keymap.set("n", "<leader>gB", function()
vim.cmd("Git blame")
end, { desc = "Blame 整个文件" })
-- <leader>gD — 查看当前文件的 git 历史
vim.keymap.set("n", "<leader>gD", function()
vim.cmd("Git log -p -- %")
end, { desc = "查看文件 Git 历史" })

View File

@ -12,7 +12,6 @@
-- 2. nvim-lspconfig直接 packadd -- 2. nvim-lspconfig直接 packadd
-- 3. mason.nvim直接 setup -- 3. mason.nvim直接 setup
-- ============================================================================= -- =============================================================================
local lazy = require("lazy") local lazy = require("lazy")
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
@ -21,7 +20,6 @@ local lazy = require("lazy")
-- conform.nvim 是一个轻量级格式化器包装器,支持保存时自动格式化。 -- conform.nvim 是一个轻量级格式化器包装器,支持保存时自动格式化。
-- 由于格式化器通常不是立即需要的,通过 lazy.on_event 在 BufWritePre 时 -- 由于格式化器通常不是立即需要的,通过 lazy.on_event 在 BufWritePre 时
-- 才进行 packadd 和 setup。 -- 才进行 packadd 和 setup。
local function setup_conform() local function setup_conform()
-- 通过 packadd 加载 conform.nvim因为 vim.pack.add 时设置了 load = false -- 通过 packadd 加载 conform.nvim因为 vim.pack.add 时设置了 load = false
vim.cmd.packadd("conform.nvim") vim.cmd.packadd("conform.nvim")
@ -81,7 +79,6 @@ end)
-- --------------------------------------------------------------------------- -- ---------------------------------------------------------------------------
-- Mason 是 LSP 服务器、DAP 适配器和格式化工具的安装管理器。 -- Mason 是 LSP 服务器、DAP 适配器和格式化工具的安装管理器。
-- nvim-lspconfig 提供常见 LSP 服务器的预设配置。 -- nvim-lspconfig 提供常见 LSP 服务器的预设配置。
vim.cmd.packadd("nvim-lspconfig") vim.cmd.packadd("nvim-lspconfig")
lazy.track("lspconfig") lazy.track("lspconfig")

121
lua/plugins/pick.lua Normal file
View File

@ -0,0 +1,121 @@
-- =============================================================================
-- mini.pick 封装 (lua/pick.lua)
-- =============================================================================
-- 本模块封装 mini.pick 和 mini.extra 的初始化逻辑,
-- 提供统一的懒加载入口和自定义 picker。
--
-- 主要功能:
-- 1. load_pick() — 懒加载 mini.pick 和 mini.extra
-- 2. Buffer picker<leader><leader>)— 自定义 buffer 列表,支持 <C-d> 删除
-- 3. Filetype picker<leader>ft— 快速切换文件类型
-- =============================================================================
local lazy = require("lazy")
local M = {}
-- ---------------------------------------------------------------------------
-- 懒加载入口
-- ---------------------------------------------------------------------------
-- 通过 lazy.load 确保 mini.pick 和 mini.extra 只初始化一次。
-- 此函数被 pack.lua 中所有 pick 相关的 on_keys 映射共用。
M.load_pick = function()
lazy.load("pick", function()
require("mini.pick").setup()
end)
lazy.load("extra", function()
require("mini.extra").setup()
end)
end
-- ---------------------------------------------------------------------------
-- Buffer Picker<leader><leader>
-- ---------------------------------------------------------------------------
-- 显示当前打开的 buffer 列表,按最近使用时间排序。
-- 特殊功能:
-- - <C-d>:删除当前选中的 buffer带图标过滤
-- - 支持 mini.icons 图标显示
vim.keymap.set("n", "<leader><leader>", function()
M.load_pick()
local MiniPick = require("mini.pick")
-- 删除当前选中 buffer 的回调函数
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
-- 安全删除 bufferforce = true 跳过未保存确认)
pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
-- 如果 picker 仍活跃,刷新列表以移除已删除的 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排除特殊 buffer 如终端、quickfix
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)
-- 构建 picker items
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
-- 启动 picker
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 = "<C-d>", func = delete_buf },
},
})
end, { desc = "Buffer 列表" })
-- ---------------------------------------------------------------------------
-- Filetype Picker<leader>ft
-- ---------------------------------------------------------------------------
-- 显示所有可用的文件类型列表,选择后设置当前 buffer 的 filetype。
-- 常用于打开无扩展名文件或纠正错误的文件类型检测。
vim.keymap.set("n", "<leader>ft", function()
M.load_pick()
local MiniPick = require("mini.pick")
-- 获取所有可用的文件类型名称
local filetypes = vim.fn.getcompletion("", "filetype")
MiniPick.start({
source = {
name = "Filetypes",
items = filetypes,
choose = function(item)
-- 选择后将当前 buffer 的 filetype 设为选中值
vim.bo.filetype = item
end,
},
})
end, { desc = "切换文件类型" })
return M

View File

@ -0,0 +1,92 @@
-- =============================================================================
-- Treesitter 配置 (lua/treesitter.lua)
-- =============================================================================
-- 本模块配置 Neovim 的 Treesitter 集成,提供语法树驱动的语法高亮、
-- 代码折叠和文本对象支持。
--
-- 加载方式:
-- 由 pack.lua 通过 lazy.on_event("treesitter", "VimEnter", "*", ...) 延迟加载。
--
-- 设计要点:
-- 1. Parser 安装延迟 100ms 执行,避免阻塞 startup
-- 2. 高亮按 buffer 动态附加FileType autocmd只安装需要的 parser
-- =============================================================================
local M = {}
-- ---------------------------------------------------------------------------
-- 预安装的 parser 列表
-- ---------------------------------------------------------------------------
-- 这些 parser 会在首次启动后延迟安装。
-- 如果某个 parser 未在此列表中,打开对应文件类型时仍可通过 nvim-treesitter
-- 的 :TSInstall 手动安装。
local ensure_installed = {
"lua",
"vim",
"vimdoc",
-- Web 前端
"javascript",
"typescript",
"tsx", -- TypeScript JSX
"jsdoc", -- JSDoc 注释
"json",
"html",
"css",
"yaml",
-- 后端
"c",
"rust",
"toml",
"go",
"gomod", -- Go Modules
"gosum", -- Go Sum
"gowork", -- Go Workspaces
-- 基础设施
"dockerfile",
"make",
}
-- ---------------------------------------------------------------------------
-- 模块初始化
-- ---------------------------------------------------------------------------
M.setup = function()
-- 加载 nvim-treesitter通过 packadd 激活 opt 插件)
vim.cmd.packadd("nvim-treesitter")
local treesitter = require("nvim-treesitter")
-- 延迟安装 parser在 startup 完成 100ms 后后台安装,
-- 避免在安装过程中阻塞编辑器。
vim.defer_fn(function()
treesitter.install(ensure_installed)
end, 100)
-- 按文件类型动态启用 treesitter 高亮。
-- 当文件的 filetype 被设置时BufRead、:setfiletype 等),
-- 尝试查找并加载对应的 parser成功后启用高亮。
vim.api.nvim_create_autocmd("FileType", {
pattern = "*", -- 匹配所有文件类型
callback = function(args)
local buf = args.buf
local ft = vim.bo[buf].filetype
-- 将文件类型映射到 treesitter 语言名
-- 例如 "javascriptreact" → "javascript"
local lang = vim.treesitter.language.get_lang(ft)
if not lang then
return -- 无对应语言,跳过
end
-- 尝试注册语言(如果 parser 已安装)
-- pcall 用于安全调用parser 未安装时不会报错
local ok_add = pcall(vim.treesitter.language.add, lang)
if not ok_add then
return -- parser 未安装,跳过
end
-- 启用 treesitter 高亮
pcall(vim.treesitter.start, buf, lang)
end,
})
end
return M

View File

@ -1,93 +0,0 @@
-- =============================================================================
-- Treesitter 配置 (lua/treesitter.lua)
-- =============================================================================
-- 本模块配置 Neovim 的 Treesitter 集成,提供语法树驱动的语法高亮、
-- 代码折叠和文本对象支持。
--
-- 加载方式:
-- 由 pack.lua 通过 lazy.on_event("treesitter", "VimEnter", "*", ...) 延迟加载。
--
-- 设计要点:
-- 1. Parser 安装延迟 100ms 执行,避免阻塞 startup
-- 2. 高亮按 buffer 动态附加FileType autocmd只安装需要的 parser
-- =============================================================================
local M = {}
-- ---------------------------------------------------------------------------
-- 预安装的 parser 列表
-- ---------------------------------------------------------------------------
-- 这些 parser 会在首次启动后延迟安装。
-- 如果某个 parser 未在此列表中,打开对应文件类型时仍可通过 nvim-treesitter
-- 的 :TSInstall 手动安装。
local ensure_installed = {
"lua",
"vim",
"vimdoc",
-- Web 前端
"javascript",
"typescript",
"tsx", -- TypeScript JSX
"jsdoc", -- JSDoc 注释
"json",
"html",
"css",
"yaml",
-- 后端
"c",
"rust",
"toml",
"go",
"gomod", -- Go Modules
"gosum", -- Go Sum
"gowork", -- Go Workspaces
-- 基础设施
"dockerfile",
"make",
}
-- ---------------------------------------------------------------------------
-- 模块初始化
-- ---------------------------------------------------------------------------
M.setup = function()
-- 加载 nvim-treesitter通过 packadd 激活 opt 插件)
vim.cmd.packadd("nvim-treesitter")
local treesitter = require("nvim-treesitter")
-- 延迟安装 parser在 startup 完成 100ms 后后台安装,
-- 避免在安装过程中阻塞编辑器。
vim.defer_fn(function()
treesitter.install(ensure_installed)
end, 100)
-- 按文件类型动态启用 treesitter 高亮。
-- 当文件的 filetype 被设置时BufRead、:setfiletype 等),
-- 尝试查找并加载对应的 parser成功后启用高亮。
vim.api.nvim_create_autocmd("FileType", {
pattern = "*", -- 匹配所有文件类型
callback = function(args)
local buf = args.buf
local ft = vim.bo[buf].filetype
-- 将文件类型映射到 treesitter 语言名
-- 例如 "javascriptreact" → "javascript"
local lang = vim.treesitter.language.get_lang(ft)
if not lang then
return -- 无对应语言,跳过
end
-- 尝试注册语言(如果 parser 已安装)
-- pcall 用于安全调用parser 未安装时不会报错
local ok_add = pcall(vim.treesitter.language.add, lang)
if not ok_add then
return -- parser 未安装,跳过
end
-- 启用 treesitter 高亮
pcall(vim.treesitter.start, buf, lang)
end,
})
end
return M

View File

@ -23,7 +23,6 @@
-- vim.pack 会从 GitHub 下载插件到 stdpath("data")/site/pack/core/opt/。 -- vim.pack 会从 GitHub 下载插件到 stdpath("data")/site/pack/core/opt/。
-- --
-- nargs = "+" 表示至少需要 1 个参数。 -- nargs = "+" 表示至少需要 1 个参数。
vim.api.nvim_create_user_command("PackAdd", function(opts) vim.api.nvim_create_user_command("PackAdd", function(opts)
vim.pack.add(opts.fargs) vim.pack.add(opts.fargs)
end, { nargs = "+", desc = "添加插件 (:PackAdd user/repo1 user/repo2)" }) end, { nargs = "+", desc = "添加插件 (:PackAdd user/repo1 user/repo2)" })
@ -39,7 +38,6 @@ end, { nargs = "+", desc = "添加插件 (:PackAdd user/repo1 user/repo2)" })
-- Neovim 0.13 Nightly 已内置此命令,这里为 0.12 提供兼容。 -- Neovim 0.13 Nightly 已内置此命令,这里为 0.12 提供兼容。
-- --
-- nargs = "+" 表示至少需要 1 个参数。 -- nargs = "+" 表示至少需要 1 个参数。
vim.api.nvim_create_user_command("PackDel", function(opts) vim.api.nvim_create_user_command("PackDel", function(opts)
vim.pack.del(opts.fargs) vim.pack.del(opts.fargs)
end, { nargs = "+", desc = "删除插件 (:PackDel plugin1 plugin2)" }) end, { nargs = "+", desc = "删除插件 (:PackDel plugin1 plugin2)" })
@ -57,7 +55,6 @@ end, { nargs = "+", desc = "删除插件 (:PackDel plugin1 plugin2)" })
-- 如果无参数,调用无参版本更新所有插件。 -- 如果无参数,调用无参版本更新所有插件。
-- --
-- nargs = "*" 表示接受 0 个或多个参数。 -- nargs = "*" 表示接受 0 个或多个参数。
vim.api.nvim_create_user_command("PackUpdate", function(opts) vim.api.nvim_create_user_command("PackUpdate", function(opts)
-- 检查是否有传入任何参数(包含非空白字符) -- 检查是否有传入任何参数(包含非空白字符)
if opts.args:match("%S") then if opts.args:match("%S") then