接 Neovim 入门:本篇把编辑器扩成可干活的 IDE 子集——插件管理、补全跳转、模糊查找、文件树、Git 签名,以及 Python / 数据分析的日常节奏。进阶 Lua 与自研插件见 Neovim 精通。
原则:少而稳。先 lazy → treesitter → lsp → telescope → 文件浏览 → git,再谈主题与状态栏。
目录约定
~/.config/nvim/
init.lua
lua/
options.lua
keymaps.lua
plugins/
init.lua -- lazy bootstrap + spec 列表也可拆文件
lsp.lua
telescope.lua
...
init.lua:
require('options')
require('keymaps')
require('plugins') -- 内含 lazy.setup
lazy.nvim:插件管理
lazy.nvim 是目前主流选择:延迟加载、锁文件、UI 清晰。
Bootstrap
在 lua/plugins/init.lua(或 lua/plugins.lua):
local lazypath = vim.fn.stdpath('data') .. '/lazy/lazy.nvim'
if not vim.loop.fs_stat(lazypath) then
vim.fn.system({
'git', 'clone', '--filter=blob:none',
'https://github.com/folke/lazy.nvim.git',
'--branch=stable', lazypath,
})
end
vim.opt.rtp:prepend(lazypath)
require('lazy').setup({
-- 规格表:可改成 { import = 'plugins' } 自动加载 lua/plugins/*.lua
{ 'folke/tokyonight.nvim', lazy = false, priority = 1000, config = function()
vim.cmd.colorscheme('tokyonight-moon')
end },
{ import = 'plugins.specs' }, -- 若你拆成 specs 目录
}, {
change_detection = { notify = false },
})
更常见写法是 lua/plugins/ 下每个文件 return { ... },然后:
require('lazy').setup({
{ import = 'plugins' },
}, { checker = { enabled = true } })
打开 UI::Lazy。同步::Lazy sync。
Treesitter:语法与文本对象
-- lua/plugins/treesitter.lua
return {
{
'nvim-treesitter/nvim-treesitter',
build = ':TSUpdate',
opts = {
ensure_installed = { 'lua', 'python', 'vim', 'vimdoc', 'markdown', 'json', 'yaml', 'bash' },
highlight = { enable = true },
indent = { enable = true },
},
config = function(_, opts)
require('nvim-treesitter.configs').setup(opts)
end,
},
}
装好后 Python / Lua 高亮明显更准。可选 nvim-treesitter-textobjects 做 af/if 函数对象。
LSP:pyright + nvim-lspconfig
系统侧:语言服务器
# Node 版 pyright
npm install -g pyright
# 或基于 Python
pipx install pyright
也可在项目用 pyproject.toml / venv,并靠 mason.nvim 安装二进制:
return {
{ 'williamboman/mason.nvim', opts = {} },
{
'williamboman/mason-lspconfig.nvim',
dependencies = { 'williamboman/mason.nvim', 'neovim/nvim-lspconfig' },
opts = { ensure_installed = { 'pyright', 'lua_ls' } },
},
}
最小 LSP 配置
-- lua/plugins/lsp.lua
return {
{
'neovim/nvim-lspconfig',
dependencies = {
'hrsh7th/nvim-cmp',
'hrsh7th/cmp-nvim-lsp',
'L3MON4D3/LuaSnip',
'saadparwaiz1/cmp_luasnip',
},
config = function()
local capabilities = require('cmp_nvim_lsp').default_capabilities()
local lspconfig = require('lspconfig')
lspconfig.pyright.setup({
capabilities = capabilities,
settings = {
python = {
analysis = {
typeCheckingMode = 'basic',
autoImportCompletions = true,
},
},
},
})
lspconfig.lua_ls.setup({
capabilities = capabilities,
settings = {
Lua = {
diagnostics = { globals = { 'vim' } },
workspace = { checkThirdParty = false },
},
},
})
vim.api.nvim_create_autocmd('LspAttach', {
callback = function(args)
local map = function(mode, lhs, rhs, desc)
vim.keymap.set(mode, lhs, rhs, { buffer = args.buf, desc = desc })
end
map('n', 'gd', vim.lsp.buf.definition, 'Goto definition')
map('n', 'gr', vim.lsp.buf.references, 'References')
map('n', 'K', vim.lsp.buf.hover, 'Hover')
map('n', '<leader>rn', vim.lsp.buf.rename, 'Rename')
map('n', '<leader>ca', vim.lsp.buf.code_action, 'Code action')
map('n', '[d', vim.diagnostic.goto_prev, 'Prev diagnostic')
map('n', ']d', vim.diagnostic.goto_next, 'Next diagnostic')
end,
})
local cmp = require('cmp')
cmp.setup({
snippet = { expand = function(args) require('luasnip').lsp_expand(args.body) end },
mapping = cmp.mapping.preset.insert({
['<C-Space>'] = cmp.mapping.complete(),
['<CR>'] = cmp.mapping.confirm({ select = true }),
['<Tab>'] = cmp.mapping.select_next_item(),
['<S-Tab>'] = cmp.mapping.select_prev_item(),
}),
sources = {
{ name = 'nvim_lsp' },
{ name = 'luasnip' },
{ name = 'buffer' },
},
})
end,
},
}
格式化可用 conform.nvim 接 ruff / black:
require('conform').setup({
format_on_save = { timeout_ms = 500, lsp_fallback = true },
formatters_by_ft = { python = { 'ruff_format', 'black' } },
})
Telescope:找文件 / 找内容
return {
{
'nvim-telescope/telescope.nvim',
dependencies = { 'nvim-lua/plenary.nvim' },
cmd = 'Telescope',
keys = {
{ '<leader>ff', '<cmd>Telescope find_files<cr>', desc = 'Find files' },
{ '<leader>fg', '<cmd>Telescope live_grep<cr>', desc = 'Live grep' },
{ '<leader>fb', '<cmd>Telescope buffers<cr>', desc = 'Buffers' },
{ '<leader>fh', '<cmd>Telescope help_tags<cr>', desc = 'Help' },
{ '<leader>fd', '<cmd>Telescope diagnostics<cr>', desc = 'Diagnostics' },
},
opts = {
defaults = {
mappings = {
i = { ['<C-j>'] = 'move_selection_next', ['<C-k>'] = 'move_selection_previous' },
},
},
},
},
}
系统需有 rg(ripgrep)才能让 live_grep 舒服:
brew install ripgrep # macOS
sudo apt install ripgrep
文件浏览:oil.nvim 或 neo-tree
oil.nvim(推荐轻量)
像编辑 buffer 一样改目录:
return {
{
'stevearc/oil.nvim',
opts = {},
dependencies = { 'nvim-tree/nvim-web-devicons' },
keys = {
{ '-', '<cmd>Oil<cr>', desc = 'Open parent directory' },
{ '<leader>e', '<cmd>Oil<cr>', desc = 'Oil' },
},
},
}
neo-tree(经典侧栏)
return {
{
'nvim-neo-tree/neo-tree.nvim',
branch = 'v3.x',
dependencies = {
'nvim-lua/plenary.nvim',
'nvim-tree/nvim-web-devicons',
'MunifTanjim/nui.nvim',
},
keys = {
{ '<leader>e', '<cmd>Neotree toggle<cr>', desc = 'Neo-tree' },
},
opts = { filesystem = { follow_current_file = { enabled = true } } },
},
}
两者选一即可,避免键位冲突。
Git:fugitive + gitsigns
return {
{ 'tpope/vim-fugitive', cmd = { 'Git', 'G' } },
{
'lewis6991/gitsigns.nvim',
opts = {
on_attach = function(bufnr)
local gs = require('gitsigns')
local map = function(mode, l, r, desc)
vim.keymap.set(mode, l, r, { buffer = bufnr, desc = desc })
end
map('n', ']c', gs.next_hunk, 'Next hunk')
map('n', '[c', gs.prev_hunk, 'Prev hunk')
map('n', '<leader>hs', gs.stage_hunk, 'Stage hunk')
map('n', '<leader>hr', gs.reset_hunk, 'Reset hunk')
map('n', '<leader>hp', gs.preview_hunk, 'Preview hunk')
map('n', '<leader>hb', function() gs.blame_line({ full = true }) end, 'Blame')
end,
},
},
}
日常::Git 打开状态,:Git blame,侧栏看 hunk,Telescope 搜改动文件。
Python / 数据分析日常工作流
典型一天:
- 终端
cd到项目根,source .venv/bin/activate,再nvim。 <leader>ff打开脚本;<leader>fg搜函数名。- 改代码 → LSP 红线用
]d/<leader>ca。 - 保存触发 ruff/black。
- 跑脚本:
vim.keymap.set('n', '<leader>r', function()
vim.cmd.write()
vim.cmd('split | terminal python %')
end, { desc = 'Save & run Python file' })
- 看 CSV / 日志:Telescope + 分屏;大数据用终端
duckdb/pythonREPL(:terminal)。 - Notebook:多数人仍用 Jupyter / VS Code;nvim 可配合
jupytext或终端跑.py脚本化分析。 - 远程数据机:SSH 后直接 nvim,或精通篇讲的远程编辑。
项目级提示:在根目录放 pyrightconfig.json:
{
"venvPath": ".",
"venv": ".venv",
"reportMissingImports": true
}
常用键位表(实战向)
| 键位 | 作用 |
|---|---|
<leader>ff | 找文件 |
<leader>fg | 活 grep |
<leader>fb | buffer 列表 |
<leader>e | 文件树 / Oil |
gd / gr / K | 定义 / 引用 / 悬停 |
<leader>rn / <leader>ca | 重命名 / code action |
]d [d | 诊断跳转 |
]c [c | git hunk |
<leader>hs / <leader>hp | stage / preview hunk |
<leader>r | 存盘并跑当前 Python |
<leader>w / <leader>q | 存 / 退 |
-(Oil) | 上级目录 |
:Lazy | 插件面板 |
:Git | Fugitive 状态 |
:LspInfo | 看 LSP 是否挂上 |
<leader> 建议空格;哪天忘了键位,用 Telescope help 或 which-key.nvim。
故障速查
| 现象 | 排查 |
|---|---|
| 无补全 | :LspInfo;pyright 是否在 PATH;venv 是否指向对 |
| grep 空 | 是否安装 ripgrep;是否在巨无 node_modules 未忽略 |
| 颜色怪 | termguicolors;终端真彩色;colorscheme 是否 lazy 错优先级 |
| 卡顿 | :Lazy profile;减少 ensure_installed;大文件关 treesitter |
| 剪贴板不行 | Linux 装 xclip/wl-clipboard;检查 clipboard |
下一步
Lua API、autocmd、keymap 设计哲学、插件骨架、性能 profiling、远程与 tmux → Neovim 精通。
插件是杠杆:先有稳固的支点(options + 键位),再撬 LSP。
评论