From 11565f3c55c200afe9097dd02736edb90fac721b Mon Sep 17 00:00:00 2001 From: kody Date: Mon, 15 Dec 2025 09:57:50 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E9=87=8D=E6=9E=84Neovim=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E7=BB=93=E6=9E=84=E5=B9=B6=E6=9B=B4=E6=96=B0=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整配置文件组织方式,新增init.lua作为入口 - 更新lazy-lock.json同步插件版本 - 完善lua/目录下的插件和配置模块结构 - 修改README.md补充配置说明和使用指南 --- README.md | 181 ++++++++++++++++++++++++++++++- init.lua | 10 ++ lazy-lock.json | 38 +++++++ lua/config/basic.lua | 75 +++++++++++++ lua/config/lazy.lua | 15 +++ lua/plugins/autopairs.lua | 138 ++++++++++++++++++++++++ lua/plugins/comment.lua | 10 ++ lua/plugins/gitsigns.lua | 15 +++ lua/plugins/init.lua | 34 ++++++ lua/plugins/lsp/cmp.lua | 123 +++++++++++++++++++++ lua/plugins/lsp/conform.lua | 130 ++++++++++++++++++++++ lua/plugins/lsp/dap.lua | 197 ++++++++++++++++++++++++++++++++++ lua/plugins/lsp/init.lua | 12 +++ lua/plugins/lsp/lspconfig.lua | 65 +++++++++++ lua/plugins/lsp/mason.lua | 39 +++++++ lua/plugins/lualine.lua | 15 +++ lua/plugins/notify.lua | 12 +++ lua/plugins/nvim-tree.lua | 26 +++++ lua/plugins/project.lua | 12 +++ lua/plugins/runner.lua | 17 +++ lua/plugins/telescope.lua | 23 ++++ lua/plugins/theme.lua | 8 ++ lua/plugins/treesitter.lua | 17 +++ lua/scripts/edit-config.lua | 4 + lua/scripts/init.lua | 5 + lua/scripts/reload-config.lua | 5 + 26 files changed, 1225 insertions(+), 1 deletion(-) create mode 100644 init.lua create mode 100644 lazy-lock.json create mode 100644 lua/config/basic.lua create mode 100644 lua/config/lazy.lua create mode 100644 lua/plugins/autopairs.lua create mode 100644 lua/plugins/comment.lua create mode 100644 lua/plugins/gitsigns.lua create mode 100644 lua/plugins/init.lua create mode 100644 lua/plugins/lsp/cmp.lua create mode 100644 lua/plugins/lsp/conform.lua create mode 100644 lua/plugins/lsp/dap.lua create mode 100644 lua/plugins/lsp/init.lua create mode 100644 lua/plugins/lsp/lspconfig.lua create mode 100644 lua/plugins/lsp/mason.lua create mode 100644 lua/plugins/lualine.lua create mode 100644 lua/plugins/notify.lua create mode 100644 lua/plugins/nvim-tree.lua create mode 100644 lua/plugins/project.lua create mode 100644 lua/plugins/runner.lua create mode 100644 lua/plugins/telescope.lua create mode 100644 lua/plugins/theme.lua create mode 100644 lua/plugins/treesitter.lua create mode 100644 lua/scripts/edit-config.lua create mode 100644 lua/scripts/init.lua create mode 100644 lua/scripts/reload-config.lua diff --git a/README.md b/README.md index 7b2b81c..db7e01d 100644 --- a/README.md +++ b/README.md @@ -1 +1,180 @@ -# NeoVim +# Neovim 配置文档 + +## 简介 + +这是一个基于 Neovim 的现代化开发环境配置,集成了 LSP、代码补全、调试工具、文件管理等功能,适用于多种编程语言开发。 + +## 功能特点 + +- 完整的 LSP 支持,提供代码补全、定义跳转、重构等功能 +- 强大的调试工具集成(DAP) +- 自动化代码格式化 +- 美观的界面主题与状态栏 +- 便捷的文件浏览与搜索 +- Git 版本控制集成 +- 自定义快捷键,提升操作效率 + +## 安装要求 + +- Neovim 0.8.0 或更高版本 +- Git +- 适当的包管理器(根据系统不同) +- 所需的 LSP 服务器和格式化工具(会通过 Mason 自动安装) + +## 安装步骤 + +1. 克隆本配置仓库到你的 Neovim 配置目录: + + ```bash + git clone <仓库地址> ~/.config/nvim + ``` + +2. 启动 Neovim,Lazy 包管理器会自动安装所需插件: + + ```bash + nvim + ``` + +3. 插件安装完成后,Mason 会自动安装配置好的 LSP 服务器和工具 + +## 目录结构 + +``` +nvim/ +├── init.lua # 入口文件 +├── lazy-lock.json # 插件版本锁定 +├── lua/ +│ ├── config/ # 基础配置 +│ │ ├── basic.lua # 基本设置 +│ │ └── lazy.lua # Lazy 包管理器配置 +│ ├── plugins/ # 插件配置 +│ │ ├── lsp/ # LSP 相关配置 +│ │ └── ... # 其他插件配置 +│ └── scripts/ # 辅助脚本 +└── README.md # 本说明文档 +``` + +## 核心插件 + +1. **LSP 相关** + - `neovim/nvim-lspconfig`:LSP 客户端配置 + - `williamboman/mason.nvim`:LSP 服务器安装管理 + - `hrsh7th/nvim-cmp`:自动补全框架 + +2. **调试工具** + - `mfussenegger/nvim-dap`:调试适配器协议客户端 + - `rcarriga/nvim-dap-ui`:调试 UI 界面 + +3. **代码格式化** + - `stevearc/conform.nvim`:代码格式化框架 + +4. **文件管理** + - `nvim-tree/nvim-tree.lua`:文件树 + - `nvim-telescope/telescope.nvim`:模糊搜索 + +5. **外观** + - `folke/tokyonight.nvim`:主题 + - `nvim-lualine/lualine.nvim`:状态栏 + +6. **其他工具** + - `numToStr/Comment.nvim`:代码注释 + - `lewis6991/gitsigns.nvim`:Git 集成 + +## 快捷键说明 + +### 基础操作 + +| 快捷键 | 功能描述 | +| ------------------- | -------------------- | +| `t` | 打开终端 | +| ``(终端模式) | 退出终端模式 | +| `:EditConfig` | 编辑 Neovim 配置文件 | +| `:ReloadConfig` | 重新加载配置 | + +### 文件管理 + +| 快捷键 | 功能描述 | +| ------------ | ----------------------- | +| `e` | 切换文件树(nvim-tree) | +| `ff` | 查找文件 | +| `fg` | 全局搜索文本 | +| `fb` | 切换缓冲区 | +| `fh` | 搜索帮助文档 | +| `fp` | 查找项目 | + +### LSP 相关 + +| 快捷键 | 功能描述 | +| ------------ | -------------------- | +| `gd` | 跳转到定义 | +| `gD` | 跳转到声明 | +| `gi` | 跳转到实现 | +| `gr` | 查找引用 | +| `K` | 显示悬浮信息 | +| `rn` | 重命名变量/函数 | +| `ca` | 代码操作(重构等) | +| `so` | 显示签名帮助 | +| `[d` | 跳转到上一个诊断错误 | +| `]d` | 跳转到下一个诊断错误 | +| `q` | 打开快速修复列表 | +| `f` | 格式化当前缓冲区 | +| `ft` | 检查格式化工具状态 | + +### 调试(DAP)相关 + +| 快捷键 | 功能描述 | +| ------------ | -------------------- | +| `` | 开始/继续调试 | +| `` | 单步跳过 | +| `` | 单步进入 | +| `` | 单步退出 | +| `b` | 切换断点 | +| `B` | 设置条件断点 | +| `lp` | 设置日志断点 | +| `dr` | 打开调试终端 | +| `dl` | 重新运行最后一次调试 | +| `dh` | 悬停查看变量信息 | +| `dp` | 预览变量信息 | +| `di` | 切换调试UI显示 | +| `de` | 显示作用域内变量 | + +### 自动补全与括号 + +| 快捷键 | 功能描述 | +| ----------------------- | -------------------------- | +| ``(插入模式) | 智能回车(处理补全和括号) | +| `)`/`}`/`]`(插入模式) | 跳过右侧括号 | +| ``(插入模式) | 智能退格(删除配对括号) | +| ``(可视模式) | 快速包裹选中内容 | +| `` | 补全项导航/展开代码片段 | +| `` | 补全项反向导航 | +| `` | 触发补全 | +| `` | 退出补全 | + +### 代码注释 + +| 快捷键 | 功能描述 | +| ------ | ---------- | +| `gcc` | 注释当前行 | + +## 自定义配置 + +你可以通过以下方式自定义配置: + +1. 修改 `lua/config/basic.lua` 调整基础设置 +2. 在 `lua/plugins/` 目录下添加或修改插件配置 +3. 通过 `:EditConfig` 快速编辑配置文件 +4. 修改配置后使用 `:ReloadConfig` 重新加载 + +## 常见问题 + +1. **格式化工具无法工作** + - 运行 `:FormatToolsCheck` 检查工具是否安装 + - 确保 Mason 已正确安装所需工具 + +2. **LSP 服务器未启动** + - 检查 `:Mason` 确认服务器已安装 + - 检查语言服务器配置是否正确 + +3. **剪贴板无法工作** + - 确保系统中安装了合适的剪贴板工具(如 xclip、wl-copy 等) diff --git a/init.lua b/init.lua new file mode 100644 index 0000000..1ab6c96 --- /dev/null +++ b/init.lua @@ -0,0 +1,10 @@ +-- 基础配置 +require("config.basic") +-- scripts +require("scripts.init") +-- Lazy 自动安装 +require("config.lazy") +-- 插件 +require("plugins.init") +-- Lsp +require("plugins.lsp.init") diff --git a/lazy-lock.json b/lazy-lock.json new file mode 100644 index 0000000..10608d5 --- /dev/null +++ b/lazy-lock.json @@ -0,0 +1,38 @@ +{ + "Comment.nvim": { "branch": "master", "commit": "e30b7f2008e52442154b66f7c519bfd2f1e32acb" }, + "DAPInstall.nvim": { "branch": "main", "commit": "8798b4c36d33723e7bba6ed6e2c202f84bb300de" }, + "LuaSnip": { "branch": "master", "commit": "3732756842a2f7e0e76a7b0487e9692072857277" }, + "cmp-buffer": { "branch": "main", "commit": "b74fab3656eea9de20a9b8116afa3cfc4ec09657" }, + "cmp-cmdline": { "branch": "main", "commit": "d126061b624e0af6c3a556428712dd4d4194ec6d" }, + "cmp-nvim-lsp": { "branch": "main", "commit": "cbc7b02bb99fae35cb42f514762b89b5126651ef" }, + "cmp-path": { "branch": "main", "commit": "c642487086dbd9a93160e1679a1327be111cbc25" }, + "cmp_luasnip": { "branch": "master", "commit": "98d9cb5c2c38532bd9bdb481067b20fea8f32e90" }, + "code_runner.nvim": { "branch": "main", "commit": "45dfea066a6110abcbc3cd361457ac3cbaefd68b" }, + "conform.nvim": { "branch": "master", "commit": "9b8fa5e0b78168f68bee9bf886dc20f287c61e02" }, + "friendly-snippets": { "branch": "main", "commit": "572f5660cf05f8cd8834e096d7b4c921ba18e175" }, + "gitsigns.nvim": { "branch": "main", "commit": "5813e4878748805f1518cee7abb50fd7205a3a48" }, + "lazy.nvim": { "branch": "main", "commit": "85c7ff3711b730b4030d03144f6db6375044ae82" }, + "lsp_signature.nvim": { "branch": "master", "commit": "7d3bb0a641f516f1c7fd2e47852580dadbd7a430" }, + "lualine.nvim": { "branch": "master", "commit": "47f91c416daef12db467145e16bed5bbfe00add8" }, + "mason-lspconfig.nvim": { "branch": "main", "commit": "c55bd8a8fb191e24176c206a7af1dd51ce7276a5" }, + "mason-nvim-dap.nvim": { "branch": "main", "commit": "9a10e096703966335bd5c46c8c875d5b0690dade" }, + "mason-tool-installer.nvim": { "branch": "main", "commit": "517ef5994ef9d6b738322664d5fdd948f0fdeb46" }, + "mason.nvim": { "branch": "main", "commit": "57e5a8addb8c71fb063ee4acda466c7cf6ad2800" }, + "nvim-autopairs": { "branch": "master", "commit": "d9e44e54384e5b0f3536339c65484f2e41b528e3" }, + "nvim-cmp": { "branch": "main", "commit": "d97d85e01339f01b842e6ec1502f639b080cb0fc" }, + "nvim-dap": { "branch": "master", "commit": "5860c7c501eb428d3137ee22c522828d20cca0b3" }, + "nvim-dap-python": { "branch": "master", "commit": "64652d1ae1db80870d9aac7132d76e37acd86a26" }, + "nvim-dap-ui": { "branch": "master", "commit": "cf91d5e2d07c72903d052f5207511bf7ecdb7122" }, + "nvim-dap-virtual-text": { "branch": "master", "commit": "fbdb48c2ed45f4a8293d0d483f7730d24467ccb6" }, + "nvim-lspconfig": { "branch": "master", "commit": "b3a31fd3b6702de9c71c27212ffacf0f54cc3b46" }, + "nvim-nio": { "branch": "master", "commit": "21f5324bfac14e22ba26553caf69ec76ae8a7662" }, + "nvim-notify": { "branch": "master", "commit": "8701bece920b38ea289b457f902e2ad184131a5d" }, + "nvim-tree.lua": { "branch": "master", "commit": "eb33612bff2fb31f54946fb5dcadc89e905e81ec" }, + "nvim-treesitter": { "branch": "master", "commit": "42fc28ba918343ebfd5565147a42a26580579482" }, + "nvim-ts-context-commentstring": { "branch": "main", "commit": "1b212c2eee76d787bbea6aa5e92a2b534e7b4f8f" }, + "nvim-web-devicons": { "branch": "master", "commit": "8dcb311b0c92d460fac00eac706abd43d94d68af" }, + "plenary.nvim": { "branch": "master", "commit": "b9fd5226c2f76c951fc8ed5923d85e4de065e509" }, + "project.nvim": { "branch": "main", "commit": "8c6bad7d22eef1b71144b401c9f74ed01526a4fb" }, + "telescope.nvim": { "branch": "master", "commit": "e69b434b968a33815e2f02a5c7bd7b8dd4c7d4b2" }, + "tokyonight.nvim": { "branch": "main", "commit": "5da1b76e64daf4c5d410f06bcb6b9cb640da7dfd" } +} diff --git a/lua/config/basic.lua b/lua/config/basic.lua new file mode 100644 index 0000000..0cd57cc --- /dev/null +++ b/lua/config/basic.lua @@ -0,0 +1,75 @@ +-- ===== 基本设置 ===== +-- 行号 +vim.opt.number = true +vim.opt.relativenumber = true + +-- 缩进 +vim.opt.tabstop = 4 +vim.opt.softtabstop = 4 +vim.opt.shiftwidth = 4 +vim.opt.expandtab = true +vim.opt.smartindent = true + +-- 搜索 +vim.opt.ignorecase = true +vim.opt.smartcase = true +vim.opt.hlsearch = true +vim.opt.incsearch = true + +-- 外观 +vim.opt.termguicolors = true +vim.opt.background = "dark" +vim.opt.signcolumn = "yes" +vim.opt.cursorline = true +vim.opt.colorcolumn = "80" -- 在第80列显示参考线 + +-- 文件和备份 +vim.opt.swapfile = false +vim.opt.backup = false +vim.opt.undofile = false + +-- 分割窗口 +vim.opt.splitright = true +vim.opt.splitbelow = true + +-- 其他 +vim.opt.mouse = "a" -- 启用鼠标 +vim.opt.updatetime = 50 -- 更快的响应时间 +vim.opt.timeoutlen = 300 -- 快捷键响应时间 +vim.opt.scrolloff = 8 -- 光标距离边界8行时滚动 +vim.opt.wrap = false -- 不自动换行 +vim.opt.errorbells = false -- 不发出错误声音 +vim.opt.visualbell = false -- 不使用视觉提示 + +-- 领导键 +vim.g.mapleader = " " +vim.g.localleader = "\\" + +-- 剪切板 +vim.g.clipboard = { + name = "wl-clipboard", + copy = { + ["+"] = { "wl-copy" }, + ["*"] = { "wl-copy" }, + }, + paste = { + ["+"] = { "wl-paste" }, + ["*"] = { "wl-paste" }, + }, + cache_enabled = 0, +} +vim.opt.clipboard = "unnamedplus" + +vim.opt.shell = "zsh" +vim.api.nvim_create_user_command("Term", "terminal", { desc = "打开终端" }) +vim.keymap.set("n", "t", "Term", { desc = "打开终端" }) + +-- 终端模式快捷键 +vim.api.nvim_create_autocmd("TermOpen", { + pattern = "*", + callback = function() + vim.keymap.set("t", "", "", { buffer = true, desc = "退出终端模式" }) + vim.opt_local.number = false + vim.opt_local.relativenumber = false + end, +}) diff --git a/lua/config/lazy.lua b/lua/config/lazy.lua new file mode 100644 index 0000000..60c2496 --- /dev/null +++ b/lua/config/lazy.lua @@ -0,0 +1,15 @@ +local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim" +if not (vim.uv or vim.loop).fs_stat(lazypath) then + local lazyrepo = "git@github.com:folke/lazy.nvim.git" + local out = vim.fn.system({ "git", "clone", "--filter=blob:none", "--branch=stable", lazyrepo, lazypath }) + if vim.v.shell_error ~= 0 then + vim.api.nvim_echo({ + { "Failed to clone lazy.nvim:\n", "ErrorMsg" }, + { out, "WarningMsg" }, + { "\nPress any key to exit..." }, + }, true, {}) + vim.fn.getchar() + os.exit(1) + end +end +vim.opt.rtp:prepend(lazypath) diff --git a/lua/plugins/autopairs.lua b/lua/plugins/autopairs.lua new file mode 100644 index 0000000..cd826cc --- /dev/null +++ b/lua/plugins/autopairs.lua @@ -0,0 +1,138 @@ +return { + "windwp/nvim-autopairs", + event = "InsertEnter", + dependencies = { + "hrsh7th/nvim-cmp", + "nvim-treesitter/nvim-treesitter", + }, + config = function() + local npairs = require("nvim-autopairs") + local Rule = require("nvim-autopairs.rule") + local ts_conds = require("nvim-autopairs.ts-conds") -- 正确导入条件模块 + + -- 基础配置(来自官方示例) + npairs.setup({ + check_ts = true, -- 启用 Treesitter 检查 + ts_config = { + lua = { "string" }, -- 在 Lua 的 string 节点中不添加配对 + javascript = { "template_string" }, -- 在 JS 模板字符串中不添加配对 + java = false, -- 在 Java 中不检查 Treesitter + python = { "string", "comment" }, -- 在 Python 字符串和注释中不添加配对 + cpp = { "string", "comment" }, + rust = { "string", "comment" }, + }, + disable_filetype = { "TelescopePrompt", "spectre_panel", "lspinfo", "dashboard" }, + fast_wrap = { + map = "", + chars = { "{", "[", "(", '"', "'", "`" }, + pattern = string.gsub([[ [%'%"%)%>%]%)%}%,] ]], "%s+", ""), + offset = 0, + end_key = "$", + keys = "qwertyuiopzxcvbnmasdfghjkl", + check_comma = true, + highlight = "PmenuSel", + highlight_grey = "LineNr", + }, + }) + + -- 在 string 或 comment 节点中添加 % 配对 + npairs.add_rules({ + Rule("%", "%", "lua"):with_pair(ts_conds.is_ts_node({ "string", "comment" })), + + -- 在非 function 节点中添加 $ 配对 + Rule("$", "$", "lua"):with_pair(ts_conds.is_not_ts_node({ "function" })), + + -- 在非 string 和非 comment 节点中添加引号配对 + Rule("'", "'", "lua"):with_pair(ts_conds.is_not_ts_node({ "string", "comment" })), + Rule('"', '"', "lua"):with_pair(ts_conds.is_not_ts_node({ "string", "comment" })), + Rule("`", "`", "lua"):with_pair(ts_conds.is_not_ts_node({ "string", "comment" })), + + -- Python 规则:在非字符串、非注释、非函数参数节点中添加括号 + Rule("(", ")", "python"):with_pair(ts_conds.is_not_ts_node({ "string", "comment", "parameters" })), + + -- JavaScript/TS 规则 + Rule("(", ")", "javascript"):with_pair(ts_conds.is_not_ts_node({ "string", "comment", "template_string" })), + Rule("(", ")", "typescript"):with_pair(ts_conds.is_not_ts_node({ "string", "comment", "template_string" })), + }) + + -- 与 nvim-cmp 集成 + local cmp_autopairs = require("nvim-autopairs.completion.cmp") + require("cmp").event:on("confirm_done", cmp_autopairs.on_confirm_done()) + + -- 智能回车:在括号内按回车时自动缩进 + npairs.add_rule(Rule("\n", "\n") + :with_pair(function(opts) + local line = opts.line + local before = line:sub(1, opts.col - 1) + local after = line:sub(opts.col) + local pair = before:sub(-1) .. after:sub(1) + return pair == "{}" or pair == "[]" or pair == "()" + end) + :with_move(function(opts) + return opts.char == "\n" + end) + :use_key("\n")) + + -- 在 % 后面不添加额外的 ) 例如: %(xxx|) -> %(xxx|) + npairs.add_rule(Rule(")", ")"):with_pair(function(opts) + local pair = opts.line:sub(opts.col - 1, opts.col) + return pair == "(%" + end)) + + -- Markdown 链接规则 + npairs.add_rules({ + Rule("[", "]", "markdown"):with_pair(function(opts) + local before = opts.line:sub(1, opts.col - 1) + return not before:match("%[.-%]%(") -- 不在已有的链接后添加 + end), + Rule("(", ")", "markdown"):with_pair(function(opts) + local before = opts.line:sub(1, opts.col - 2) + return before:match("%[.-%]$") -- 只在 [text] 后添加 + end), + }) + + -- 快捷键映射 + local opts = { noremap = true, silent = true, expr = true } + + -- 智能回车(处理括号和 completion) + vim.keymap.set("i", "", function() + if vim.fn.pumvisible() == 1 then + return "" + end + return npairs.autopairs_cr() + end, opts) + + -- 跳过右侧括号 + vim.keymap.set("i", ")", function() + if npairs.jumpable() then + return "nvim-autopairs-jump-right" + else + return ")" + end + end, opts) + + vim.keymap.set("i", "}", function() + if npairs.jumpable() then + return "nvim-autopairs-jump-right" + else + return "}" + end + end, opts) + + vim.keymap.set("i", "]", function() + if npairs.jumpable() then + return "nvim-autopairs-jump-right" + else + return "]" + end + end, opts) + + -- 智能退格 + vim.keymap.set("i", "", function() + return npairs.autopairs_bs() + end, opts) + + -- 快速包裹 + vim.keymap.set("v", "", "nvim-autopairs-fast-wrap", { noremap = true }) + end, +} diff --git a/lua/plugins/comment.lua b/lua/plugins/comment.lua new file mode 100644 index 0000000..6fec21c --- /dev/null +++ b/lua/plugins/comment.lua @@ -0,0 +1,10 @@ +return { + "numToStr/Comment.nvim", + dependencies = { "JoosepAlviste/nvim-ts-context-commentstring" }, + config = function() + require("Comment").setup({ + pre_hook = require("ts_context_commentstring.integrations.comment_nvim").create_pre_hook(), + }) + end, + event = "BufReadPost", +} diff --git a/lua/plugins/gitsigns.lua b/lua/plugins/gitsigns.lua new file mode 100644 index 0000000..c396b24 --- /dev/null +++ b/lua/plugins/gitsigns.lua @@ -0,0 +1,15 @@ +return { + "lewis6991/gitsigns.nvim", + config = function() + require("gitsigns").setup({ + signs = { + add = { text = "+" }, + change = { text = "~" }, + delete = { text = "_" }, + topdelete = { text = "‾" }, + changedelete = { text = "~" }, + }, + current_line_blame = true, + }) + end, +} diff --git a/lua/plugins/init.lua b/lua/plugins/init.lua new file mode 100644 index 0000000..66db44c --- /dev/null +++ b/lua/plugins/init.lua @@ -0,0 +1,34 @@ +local opts = { + git = { + log = { "-1" }, + timeout = 120, + url_format = "git@github.com:%s.git", + filter = true, + throttle = { + enabled = false, + rate = 2, + duration = 5 * 1000, + }, + cooldown = 0, + }, +} + +require("lazy").setup({ + require("plugins.nvim-tree"), + require("plugins.lualine"), + require("plugins.theme"), + require("plugins.telescope"), + require("plugins.treesitter"), + require("plugins.notify"), + require("plugins.gitsigns"), + require("plugins.autopairs"), + require("plugins.comment"), + require("plugins.project"), + require("plugins.runner"), + -- LSP + require("plugins.lsp.mason"), + require("plugins.lsp.lspconfig"), + require("plugins.lsp.cmp"), + require("plugins.lsp.dap"), + require("plugins.lsp.conform"), +}, opts) diff --git a/lua/plugins/lsp/cmp.lua b/lua/plugins/lsp/cmp.lua new file mode 100644 index 0000000..b9fb6a8 --- /dev/null +++ b/lua/plugins/lsp/cmp.lua @@ -0,0 +1,123 @@ +return { + "hrsh7th/nvim-cmp", + dependencies = { + "hrsh7th/cmp-nvim-lsp", -- LSP 补全源 + "hrsh7th/cmp-buffer", -- 缓冲区补全源 + "hrsh7th/cmp-path", -- 路径补全源 + "hrsh7th/cmp-cmdline", -- 命令行补全 + "saadparwaiz1/cmp_luasnip", -- snippet 补全 + "L3MON4D3/LuaSnip", -- snippet 引擎 + "rafamadriz/friendly-snippets", -- 预设 snippets + }, + config = function() + -- 加载友好 snippets + require("luasnip.loaders.from_vscode").lazy_load() + + local cmp = require("cmp") + local luasnip = require("luasnip") + + cmp.setup({ + snippet = { + expand = function(args) + luasnip.lsp_expand(args.body) + end, + }, + mapping = cmp.mapping.preset.insert({ + [""] = cmp.mapping.select_prev_item(), -- 上一个补全项 + [""] = cmp.mapping.select_next_item(), -- 下一个补全项 + [""] = cmp.mapping.scroll_docs(-4), + [""] = cmp.mapping.scroll_docs(4), + [""] = cmp.mapping.complete(), -- 触发补全 + [""] = cmp.mapping.abort(), -- 退出补全 + [""] = cmp.mapping.confirm({ select = true }), -- 确认选择 + + -- 使用 Tab 和 Shift+Tab 在补全项间导航 + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_next_item() + elseif luasnip.expand_or_jumpable() then + luasnip.expand_or_jump() + else + fallback() + end + end, { "i", "s" }), + + [""] = cmp.mapping(function(fallback) + if cmp.visible() then + cmp.select_prev_item() + elseif luasnip.jumpable(-1) then + luasnip.jump(-1) + else + fallback() + end + end, { "i", "s" }), + }), + sources = cmp.config.sources({ + { name = "nvim_lsp" }, -- LSP + { name = "luasnip" }, -- Snippets + { name = "buffer" }, -- 缓冲区 + { name = "path" }, -- 路径 + }), + formatting = { + format = function(entry, item) + -- 为不同类型的补全项添加图标 + local kind_icons = { + Text = "", + Method = "", + Function = "", + Constructor = "", + Field = "", + Variable = "", + Class = "", + Interface = "", + Module = "", + Property = "", + Unit = "", + Value = "", + Enum = "", + Keyword = "", + Snippet = "", + Color = "", + File = "", + Reference = "", + Folder = "", + EnumMember = "", + Constant = "", + Struct = "", + Event = "", + Operator = "", + TypeParameter = "", + } + item.kind = kind_icons[item.kind] .. " " .. item.kind + item.menu = ({ + nvim_lsp = "[LSP]", + luasnip = "[Snippet]", + buffer = "[Buffer]", + path = "[Path]", + })[entry.source.name] + return item + end, + }, + experimental = { + ghost_text = true, -- 显示灰色的建议文本 + }, + }) + + -- 命令行补全 + cmp.setup.cmdline("/", { + mapping = cmp.mapping.preset.cmdline(), + sources = { + { name = "buffer" }, + }, + }) + + cmp.setup.cmdline(":", { + mapping = cmp.mapping.preset.cmdline(), + sources = cmp.config.sources({ + { name = "path" }, + }, { + { name = "cmdline" }, + }), + }) + end, +} diff --git a/lua/plugins/lsp/conform.lua b/lua/plugins/lsp/conform.lua new file mode 100644 index 0000000..7960853 --- /dev/null +++ b/lua/plugins/lsp/conform.lua @@ -0,0 +1,130 @@ +return { + "stevearc/conform.nvim", + event = { "BufWritePre" }, + cmd = { "ConformInfo" }, + keys = { + { + "f", + function() + require("conform").format({ async = true, lsp_fallback = true }) + end, + mode = "", + desc = "Format buffer", + }, + }, + config = function() + local conform = require("conform") + + conform.setup({ + -- 定义格式化工具 - 每个工具都有正确的参数配置 + formatters_by_ft = { + lua = { "stylua" }, + python = { "isort", "black" }, + javascript = { "prettierd", "prettier" }, -- 优先使用prettierd(守护进程版本) + typescript = { "prettierd", "prettier" }, + javascriptreact = { "prettierd", "prettier" }, + typescriptreact = { "prettierd", "prettier" }, + css = { "prettierd", "prettier" }, + html = { "prettierd", "prettier" }, + json = { "prettierd", "prettier" }, + jsonc = { "prettierd", "prettier" }, + yaml = { "prettierd", "prettier" }, + markdown = { "prettierd", "prettier" }, + graphql = { "prettierd", "prettier" }, + scss = { "prettierd", "prettier" }, + less = { "prettierd", "prettier" }, + sh = { "shfmt" }, + ["*"] = { "lsp" }, -- 任何文件类型的后备 + }, + + -- 精确配置每个格式化工具 + formatters = { + -- 修正Stylua配置 + stylua = { + command = "stylua", + args = { "--stdin-filepath", "$FILENAME", "-" }, -- 关键修正:添加"-"表示从stdin读取 + stdin = true, + }, + + -- Prettier配置 + prettier = { + command = "prettier", + args = { "--stdin-filepath", "$FILENAME" }, + stdin = true, + }, + + -- Prettier守护进程版本(更快) + prettierd = { + command = "prettierd", + args = { "$FILENAME" }, + stdin = true, + }, + + -- Black (Python) + black = { + command = "black", + args = { "--quiet", "-" }, + stdin = true, + }, + + -- isort (Python导入排序) + isort = { + command = "isort", + args = { "--quiet", "-" }, + stdin = true, + }, + + -- shfmt (Shell脚本) + shfmt = { + command = "shfmt", + args = { "-i", "2", "-ci", "-bn" }, -- 2空格缩进,case缩进,二元运算符换行 + stdin = true, + }, + }, + + -- 保存时自动格式化 + format_on_save = { + -- 可以排除特定文件类型 + ignore_filetypes = { "gitcommit", "markdown", "help", "txt" }, + -- 延迟毫秒数,避免在输入时频繁触发 + -- delay_ms = 200, + -- 回退到LSP格式化 + lsp_fallback = true, + }, + + -- 异步格式化 + async = true, + timeout_ms = 1000, -- 1秒超时 + log_level = vim.log.levels.WARN, + + -- 格式化后显示通知 + notify_on_error = true, + }) + + -- 添加一个命令来检查格式化工具状态 + vim.api.nvim_create_user_command("FormatToolsCheck", function() + local tools = { + "stylua", + "prettier", + "prettierd", + "black", + "isort", + "shfmt", + } + + local status = {} + for _, tool in ipairs(tools) do + local found = vim.fn.executable(tool) == 1 + table.insert(status, string.format("%s: %s", tool, found and "✓ installed" or "✗ not found")) + end + + vim.notify(table.concat(status, "\n"), found and "info" or "warn", { + title = "Formatting Tools Status", + timeout = 5000, + }) + end, {}) + + -- 添加一个键映射来检查工具 + vim.keymap.set("n", "ft", "FormatToolsCheck", { desc = "Check formatting tools" }) + end, +} diff --git a/lua/plugins/lsp/dap.lua b/lua/plugins/lsp/dap.lua new file mode 100644 index 0000000..0767d84 --- /dev/null +++ b/lua/plugins/lsp/dap.lua @@ -0,0 +1,197 @@ +-- 完整的 DAP 配置 (保存为 lua/plugins/dap.lua 或类似路径) +return { + -- 1. 必须首先加载 nvim-nio + { + "nvim-neotest/nvim-nio", + lazy = true, + }, + + -- 2. 核心 DAP 插件 + { + "mfussenegger/nvim-dap", + dependencies = { + -- 3. DAP UI (依赖 nvim-nio) + "rcarriga/nvim-dap-ui", + -- 4. 虚拟文本显示 + "theHamsta/nvim-dap-virtual-text", + -- 5. Mason 集成 + "williamboman/mason.nvim", + "jay-babu/mason-nvim-dap.nvim", + -- 6. 调试适配器安装 (Python) + "mfussenegger/nvim-dap-python", + }, + config = function() + local dap = require("dap") + local dapui = require("dapui") + + -- 配置 Python 调试器 + require("dap-python").setup("python", { + console = "integratedTerminal", + }) + + -- 虚拟文本配置 + require("nvim-dap-virtual-text").setup({ + highlight_changed_variables = true, + highlight_new_as_changed = true, + show_stop_reason = true, + commented = false, + only_first_definition = true, + all_references = false, + }) + + -- DAP UI 配置 + dapui.setup({ + icons = { expanded = "▾", collapsed = "▸" }, + mappings = { + expand = { "", "<2-LeftMouse>" }, + open = "o", + remove = "d", + edit = "e", + repl = "r", + toggle = "t", + }, + layouts = { + { + elements = { + { id = "scopes", size = 0.33 }, + { id = "breakpoints", size = 0.17 }, + { id = "stacks", size = 0.25 }, + { id = "watches", size = 0.25 }, + }, + size = 40, + position = "left", + }, + { + elements = { + { id = "repl", size = 0.45 }, + { id = "console", size = 0.55 }, + }, + size = 0.25, + position = "bottom", + }, + }, + }) + + -- 自动打开/关闭 UI + dap.listeners.after.event_initialized["dapui_config"] = function() + dapui.open() + end + dap.listeners.before.event_terminated["dapui_config"] = function() + dapui.close() + end + dap.listeners.before.event_exited["dapui_config"] = function() + dapui.close() + end + + -- Mason DAP 配置 + require("mason-nvim-dap").setup({ + ensure_installed = { "python" }, + handlers = { + function(config) + require("mason-nvim-dap").default_setup(config) + end, + }, + }) + + -- Python 调试配置 + dap.configurations.python = { + { + type = "python", + request = "launch", + name = "Launch file", + program = "${file}", + pythonPath = function() + -- 智能检测 Python 路径 + local cwd = vim.fn.getcwd() + local paths = { + cwd .. "/venv/bin/python", + cwd .. "/.venv/bin/python", + vim.fn.expand("~/.virtualenvs") .. "/*/bin/python", + vim.fn.expand("~/.pyenv/shims/python"), + "/usr/bin/python3", + "/usr/local/bin/python3", + } + + for _, path in ipairs(paths) do + if vim.fn.executable(path:gsub("%*", "")) == 1 then + return path:gsub("%*", "") + end + end + + return "python" + end, + console = "integratedTerminal", + justMyCode = true, + }, + { + type = "python", + request = "attach", + name = "Attach to remote", + host = "127.0.0.1", + port = 5678, + pathMappings = { + { + localRoot = "${workspaceFolder}", + remoteRoot = ".", + }, + }, + }, + } + + -- 调试快捷键 + local opts = { noremap = true, silent = true } + vim.keymap.set("n", "", function() + dap.continue() + end, opts) + vim.keymap.set("n", "", function() + dap.step_over() + end, opts) + vim.keymap.set("n", "", function() + dap.step_into() + end, opts) + vim.keymap.set("n", "", function() + dap.step_out() + end, opts) + vim.keymap.set("n", "b", function() + dap.toggle_breakpoint() + end, opts) + vim.keymap.set("n", "B", function() + dap.set_breakpoint(vim.fn.input("Breakpoint condition: ")) + end, opts) + vim.keymap.set("n", "lp", function() + dap.set_breakpoint(nil, nil, vim.fn.input("Log point message: ")) + end, opts) + vim.keymap.set("n", "dr", function() + dap.repl.open() + end, opts) + vim.keymap.set("n", "dl", function() + dap.run_last() + end, opts) + vim.keymap.set({ "n", "v" }, "dh", function() + require("dap.ui.widgets").hover() + end, opts) + vim.keymap.set({ "n", "v" }, "dp", function() + require("dap.ui.widgets").preview() + end, opts) + vim.keymap.set("n", "di", function() + dapui.toggle() + end, opts) + + -- 增强的变量检查 + vim.keymap.set("n", "de", function() + local widget = require("dap.ui.widgets") + widget.centered_float(widget.scopes) + end, opts) + end, + }, + + -- 3. 可选:DAP 安装助手 + { + "ravenxrz/DAPInstall.nvim", + dependencies = { "mfussenegger/nvim-dap" }, + config = function() + require("dap-install").setup({}) + end, + cmd = { "DAPInstall", "DAPInstallConfig" }, + }, +} diff --git a/lua/plugins/lsp/init.lua b/lua/plugins/lsp/init.lua new file mode 100644 index 0000000..c778b50 --- /dev/null +++ b/lua/plugins/lsp/init.lua @@ -0,0 +1,12 @@ +-- LSP 快捷键 +vim.keymap.set("n", "gd", "lua vim.lsp.buf.definition()", { desc = "跳转到定义" }) +vim.keymap.set("n", "gD", "lua vim.lsp.buf.declaration()", { desc = "跳转到声明" }) +vim.keymap.set("n", "gi", "lua vim.lsp.buf.implementation()", { desc = "跳转到实现" }) +vim.keymap.set("n", "gr", "lua vim.lsp.buf.references()", { desc = "查找引用" }) +vim.keymap.set("n", "K", "lua vim.lsp.buf.hover()", { desc = "显示信息" }) +vim.keymap.set("n", "rn", "lua vim.lsp.buf.rename()", { desc = "重命名" }) +vim.keymap.set("n", "ca", "lua vim.lsp.buf.code_action()", { desc = "代码操作" }) +vim.keymap.set("n", "so", "lua vim.lsp.buf.signature_help()", { desc = "签名帮助" }) +vim.keymap.set("n", "[d", "lua vim.diagnostic.goto_prev()", { desc = "上一个诊断" }) +vim.keymap.set("n", "]d", "lua vim.diagnostic.goto_next()", { desc = "下一个诊断" }) +vim.keymap.set("n", "q", "lua vim.diagnostic.setloclist()", { desc = "快速修复" }) diff --git a/lua/plugins/lsp/lspconfig.lua b/lua/plugins/lsp/lspconfig.lua new file mode 100644 index 0000000..ea791a8 --- /dev/null +++ b/lua/plugins/lsp/lspconfig.lua @@ -0,0 +1,65 @@ +return { + "neovim/nvim-lspconfig", + dependencies = { + "williamboman/mason-lspconfig.nvim", + "hrsh7th/cmp-nvim-lsp", + "ray-x/lsp_signature.nvim", + }, + config = function() + local capabilities = require("cmp_nvim_lsp").default_capabilities() + + local function on_attach(client, bufnr) + -- 格式化配置保持不变 + if client.supports_method("textDocument/formatting") then + local augroup = vim.api.nvim_create_augroup("LspFormatting", {}) + vim.api.nvim_clear_autocmds({ group = augroup, buffer = bufnr }) + vim.api.nvim_create_autocmd("BufWritePre", { + group = augroup, + buffer = bufnr, + callback = function() + vim.lsp.buf.format({ async = false }) + end, + }) + end + + -- 其他 on_attach 配置... + end + + -- ✅ 修正: 这里使用 LSP 服务器名称 (不是 Mason 包名) + require("mason-lspconfig").setup({ + ensure_installed = { + "lua_ls", -- LSP 服务器名称 + "pyright", -- LSP 服务器名称 (与包名相同) + "jsonls", -- JSON LSP 服务器 + "vimls", -- Vim Script LSP 服务器 + }, + handlers = { + function(server_name) + local opts = { + capabilities = capabilities, + on_attach = on_attach, + } + + -- 特定服务器配置 + if server_name == "lua_ls" then + opts.settings = { + Lua = { + runtime = { version = "LuaJIT" }, + diagnostics = { globals = { "vim" } }, + workspace = { + library = { + [vim.fn.expand("$VIMRUNTIME/lua")] = true, + [vim.fn.expand("$VIMRUNTIME/lua/vim/lsp")] = true, + }, + }, + telemetry = { enable = false }, + }, + } + end + + require("lspconfig")[server_name].setup(opts) + end, + }, + }) + end, +} diff --git a/lua/plugins/lsp/mason.lua b/lua/plugins/lsp/mason.lua new file mode 100644 index 0000000..df425be --- /dev/null +++ b/lua/plugins/lsp/mason.lua @@ -0,0 +1,39 @@ +return { + "williamboman/mason.nvim", + dependencies = { + "williamboman/mason-lspconfig.nvim", + "WhoIsSethDaniel/mason-tool-installer.nvim", + }, + config = function() + require("mason").setup({ + ui = { + border = "rounded", + icons = { + package_installed = "✓", + package_pending = "➜", + package_uninstalled = "✗", + }, + }, + max_concurrent_installers = 10, + }) + + require("mason-tool-installer").setup({ + ensure_installed = { + -- LSP 服务器 + "lua-language-server", + "pyright", + "vim-language-server", + + -- 格式化工具 + "stylua", + "prettier", + "eslint_d", + "black", + "isort", + "shfmt", + }, + auto_update = true, -- 自动更新已安装的工具 + run_on_start = true, -- 启动时运行安装 + }) + end, +} diff --git a/lua/plugins/lualine.lua b/lua/plugins/lualine.lua new file mode 100644 index 0000000..4ca1ba1 --- /dev/null +++ b/lua/plugins/lualine.lua @@ -0,0 +1,15 @@ +return { + "nvim-lualine/lualine.nvim", + dependencies = { "nvim-tree/nvim-web-devicons" }, + config = function() + require("lualine").setup({ + options = { + icons_enabled = true, + theme = "tokyonight", + component_separators = { left = "", right = "" }, + section_separators = { left = "", right = "" }, + disabled_filetypes = { statusline = { "dashboard", "NvimTree" } }, + }, + }) + end, +} diff --git a/lua/plugins/notify.lua b/lua/plugins/notify.lua new file mode 100644 index 0000000..dc05a3d --- /dev/null +++ b/lua/plugins/notify.lua @@ -0,0 +1,12 @@ +return { + "rcarriga/nvim-notify", + config = function() + vim.notify = require("notify") + require("notify").setup({ + background_colour = "#000000", + render = "minimal", + stages = "fade_in_slide_out", + }) + end, + event = "VeryLazy", +} diff --git a/lua/plugins/nvim-tree.lua b/lua/plugins/nvim-tree.lua new file mode 100644 index 0000000..93df647 --- /dev/null +++ b/lua/plugins/nvim-tree.lua @@ -0,0 +1,26 @@ +return { + "nvim-tree/nvim-tree.lua", + dependencies = { "nvim-tree/nvim-web-devicons" }, + config = function() + require("nvim-tree").setup({ + view = { + width = 30, + }, + renderer = { + group_empty = true, + }, + actions = { + open_file = { + window_picker = { + enable = false, + }, + }, + }, + filters = { + dotfiles = false, + exclude = { ".git", "node_modules" }, + }, + }) + vim.keymap.set("n", "e", "NvimTreeToggle", { desc = "切换文件树" }) + end, +} diff --git a/lua/plugins/project.lua b/lua/plugins/project.lua new file mode 100644 index 0000000..f77d01e --- /dev/null +++ b/lua/plugins/project.lua @@ -0,0 +1,12 @@ +return { + "ahmedkhalf/project.nvim", + config = function() + require("project_nvim").setup({ + detection_methods = { "pattern", "lsp" }, + patterns = { ".git", "_darcs", ".hg", ".bzr", ".svn", "Makefile", "package.json" }, + }) + require("telescope").load_extension("projects") + vim.keymap.set("n", "fp", "Telescope projects", { desc = "查找项目" }) + end, + dependencies = { "nvim-telescope/telescope.nvim" }, +} diff --git a/lua/plugins/runner.lua b/lua/plugins/runner.lua new file mode 100644 index 0000000..ea371c6 --- /dev/null +++ b/lua/plugins/runner.lua @@ -0,0 +1,17 @@ +return { + "CRAG666/code_runner.nvim", + dependencies = "nvim-lua/plenary.nvim", + config = function() + require("code_runner").setup({ + filetype = { + python = "python -u", + lua = "lua", + sh = "bash", + }, + }) + -- 添加快捷键 + vim.keymap.set("n", "rr", "RunCode", { desc = "一键运行当前文件" }) + vim.keymap.set("n", "rf", "RunFile", { desc = "运行当前文件" }) + vim.keymap.set("n", "", "RunFile tab", { desc = "在新标签页运行" }) + end, +} diff --git a/lua/plugins/telescope.lua b/lua/plugins/telescope.lua new file mode 100644 index 0000000..c4f0608 --- /dev/null +++ b/lua/plugins/telescope.lua @@ -0,0 +1,23 @@ +return { + "nvim-telescope/telescope.nvim", + dependencies = { "nvim-lua/plenary.nvim" }, + config = function() + local builtin = require("telescope.builtin") + vim.keymap.set("n", "ff", builtin.find_files, { desc = "查找文件" }) + vim.keymap.set("n", "fg", builtin.live_grep, { desc = "全局搜索" }) + vim.keymap.set("n", "fb", builtin.buffers, { desc = "切换缓冲区" }) + vim.keymap.set("n", "fh", builtin.help_tags, { desc = "搜索帮助" }) + + require("telescope").setup({ + defaults = { + file_ignore_patterns = { "node_modules", ".git" }, + mappings = { + i = { + [""] = false, + [""] = false, + }, + }, + }, + }) + end, +} diff --git a/lua/plugins/theme.lua b/lua/plugins/theme.lua new file mode 100644 index 0000000..bf5fb4b --- /dev/null +++ b/lua/plugins/theme.lua @@ -0,0 +1,8 @@ +return { + "folke/tokyonight.nvim", + lazy = false, + priority = 1000, + config = function() + vim.cmd("colorscheme tokyonight") + end, +} diff --git a/lua/plugins/treesitter.lua b/lua/plugins/treesitter.lua new file mode 100644 index 0000000..1d5c096 --- /dev/null +++ b/lua/plugins/treesitter.lua @@ -0,0 +1,17 @@ +return { + "nvim-treesitter/nvim-treesitter", + build = ":TSUpdate", + config = function() + require("nvim-treesitter.configs").setup({ + ensure_installed = { "vim", "lua", "javascript", "typescript", "python", "html", "css", "markdown" }, + sync_install = false, + auto_install = true, + highlight = { + enable = true, + additional_vim_regex_highlighting = false, + }, + indent = { enable = true }, + }) + end, + event = "BufReadPost", +} diff --git a/lua/scripts/edit-config.lua b/lua/scripts/edit-config.lua new file mode 100644 index 0000000..430c957 --- /dev/null +++ b/lua/scripts/edit-config.lua @@ -0,0 +1,4 @@ +-- 快速编辑配置 +vim.api.nvim_create_user_command("EditConfig", function() + vim.cmd("e $MYVIMRC") +end, {}) diff --git a/lua/scripts/init.lua b/lua/scripts/init.lua new file mode 100644 index 0000000..097759b --- /dev/null +++ b/lua/scripts/init.lua @@ -0,0 +1,5 @@ +-- 快速编辑配置 +require("scripts.edit-config") + +-- 重新加载配置 +require("scripts.reload-config") diff --git a/lua/scripts/reload-config.lua b/lua/scripts/reload-config.lua new file mode 100644 index 0000000..6b5ca64 --- /dev/null +++ b/lua/scripts/reload-config.lua @@ -0,0 +1,5 @@ +-- 重新加载配置 +vim.api.nvim_create_user_command("ReloadConfig", function() + vim.cmd("source $MYVIMRC") + vim.notify("配置已重新加载", "info") +end, {})