T 教程 Tutorials

Neovim 实战:lazy.nvim、LSP、Telescope 与 Python 工作流

中文长文:lazy.nvim 插件管理、pyright LSP、treesitter、telescope、oil/neo-tree、fugitive/gitsigns、Python/数据日常工作流与常用键位表。

教程

接 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 / 数据分析日常工作流

典型一天:

  1. 终端 cd 到项目根,source .venv/bin/activate,再 nvim。
  2. <leader>ff 打开脚本;<leader>fg 搜函数名。
  3. 改代码 → LSP 红线用 ]d / <leader>ca。
  4. 保存触发 ruff/black。
  5. 跑脚本:
vim.keymap.set('n', '<leader>r', function()
  vim.cmd.write()
  vim.cmd('split | terminal python %')
end, { desc = 'Save & run Python file' })
  1. 看 CSV / 日志:Telescope + 分屏;大数据用终端 duckdb / python REPL(:terminal)。
  2. Notebook:多数人仍用 Jupyter / VS Code;nvim 可配合 jupytext 或终端跑 .py 脚本化分析。
  3. 远程数据机:SSH 后直接 nvim,或精通篇讲的远程编辑。

项目级提示:在根目录放 pyrightconfig.json:

{
  "venvPath": ".",
  "venv": ".venv",
  "reportMissingImports": true
}

常用键位表(实战向)

键位作用
<leader>ff找文件
<leader>fg活 grep
<leader>fbbuffer 列表
<leader>e文件树 / Oil
gd / gr / K定义 / 引用 / 悬停
<leader>rn / <leader>ca重命名 / code action
]d [d诊断跳转
]c [cgit hunk
<leader>hs / <leader>hpstage / preview hunk
<leader>r存盘并跑当前 Python
<leader>w / <leader>q存 / 退
-(Oil)上级目录
:Lazy插件面板
:GitFugitive 状态
: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。

评论