diff --git a/colors/catppuccin-frappe.lua b/colors/catppuccin-frappe.lua index 17ef85f..248f46a 100644 --- a/colors/catppuccin-frappe.lua +++ b/colors/catppuccin-frappe.lua @@ -1,5 +1,7 @@ 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" local h = vim.api.nvim_set_hl h(0, "DashboardFooter", { fg = "#e5c890", italic = true }) @@ -346,7 +348,7 @@ h(0, "MiniPickPreviewRegion", { link = "IncSearch" }) h(0, "MiniPickPrompt", { fg = "#c6d0f5", bg = "#292c3c" }) h(0, "MiniPickPromptCaret", { fg = "#eebebe", bg = "#292c3c" }) h(0, "MiniPickPromptPrefix", { fg = "#eebebe", bg = "#292c3c" }) -h(0, "MiniStarterCurrent", { }) +h(0, "MiniStarterCurrent", {}) h(0, "MiniStarterFooter", { fg = "#e5c890", italic = true }) h(0, "MiniStarterHeader", { fg = "#8caaee" }) h(0, "MiniStarterInactive", { fg = "#626880", 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, "MiniStatuslineModeVisual", { fg = "#303446", bold = true, bg = "#ca9ee6" }) 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, "MiniTablineHidden", { fg = "#c6d0f5", bg = "NONE" }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#e78284", bold = true }) @@ -800,7 +806,7 @@ h(0, "@markup.heading", { fg = "#8caaee" }) h(0, "@markup.underline", { link = "Underlined" }) h(0, "@function.builtin", { fg = "#ef9f76" }) h(0, "@lsp.typemod.function.defaultLibrary", { link = "@function.builtin" }) -h(0, "@lsp.type.variable", { }) +h(0, "@lsp.type.variable", {}) h(0, "@lsp.type.enumMember", { fg = "#81c8be" }) h(0, "diffIndexLine", { fg = "#81c8be" }) h(0, "diffLine", { fg = "#737994" }) diff --git a/colors/catppuccin-latte.lua b/colors/catppuccin-latte.lua index c732bc0..e074b5d 100644 --- a/colors/catppuccin-latte.lua +++ b/colors/catppuccin-latte.lua @@ -1,5 +1,7 @@ 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" local h = vim.api.nvim_set_hl h(0, "DashboardFooter", { fg = "#df8e1d", italic = true }) @@ -346,7 +348,7 @@ h(0, "MiniPickPreviewRegion", { link = "IncSearch" }) h(0, "MiniPickPrompt", { fg = "#4c4f69", bg = "#e6e9ef" }) h(0, "MiniPickPromptCaret", { fg = "#dd7878", bg = "#e6e9ef" }) h(0, "MiniPickPromptPrefix", { fg = "#dd7878", bg = "#e6e9ef" }) -h(0, "MiniStarterCurrent", { }) +h(0, "MiniStarterCurrent", {}) h(0, "MiniStarterFooter", { fg = "#df8e1d", italic = true }) h(0, "MiniStarterHeader", { fg = "#1e66f5" }) h(0, "MiniStarterInactive", { fg = "#acb0be", 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, "MiniStatuslineModeVisual", { fg = "#eff1f5", bold = true, bg = "#8839ef" }) 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, "MiniTablineHidden", { fg = "#4c4f69", bg = "NONE" }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#d20f39", bold = true }) @@ -800,7 +806,7 @@ h(0, "@markup.heading", { fg = "#1e66f5" }) h(0, "@markup.underline", { link = "Underlined" }) h(0, "@function.builtin", { fg = "#fe640b" }) h(0, "@lsp.typemod.function.defaultLibrary", { link = "@function.builtin" }) -h(0, "@lsp.type.variable", { }) +h(0, "@lsp.type.variable", {}) h(0, "@lsp.type.enumMember", { fg = "#179299" }) h(0, "diffIndexLine", { fg = "#179299" }) h(0, "diffLine", { fg = "#9ca0b0" }) diff --git a/colors/catppuccin-macchiato.lua b/colors/catppuccin-macchiato.lua index b8b8c45..9e9abb6 100644 --- a/colors/catppuccin-macchiato.lua +++ b/colors/catppuccin-macchiato.lua @@ -1,5 +1,7 @@ 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" local h = vim.api.nvim_set_hl h(0, "DashboardFooter", { fg = "#eed49f", italic = true }) @@ -346,7 +348,7 @@ h(0, "MiniPickPreviewRegion", { link = "IncSearch" }) h(0, "MiniPickPrompt", { fg = "#cad3f5", bg = "#1e2030" }) h(0, "MiniPickPromptCaret", { fg = "#f0c6c6", bg = "#1e2030" }) h(0, "MiniPickPromptPrefix", { fg = "#f0c6c6", bg = "#1e2030" }) -h(0, "MiniStarterCurrent", { }) +h(0, "MiniStarterCurrent", {}) h(0, "MiniStarterFooter", { fg = "#eed49f", italic = true }) h(0, "MiniStarterHeader", { fg = "#8aadf4" }) h(0, "MiniStarterInactive", { fg = "#5b6078", 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, "MiniStatuslineModeVisual", { fg = "#24273a", bold = true, bg = "#c6a0f6" }) 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, "MiniTablineHidden", { fg = "#cad3f5", bg = "NONE" }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#ed8796", bold = true }) @@ -800,7 +806,7 @@ h(0, "@markup.heading", { fg = "#8aadf4" }) h(0, "@markup.underline", { link = "Underlined" }) h(0, "@function.builtin", { fg = "#f5a97f" }) h(0, "@lsp.typemod.function.defaultLibrary", { link = "@function.builtin" }) -h(0, "@lsp.type.variable", { }) +h(0, "@lsp.type.variable", {}) h(0, "@lsp.type.enumMember", { fg = "#8bd5ca" }) h(0, "diffIndexLine", { fg = "#8bd5ca" }) h(0, "diffLine", { fg = "#6e738d" }) diff --git a/colors/catppuccin-mocha.lua b/colors/catppuccin-mocha.lua index 9ae49ce..e19a986 100644 --- a/colors/catppuccin-mocha.lua +++ b/colors/catppuccin-mocha.lua @@ -1,5 +1,7 @@ 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" local h = vim.api.nvim_set_hl h(0, "DashboardFooter", { fg = "#f9e2af", italic = true }) @@ -350,7 +352,7 @@ h(0, "MiniPickPreviewRegion", { link = "IncSearch" }) h(0, "MiniPickPrompt", { fg = "#cdd6f4", bg = "#181825" }) h(0, "MiniPickPromptCaret", { fg = "#f2cdcd", bg = "#181825" }) h(0, "MiniPickPromptPrefix", { fg = "#f2cdcd", bg = "#181825" }) -h(0, "MiniStarterCurrent", { }) +h(0, "MiniStarterCurrent", {}) h(0, "MiniStarterFooter", { fg = "#f9e2af", italic = true }) h(0, "MiniStarterHeader", { fg = "#89b4fa" }) h(0, "MiniStarterInactive", { fg = "#585b70", 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, "MiniStatuslineModeVisual", { fg = "#1e1e2e", bold = true, bg = "#cba6f7" }) 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, "MiniTablineHidden", { fg = "#cdd6f4", bg = "NONE" }) h(0, "MiniTablineModifiedCurrent", { italic = true, bg = "NONE", fg = "#f38ba8", bold = true }) @@ -804,7 +810,7 @@ h(0, "@markup.heading", { fg = "#89b4fa" }) h(0, "@markup.underline", { link = "Underlined" }) h(0, "@function.builtin", { fg = "#fab387" }) h(0, "@lsp.typemod.function.defaultLibrary", { link = "@function.builtin" }) -h(0, "@lsp.type.variable", { }) +h(0, "@lsp.type.variable", {}) h(0, "@lsp.type.enumMember", { fg = "#94e2d5" }) h(0, "diffIndexLine", { fg = "#94e2d5" }) h(0, "diffLine", { fg = "#6c7086" }) diff --git a/init.lua b/init.lua index bfac505..b85bbc7 100644 --- a/init.lua +++ b/init.lua @@ -27,35 +27,34 @@ _G.nvim_start_time = (vim.uv or vim.loop).hrtime() -- -- 注意:loaded_syntax 设为 1 是为了避免旧版 Vim 兼容的 syntax 加载路径; -- 实际语法高亮由 treesitter 接管,不受影响。 - -vim.g.loaded_2html_plugin = 1 -- :TOhtml 命令(将 buffer 导出为 HTML) -vim.g.loaded_getscript = 1 -- GetScript 插件(自动下载脚本) -vim.g.loaded_getscriptPlugin = 1 -- GetScript 插件主体 -vim.g.loaded_gzip = 1 -- Gzip 压缩文件读写支持 -vim.g.loaded_logipat = 1 -- LogiPat 逻辑模式匹配 -vim.g.loaded_matchit = 1 -- % 匹配扩展(由 treesitter 或 mini.pairs 替代) -vim.g.loaded_matchparen = 1 -- 括号匹配高亮(由 treesitter 替代) -vim.g.loaded_netrw = 1 -- Netrw 文件浏览器(由 mini.files 替代) -vim.g.loaded_netrwFileHandlers = 1 -- Netrw 文件处理器 -vim.g.loaded_netrwPlugin = 1 -- Netrw 插件主体 -vim.g.loaded_netrwSettings = 1 -- Netrw 设置 -vim.g.loaded_rrhelper = 1 -- RRHelper(R 语言相关) -vim.g.loaded_spellfile_plugin = 1 -- 拼写文件自动下载 -vim.g.loaded_tar = 1 -- Tar 归档支持 -vim.g.loaded_tarPlugin = 1 -- Tar 插件主体 -vim.g.loaded_tutor_mode_plugin = 1 -- Vim Tutor 教程模式 -vim.g.loaded_vimball = 1 -- Vimball 归档格式 -vim.g.loaded_vimballPlugin = 1 -- Vimball 插件主体 -vim.g.loaded_zip = 1 -- Zip 归档支持 -vim.g.loaded_zipPlugin = 1 -- Zip 插件主体 -vim.g.loaded_rplugin = 1 -- 远程插件框架 -vim.g.loaded_tohtml = 1 -- :TOhtml 的另一入口 -vim.g.loaded_syntax = 1 -- 旧版 syntax 自动加载 -vim.g.loaded_synmenu = 1 -- Syntax 菜单 -vim.g.loaded_optwin = 1 -- 选项窗口 (:options) -vim.g.loaded_compiler = 1 -- 编译器插件自动加载 -vim.g.loaded_bugreport = 1 -- Bug 报告生成 -vim.g.loaded_ftplugin = 1 -- 文件类型插件(手动管理) +vim.g.loaded_2html_plugin = 1 -- :TOhtml 命令(将 buffer 导出为 HTML) +vim.g.loaded_getscript = 1 -- GetScript 插件(自动下载脚本) +vim.g.loaded_getscriptPlugin = 1 -- GetScript 插件主体 +vim.g.loaded_gzip = 1 -- Gzip 压缩文件读写支持 +vim.g.loaded_logipat = 1 -- LogiPat 逻辑模式匹配 +vim.g.loaded_matchit = 1 -- % 匹配扩展(由 treesitter 或 mini.pairs 替代) +vim.g.loaded_matchparen = 1 -- 括号匹配高亮(由 treesitter 替代) +vim.g.loaded_netrw = 1 -- Netrw 文件浏览器(由 mini.files 替代) +vim.g.loaded_netrwFileHandlers = 1 -- Netrw 文件处理器 +vim.g.loaded_netrwPlugin = 1 -- Netrw 插件主体 +vim.g.loaded_netrwSettings = 1 -- Netrw 设置 +vim.g.loaded_rrhelper = 1 -- RRHelper(R 语言相关) +vim.g.loaded_spellfile_plugin = 1 -- 拼写文件自动下载 +vim.g.loaded_tar = 1 -- Tar 归档支持 +vim.g.loaded_tarPlugin = 1 -- Tar 插件主体 +vim.g.loaded_tutor_mode_plugin = 1 -- Vim Tutor 教程模式 +vim.g.loaded_vimball = 1 -- Vimball 归档格式 +vim.g.loaded_vimballPlugin = 1 -- Vimball 插件主体 +vim.g.loaded_zip = 1 -- Zip 归档支持 +vim.g.loaded_zipPlugin = 1 -- Zip 插件主体 +vim.g.loaded_rplugin = 1 -- 远程插件框架 +vim.g.loaded_tohtml = 1 -- :TOhtml 的另一入口 +vim.g.loaded_syntax = 1 -- 旧版 syntax 自动加载 +vim.g.loaded_synmenu = 1 -- Syntax 菜单 +vim.g.loaded_optwin = 1 -- 选项窗口 (:options) +vim.g.loaded_compiler = 1 -- 编译器插件自动加载 +vim.g.loaded_bugreport = 1 -- Bug 报告生成 +vim.g.loaded_ftplugin = 1 -- 文件类型插件(手动管理) -- ============================================================================= -- 启用 Neovim 0.12+ 内置 UI 增强 @@ -69,12 +68,11 @@ require("vim._core.ui2").enable({}) -- ============================================================================= -- 这些模块按顺序加载,后续模块可以依赖前面模块的设置。 -- 例如 pack.lua 中使用的键位映射依赖于 keymaps.lua 中设置的 leader 键。 - -require("options") -- 全局 vim 选项设置 -require("keymaps") -- 键位映射定义 -require("autocmds") -- 自动命令(autocmd) -require("usercmds") -- 用户自定义命令 -require("pack") -- 插件管理与懒加载配置 +require("options") -- 全局 vim 选项设置 +require("keymaps") -- 键位映射定义 +require("autocmds") -- 自动命令(autocmd) +require("usercmds") -- 用户自定义命令 +require("pack") -- 插件管理与懒加载配置 -- ============================================================================= -- Colorscheme 设置 diff --git a/lua/autocmds.lua b/lua/autocmds.lua index baa8308..e380b1c 100644 --- a/lua/autocmds.lua +++ b/lua/autocmds.lua @@ -23,23 +23,22 @@ local group = vim.api.nvim_create_augroup("UserAutocmds", { clear = true }) -- - commit / gitcommit:Git 提交消息文件,总是从头开始编辑 -- - xxd:十六进制编辑模式 -- - gitrebase:Git rebase 交互式编辑 - vim.api.nvim_create_autocmd("BufReadPost", { - group = group, - callback = function() - -- 获取 `"` 标记的位置(行, 列) - local mark = vim.api.nvim_buf_get_mark(0, '"') - local row = mark[1] -- 行号(1-based) - local ft = vim.bo.filetype + group = group, + callback = function() + -- 获取 `"` 标记的位置(行, 列) + local mark = vim.api.nvim_buf_get_mark(0, '"') + local row = mark[1] -- 行号(1-based) + local ft = vim.bo.filetype - -- 仅当标记有效(行号 > 1 且在文件范围内)时恢复光标 - if row > 1 and row <= vim.fn.line("$") then - -- 排除特定文件类型 - if ft ~= "commit" and ft ~= "gitcommit" and not ft:match("xxd") and not ft:match("gitrebase") then - vim.api.nvim_win_set_cursor(0, mark) - end - end - end, + -- 仅当标记有效(行号 > 1 且在文件范围内)时恢复光标 + if row > 1 and row <= vim.fn.line("$") then + -- 排除特定文件类型 + if ft ~= "commit" and ft ~= "gitcommit" and not ft:match("xxd") and not ft:match("gitrebase") then + vim.api.nvim_win_set_cursor(0, mark) + end + end + end, }) -- --------------------------------------------------------------------------- @@ -52,15 +51,14 @@ vim.api.nvim_create_autocmd("BufReadPost", { -- r - 按回车时延续注释(保留,insert 模式下回车延续注释符) -- -- 注意:此设置对自动格式化(conform.nvim)的行为也有影响。 - vim.api.nvim_create_autocmd("FileType", { - group = group, - callback = function() - -- 移除 'o':按 o/O 时不自动插入注释符号 - vim.opt_local.formatoptions:remove("o") - -- 追加 'r':按回车时自动插入注释符号 - vim.opt_local.formatoptions:append("r") - end, + group = group, + callback = function() + -- 移除 'o':按 o/O 时不自动插入注释符号 + vim.opt_local.formatoptions:remove("o") + -- 追加 'r':按回车时自动插入注释符号 + vim.opt_local.formatoptions:append("r") + end, }) -- --------------------------------------------------------------------------- @@ -73,12 +71,11 @@ vim.api.nvim_create_autocmd("FileType", { -- foldmethod = "expr" 表示使用表达式(foldexpr)计算折叠范围。 -- foldexpr = "v:lua.vim.treesitter.foldexpr()" 是 Neovim 0.10+ 的内置函数, -- 基于 treesitter 语法树计算折叠边界。 - vim.api.nvim_create_autocmd("BufEnter", { - group = group, - once = true, - callback = function() - vim.o.foldmethod = "expr" - vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" - end, + group = group, + once = true, + callback = function() + vim.o.foldmethod = "expr" + vim.o.foldexpr = "v:lua.vim.treesitter.foldexpr()" + end, }) diff --git a/lua/git.lua b/lua/git.lua deleted file mode 100644 index c5eeddf..0000000 --- a/lua/git.lua +++ /dev/null @@ -1,202 +0,0 @@ --- ============================================================================= --- Git 工具封装 (lua/git.lua) --- ============================================================================= --- 本模块提供基于 mini.diff 和原生 git 命令的 Git 相关功能。 --- --- 功能列表: --- 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 --- --- 依赖: --- - 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", "", function() - vim.api.nvim_win_close(win, true) - end, { buffer = preview_buf }) -end - --- --------------------------------------------------------------------------- --- Blame Line — 查看当前行的 Git blame 信息 --- --------------------------------------------------------------------------- --- 执行 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 - ) -end - --- --------------------------------------------------------------------------- --- 键位映射 --- --------------------------------------------------------------------------- - --- ghp — 预览当前 hunk -vim.keymap.set("n", "ghp", preview_hunk, { desc = "预览 hunk" }) - --- ghb — 查看当前行 blame -vim.keymap.set("n", "ghb", blame_line, { desc = "Blame 当前行" }) - --- gB — 整文件 blame(使用 fugitive 的 :Git blame) -vim.keymap.set("n", "gB", function() - vim.cmd("Git blame") -end, { desc = "Blame 整个文件" }) - --- gD — 查看当前文件的 git 历史 -vim.keymap.set("n", "gD", function() - vim.cmd("Git log -p -- %") -end, { desc = "查看文件 Git 历史" }) diff --git a/lua/keymaps.lua b/lua/keymaps.lua index 00b7867..0c749db 100644 --- a/lua/keymaps.lua +++ b/lua/keymaps.lua @@ -76,7 +76,6 @@ map("v", "<", "u", function() vim.cmd.packadd("nvim.undotree") require("undotree").open() @@ -107,7 +106,6 @@ map("n", "tt", ":term", { desc = "打开新终端" }) -- 窗口导航 -- --------------------------------------------------------------------------- -- 使用 在窗口间快速跳转,替代 h/j/k/l 的繁琐操作。 - map("n", "", "h", { desc = "切换到左侧窗口" }) map("n", "", "j", { desc = "切换到下方窗口" }) map("n", "", "k", { desc = "切换到上方窗口" }) @@ -116,7 +114,6 @@ map("n", "", "l", { desc = "切换到右侧窗口" }) -- --------------------------------------------------------------------------- -- Tab 管理 -- --------------------------------------------------------------------------- - map("n", "tc", ":tabclose", { desc = "关闭当前标签页" }) map("n", "tn", ":tabnew", { desc = "新建标签页" }) map("n", "]", ":tabnext", { desc = "下一个标签页" }) @@ -125,7 +122,6 @@ map("n", "[", ":tabprevious", { desc = "上一个标签页" }) -- --------------------------------------------------------------------------- -- 复制文件路径 -- --------------------------------------------------------------------------- - -- 复制当前文件的相对路径到系统剪贴板 map("n", "yp", function() local path = vim.fn.expand("%") @@ -143,7 +139,6 @@ end, { desc = "复制绝对文件路径" }) -- --------------------------------------------------------------------------- -- Buffer 管理 -- --------------------------------------------------------------------------- - -- 关闭当前 Buffer,有未保存更改时提示确认。 -- 使用 vim.ui.select 提供交互式选择对话框。 map("n", "x", function() diff --git a/lua/lazy.lua b/lua/lazy.lua index 2cfbbe3..41db290 100644 --- a/lua/lazy.lua +++ b/lua/lazy.lua @@ -34,18 +34,18 @@ M._loaded = {} -- 使用场景: -- 在按键回调中手动触发加载,或在 on_event 回调中延迟初始化。 M.load = function(name, fn) - if M._loaded[name] then - return - end - M._loaded[name] = true - if fn then - fn() - end + if M._loaded[name] then + return + end + M._loaded[name] = true + if fn then + fn() + end end -- 仅标记模块为已加载,不执行 setup(用于直接加载的插件)。 M.track = function(name) - M._loaded[name] = true + M._loaded[name] = true end -- --------------------------------------------------------------------------- @@ -67,13 +67,13 @@ end -- require("mini.completion").setup({ ... }) -- end) M.on_event = function(name, event, pattern, fn) - vim.api.nvim_create_autocmd(event, { - pattern = pattern or "*", - once = true, - callback = function() - M.load(name, fn) - end, - }) + vim.api.nvim_create_autocmd(event, { + pattern = pattern or "*", + once = true, + callback = function() + M.load(name, fn) + end, + }) end -- --------------------------------------------------------------------------- @@ -97,14 +97,14 @@ end -- 此设计使得首次按键稍慢(需要 setup),后续按键与直接映射无异。 -- 适合重型插件如 mini.pick、vim-fugitive。 M.on_keys = function(name, keys, mode, fn, action, opts) - mode = mode or "n" - opts = opts or {} - vim.keymap.set(mode, keys, function() - M.load(name, fn) - if action then - action() - end - end, { desc = opts.desc, buffer = opts.buffer }) + mode = mode or "n" + opts = opts or {} + vim.keymap.set(mode, keys, function() + M.load(name, fn) + if action then + action() + end + end, { desc = opts.desc, buffer = opts.buffer }) end return M diff --git a/lua/options.lua b/lua/options.lua index 97a09ed..b9a47e3 100644 --- a/lua/options.lua +++ b/lua/options.lua @@ -19,64 +19,57 @@ vim.g.netrw_banner = 0 -- --------------------------------------------------------------------------- -- 1. 界面显示 -- --------------------------------------------------------------------------- - -vim.opt.nu = true -- 显示绝对行号 -vim.opt.relativenumber = true -- 显示相对行号(便于配合数字 + j/k 跳转) -vim.opt.cursorline = true -- 高亮当前行 -vim.opt.cursorlineopt = "both" -- 高亮当前行的行号和文本行(number + line) -vim.opt.autoread = true -- 当文件被外部修改时自动重新读取 +vim.opt.nu = true -- 显示绝对行号 +vim.opt.relativenumber = true -- 显示相对行号(便于配合数字 + j/k 跳转) +vim.opt.cursorline = true -- 高亮当前行 +vim.opt.cursorlineopt = "both" -- 高亮当前行的行号和文本行(number + line) +vim.opt.autoread = true -- 当文件被外部修改时自动重新读取 -- --------------------------------------------------------------------------- -- 2. 缩进与格式化 -- --------------------------------------------------------------------------- -- 使用 4 空格缩进,适用于大多数语言。 -- 特定语言的缩进设置(如 JS/TS 的 2 空格)应在 ftplugin 中覆盖。 +vim.opt.tabstop = 4 -- Tab 键显示的宽度(字符数) +vim.opt.softtabstop = 4 -- 插入模式下 Tab/BS 的行为宽度 +vim.opt.shiftwidth = 4 -- 自动缩进和 >/< 操作的宽度 +vim.opt.expandtab = true -- 将 Tab 键转换为空格 -vim.opt.tabstop = 4 -- Tab 键显示的宽度(字符数) -vim.opt.softtabstop = 4 -- 插入模式下 Tab/BS 的行为宽度 -vim.opt.shiftwidth = 4 -- 自动缩进和 >/< 操作的宽度 -vim.opt.expandtab = true -- 将 Tab 键转换为空格 - -vim.opt.smartindent = true -- 基于语法的智能缩进(C-style 语言效果最佳) -vim.opt.inccommand = "split" -- 实时预览替换效果(:s 命令在分屏中显示预览) +vim.opt.smartindent = true -- 基于语法的智能缩进(C-style 语言效果最佳) +vim.opt.inccommand = "split" -- 实时预览替换效果(:s 命令在分屏中显示预览) -- --------------------------------------------------------------------------- -- 3. 窗口与分割 -- --------------------------------------------------------------------------- - -vim.opt.splitbelow = true -- 水平分割时新窗口在下方 -vim.opt.splitright = true -- 垂直分割时新窗口在右侧 +vim.opt.splitbelow = true -- 水平分割时新窗口在下方 +vim.opt.splitright = true -- 垂直分割时新窗口在右侧 -- --------------------------------------------------------------------------- -- 4. 搜索行为 -- --------------------------------------------------------------------------- - -vim.opt.ignorecase = true -- 搜索时忽略大小写 -vim.opt.smartcase = true -- 如果搜索包含大写字母,则区分大小写 - -- 与 ignorecase 配合:全小写时忽略大小写, - -- 包含大写时精确匹配 +vim.opt.ignorecase = true -- 搜索时忽略大小写 +vim.opt.smartcase = true -- 如果搜索包含大写字母,则区分大小写 +-- 与 ignorecase 配合:全小写时忽略大小写, +-- 包含大写时精确匹配 -- --------------------------------------------------------------------------- -- 5. 状态栏 -- --------------------------------------------------------------------------- - -vim.opt.laststatus = 3 -- 全局状态栏(所有窗口共享一个状态栏) - -- 值为 2 时每个窗口有独立状态栏 +vim.opt.laststatus = 3 -- 全局状态栏(所有窗口共享一个状态栏) +-- 值为 2 时每个窗口有独立状态栏 -- --------------------------------------------------------------------------- -- 6. 文件持久化 -- --------------------------------------------------------------------------- -- 禁用 swap 和 backup 文件,使用 undofile 实现跨会话的撤销历史。 - -vim.opt.swapfile = false -- 禁用交换文件(.swp) -vim.opt.backup = false -- 禁用备份文件(~ 后缀) -vim.opt.undodir = vim.fn.stdpath("data") .. "/undodir" -- 撤销历史存放目录 -vim.opt.undofile = true -- 启用持久化撤销(关闭文件后仍能撤销) +vim.opt.swapfile = false -- 禁用交换文件(.swp) +vim.opt.backup = false -- 禁用备份文件(~ 后缀) +vim.opt.undodir = vim.fn.stdpath("data") .. "/undodir" -- 撤销历史存放目录 +vim.opt.undofile = true -- 启用持久化撤销(关闭文件后仍能撤销) -- --------------------------------------------------------------------------- -- 7. 补全与消息 -- --------------------------------------------------------------------------- - -- 补全选项: -- menuone - 即使只有一个匹配项也显示菜单 -- noselect - 不自动选择第一项(需手动选择) @@ -96,13 +89,12 @@ vim.opt.shortmess:append("c") -- 通过 vim.schedule() 将剪贴板设置推迟到启动事件循环之后, -- 避免阻塞 startup 的关键路径。 vim.schedule(function() - vim.opt.clipboard:append("unnamedplus") + vim.opt.clipboard:append("unnamedplus") end) -- --------------------------------------------------------------------------- -- 9. 其他杂项 -- --------------------------------------------------------------------------- - -- 将 @ 和 - 视为文件名的一部分(用于 gf/gx 等命令) vim.opt.isfname:append("@-@") @@ -123,8 +115,7 @@ vim.opt.termguicolors = true -- --------------------------------------------------------------------------- -- 将 mini.diff 的符号高亮组链接到标准 diff 高亮组, -- 使 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, "MiniDiffSignDelete", { link = "DiffDelete" }) -- 删除的行 @@ -134,12 +125,11 @@ vim.api.nvim_set_hl(0, "MiniDiffSignDelete", { link = "DiffDelete" }) -- 删除 -- 复制文本时短暂高亮被复制的区域,提供视觉反馈。 -- vim.hl.on_yank() 是 Neovim 0.11+ 的内置函数, -- 旧版使用 vim.highlight.on_yank()。 - vim.api.nvim_create_autocmd("TextYankPost", { - desc = "复制文本时高亮被复制的区域", - callback = function() - vim.hl.on_yank() - end, + desc = "复制文本时高亮被复制的区域", + callback = function() + vim.hl.on_yank() + end, }) -- --------------------------------------------------------------------------- diff --git a/lua/pack.lua b/lua/pack.lua index 70e3c34..65eea53 100644 --- a/lua/pack.lua +++ b/lua/pack.lua @@ -24,17 +24,17 @@ -- ============================================================================= local lazy = require("lazy") -local pick = require("pick") -require("git") +local pick = require("plugins.pick") +require("plugins.git") -- 在 VimEnter 时记录完整启动时间(此时 runtime 插件、ShaDa 等已加载完毕), -- 使启动页显示的耗时更接近 --startuptime 的口径。 -- starter 的 autoopen 在 VimEnter 之后渲染,footer 可以安全读取此值。 vim.api.nvim_create_autocmd("VimEnter", { - once = true, - callback = function() - _G.nvim_vimenter_done = (vim.uv or vim.loop).hrtime() - end, + once = true, + callback = function() + _G.nvim_vimenter_done = (vim.uv or vim.loop).hrtime() + end, }) -- --------------------------------------------------------------------------- @@ -43,16 +43,15 @@ vim.api.nvim_create_autocmd("VimEnter", { -- vim.pack.add(urls, { load = false }) 将插件下载到 pack 目录, -- 但不自动加载(load = false)。后续通过 packadd 或 require 按需加载。 -- 所有插件的状态锁定在 nvim-pack-lock.json 中。 - vim.pack.add({ - "https://github.com/nvim-mini/mini.nvim", -- 核心 UI/功能插件集 - "https://github.com/rafamadriz/friendly-snippets", -- 代码片段库 - { src = "https://github.com/nvim-treesitter/nvim-treesitter", branch = "main" }, -- 语法树 - "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/MagicDuck/grug-far.nvim", -- 搜索替换 + "https://github.com/nvim-mini/mini.nvim", -- 核心 UI/功能插件集 + "https://github.com/rafamadriz/friendly-snippets", -- 代码片段库 + { src = "https://github.com/nvim-treesitter/nvim-treesitter", branch = "main" }, -- 语法树 + "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/MagicDuck/grug-far.nvim", -- 搜索替换 }, { load = false }) -- --------------------------------------------------------------------------- @@ -63,32 +62,32 @@ vim.pack.add({ -- ASCII art 集合(用于启动页等展示) local logs = { - a = { - "⣇⣿⠘⣿⣿⣿⡿⡿⣟⣟⢟⢟⢝⠵⡝⣿⡿⢂⣼⣿⣷⣌⠩⡫⡻⣝⠹⢿⣿⣷", - "⡆⣿⣆⠱⣝⡵⣝⢅⠙⣿⢕⢕⢕⢕⢝⣥⢒⠅⣿⣿⣿⡿⣳⣌⠪⡪⣡⢑⢝⣇", - "⡆⣿⣿⣦⠹⣳⣳⣕⢅⠈⢗⢕⢕⢕⢕⢕⢈⢆⠟⠋⠉⠁⠉⠉⠁⠈⠼⢐⢕⢽", - "⡗⢰⣶⣶⣦⣝⢝⢕⢕⠅⡆⢕⢕⢕⢕⢕⣴⠏⣠⡶⠛⡉⡉⡛⢶⣦⡀⠐⣕⢕", - "⡝⡄⢻⢟⣿⣿⣷⣕⣕⣅⣿⣔⣕⣵⣵⣿⣿⢠⣿⢠⣮⡈⣌⠨⠅⠹⣷⡀⢱⢕", - "⡝⡵⠟⠈⢀⣀⣀⡀⠉⢿⣿⣿⣿⣿⣿⣿⣿⣼⣿⢈⡋⠴⢿⡟⣡⡇⣿⡇⡀⢕", - "⡝⠁⣠⣾⠟⡉⡉⡉⠻⣦⣻⣿⣿⣿⣿⣿⣿⣿⣿⣧⠸⣿⣦⣥⣿⡇⡿⣰⢗⢄", - "⠁⢰⣿⡏⣴⣌⠈⣌⠡⠈⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣬⣉⣉⣁⣄⢖⢕⢕⢕", - "⡀⢻⣿⡇⢙⠁⠴⢿⡟⣡⡆⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣵⣵⣿", - "⡻⣄⣻⣿⣌⠘⢿⣷⣥⣿⠇⣿⣿⣿⣿⣿⣿⠛⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - "⣷⢄⠻⣿⣟⠿⠦⠍⠉⣡⣾⣿⣿⣿⣿⣿⣿⢸⣿⣦⠙⣿⣿⣿⣿⣿⣿⣿⣿⠟", - "⡕⡑⣑⣈⣻⢗⢟⢞⢝⣻⣿⣿⣿⣿⣿⣿⣿⠸⣿⠿⠃⣿⣿⣿⣿⣿⣿⡿⠁⣠", - "⡝⡵⡈⢟⢕⢕⢕⢕⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⠿⠋⣀⣈⠙", - "⡝⡵⡕⡀⠑⠳⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⢉⡠⡲⡫⡪⡪⡣", - }, - b = { - "░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░ ", - "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", - "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", - "░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░", - "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", - "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", - "░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░", - }, - c = [[ + a = { + "⣇⣿⠘⣿⣿⣿⡿⡿⣟⣟⢟⢟⢝⠵⡝⣿⡿⢂⣼⣿⣷⣌⠩⡫⡻⣝⠹⢿⣿⣷", + "⡆⣿⣆⠱⣝⡵⣝⢅⠙⣿⢕⢕⢕⢕⢝⣥⢒⠅⣿⣿⣿⡿⣳⣌⠪⡪⣡⢑⢝⣇", + "⡆⣿⣿⣦⠹⣳⣳⣕⢅⠈⢗⢕⢕⢕⢕⢕⢈⢆⠟⠋⠉⠁⠉⠉⠁⠈⠼⢐⢕⢽", + "⡗⢰⣶⣶⣦⣝⢝⢕⢕⠅⡆⢕⢕⢕⢕⢕⣴⠏⣠⡶⠛⡉⡉⡛⢶⣦⡀⠐⣕⢕", + "⡝⡄⢻⢟⣿⣿⣷⣕⣕⣅⣿⣔⣕⣵⣵⣿⣿⢠⣿⢠⣮⡈⣌⠨⠅⠹⣷⡀⢱⢕", + "⡝⡵⠟⠈⢀⣀⣀⡀⠉⢿⣿⣿⣿⣿⣿⣿⣿⣼⣿⢈⡋⠴⢿⡟⣡⡇⣿⡇⡀⢕", + "⡝⠁⣠⣾⠟⡉⡉⡉⠻⣦⣻⣿⣿⣿⣿⣿⣿⣿⣿⣧⠸⣿⣦⣥⣿⡇⡿⣰⢗⢄", + "⠁⢰⣿⡏⣴⣌⠈⣌⠡⠈⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣬⣉⣉⣁⣄⢖⢕⢕⢕", + "⡀⢻⣿⡇⢙⠁⠴⢿⡟⣡⡆⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣵⣵⣿", + "⡻⣄⣻⣿⣌⠘⢿⣷⣥⣿⠇⣿⣿⣿⣿⣿⣿⠛⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + "⣷⢄⠻⣿⣟⠿⠦⠍⠉⣡⣾⣿⣿⣿⣿⣿⣿⢸⣿⣦⠙⣿⣿⣿⣿⣿⣿⣿⣿⠟", + "⡕⡑⣑⣈⣻⢗⢟⢞⢝⣻⣿⣿⣿⣿⣿⣿⣿⠸⣿⠿⠃⣿⣿⣿⣿⣿⣿⡿⠁⣠", + "⡝⡵⡈⢟⢕⢕⢕⢕⣵⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣿⣿⣿⣿⣿⠿⠋⣀⣈⠙", + "⡝⡵⡕⡀⠑⠳⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠛⢉⡠⡲⡫⡪⡪⡣", + }, + b = { + "░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░ ", + "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", + "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", + "░▒▓███████▓▒░░▒▓█▓▒░░▒▓█▓▒░▒▓████████▓▒░", + "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", + "░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░", + "░▒▓█▓▒░░▒▓█▓▒░░▒▓██████▓▒░░▒▓█▓▒░░▒▓█▓▒░", + }, + c = [[ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠋⣠⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣡⣾⣿⣿⣿⣿⣿⢿⣿⣿⣿⣿⣿⣿⣟⠻⣿⣿⣿⣿⣿⣿⣿⣿ @@ -108,52 +107,52 @@ local logs = { ⢀⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣀⡆⣠⢀⣴⣏⡀⠀⠀⠀⠉⠀⠀⢀⣠⣰⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⠿⠛⠛⠛⠛⠛⠛⠻⢿⣿⣿⣿⣿⣯⣟⠷⢷⣿⡿⠋⠀⠀⠀⠀⣵⡀⢠⡿⠋⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠛⢿⣿⣿⠂⠀⠀⠀⠀⠀⢀⣽⣿⣿⣿⣿⣿⣿⣿⣍⠛⠿⣿⣿⣿⣿⣿⣿]], - d = { - "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠋⠀⢀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠈⠉⠉⠙⠛⠛⠻⢿⣿⡿⠟⠁⠀⣀⣴⣿⣿⣿⣿⣿⠟", - "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠛⣉⣡⠀⣠⣴⣶⣶⣦⠄⣀⡀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⡿⢃⣾", - "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⣾⣤⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⣠⣾⡟⢡⣾⣿⣿⣿⡿⢋⣴⣿⡿⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⢡⣾⣿", - "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠃⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣼⣿⡟⣰⣿⣿⣿⣿⠏⣰⣿⣿⠟⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢚⣛⢿", - "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⠸⣿⠟⢰⣿⣿⣿⣿⠃⣾⣿⣿⠏⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⣾", - "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠻⠻⠃⠀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⢉⣴⣿⣿⣿⣿⡇⠘⣿⣿⠋⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⡘⣿", - "⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⣿⣿⣿⣿⠁⢀⣀⠀⢀⣾⣿⣿⣿⣿⣿⣿⠟⠉⠉⠉⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣤⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣌", - "⣿⣿⣿⣿⣿⣿⡿⠁⣀⣤⡀⠀⠈⠻⢿⠀⣼⣿⣷⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - "⣿⣿⣿⠟⠛⠙⠃⠀⣿⣿⣿⠀⠀⠀⠀⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⡿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⠁⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿", - "⣿⠟⠁⢀⣴⣶⣶⣾⣿⣿⣿⣿⣶⡐⢦⣄⠀⠀⠈⠛⢿⣿⣿⣿⣿⡀⠀⠀⠀⠀⢀⣼⡿⢛⣩⣴⣶⣶⣶⣶⣶⣶⣭⣙⠻⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿", - "⠁⠀⣴⣿⣿⣿⣿⠿⠿⣿⣿⣿⣿⣿⣦⡙⠻⣶⣄⡀⠀⠈⠙⢿⣿⣷⣦⣤⣤⣴⣿⡏⣠⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣌⠻⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿", - "⠀⢸⣿⣿⣿⠋⣠⠔⠀⠀⠻⣿⣿⣿⣿⢉⡳⢦⣉⠛⢷⣤⣀⠀⠈⠙⠿⣿⣿⣿⣿⢸⣿⡄⠻⣿⣿⠟⡈⣿⣿⣿⣿⣿⢉⣿⣧⢹⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿", - "⠀⢸⣿⣿⡇⠠⡇⠀⠀⠀⠀⣿⣿⣿⣿⢸⣿⣷⣤⣙⠢⢌⡛⠷⣤⣄⠀⠈⠙⠿⣿⣿⣿⣿⣷⣦⣴⣾⣿⣤⣙⣛⣛⣥⣾⣿⣿⡌⣿⣿⣿⣷⣤⣀⣀⣀⣠⣴⣿⣿⣿⣿⣿⣿⣿", - "⠀⢸⣿⣿⣷⡀⠡⠀⠀⠀⣰⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣦⣌⡓⠤⣙⣿⣦⡄⠀⠈⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢡⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - "⠀⢸⣿⣿⣿⣿⣶⣤⣴⣾⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣿⣷⠀⣶⡄⠀⠈⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢃⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - "⠀⢸⣿⣿⣿⣿⣿⠟⠻⣿⣿⡏⣉⣭⣭⡘⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⣿⡇⢸⡇⢠⡀⠈⠙⠋⠉⠉⠉⠉⠛⠫⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - "⠀⢸⣿⣿⠛⣿⣿⣀⣀⣾⡿⢀⣿⣿⣿⢻⣷⣦⢈⡙⠻⢿⣿⣿⣿⣿⣿⣿⣿⠀⣿⡇⢸⡇⢸⣿⠀⣦⠀⠀⠶⣶⣦⣀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - "⠀⢸⣿⣿⣦⣈⡛⠿⠟⣋⣤⣾⣿⣿⣿⣸⣿⣿⢸⡇⢰⡆⢈⡙⠻⢿⣿⣿⣿⠀⢿⡇⢸⡇⢸⣿⢠⣿⡇⣿⡆⢈⡙⠻⠧⠀⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - "⠀⠀⣝⠛⢿⣿⣿⣿⣿⣿⣿⠟⣁⠀⠀⢈⠛⠿⢸⣇⢸⡇⢸⡇⣶⣦⣌⡙⠻⢄⡀⠁⠘⠇⠘⣿⢸⣿⡇⣿⡇⢸⡛⠷⣦⣄⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", - }, - e = { - " ____ __ _____ ", - " / __ \\/ / / / |", - " / /_/ / / / / /| |", - " / _, _/ /_/ / ___ |", - "/_/ |_|\\____/_/ |_|", - }, - f = { - "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡤⠤⠤⠤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠞⠋⠁⠀⠀⠀⠀⠀⠀⠀⠉⠛⢦⣤⠶⠦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠀⠀⠀⠀⠀⠀⢀⣴⠞⢋⡽⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠃⠀⠀⠙⢶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠀⠀⠀⠀⠀⣰⠟⠁⠀⠘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⡀⠀⠀⠉⠓⠦⣤⣤⣤⣤⣤⣤⣄⣀⠀⠀⠀", - "⠀⠀⠀⠀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣷⡄⠀⠀⢻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣆⠀", - "⠀⠀⣠⠞⠁⠀⠀⣀⣠⣏⡀⠀⢠⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⠿⡃⠀⠀⠀⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡆", - "⢀⡞⠁⠀⣠⠶⠛⠉⠉⠉⠙⢦⡸⣿⡿⠀⠀⠀⡄⢀⣀⣀⡶⠀⠀⠀⢀⡄⣀⠀⣢⠟⢦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠃", - "⡞⠀⠀⠸⠁⠀⠀⠀⠀⠀⠀⠀⢳⢀⣠⠀⠀⠀⠉⠉⠀⠀⣀⠀⠀⠀⢀⣠⡴⠞⠁⠀⠀⠈⠓⠦⣄⣀⠀⠀⠀⠀⣀⣤⠞⠁⠀", - "⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⠁⠀⢀⣀⣀⡴⠋⢻⡉⠙⠾⡟⢿⣅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠙⠛⠉⠉⠀⠀⠀⠀", - "⠘⣦⡀⠀⠀⠀⠀⠀⠀⣀⣤⠞⢉⣹⣯⣍⣿⠉⠟⠀⠀⣸⠳⣄⡀⠀⠀⠙⢧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠈⠙⠒⠒⠒⠒⠚⠋⠁⠀⡴⠋⢀⡀⢠⡇⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⢀⡾⠋⢻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⢸⡀⠸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⢠⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣇⠀⠀⠉⠋⠻⣄⠀⠀⠀⠀⠀⣀⣠⣴⠞⠋⠳⠶⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⠦⢤⠤⠶⠋⠙⠳⣆⣀⣈⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", - }, - g = [[ + d = { + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠋⠀⢀⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠈⠉⠉⠙⠛⠛⠻⢿⣿⡿⠟⠁⠀⣀⣴⣿⣿⣿⣿⣿⠟", + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⢀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠛⣉⣡⠀⣠⣴⣶⣶⣦⠄⣀⡀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⡿⢃⣾", + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡏⠀⣾⣤⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⣠⣾⡟⢡⣾⣿⣿⣿⡿⢋⣴⣿⡿⢀⣴⣾⣿⣿⣿⣿⣿⣿⣿⢡⣾⣿", + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠟⠃⠀⢻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⣼⣿⡟⣰⣿⣿⣿⣿⠏⣰⣿⣿⠟⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⢚⣛⢿", + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠏⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣟⠸⣿⠟⢰⣿⣿⣿⣿⠃⣾⣿⣿⠏⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢋⣾", + "⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠿⠻⠻⠃⠀⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡄⢉⣴⣿⣿⣿⣿⡇⠘⣿⣿⠋⣼⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⡘⣿", + "⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠿⣿⣿⣿⣿⠁⢀⣀⠀⢀⣾⣿⣿⣿⣿⣿⣿⠟⠉⠉⠉⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⣤⣤⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣌", + "⣿⣿⣿⣿⣿⣿⡿⠁⣀⣤⡀⠀⠈⠻⢿⠀⣼⣿⣷⣿⣿⣿⣿⣿⣿⡿⠁⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + "⣿⣿⣿⠟⠛⠙⠃⠀⣿⣿⣿⠀⠀⠀⠀⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⡿⠿⠿⠿⠿⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⠁⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿", + "⣿⠟⠁⢀⣴⣶⣶⣾⣿⣿⣿⣿⣶⡐⢦⣄⠀⠀⠈⠛⢿⣿⣿⣿⣿⡀⠀⠀⠀⠀⢀⣼⡿⢛⣩⣴⣶⣶⣶⣶⣶⣶⣭⣙⠻⣿⣿⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⣿⣿⣿⣿⣿", + "⠁⠀⣴⣿⣿⣿⣿⠿⠿⣿⣿⣿⣿⣿⣦⡙⠻⣶⣄⡀⠀⠈⠙⢿⣿⣷⣦⣤⣤⣴⣿⡏⣠⢸⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⣌⠻⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⣿⣿⣿⣿", + "⠀⢸⣿⣿⣿⠋⣠⠔⠀⠀⠻⣿⣿⣿⣿⢉⡳⢦⣉⠛⢷⣤⣀⠀⠈⠙⠿⣿⣿⣿⣿⢸⣿⡄⠻⣿⣿⠟⡈⣿⣿⣿⣿⣿⢉⣿⣧⢹⣿⣿⣄⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿", + "⠀⢸⣿⣿⡇⠠⡇⠀⠀⠀⠀⣿⣿⣿⣿⢸⣿⣷⣤⣙⠢⢌⡛⠷⣤⣄⠀⠈⠙⠿⣿⣿⣿⣿⣷⣦⣴⣾⣿⣤⣙⣛⣛⣥⣾⣿⣿⡌⣿⣿⣿⣷⣤⣀⣀⣀⣠⣴⣿⣿⣿⣿⣿⣿⣿", + "⠀⢸⣿⣿⣷⡀⠡⠀⠀⠀⣰⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣦⣌⡓⠤⣙⣿⣦⡄⠀⠈⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⢡⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + "⠀⢸⣿⣿⣿⣿⣶⣤⣴⣾⣿⣿⣿⣿⣿⢸⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣿⣷⠀⣶⡄⠀⠈⠙⠿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⢃⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + "⠀⢸⣿⣿⣿⣿⣿⠟⠻⣿⣿⡏⣉⣭⣭⡘⠻⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠀⣿⡇⢸⡇⢠⡀⠈⠙⠋⠉⠉⠉⠉⠛⠫⣴⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + "⠀⢸⣿⣿⠛⣿⣿⣀⣀⣾⡿⢀⣿⣿⣿⢻⣷⣦⢈⡙⠻⢿⣿⣿⣿⣿⣿⣿⣿⠀⣿⡇⢸⡇⢸⣿⠀⣦⠀⠀⠶⣶⣦⣀⠀⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + "⠀⢸⣿⣿⣦⣈⡛⠿⠟⣋⣤⣾⣿⣿⣿⣸⣿⣿⢸⡇⢰⡆⢈⡙⠻⢿⣿⣿⣿⠀⢿⡇⢸⡇⢸⣿⢠⣿⡇⣿⡆⢈⡙⠻⠧⠀⢹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + "⠀⠀⣝⠛⢿⣿⣿⣿⣿⣿⣿⠟⣁⠀⠀⢈⠛⠿⢸⣇⢸⡇⢸⡇⣶⣦⣌⡙⠻⢄⡀⠁⠘⠇⠘⣿⢸⣿⡇⣿⡇⢸⡛⠷⣦⣄⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿", + }, + e = { + " ____ __ _____ ", + " / __ \\/ / / / |", + " / /_/ / / / / /| |", + " / _, _/ /_/ / ___ |", + "/_/ |_|\\____/_/ |_|", + }, + f = { + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣤⡤⠤⠤⠤⣤⣄⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡤⠞⠋⠁⠀⠀⠀⠀⠀⠀⠀⠉⠛⢦⣤⠶⠦⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠀⠀⠀⠀⠀⠀⢀⣴⠞⢋⡽⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠃⠀⠀⠙⢶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠀⠀⠀⠀⠀⣰⠟⠁⠀⠘⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⡀⠀⠀⠉⠓⠦⣤⣤⣤⣤⣤⣤⣄⣀⠀⠀⠀", + "⠀⠀⠀⠀⣠⠞⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣴⣷⡄⠀⠀⢻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣆⠀", + "⠀⠀⣠⠞⠁⠀⠀⣀⣠⣏⡀⠀⢠⣶⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠹⠿⡃⠀⠀⠀⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡆", + "⢀⡞⠁⠀⣠⠶⠛⠉⠉⠉⠙⢦⡸⣿⡿⠀⠀⠀⡄⢀⣀⣀⡶⠀⠀⠀⢀⡄⣀⠀⣢⠟⢦⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣸⠃", + "⡞⠀⠀⠸⠁⠀⠀⠀⠀⠀⠀⠀⢳⢀⣠⠀⠀⠀⠉⠉⠀⠀⣀⠀⠀⠀⢀⣠⡴⠞⠁⠀⠀⠈⠓⠦⣄⣀⠀⠀⠀⠀⣀⣤⠞⠁⠀", + "⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⠁⠀⢀⣀⣀⡴⠋⢻⡉⠙⠾⡟⢿⣅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠙⠛⠉⠉⠀⠀⠀⠀", + "⠘⣦⡀⠀⠀⠀⠀⠀⠀⣀⣤⠞⢉⣹⣯⣍⣿⠉⠟⠀⠀⣸⠳⣄⡀⠀⠀⠙⢧⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠈⠙⠒⠒⠒⠒⠚⠋⠁⠀⡴⠋⢀⡀⢠⡇⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⢀⡾⠋⢻⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⡇⠀⢸⡀⠸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠀⠀⢠⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣇⠀⠀⠉⠋⠻⣄⠀⠀⠀⠀⠀⣀⣠⣴⠞⠋⠳⠶⠞⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⠦⢤⠤⠶⠋⠙⠳⣆⣀⣈⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + "⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀", + }, + g = [[ ███╗ ██╗███████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗ ████╗ ██║██╔════╝██╔═══██╗██║ ██║██║████╗ ████║ ██╔██╗ ██║█████╗ ██║ ██║██║ ██║██║██╔████╔██║ @@ -168,47 +167,57 @@ local starter = require("mini.starter") -- 口径与 lazy._loaded 一致:每个独立初始化的功能模块算一个, -- 不再按 vim.pack 的插件包统计(如 mini.nvim 包含多个模块)。 local all_modules = { - "starter", "notify", "cmdline", "icons", - "pick", "files", - "diff", "surround", - "completion", "snippets", - "treesitter", "lsp", - "conform", "lspconfig", "mason", - "fugitive", "grugfar", + "starter", + "notify", + "cmdline", + "icons", + "pick", + "files", + "diff", + "surround", + "completion", + "snippets", + "treesitter", + "lsp", + "conform", + "lspconfig", + "mason", + "fugitive", + "grugfar", } -- 计算 Logo 的最大显示宽度,用于页脚居中计算 local max_header_width = 0 for _, line in ipairs(logs.f) do - max_header_width = math.max(max_header_width, vim.fn.strdisplaywidth(line)) + max_header_width = math.max(max_header_width, vim.fn.strdisplaywidth(line)) end starter.setup({ - autoopen = true, -- 无参数启动时自动打开 - evaluate_single = false, -- 只有一个选项时不自动执行 - items = { { name = " ", action = "", section = "" } }, -- 空选项(仅展示页眉页脚) - header = table.concat(logs.f, "\n"), -- Logo 文本 + autoopen = true, -- 无参数启动时自动打开 + evaluate_single = false, -- 只有一个选项时不自动执行 + items = { { name = " ", action = "", section = "" } }, -- 空选项(仅展示页眉页脚) + header = table.concat(logs.f, "\n"), -- Logo 文本 - -- 页脚函数:显示启动耗时和模块加载统计 - footer = function() - -- 已加载的模块数(动态统计,包含直接加载和懒加载) - local loaded = vim.tbl_count(require("lazy")._loaded) - local total = #all_modules - -- 优先使用 VimEnter 时间(runtime 插件、ShaDa 等已加载,最接近 --startuptime), - -- 回退到 init.lua 完成时间,最后回退到当前时间(理论上不会走到) - local end_time = _G.nvim_vimenter_done or _G.nvim_init_done or (vim.uv or vim.loop).hrtime() - local ms = (end_time - _G.nvim_start_time) / 1e6 - local text = string.format(" %d/%d modules | %.0f ms", loaded, total, ms) - local text_width = vim.fn.strdisplaywidth(text) - -- 计算左填充空格数以实现居中 - local pad = math.floor((max_header_width - text_width) / 2) - return string.rep(" ", pad) .. text - end, + -- 页脚函数:显示启动耗时和模块加载统计 + footer = function() + -- 已加载的模块数(动态统计,包含直接加载和懒加载) + local loaded = vim.tbl_count(require("lazy")._loaded) + local total = #all_modules + -- 优先使用 VimEnter 时间(runtime 插件、ShaDa 等已加载,最接近 --startuptime), + -- 回退到 init.lua 完成时间,最后回退到当前时间(理论上不会走到) + local end_time = _G.nvim_vimenter_done or _G.nvim_init_done or (vim.uv or vim.loop).hrtime() + local ms = (end_time - _G.nvim_start_time) / 1e6 + local text = string.format(" %d/%d modules | %.0f ms", loaded, total, ms) + local text_width = vim.fn.strdisplaywidth(text) + -- 计算左填充空格数以实现居中 + local pad = math.floor((max_header_width - text_width) / 2) + return string.rep(" ", pad) .. text + end, - -- 内容钩子:水平和垂直居中 - content_hooks = { - starter.gen_hook.aligning("center", "center"), - }, + -- 内容钩子:水平和垂直居中 + content_hooks = { + starter.gen_hook.aligning("center", "center"), + }, }) lazy.track("starter") @@ -217,49 +226,48 @@ lazy.track("starter") -- mini.files — 文件浏览器(按键触发懒加载) -- --------------------------------------------------------------------------- -- 提供悬浮文件浏览器,支持目录导航和文件操作。 - -- mini.files setup 配置(复用) local function setup_mini_files() - require("mini.files").setup({ - mappings = { - go_in = "", -- 回车进入目录或打开文件 - go_in_plus = "L", -- L 进入并同步光标 - go_out = "_", -- _ 返回上级目录 - go_out_plus = "H", -- H 返回上级并同步光标 - }, - }) + require("mini.files").setup({ + mappings = { + go_in = "", -- 回车进入目录或打开文件 + go_in_plus = "L", -- L 进入并同步光标 + go_out = "_", -- _ 返回上级目录 + go_out_plus = "H", -- H 返回上级并同步光标 + }, + }) end -- - :在当前工作目录打开文件浏览器 lazy.on_keys("files", "-", "n", setup_mini_files, function() - require("mini.files").open() + require("mini.files").open() end, { desc = "打开文件浏览器(工作目录)" }) -- - :在当前文件所在目录打开文件浏览器 lazy.on_keys("files", "-", "n", setup_mini_files, function() - local MiniFiles = require("mini.files") - -- 打开当前文件所在目录,并定位到当前文件 - MiniFiles.open(vim.api.nvim_buf_get_name(0), false) - MiniFiles.reveal_cwd() + local MiniFiles = require("mini.files") + -- 打开当前文件所在目录,并定位到当前文件 + MiniFiles.open(vim.api.nvim_buf_get_name(0), false) + MiniFiles.reveal_cwd() end, { desc = "打开文件浏览器(当前文件目录)" }) -- 在启动页(mini.starter)中也绑定 - / -, -- 因为 starter buffer 可能拦截全局映射 vim.api.nvim_create_autocmd("User", { - pattern = "MiniStarterOpened", - callback = function(args) - vim.keymap.set("n", "-", function() - require("lazy").load("files", setup_mini_files) - require("mini.files").open() - end, { buffer = args.buf, desc = "打开文件浏览器(工作目录)" }) + pattern = "MiniStarterOpened", + callback = function(args) + vim.keymap.set("n", "-", function() + require("lazy").load("files", setup_mini_files) + require("mini.files").open() + end, { buffer = args.buf, desc = "打开文件浏览器(工作目录)" }) - vim.keymap.set("n", "-", function() - require("lazy").load("files", setup_mini_files) - local MiniFiles = require("mini.files") - -- starter 无当前文件,打开工作目录 - MiniFiles.open(vim.fn.getcwd()) - end, { buffer = args.buf, desc = "打开文件浏览器(工作目录)" }) - end, + vim.keymap.set("n", "-", function() + require("lazy").load("files", setup_mini_files) + local MiniFiles = require("mini.files") + -- starter 无当前文件,打开工作目录 + MiniFiles.open(vim.fn.getcwd()) + end, { buffer = args.buf, desc = "打开文件浏览器(工作目录)" }) + end, }) -- --------------------------------------------------------------------------- @@ -267,13 +275,12 @@ vim.api.nvim_create_autocmd("User", { -- --------------------------------------------------------------------------- -- 提供文件类型图标,供 mini.pick、mini.files 等使用。 -- 在 VimEnter 后 setup,确保 UI 已初始化。 - vim.api.nvim_create_autocmd("VimEnter", { - once = true, - callback = function() - require("mini.icons").setup() - lazy._loaded["icons"] = true - end, + once = true, + callback = function() + require("mini.icons").setup() + lazy._loaded["icons"] = true + end, }) -- --------------------------------------------------------------------------- @@ -281,14 +288,13 @@ vim.api.nvim_create_autocmd("VimEnter", { -- --------------------------------------------------------------------------- -- 替换默认的 vim.notify,提供美观的浮动通知窗口。 -- 直接初始化(轻量,不阻塞启动)。 - require("mini.notify").setup({ - content = { - -- 简化格式:只显示消息内容,不附加时间戳等元信息 - format = function(notif) - return notif.msg - end, - }, + content = { + -- 简化格式:只显示消息内容,不附加时间戳等元信息 + format = function(notif) + return notif.msg + end, + }, }) -- 将 vim.notify 重定向到 mini.notify vim.notify = require("mini.notify").make_notify() @@ -299,20 +305,19 @@ lazy.track("notify") -- --------------------------------------------------------------------------- -- 提供美观的命令行界面,首次按 : 时加载。 -- 使用 expr 映射:返回 ":" 让 Vim 继续处理命令行输入。 - vim.keymap.set("n", ":", function() - -- 首次按下 : 时删除此映射,避免后续重复触发 setup - vim.keymap.del("n", ":") - require("mini.cmdline").setup({ - autocorrect = { enable = false }, -- 禁用自动纠正 - }) - lazy.track("cmdline") - return ":" + -- 首次按下 : 时删除此映射,避免后续重复触发 setup + vim.keymap.del("n", ":") + require("mini.cmdline").setup({ + autocorrect = { enable = false }, -- 禁用自动纠正 + }) + lazy.track("cmdline") + return ":" end, { expr = true, noremap = true }) -- 诊断位置列表(Quickfix 的本地化版本) vim.keymap.set("n", "ds", function() - vim.diagnostic.setloclist() + vim.diagnostic.setloclist() end, { desc = "诊断位置列表" }) -- --------------------------------------------------------------------------- @@ -320,120 +325,114 @@ end, { desc = "诊断位置列表" }) -- --------------------------------------------------------------------------- -- 所有 pick 相关映射共用 pick.load_pick() 初始化,确保 mini.pick 和 mini.extra -- 只在首次使用时 setup 一次。 - -- ff :文件查找 lazy.on_keys("pick", "ff", "n", pick.load_pick, function() - require("mini.pick").builtin.files() + require("mini.pick").builtin.files() end, { desc = "文件查找" }) -- fw :实时 grep(在项目中搜索文本) lazy.on_keys("pick", "fw", "n", pick.load_pick, function() - require("mini.pick").builtin.grep_live() + require("mini.pick").builtin.grep_live() end, { desc = "项目内实时搜索" }) -- sk :键位映射搜索 lazy.on_keys("pick", "sk", "n", pick.load_pick, function() - require("mini.extra").pickers.keymaps() + require("mini.extra").pickers.keymaps() end, { desc = "搜索键位映射" }) -- fa :查找所有文件(含隐藏和忽略的文件) lazy.on_keys("pick", "fa", "n", pick.load_pick, function() - require("mini.pick").builtin.cli({ - command = { "rg", "--files", "--hidden", "--no-ignore", "--color=never" } - }) + require("mini.pick").builtin.cli({ + command = { "rg", "--files", "--hidden", "--no-ignore", "--color=never" }, + }) end, { desc = "查找所有文件(含隐藏/忽略)" }) -- fh :帮助标签搜索 lazy.on_keys("pick", "fh", "n", pick.load_pick, function() - require("mini.pick").builtin.help() + require("mini.pick").builtin.help() end, { desc = "搜索帮助标签" }) -- fo :最近打开的文件 lazy.on_keys("pick", "fo", "n", pick.load_pick, function() - require("mini.extra").pickers.oldfiles() + require("mini.extra").pickers.oldfiles() end, { desc = "最近打开的文件" }) -- fz :当前缓冲区行内搜索 lazy.on_keys("pick", "fz", "n", pick.load_pick, function() - require("mini.extra").pickers.buf_lines({ scope = "current" }) + require("mini.extra").pickers.buf_lines({ scope = "current" }) end, { desc = "当前缓冲区行内搜索" }) -- --------------------------------------------------------------------------- -- vim-fugitive — Git 集成(按键触发懒加载) -- --------------------------------------------------------------------------- -- tpope 的 Git 包装插件,提供 :Git 等命令。 - local function load_fugitive() - vim.cmd.packadd("vim-fugitive") + vim.cmd.packadd("vim-fugitive") end -- gg :在新标签页中打开 Fugitive 全屏 lazy.on_keys("fugitive", "gg", "n", load_fugitive, function() - vim.cmd("tabnew | Git | only") + vim.cmd("tabnew | Git | only") end, { desc = "Fugitive 全屏新标签" }) -- gd :Git diff 垂直分割 lazy.on_keys("fugitive", "gd", "n", load_fugitive, function() - vim.cmd("Gvdiffsplit") + vim.cmd("Gvdiffsplit") end, { desc = "Git diff 分割" }) -- --------------------------------------------------------------------------- -- mini.completion — 自动补全(InsertEnter 懒加载) -- --------------------------------------------------------------------------- -- 轻量级补全引擎,支持 LSP 补全、buffer 单词、路径补全。 - lazy.on_event("completion", "InsertEnter", "*", function() - require("mini.completion").setup({ - lsp_completion = { - auto_setup = true, -- 自动配置 LSP 补全源 - }, - }) + require("mini.completion").setup({ + lsp_completion = { + auto_setup = true, -- 自动配置 LSP 补全源 + }, + }) end) -- --------------------------------------------------------------------------- -- mini.snippets — 代码片段(InsertEnter 懒加载) -- --------------------------------------------------------------------------- -- 代码片段引擎,支持 LSP 片段扩展。 - lazy.on_event("snippets", "InsertEnter", "*", function() - local MiniSnippets = require("mini.snippets") - MiniSnippets.setup({ - snippets = { - -- 从 friendly-snippets 加载对应语言的片段 - MiniSnippets.gen_loader.from_lang(), - }, - }) - -- 启动 LSP 片段服务器(match = false 表示不匹配时不自动触发) - MiniSnippets.start_lsp_server({ match = false }) + local MiniSnippets = require("mini.snippets") + MiniSnippets.setup({ + snippets = { + -- 从 friendly-snippets 加载对应语言的片段 + MiniSnippets.gen_loader.from_lang(), + }, + }) + -- 启动 LSP 片段服务器(match = false 表示不匹配时不自动触发) + 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", -- 以符号形式显示(在 signcolumn 中) - signs = { add = "│", change = "│", delete = "│" }, -- 统一的竖线符号 - }, - mappings = { - apply = "gs", -- gs 应用当前 hunk - textobject = "", -- 禁用文本对象映射 - }, - }) + require("mini.diff").setup({ + -- 使用 git 作为 diff 源(index = false 表示对比工作区 vs HEAD,不含暂存区) + source = require("mini.diff").gen_source.git({ index = false }), + view = { + style = "sign", -- 以符号形式显示(在 signcolumn 中) + signs = { add = "│", change = "│", delete = "│" }, -- 统一的竖线符号 + }, + mappings = { + apply = "gs", -- gs 应用当前 hunk + textobject = "", -- 禁用文本对象映射 + }, + }) end) -- --------------------------------------------------------------------------- -- mini.surround — 环绕文本操作(BufReadPost 懒加载) -- --------------------------------------------------------------------------- -- 快速添加、删除、修改包围符号(如括号、引号、HTML 标签等)。 - lazy.on_event("surround", "BufReadPost", "*", function() - require("mini.surround").setup() + require("mini.surround").setup() end) -- --------------------------------------------------------------------------- @@ -441,35 +440,33 @@ end) -- --------------------------------------------------------------------------- -- treesitter 和 lsp 是启动时最耗时的模块, -- 延迟到 VimEnter 事件触发后加载,让编辑器界面先渲染出来。 - lazy.on_event("treesitter", "VimEnter", "*", function() - require("treesitter").setup() + require("plugins.treesitter").setup() end) lazy.on_event("lsp", "VimEnter", "*", function() - require("lsp") + require("plugins.lsp") end) -- --------------------------------------------------------------------------- -- grug-far.nvim — 搜索与替换(按键触发懒加载) -- --------------------------------------------------------------------------- -- 提供类似 VS Code 的查找替换界面,支持正则和文件过滤。 - local function load_grug_far() - vim.cmd.packadd("grug-far.nvim") - require("grug-far").setup({ headerMaxWidth = 80 }) + vim.cmd.packadd("grug-far.nvim") + require("grug-far").setup({ headerMaxWidth = 80 }) end local function open_grug_far() - local grug = require("grug-far") - -- 根据当前文件扩展名预填充文件过滤器 - local ext = vim.bo.buftype == "" and vim.fn.expand("%:e") - grug.open({ - transient = true, -- 关闭后自动销毁 buffer - prefills = { - filesFilter = ext and ext ~= "" and "*." .. ext or nil, - }, - }) + local grug = require("grug-far") + -- 根据当前文件扩展名预填充文件过滤器 + local ext = vim.bo.buftype == "" and vim.fn.expand("%:e") + grug.open({ + transient = true, -- 关闭后自动销毁 buffer + prefills = { + filesFilter = ext and ext ~= "" and "*." .. ext or nil, + }, + }) end -- Normal 模式和 Visual 模式均绑定 sr diff --git a/lua/pick.lua b/lua/pick.lua deleted file mode 100644 index 9b8a5f9..0000000 --- a/lua/pick.lua +++ /dev/null @@ -1,125 +0,0 @@ --- ============================================================================= --- mini.pick 封装 (lua/pick.lua) --- ============================================================================= --- 本模块封装 mini.pick 和 mini.extra 的初始化逻辑, --- 提供统一的懒加载入口和自定义 picker。 --- --- 主要功能: --- 1. load_pick() — 懒加载 mini.pick 和 mini.extra --- 2. Buffer picker()— 自定义 buffer 列表,支持 删除 --- 3. Filetype picker(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() --- --------------------------------------------------------------------------- --- 显示当前打开的 buffer 列表,按最近使用时间排序。 --- 特殊功能: --- - :删除当前选中的 buffer(带图标过滤) --- - 支持 mini.icons 图标显示 - -vim.keymap.set("n", "", 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 - - -- 安全删除 buffer(force = 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 = "", func = delete_buf }, - }, - }) -end, { desc = "Buffer 列表" }) - --- --------------------------------------------------------------------------- --- Filetype Picker(ft) --- --------------------------------------------------------------------------- --- 显示所有可用的文件类型列表,选择后设置当前 buffer 的 filetype。 --- 常用于打开无扩展名文件或纠正错误的文件类型检测。 - -vim.keymap.set("n", "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 diff --git a/lua/plugins/git.lua b/lua/plugins/git.lua new file mode 100644 index 0000000..bbf162b --- /dev/null +++ b/lua/plugins/git.lua @@ -0,0 +1,199 @@ +-- ============================================================================= +-- Git 工具封装 (lua/git.lua) +-- ============================================================================= +-- 本模块提供基于 mini.diff 和原生 git 命令的 Git 相关功能。 +-- +-- 功能列表: +-- 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 +-- +-- 依赖: +-- - 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", "", function() + vim.api.nvim_win_close(win, true) + end, { buffer = preview_buf }) +end + +-- --------------------------------------------------------------------------- +-- Blame Line — 查看当前行的 Git blame 信息 +-- --------------------------------------------------------------------------- +-- 执行 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 + ) +end + +-- --------------------------------------------------------------------------- +-- 键位映射 +-- --------------------------------------------------------------------------- + +-- ghp — 预览当前 hunk +vim.keymap.set("n", "ghp", preview_hunk, { desc = "预览 hunk" }) + +-- ghb — 查看当前行 blame +vim.keymap.set("n", "ghb", blame_line, { desc = "Blame 当前行" }) + +-- gB — 整文件 blame(使用 fugitive 的 :Git blame) +vim.keymap.set("n", "gB", function() + vim.cmd("Git blame") +end, { desc = "Blame 整个文件" }) + +-- gD — 查看当前文件的 git 历史 +vim.keymap.set("n", "gD", function() + vim.cmd("Git log -p -- %") +end, { desc = "查看文件 Git 历史" }) diff --git a/lua/lsp.lua b/lua/plugins/lsp.lua similarity index 62% rename from lua/lsp.lua rename to lua/plugins/lsp.lua index a03fe6f..59f1d31 100644 --- a/lua/lsp.lua +++ b/lua/plugins/lsp.lua @@ -12,7 +12,6 @@ -- 2. nvim-lspconfig(直接 packadd) -- 3. mason.nvim(直接 setup) -- ============================================================================= - local lazy = require("lazy") -- --------------------------------------------------------------------------- @@ -21,47 +20,46 @@ local lazy = require("lazy") -- conform.nvim 是一个轻量级格式化器包装器,支持保存时自动格式化。 -- 由于格式化器通常不是立即需要的,通过 lazy.on_event 在 BufWritePre 时 -- 才进行 packadd 和 setup。 - local function setup_conform() - -- 通过 packadd 加载 conform.nvim(因为 vim.pack.add 时设置了 load = false) - vim.cmd.packadd("conform.nvim") + -- 通过 packadd 加载 conform.nvim(因为 vim.pack.add 时设置了 load = false) + vim.cmd.packadd("conform.nvim") - -- 动态判断使用 Biome 还是 Prettier。 - -- 从当前文件向上搜索 biome.json / biome.jsonc,如果找到则使用 Biome, - -- 否则回退到 Prettier。stop 参数限制搜索到用户主目录,避免向上搜索到根目录。 - local function biome_or_prettier() - if vim.fs.find({ "biome.json", "biome.jsonc" }, { upward = true, stop = vim.uv.os_homedir() })[1] then - return { "biome-check", "biome", stop_after_first = true } - end - return { "prettier" } - end + -- 动态判断使用 Biome 还是 Prettier。 + -- 从当前文件向上搜索 biome.json / biome.jsonc,如果找到则使用 Biome, + -- 否则回退到 Prettier。stop 参数限制搜索到用户主目录,避免向上搜索到根目录。 + local function biome_or_prettier() + if vim.fs.find({ "biome.json", "biome.jsonc" }, { upward = true, stop = vim.uv.os_homedir() })[1] then + return { "biome-check", "biome", stop_after_first = true } + end + return { "prettier" } + end - require("conform").setup({ - -- 按文件类型配置格式化器 - formatters_by_ft = { - lua = { "stylua" }, -- Lua 格式化 - go = { "gofumpt", "goimports" }, -- Go:先 gofumpt 格式化,再 goimports 整理导入 - javascript = biome_or_prettier, -- JS:Biome 或 Prettier - typescript = biome_or_prettier, -- TS:Biome 或 Prettier - javascriptreact = biome_or_prettier, -- JSX:Biome 或 Prettier - typescriptreact = biome_or_prettier, -- TSX:Biome 或 Prettier - json = biome_or_prettier, -- JSON:Biome 或 Prettier - css = { "prettier" }, -- CSS:Prettier - html = { "prettier" }, -- HTML:Prettier - markdown = { "prettier" }, -- Markdown:Prettier - toml = { "taplo" }, -- TOML:taplo - }, + require("conform").setup({ + -- 按文件类型配置格式化器 + formatters_by_ft = { + lua = { "stylua" }, -- Lua 格式化 + go = { "gofumpt", "goimports" }, -- Go:先 gofumpt 格式化,再 goimports 整理导入 + javascript = biome_or_prettier, -- JS:Biome 或 Prettier + typescript = biome_or_prettier, -- TS:Biome 或 Prettier + javascriptreact = biome_or_prettier, -- JSX:Biome 或 Prettier + typescriptreact = biome_or_prettier, -- TSX:Biome 或 Prettier + json = biome_or_prettier, -- JSON:Biome 或 Prettier + css = { "prettier" }, -- CSS:Prettier + html = { "prettier" }, -- HTML:Prettier + markdown = { "prettier" }, -- Markdown:Prettier + toml = { "taplo" }, -- TOML:taplo + }, - -- 保存时自动格式化回调 - -- 可通过 vim.b[bufnr].autoformat = false 关闭当前 buffer 的自动格式化 - -- 或通过 vim.g.autoformat = false 全局关闭 - format_on_save = function(bufnr) - if vim.b[bufnr].autoformat == false or vim.g.autoformat == false then - return nil -- 返回 nil 表示不格式化 - end - return { timeout_ms = 500, lsp_fallback = true } - end, - }) + -- 保存时自动格式化回调 + -- 可通过 vim.b[bufnr].autoformat = false 关闭当前 buffer 的自动格式化 + -- 或通过 vim.g.autoformat = false 全局关闭 + format_on_save = function(bufnr) + if vim.b[bufnr].autoformat == false or vim.g.autoformat == false then + return nil -- 返回 nil 表示不格式化 + end + return { timeout_ms = 500, lsp_fallback = true } + end, + }) end -- BufWritePre 时触发 conform 加载。 @@ -69,11 +67,11 @@ end -- 但新创建的 autocmd 不会在同一次事件中触发。 -- 因此这里手动调用 format 补偿第一次保存(确保首次保存也能格式化)。 lazy.on_event("conform", "BufWritePre", "*", function() - setup_conform() - local bufnr = vim.api.nvim_get_current_buf() - if vim.b[bufnr].autoformat ~= false and vim.g.autoformat ~= false then - require("conform").format({ bufnr = bufnr, timeout_ms = 500, lsp_fallback = true }) - end + setup_conform() + local bufnr = vim.api.nvim_get_current_buf() + if vim.b[bufnr].autoformat ~= false and vim.g.autoformat ~= false then + require("conform").format({ bufnr = bufnr, timeout_ms = 500, lsp_fallback = true }) + end end) -- --------------------------------------------------------------------------- @@ -81,7 +79,6 @@ end) -- --------------------------------------------------------------------------- -- Mason 是 LSP 服务器、DAP 适配器和格式化工具的安装管理器。 -- nvim-lspconfig 提供常见 LSP 服务器的预设配置。 - vim.cmd.packadd("nvim-lspconfig") lazy.track("lspconfig") @@ -98,11 +95,11 @@ vim.lsp.config("*", { capabilities = capabilities }) -- Lua 语言服务器特殊配置:将 "vim" 声明为全局变量,避免 "Undefined global" 诊断 vim.lsp.config("lua_ls", { - settings = { - Lua = { - diagnostics = { globals = { "vim" } }, - }, - }, + settings = { + Lua = { + diagnostics = { globals = { "vim" } }, + }, + }, }) -- --------------------------------------------------------------------------- @@ -127,16 +124,16 @@ vim.lsp.config("lua_ls", { -- 启用指定的 LSP 服务器 vim.lsp.enable({ - "html", -- HTML 语言支持 - "cssls", -- CSS/SCSS/Less 语言支持 - "gopls", -- Go 语言支持 - "vtsls", -- TypeScript/JavaScript 支持(推荐替代 tsserver) - "rust_analyzer", -- Rust 语言支持 - "lua_ls", -- Lua 语言支持 - "taplo", -- TOML 支持 - "svelte", -- Svelte 框架支持 - "dartls", -- Dart/Flutter 支持 - "kotlin_lsp", -- Kotlin 支持 + "html", -- HTML 语言支持 + "cssls", -- CSS/SCSS/Less 语言支持 + "gopls", -- Go 语言支持 + "vtsls", -- TypeScript/JavaScript 支持(推荐替代 tsserver) + "rust_analyzer", -- Rust 语言支持 + "lua_ls", -- Lua 语言支持 + "taplo", -- TOML 支持 + "svelte", -- Svelte 框架支持 + "dartls", -- Dart/Flutter 支持 + "kotlin_lsp", -- Kotlin 支持 }) -- --------------------------------------------------------------------------- @@ -147,28 +144,28 @@ vim.lsp.enable({ -- gd - Go to Definition:跳转到定义位置 vim.keymap.set("n", "gd", function() - vim.lsp.buf.definition() + vim.lsp.buf.definition() end, { desc = "跳转到定义" }) -- gh - Hover:显示光标下符号的文档悬浮窗 vim.keymap.set("n", "gh", function() - vim.lsp.buf.hover() + vim.lsp.buf.hover() end, { desc = "悬停查看文档" }) -- fm - 手动格式化当前 buffer vim.keymap.set("n", "fm", function() - lazy.load("conform", setup_conform) - require("conform").format({ lsp_fallback = true }) + lazy.load("conform", setup_conform) + require("conform").format({ lsp_fallback = true }) end, { desc = "格式化当前 Buffer" }) -- df - 显示当前行的诊断浮动窗口 vim.keymap.set("n", "df", function() - vim.diagnostic.open_float() + vim.diagnostic.open_float() end, { desc = "显示行诊断" }) -- ca - Code Action:显示可用的代码操作(如自动修复、重构) vim.keymap.set("n", "ca", function() - vim.lsp.buf.code_action() + vim.lsp.buf.code_action() end, { desc = "代码操作" }) -- --------------------------------------------------------------------------- @@ -182,25 +179,25 @@ end, { desc = "代码操作" }) -- vim.v.count1 是计数前缀的默认值(无数字时为 1), -- 支持 3]d 跳转到第 3 个诊断。 local diagnostic_goto = function(next, severity) - return function() - vim.diagnostic.jump({ - count = (next and 1 or -1) * vim.v.count1, - severity = severity and vim.diagnostic.severity[severity] or nil, - float = true, -- 跳转时显示诊断浮动窗口 - }) - end + return function() + vim.diagnostic.jump({ + count = (next and 1 or -1) * vim.v.count1, + severity = severity and vim.diagnostic.severity[severity] or nil, + float = true, -- 跳转时显示诊断浮动窗口 + }) + end end -- ]d / [d - 下一个 / 上一个诊断(所有级别) -vim.keymap.set("n", "]d", diagnostic_goto(true), { desc = "下一个诊断" }) +vim.keymap.set("n", "]d", diagnostic_goto(true), { desc = "下一个诊断" }) vim.keymap.set("n", "[d", diagnostic_goto(false), { desc = "上一个诊断" }) -- ]e / [e - 下一个 / 上一个错误 -vim.keymap.set("n", "]e", diagnostic_goto(true, "ERROR"), { desc = "下一个错误" }) +vim.keymap.set("n", "]e", diagnostic_goto(true, "ERROR"), { desc = "下一个错误" }) vim.keymap.set("n", "[e", diagnostic_goto(false, "ERROR"), { desc = "上一个错误" }) -- ]w / [w - 下一个 / 上一个警告 -vim.keymap.set("n", "]w", diagnostic_goto(true, "WARN"), { desc = "下一个警告" }) +vim.keymap.set("n", "]w", diagnostic_goto(true, "WARN"), { desc = "下一个警告" }) vim.keymap.set("n", "[w", diagnostic_goto(false, "WARN"), { desc = "上一个警告" }) -- --------------------------------------------------------------------------- diff --git a/lua/plugins/pick.lua b/lua/plugins/pick.lua new file mode 100644 index 0000000..c520880 --- /dev/null +++ b/lua/plugins/pick.lua @@ -0,0 +1,121 @@ +-- ============================================================================= +-- mini.pick 封装 (lua/pick.lua) +-- ============================================================================= +-- 本模块封装 mini.pick 和 mini.extra 的初始化逻辑, +-- 提供统一的懒加载入口和自定义 picker。 +-- +-- 主要功能: +-- 1. load_pick() — 懒加载 mini.pick 和 mini.extra +-- 2. Buffer picker()— 自定义 buffer 列表,支持 删除 +-- 3. Filetype picker(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() +-- --------------------------------------------------------------------------- +-- 显示当前打开的 buffer 列表,按最近使用时间排序。 +-- 特殊功能: +-- - :删除当前选中的 buffer(带图标过滤) +-- - 支持 mini.icons 图标显示 +vim.keymap.set("n", "", 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 + + -- 安全删除 buffer(force = 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 = "", func = delete_buf }, + }, + }) +end, { desc = "Buffer 列表" }) + +-- --------------------------------------------------------------------------- +-- Filetype Picker(ft) +-- --------------------------------------------------------------------------- +-- 显示所有可用的文件类型列表,选择后设置当前 buffer 的 filetype。 +-- 常用于打开无扩展名文件或纠正错误的文件类型检测。 +vim.keymap.set("n", "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 diff --git a/lua/plugins/treesitter.lua b/lua/plugins/treesitter.lua new file mode 100644 index 0000000..6091fb0 --- /dev/null +++ b/lua/plugins/treesitter.lua @@ -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 diff --git a/lua/treesitter.lua b/lua/treesitter.lua deleted file mode 100644 index 93237ca..0000000 --- a/lua/treesitter.lua +++ /dev/null @@ -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 diff --git a/lua/usercmds.lua b/lua/usercmds.lua index 017f8ed..ba5cd63 100644 --- a/lua/usercmds.lua +++ b/lua/usercmds.lua @@ -23,9 +23,8 @@ -- vim.pack 会从 GitHub 下载插件到 stdpath("data")/site/pack/core/opt/。 -- -- nargs = "+" 表示至少需要 1 个参数。 - 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)" }) -- --------------------------------------------------------------------------- @@ -39,9 +38,8 @@ end, { nargs = "+", desc = "添加插件 (:PackAdd user/repo1 user/repo2)" }) -- Neovim 0.13 Nightly 已内置此命令,这里为 0.12 提供兼容。 -- -- nargs = "+" 表示至少需要 1 个参数。 - 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)" }) -- --------------------------------------------------------------------------- @@ -57,16 +55,15 @@ end, { nargs = "+", desc = "删除插件 (:PackDel plugin1 plugin2)" }) -- 如果无参数,调用无参版本更新所有插件。 -- -- nargs = "*" 表示接受 0 个或多个参数。 - vim.api.nvim_create_user_command("PackUpdate", function(opts) - -- 检查是否有传入任何参数(包含非空白字符) - if opts.args:match("%S") then - -- 按空白字符分割参数,trimempty 去除空字符串 - local plugins = vim.split(opts.args, "%s+", { trimempty = true }) - -- 仅更新指定的插件 - vim.pack.update(plugins) - else - -- 无参数,更新所有插件 - vim.pack.update() - end + -- 检查是否有传入任何参数(包含非空白字符) + if opts.args:match("%S") then + -- 按空白字符分割参数,trimempty 去除空字符串 + local plugins = vim.split(opts.args, "%s+", { trimempty = true }) + -- 仅更新指定的插件 + vim.pack.update(plugins) + else + -- 无参数,更新所有插件 + vim.pack.update() + end end, { nargs = "*", desc = "更新所有插件或指定插件" })