-- python.lua - Python 开发专用配置 return { -- Python 专用插件 { "linux-cultist/venv-selector.nvim", cmd = "VenvSelect", keys = { { "pv", "VenvSelect", desc = "选择 Python 虚拟环境" }, { "pc", "VenvSelectCached", desc = "选择缓存的虚拟环境" }, }, opts = { name = { "venv", ".venv", "env", ".env", "virtualenv", }, auto_refresh = true, search_from = "root", -- 从项目根目录搜索 }, config = function(_, opts) require("venv-selector").setup(opts) -- 自动激活虚拟环境 vim.api.nvim_create_autocmd("BufEnter", { pattern = { "*.py" }, callback = function() -- 使用缓存的 venv require("venv-selector").retrieve_from_cache() end, }) end, }, -- Python 单元测试插件 { "nvim-neotest/neotest", optional = true, dependencies = { "nvim-neotest/neotest-python", }, opts = function(_, opts) -- 确保 adapters 是列表 opts.adapters = opts.adapters or {} -- 添加 neotest-python 适配器 table.insert(opts.adapters, require("neotest-python")({ -- 使用项目根目录中的 pytest.ini runner = "pytest", -- 额外的 pytest 参数 args = { "--color=yes", "-v", }, -- Python 测试发现模式 python = function() -- 如果激活了虚拟环境,使用它 if vim.env.VIRTUAL_ENV then return vim.env.VIRTUAL_ENV .. "/bin/python" end -- 否则使用系统的 Python return "python" end, })) return opts end, keys = { { "pt", "lua require('neotest').run.run()", desc = "运行最近的测试" }, { "pT", "lua require('neotest').run.run(vim.fn.expand('%'))", desc = "运行文件中的测试" }, { "pd", "lua require('neotest').run.run({strategy = 'dap'})", desc = "调试最近的测试" }, { "ps", "lua require('neotest').run.stop()", desc = "停止测试" }, { "pa", "lua require('neotest').run.attach()", desc = "附加到测试" }, { "po", "lua require('neotest').output.open()", desc = "查看测试输出" }, { "pO", "lua require('neotest').output_panel.toggle()", desc = "切换输出面板" }, { "pS", "lua require('neotest').summary.toggle()", desc = "切换摘要窗口" }, }, }, -- Python 导入排序和格式化 { "stevearc/conform.nvim", optional = true, opts = { formatters_by_ft = { python = { "isort", "black" }, }, formatters = { black = { args = { "--line-length", "88", "--fast", "-" }, }, isort = { args = { "--stdout", "--profile", "black", "--line-length", "88", "-" }, }, }, }, }, -- Python 调试 { "mfussenegger/nvim-dap", optional = true, dependencies = { "mfussenegger/nvim-dap-python", }, config = function() local ok, mason_registry = pcall(require, "mason-registry") local debugpy_path = nil if ok then local ok2, debugpy_pkg = pcall(mason_registry.get_package, "debugpy") if ok2 and debugpy_pkg then local ok3, path = pcall(debugpy_pkg.get_install_path, debugpy_pkg) if ok3 then debugpy_path = path .. "/venv/bin/python" end end end -- 如果无法通过 mason 获取路径,使用默认路径 if not debugpy_path then debugpy_path = vim.fn.exepath("python3") or "python3" end require("dap-python").setup(debugpy_path) -- 设置 pytest 调试 require("dap-python").test_runner = "pytest" -- 添加自定义配置 table.insert(require("dap").configurations.python, { type = "python", request = "launch", name = "启动带参数的文件", program = "${file}", args = function() local args_string = vim.fn.input("命令行参数: ") return vim.split(args_string, " ") end, console = "integratedTerminal", }) -- 添加 FastAPI 配置 table.insert(require("dap").configurations.python, { type = "python", request = "launch", name = "FastAPI", module = "uvicorn", args = function() local args = vim.fn.input("uvicorn 参数 (例如 main:app --reload): ") return vim.split(args, " ") end, console = "integratedTerminal", }) end, keys = { -- Python 特定调试键 { "dpr", function() require("dap-python").test_method() end, desc = "调试当前方法" }, { "dpc", function() require("dap-python").test_class() end, desc = "调试当前类" }, { "dpf", function() require("dap-python").test_method() end, desc = "调试当前函数" }, }, }, -- Python 代码格式化和排序 { "williamboman/mason.nvim", optional = true, opts = function(_, opts) -- 将额外的 Python 工具添加到确保安装列表中 if type(opts.ensure_installed) == "table" then vim.list_extend(opts.ensure_installed, { "black", "isort", "mypy", "ruff", "ruff-lsp", "pyright", "debugpy", }) end end, }, -- 代码折叠 { "kevinhwang91/nvim-ufo", optional = true, opts = { close_fold_kinds_for_ft = { python = { "imports", "comment" }, }, }, }, -- REPL 集成 { "michaelb/sniprun", build = "sh ./install.sh", cmd = { "SnipRun", "SnipInfo", "SnipReset", "SnipClose" }, keys = { { "pe", "SnipRun", desc = "执行选中代码", mode = { "n", "v" } }, { "pE", "SnipInfo", desc = "Sniprun 信息" }, }, opts = { display = { "NvimNotify" }, interpreter_options = { Python3_fifo = { venv = function() return vim.env.VIRTUAL_ENV end, cwd = function() return vim.fn.getcwd() end, }, }, }, config = function(_, opts) require("sniprun").setup(opts) end, }, }