diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index d412389..78f934d 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -23,7 +23,7 @@ lua/plugins/ lsp.lua -- LSP config (nvim-lspconfig + mason), conform.nvim formatter treesitter.lua -- Treesitter setup: deferred parser install, per-buffer attach pick.lua -- mini.pick wrappers: buffer picker, filetype picker - git.lua -- Custom git utilities: hunk preview, blame line, fugitive keymaps + git.lua -- Git toolstack: gitsigns (buffer), neogit (repo), codediff (file) colors/ -- Catppuccin colorscheme variants (frappe, latte, macchiato, mocha) nvim-pack-lock.json -- Lock file for vim.pack managed plugins ``` @@ -44,10 +44,10 @@ Plugins are loaded lazily via the custom framework in `lua/lazy.lua`: Key lazy-loading points in `pack.lua`: - `mini.completion` / `mini.snippets` — `InsertEnter` -- `mini.diff` / `mini.surround` — `BufReadPost` +- `gitsigns` / `mini.surround` — `BufReadPost` - `treesitter` / `lsp` — `VimEnter` - `mini.pick` / `mini.extra` — key trigger (`ff`, etc.) -- `vim-fugitive` — key trigger (`gg`) +- `neogit` / `codediff` — key trigger (`gg`, `gd`) - `conform.nvim` — `BufWritePre` ## LSP and Formatting @@ -85,12 +85,14 @@ Common bindings defined across files: - `_` — open at project root (git root) **Git:** -- `gg` — fugitive (fullscreen new tab) -- `gd` — git diff vertical split -- `ghp` — preview current hunk -- `ghb` — blame current line -- `gB` — blame entire file -- `gD` — file git history +- `gg` — neogit status (new tab) +- `gd` — codediff git status +- `gD` — codediff file history +- `ghp` — preview hunk (gitsigns) +- `ghb` — blame current line (gitsigns) +- `ghB` — blame entire file (gitsigns) +- `ghs` — stage hunk (gitsigns) +- `ghr` — reset hunk (gitsigns) - `sr` — search and replace (grug-far) **LSP & diagnostics:** @@ -154,6 +156,6 @@ nvim --startuptime /tmp/startup.log +q 2. **Custom lazy-loading in `lua/lazy.lua`**. Do not confuse with the lazy.nvim plugin manager — this is a ~35-line custom module. When adding new plugins that should load lazily, use `lazy.on_event()` or `lazy.on_keys()`. -3. **mini.nvim monorepo**. Most UI/functionality comes from the single `mini.nvim` package (starter, pick, extra, files, icons, notify, cmdline, completion, snippets, diff, surround). These are configured individually in `pack.lua` or on-demand. +3. **mini.nvim monorepo**. Most UI/functionality comes from the single `mini.nvim` package (starter, pick, extra, files, icons, notify, cmdline, completion, snippets, surround). These are configured individually in `pack.lua` or on-demand. 4. **Colorscheme**. The active colorscheme is `catppuccin-mocha`, defined in `colors/catppuccin-mocha.lua`. The `moonflyTransparent` variable is still set in `init.lua` for compatibility but the moonfly theme was removed. diff --git a/AGENTS.md b/AGENTS.md index 4974954..c7913f9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ This is a personal Neovim configuration targeting **Neovim 0.12+**. It lives at - **Plugin manager**: Neovim 0.12+ built-in `vim.pack` (not lazy.nvim / packer). Plugins are declared in `lua/pack.lua` via `vim.pack.add({ urls }, { load = false })` and tracked in `nvim-pack-lock.json`. - **Custom lazy-loading**: `lua/lazy.lua` is a ~35-line custom framework (unrelated to lazy.nvim). It provides `load()`, `on_event()`, `on_keys()`, `on_cmd()` and tracks loaded modules in `M._loaded`. -- **Mini.nvim monorepo**: Most UI/functionality comes from the single `mini.nvim` package. Its submodules (starter, pick, extra, files, icons, notify, cmdline, completion, snippets, diff, surround, ai, cursorword, pairs) are configured individually in `lua/pack.lua` or on-demand. +- **Mini.nvim monorepo**: Most UI/functionality comes from the single `mini.nvim` package. Its submodules (starter, pick, extra, files, icons, notify, cmdline, completion, snippets, surround, ai, cursorword, pairs) are configured individually in `lua/pack.lua` or on-demand. ## File Loading Order @@ -54,9 +54,9 @@ When adding new plugins that should load lazily, use the custom framework in `lu |---------------|--------------------------------------| | `VimEnter` | treesitter, lsp, icons | | `InsertEnter` | completion, snippets, pairs | -| `BufReadPost` | diff, surround, ai, cursorword | +| `BufReadPost` | gitsigns, surround, ai, cursorword | | `BufWritePre` | conform (format-on-save) | -| Key press | pick, files, fugitive, grugfar | +| Key press | pick, files, neogit, codediff, grugfar | | Command | ex-colors (`:ExColors`) | ## LSP & Formatting diff --git a/MAPS.md b/MAPS.md index 813269c..8554e8d 100644 --- a/MAPS.md +++ b/MAPS.md @@ -25,15 +25,24 @@ ## Git -| 键位 | 动作 | -|----------------|--------------------------| -| `gg` | Fugitive(全屏新标签页) | -| `gd` | Git diff 垂直分割 | -| `ghp` | 预览当前 hunk | -| `ghb` | Blame 当前行 | -| `gB` | Blame 整个文件 | -| `gD` | 查看文件 Git 历史 | -| `sr` | 搜索并替换(grug-far) | +| 键位 | 动作 | +|----------------|-----------------------------------| +| `gg` | Neogit status(新标签页) | +| `gd` | CodeDiff 工作区 diff | +| `gD` | CodeDiff 文件历史 | +| `ghp` | 预览当前 hunk(gitsigns) | +| `ghb` | Blame 当前行(gitsigns) | +| `ghB` | Blame 整个文件(gitsigns) | +| `ghs` | Stage hunk(gitsigns) | +| `ghr` | Reset hunk(gitsigns) | +| `ghS` | Stage 整个 buffer(gitsigns) | +| `ghR` | Reset 整个 buffer(gitsigns) | +| `ghu` | 撤销 stage hunk(gitsigns) | +| `ghd` | Diff this(vs index) | +| `ghD` | Diff this ~(vs HEAD) | +| `]h` / `[h` | 下一个 / 上一个 hunk | +| `]H` / `[H` | 最后 / 第一个 hunk | +| `sr` | 搜索并替换(grug-far) | ## LSP 与诊断 diff --git a/PLUGINS.md b/PLUGINS.md index e7a4c12..457339a 100644 --- a/PLUGINS.md +++ b/PLUGINS.md @@ -13,7 +13,9 @@ | **nvim-lspconfig** | [neovim/nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) | LSP 客户端预设配置 | | **mason.nvim** | [mason-org/mason.nvim](https://github.com/mason-org/mason.nvim) | LSP/DAP/格式化工具安装管理 | | **conform.nvim** | [stevearc/conform.nvim](https://github.com/stevearc/conform.nvim) | 代码格式化(保存时自动格式化) | -| **vim-fugitive** | [tpope/vim-fugitive](https://github.com/tpope/vim-fugitive) | Git 集成 | +| **gitsigns.nvim** | [lewis6991/gitsigns.nvim](https://github.com/lewis6991/gitsigns.nvim) | Git inline gutter + hunk 操作(buffer 级) | +| **neogit** | [NeogitOrg/neogit](https://github.com/NeogitOrg/neogit) | Git status 客户端(仓库级,依赖 plenary) | +| **codediff.nvim** | [esmuellert/codediff.nvim](https://github.com/esmuellert/codediff.nvim) | side-by-side diff 可视化(文件级) | | **grug-far.nvim** | [MagicDuck/grug-far.nvim](https://github.com/MagicDuck/grug-far.nvim) | 搜索与替换 | | **friendly-snippets** | [rafamadriz/friendly-snippets](https://github.com/rafamadriz/friendly-snippets) | 社区代码片段集合 | @@ -33,5 +35,4 @@ | **mini.extra** | pick 扩展(keymaps、oldfiles 等) | 按键触发 | | **mini.completion** | 自动补全引擎(LSP + Buffer) | InsertEnter 懒加载 | | **mini.snippets** | 代码片段引擎 | InsertEnter 懒加载 | -| **mini.diff** | Git diff 标记(signcolumn) | BufReadPost 懒加载 | | **mini.surround** | 环绕文本操作(括号、引号等) | BufReadPost 懒加载 | diff --git a/README.md b/README.md index 1ee2196..bcd764e 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ - **内置插件管理器** — `vim.pack`(Neovim 0.12+),无需 lazy.nvim / packer - **自定义懒加载框架** — `lua/lazy.lua`(约 35 行),通过 `on_event` / `on_keys` / `load` 延迟加载重型模块 -- **mini.nvim 单体插件集** — starter、pick、extra、files、icons、notify、cmdline、completion、snippets、diff、surround、ai、cursorword、pairs +- **mini.nvim 单体插件集** — starter、pick、extra、files、icons、notify、cmdline、completion、snippets、surround、ai、cursorword、pairs - **LSP + 代码格式化** — nvim-lspconfig + mason + conform.nvim,保存时自动格式化,支持全局/Buffer 级别开关 - **快速启动** — 禁用约 20 个内置插件,启用 Neovim 0.12+ 内置 UI 增强 (`vim._core.ui2`) - **启动仪表盘** — ASCII Logo + 实时模块加载统计 + 启动耗时 @@ -16,15 +16,15 @@ ## 懒加载策略 -| 触发条件 | 插件 | -| ------------- | ------------------------------ | -| `VimEnter` | treesitter、lsp、icons | -| `InsertEnter` | completion、snippets、pairs | -| `BufReadPost` | diff、surround、ai、cursorword | -| `BufWritePre` | conform | -| 按键触发 | pick、files、fugitive、grugfar | -| 首次按 `:` | cmdline | -| 命令触发 | ex-colors (`:ExColors`) | +| 触发条件 | 插件 | +| ------------- | ----------------------------------- | +| `VimEnter` | treesitter、lsp、icons | +| `InsertEnter` | completion、snippets、pairs | +| `BufReadPost` | gitsigns、surround、ai、cursorword | +| `BufWritePre` | conform | +| 按键触发 | pick、files、neogit、codediff、grugfar | +| 首次按 `:` | cmdline | +| 命令触发 | ex-colors (`:ExColors`) | ## 键位映射 diff --git a/lua/lazy.lua b/lua/lazy.lua index a99e8b6..b05f3c8 100644 --- a/lua/lazy.lua +++ b/lua/lazy.lua @@ -105,7 +105,7 @@ end -- -- 注意: -- 此设计使得首次按键稍慢(需要 setup),后续按键与直接映射无异。 --- 适合重型插件如 mini.pick、vim-fugitive。 +-- 适合重型插件如 mini.pick、neogit。 M.on_keys = function(name, keys, mode, fn, action, opts) mode = mode or "n" opts = opts or {} diff --git a/lua/pack.lua b/lua/pack.lua index d5fd072..53b305a 100644 --- a/lua/pack.lua +++ b/lua/pack.lua @@ -12,20 +12,22 @@ -- -- 插件列表: -- mini.nvim - 单体插件集(starter、pick、extra、files、icons、 --- notify、cmdline、completion、snippets、diff、surround) +-- notify、cmdline、completion、snippets、surround) -- friendly-snippets - 社区代码片段集合 -- nvim-treesitter - 语法树解析与高亮 -- nvim-lspconfig - LSP 客户端配置 -- mason.nvim - LSP/DAP/格式化工具安装管理器 -- conform.nvim - 代码格式化(保存时自动格式化) --- vim-fugitive - Git 集成 +-- gitsigns.nvim - Git inline gutter + hunk 操作(buffer 级) +-- neogit - Git status 客户端(仓库级,依赖 plenary) +-- codediff.nvim - side-by-side diff 可视化(文件级) -- grug-far.nvim - 搜索与替换 -- -- 懒加载策略: -- InsertEnter → completion, snippets, pairs --- BufReadPost → diff, surround, ai, cursorword +-- BufReadPost → gitsigns, surround, ai, cursorword -- VimEnter → treesitter, lsp, icons --- 按键触发 → pick, fugitive, files, grugfar +-- 按键触发 → pick, neogit, codediff, files, grugfar -- BufWritePre → conform -- 命令触发 → ex-colors -- ============================================================================= @@ -45,7 +47,10 @@ vim.pack.add({ "https://github.com/neovim/nvim-lspconfig", -- LSP 配置 "https://github.com/mason-org/mason.nvim", -- 工具安装器 "https://github.com/stevearc/conform.nvim", -- 格式化 - "https://github.com/tpope/vim-fugitive", -- Git 集成 + "https://github.com/lewis6991/gitsigns.nvim", -- Git inline gutter + hunk 操作 + "https://github.com/nvim-lua/plenary.nvim", -- neogit 依赖 + "https://github.com/NeogitOrg/neogit", -- Git status 客户端 + "https://github.com/esmuellert/codediff.nvim", -- side-by-side diff 可视化 "https://github.com/MagicDuck/grug-far.nvim", -- 搜索替换 -- "https://github.com/aileot/ex-colors.nvim", -- colorscheme 提取与优化 }, { load = false }) @@ -149,25 +154,6 @@ lazy.on_event("snippets", "InsertEnter", "*", function() MiniSnippets.start_lsp_server({ match = false }) end) --- --------------------------------------------------------------------------- --- mini.diff — Git diff 标记(BufReadPost 懒加载) --- --------------------------------------------------------------------------- --- 在 signcolumn 中显示当前 buffer 相对于 git HEAD 的变更标记。 -lazy.on_event("diff", "BufReadPost", "*", function() - require("mini.diff").setup({ - -- 使用 git 作为 diff 源(index = false 表示对比工作区 vs HEAD,不含暂存区) - source = require("mini.diff").gen_source.git({ index = false }), - view = { - style = "sign", - signs = { add = "│", change = "│", delete = "│" }, - }, - mappings = { - apply = "gs", - textobject = "", - }, - }) -end) - -- --------------------------------------------------------------------------- -- mini.surround — 环绕文本操作(BufReadPost 懒加载) -- --------------------------------------------------------------------------- diff --git a/lua/plugins/git.lua b/lua/plugins/git.lua index 361c389..d93f351 100644 --- a/lua/plugins/git.lua +++ b/lua/plugins/git.lua @@ -1,257 +1,161 @@ -- ============================================================================= -- Git 工具封装 (lua/plugins/git.lua) -- ============================================================================= --- 本模块提供基于 mini.diff 和原生 git 命令的 Git 相关功能。 +-- 本模块提供三层 Git 工具栈,职责无重叠(对齐 nvchad 分支设计): -- --- 功能列表: --- 1. Hunk Preview(ghp)— 预览当前光标处 git diff hunk --- 2. Blame Line(ghb)— 查看当前行的 git blame 信息 --- 3. Blame Buffer(gB)— 整文件 blame(via fugitive) --- 4. File History(gD)— 查看当前文件的 git log --- 5. Fugitive 全屏(gg)— 在新标签页打开 fugitive --- 6. Git diff 分割(gd)— 垂直分割查看 diff +-- 1. buffer 级(实时)— gitsigns.nvim +-- signcolumn gutter 标记 + hunk stage/reset/preview/blame + diffthis +-- 键位:ghs/ghr/ghp/ghb/ghB/ghd/ghD + ]h/[h hunk 导航 +-- +-- 2. 仓库级(交互式)— neogit +-- Magit 风格 status 界面,commit/push/pull/log +-- 键位:gg +-- +-- 3. 文件级(可视化)— codediff.nvim +-- VSCode 风格 side-by-side diff + 文件历史 +-- 键位:gd / gD -- -- 依赖: --- - mini.diff(已在 pack.lua 中通过 BufReadPost 懒加载) --- - vim-fugitive(本文件通过 lazy.on_keys 按键触发懒加载) +-- - gitsigns.nvim(本文件通过 BufReadPost 懒加载) +-- - neogit(依赖 plenary.nvim,按键触发懒加载) +-- - codediff.nvim(按键触发懒加载) -- ============================================================================= local lazy = require("lazy") -- --------------------------------------------------------------------------- --- 辅助函数:获取光标所在行的 hunk +-- gitsigns.nvim — buffer 级 Git 集成(BufReadPost 懒加载) -- --------------------------------------------------------------------------- --- 遍历当前 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 +-- 在 signcolumn 中显示 add/change/delete 标记,并提供 hunk 级操作。 +-- on_attach 回调中注册 buffer-local 键位映射,仅对已 attach 的 buffer 生效。 +local function load_gitsigns() + vim.cmd.packadd("gitsigns.nvim") + require("gitsigns").setup({ + signs = { + add = { text = "▎" }, + change = { text = "▎" }, + delete = { text = "" }, + topdelete = { text = "" }, + changedelete = { text = "~" }, + untracked = { text = "┆" }, + }, + signcolumn = true, + on_attach = function(bufnr) + local gs = require("gitsigns") - local cursor_line = vim.api.nvim_win_get_cursor(0)[1] + local function map(mode, l, r, desc) + vim.keymap.set(mode, l, r, { buffer = bufnr, silent = true, desc = desc }) + end - 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 导航(diff 模式下降级为原生 ]c/[c) + map("n", "]h", function() + if vim.wo.diff then + vim.cmd.normal({ "]c", bang = true }) + else + gs.nav_hunk("next") + end + end, "下一个 hunk") + map("n", "[h", function() + if vim.wo.diff then + vim.cmd.normal({ "[c", bang = true }) + else + gs.nav_hunk("prev") + end + end, "上一个 hunk") + map("n", "]H", function() + gs.nav_hunk("last") + end, "最后一个 hunk") + map("n", "[H", function() + gs.nav_hunk("first") + end, "第一个 hunk") - -- 检查光标是否在此 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", + -- hunk 操作(Normal + Visual) + -- stylua: ignore start + map({ "n", "x" }, "ghs", ":Gitsigns stage_hunk", "Stage Hunk") + map({ "n", "x" }, "ghr", ":Gitsigns reset_hunk", "Reset Hunk") + map("n", "ghS", gs.stage_buffer, "Stage Buffer") + map("n", "ghu", gs.undo_stage_hunk, "Undo Stage Hunk") + map("n", "ghR", gs.reset_buffer, "Reset Buffer") + map("n", "ghp", gs.preview_hunk, "Preview Hunk") + map("n", "ghb", function() + gs.blame_line({ full = true }) + end, "Blame 当前行") + map("n", "ghB", gs.blame, "Blame 整个文件") + map("n", "ghd", gs.diffthis, "Diff This") + map("n", "ghD", function() + gs.diffthis("~") + end, "Diff This ~") + map({ "o", "x" }, "ih", ":Gitsigns select_hunk", "Gitsigns Select Hunk") + -- stylua: ignore end + end, }) - - -- q / Esc 关闭窗口 - vim.keymap.set("n", "q", function() - vim.api.nvim_win_close(win, true) - end, { buffer = preview_buf }) - vim.keymap.set("n", "", function() - vim.api.nvim_win_close(win, true) - end, { buffer = preview_buf }) end +lazy.on_event("gitsigns", "BufReadPost", "*", load_gitsigns) + -- --------------------------------------------------------------------------- --- Blame Line — 查看当前行的 Git blame 信息 +-- neogit — 仓库级 Git 客户端(按键触发懒加载) -- --------------------------------------------------------------------------- --- 执行 git blame --porcelain 获取当前行的详细提交信息, --- 以通知消息的形式展示提交哈希、作者、时间和提交摘要。 --- --- --porcelain 格式是机器可读的 blame 输出,包含以下字段: --- [] --- author --- author-mail --- author-time --- author-tz --- summary --- ... -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 - ) +-- Magit 风格的交互式 status 界面,集成 commit/push/pull/log/rebase 等。 +-- 依赖 plenary.nvim,加载 neogit 前需确保 plenary 已在 runtimepath。 +local function load_neogit() + vim.cmd.packadd("plenary.nvim") + vim.cmd.packadd("neogit") + require("neogit").setup({}) end +-- gg — 打开 Neogit status(新标签页) +lazy.on_keys("neogit", "gg", "n", load_neogit, function() + require("neogit").open({ kind = "tab" }) +end, { desc = "Neogit status" }) + -- --------------------------------------------------------------------------- --- vim-fugitive 懒加载触发器 +-- codediff.nvim — 文件级 diff 可视化(按键触发懒加载) -- --------------------------------------------------------------------------- -local function load_fugitive() - vim.cmd.packadd("vim-fugitive") +-- VSCode 风格 side-by-side diff,支持行级 + 字符级双层高亮。 +-- 提供工作区 diff(:CodeDiff)和文件历史(:CodeDiff history)两种视图。 +local function load_codediff() + vim.cmd.packadd("codediff.nvim") + require("codediff").setup({ + diff = { + layout = "side-by-side", + disable_inlay_hints = true, + jump_to_first_change = true, + cycle_next_hunk = true, + cycle_next_file = true, + }, + explorer = { + width = 35, + }, + keymaps = { + view = { + quit = "q", + next_hunk = "]c", + prev_hunk = "[c", + next_file = "]f", + prev_file = "[f", + diff_get = "do", + diff_put = "dp", + open_in_prev_tab = "gf", + toggle_stage = "cs", + stage_hunk = "hs", + unstage_hunk = "hu", + discard_hunk = "hr", + hunk_textobject = "ih", + show_help = "g?", + align_move = "gm", + toggle_layout = "t", + }, + }, + }) end --- --------------------------------------------------------------------------- --- 键位映射 --- --------------------------------------------------------------------------- +-- gd — CodeDiff 工作区 diff 视图 +lazy.on_keys("codediff", "gd", "n", load_codediff, function() + vim.cmd("CodeDiff") +end, { desc = "CodeDiff git status" }) --- ghp — 预览当前 hunk -vim.keymap.set("n", "ghp", preview_hunk, { desc = "预览 hunk" }) - --- ghb — 查看当前行 blame -vim.keymap.set("n", "ghb", blame_line, { desc = "Blame 当前行" }) - --- ghr — 重置当前 hunk(恢复为 HEAD 版本) -vim.keymap.set("n", "ghr", function() - local hunk, ref_text = get_cursor_hunk() - if not hunk then - vim.notify("光标不在变更区域", vim.log.levels.WARN) - return - end - - local buf = vim.api.nvim_get_current_buf() - local ref_lines = vim.split(ref_text or "", "\n") - - if hunk.type == "add" then - -- 新增型 hunk:删除这些行 - vim.api.nvim_buf_set_lines(buf, hunk.buf_start - 1, hunk.buf_start + hunk.buf_count - 1, false, {}) - elseif hunk.type == "delete" then - -- 删除型 hunk:插入被删除的内容 - local lines_to_insert = {} - for i = hunk.ref_start, hunk.ref_start + hunk.ref_count - 1 do - table.insert(lines_to_insert, ref_lines[i] or "") - end - vim.api.nvim_buf_set_lines(buf, hunk.buf_start - 1, hunk.buf_start - 1, false, lines_to_insert) - else - -- 修改型 hunk:用参考版本替换 - local lines_to_replace = {} - for i = hunk.ref_start, hunk.ref_start + hunk.ref_count - 1 do - table.insert(lines_to_replace, ref_lines[i] or "") - end - vim.api.nvim_buf_set_lines(buf, hunk.buf_start - 1, hunk.buf_start + hunk.buf_count - 1, false, lines_to_replace) - end - - vim.notify("已重置 hunk", vim.log.levels.INFO) -end, { desc = "重置 hunk" }) - --- gg — 在新标签页中打开 Fugitive 全屏 -lazy.on_keys("fugitive", "gg", "n", load_fugitive, function() - vim.cmd("Git") -end, { desc = "Fugitive 全屏新标签" }) - --- gd — Git diff 垂直分割 -lazy.on_keys("fugitive", "gd", "n", load_fugitive, function() - vim.cmd("Gvdiffsplit") -end, { desc = "Git diff 分割" }) - --- gB — 整文件 blame(使用 fugitive 的 :Git blame) -lazy.on_keys("fugitive", "gB", "n", load_fugitive, function() - vim.cmd("Git blame") -end, { desc = "Blame 整个文件" }) - --- gD — 查看当前文件的 git 历史 -lazy.on_keys("fugitive", "gD", "n", load_fugitive, function() - vim.cmd("Git log -p -- %") -end, { desc = "查看文件 Git 历史" }) - --- gl — 查看当前文件的 Gclog -lazy.on_keys("fugitive", "gl", "n", load_fugitive, function() - vim.cmd("GcLog") -end, { desc = "查看文件 GcLog" }) +-- gD — CodeDiff 当前文件历史(共享 codediff 懒加载,setup 仅首次执行) +lazy.on_keys("codediff", "gD", "n", load_codediff, function() + vim.cmd("CodeDiff history") +end, { desc = "CodeDiff 文件历史" }) diff --git a/lua/plugins/starter.lua b/lua/plugins/starter.lua index 559fbb3..517d344 100644 --- a/lua/plugins/starter.lua +++ b/lua/plugins/starter.lua @@ -118,7 +118,6 @@ local all_modules = { "icons", "pick", "files", - "diff", "surround", "completion", "snippets", @@ -127,7 +126,9 @@ local all_modules = { "conform", "lspconfig", "mason", - "fugitive", + "gitsigns", + "neogit", + "codediff", "grugfar", "pairs", "ai", diff --git a/lua/usercmds.lua b/lua/usercmds.lua index 5d2fd87..568df51 100644 --- a/lua/usercmds.lua +++ b/lua/usercmds.lua @@ -15,7 +15,7 @@ -- :PackAdd — 添加插件 -- --------------------------------------------------------------------------- -- 用法::PackAdd user/repo1 user/repo2 ... --- 示例::PackAdd stevearc/conform.nvim tpope/vim-fugitive +-- 示例::PackAdd stevearc/conform.nvim lewis6991/gitsigns.nvim -- -- 原理: -- 将命令行参数(opts.fargs,已按空格分割的字符串数组) @@ -31,7 +31,7 @@ end, { nargs = "+", desc = "添加插件 (:PackAdd user/repo1 user/repo2)" }) -- :PackDel — 删除插件 -- --------------------------------------------------------------------------- -- 用法::PackDel plugin1 plugin2 ... --- 示例::PackDel conform.nvim vim-fugitive +-- 示例::PackDel conform.nvim gitsigns.nvim -- -- 注意: -- 参数是插件目录名(即 repo 名),不是完整 URL。 diff --git a/nvim-pack-lock.json b/nvim-pack-lock.json index 07cadd6..1517585 100644 --- a/nvim-pack-lock.json +++ b/nvim-pack-lock.json @@ -1,5 +1,9 @@ { "plugins": { + "codediff.nvim": { + "rev": "7608f3b436ce1f84a4888f75e53079caa981a2e9", + "src": "https://github.com/esmuellert/codediff.nvim" + }, "conform.nvim": { "rev": "619363c30309d29ffa631e67c8183f2a72caa373", "src": "https://github.com/stevearc/conform.nvim" @@ -8,6 +12,10 @@ "rev": "6cd7280adead7f586db6fccbd15d2cac7e2188b9", "src": "https://github.com/rafamadriz/friendly-snippets" }, + "gitsigns.nvim": { + "rev": "25050e4ed39e628282831d4cbecb1850454ce915", + "src": "https://github.com/lewis6991/gitsigns.nvim" + }, "grug-far.nvim": { "rev": "5506c2f59dc9ab2ed6c233585412b24d31d51521", "src": "https://github.com/MagicDuck/grug-far.nvim" @@ -20,6 +28,10 @@ "rev": "44657837c7338e52727facc85c1d95bec1f6bd7c", "src": "https://github.com/nvim-mini/mini.nvim" }, + "neogit": { + "rev": "5d1b65d6215928e941e1a6a4e76e02fd45ada31f", + "src": "https://github.com/NeogitOrg/neogit" + }, "nvim-lspconfig": { "rev": "6f76a3eeadc2ee235d74cd7d5319e95a261084af", "src": "https://github.com/neovim/nvim-lspconfig" @@ -28,9 +40,9 @@ "rev": "4916d6592ede8c07973490d9322f187e07dfefac", "src": "https://github.com/nvim-treesitter/nvim-treesitter" }, - "vim-fugitive": { - "rev": "3b753cf8c6a4dcde6edee8827d464ba9b8c4a6f0", - "src": "https://github.com/tpope/vim-fugitive" + "plenary.nvim": { + "rev": "74b06c6c75e4eeb3108ec01852001636d85a932b", + "src": "https://github.com/nvim-lua/plenary.nvim" } } }