返回 Skill 列表
extension
分类: 开发与工程无需 API Key

Configuring Neovim

此技能指导使用Neovim配置,包括无头测试更改、使用lazy.nvim管理插件、配置LSP服务器以及排查启动错误。在修改nvim配置文件或调试nvim问题时使用。

person作者: jakexiaohubgithub

Configuring Neovim

This skill provides patterns for modifying and testing Neovim configuration safely and efficiently.

What This Skill Does

  • Tests nvim configuration changes without manual launch
  • Verifies plugins load correctly via headless testing
  • Configures LSP servers with mason.nvim
  • Debugs startup errors and plugin loading issues
  • Tests LSP attachment and functionality
  • Manages plugin configuration with lazy.nvim

Key Technique: Headless Testing

The critical pattern for working with nvim configuration is testing headlessly before manually launching nvim. This catches errors immediately without disrupting your workflow.

Basic Headless Test

# Test if nvim loads without errors
nvim --headless -c "echo 'Config loaded successfully'" -c "quit" 2>&1

What this does:

  • --headless: Run without UI
  • -c "command": Execute vim command
  • -c "quit": Exit after commands
  • 2>&1: Capture stderr to see errors

Success output:

Config loaded successfully

Error output shows:

  • File and line number where error occurred
  • Stack trace
  • Specific error message

Testing Specific Features

# Test plugin loading
nvim --headless -c "lua print('Plugins loaded')" -c "sleep 2" -c "quit" 2>&1

# Test LSP configuration
nvim --headless -c "lua print('LSP config:', vim.inspect(vim.lsp))" -c "quit" 2>&1

# Test with a specific file type
cd /tmp && echo 'print("test")' > test.lua && \
  nvim --headless test.lua -c "lua print('Buffer filetype:', vim.bo.filetype)" -c "quit" 2>&1

Testing LSP Server Attachment

# Test if LSP attaches to a buffer (requires servers installed)
cd /tmp && echo 'print("hello")' > test.lua && \
  nvim --headless test.lua \
    -c "lua vim.defer_fn(function() print('LSP clients:', vim.inspect(vim.lsp.get_clients())) end, 2000)" \
    -c "sleep 3" \
    -c "quit" 2>&1 | grep -A5 "LSP clients"

Why defer_fn? LSP servers need time to start and attach. The 2000ms delay allows initialization.

Prerequisites

Required Tools

  • nvim - Neovim 0.9+
  • git - For plugin installation

Configuration Location

  • Main config: ~/.config/nvim/init.lua
  • Plugins: ~/.config/nvim/lua/plugins/*.lua
  • User config: ~/.config/nvim/lua/user/*.lua
  • Plugin manager: ~/.config/nvim/lua/config/lazy.lua

Common Configuration Tasks

Adding a New Plugin

  1. Create plugin file in ~/.config/nvim/lua/plugins/:
-- ~/.config/nvim/lua/plugins/my-plugin.lua
return {
  "username/plugin-name",
  lazy = false,  -- Load immediately (or set to true for lazy loading)
  config = function()
    require("plugin-name").setup({
      -- Plugin configuration here
    })
  end,
}
  1. Test the plugin loads without errors:
nvim --headless -c "lua print('Testing plugin load')" -c "sleep 2" -c "quit" 2>&1
  1. Launch nvim normally - lazy.nvim will auto-install the plugin

Configuring LSP with Mason

The modern approach uses three plugins:

  • mason.nvim - Installs language servers
  • mason-lspconfig.nvim - Bridges mason and lspconfig
  • nvim-lspconfig - Configures LSP clients

Recommended structure (~/.config/nvim/lua/plugins/lsp.lua):

return {
  -- Mason for installing language servers
  {
    "williamboman/mason.nvim",
    lazy = false,
    priority = 1000,
    config = function()
      require("mason").setup()
    end,
  },

  -- LSP configuration (load early as dependency)
  {
    "neovim/nvim-lspconfig",
    lazy = false,
  },

  -- Mason-lspconfig bridge
  {
    "williamboman/mason-lspconfig.nvim",
    lazy = false,
    dependencies = {
      "williamboman/mason.nvim",
      "neovim/nvim-lspconfig",
      "hrsh7th/cmp-nvim-lsp",
    },
    config = function()
      -- Setup mason-lspconfig
      require("mason-lspconfig").setup({
        ensure_installed = {
          "lua_ls",      -- Lua
          "pyright",     -- Python
          "ts_ls",       -- TypeScript/JavaScript
          "rust_analyzer", -- Rust
          -- Add more servers as needed
        },
      })

      -- Define on_attach for keymaps
      local on_attach = function(_, bufnr)
        local map = function(mode, lhs, rhs, desc)
          vim.keymap.set(mode, lhs, rhs, { buffer = bufnr, silent = true, desc = desc })
        end

        -- Navigation
        map("n", "gd", vim.lsp.buf.definition, "Go to definition")
        map("n", "gD", vim.lsp.buf.declaration, "Go to declaration")
        map("n", "gr", vim.lsp.buf.references, "Go to references")
        map("n", "K", vim.lsp.buf.hover, "Hover documentation")
      end

      -- Get capabilities
      local capabilities = require("cmp_nvim_lsp").default_capabilities()

      -- Configure each server
      local lspconfig = require("lspconfig")
      lspconfig.lua_ls.setup({
        on_attach = on_attach,
        capabilities = capabilities,
        settings = {
          Lua = {
            diagnostics = { globals = { "vim" } },
          },
        },
      })
      -- Add more server configs as needed
    end,
  },
}

Test LSP config loads:

nvim --headless -c "lua print('Testing LSP')" -c "sleep 2" -c "quit" 2>&1

Common errors:

  • attempt to call field 'setup_handlers' - Don't use setup_handlers, configure servers manually
  • module 'cmp_nvim_lsp' not found - Add nvim-cmp as dependency
  • Priority issues - Set explicit priority values (1000, 900, 800)

Modifying Existing Configuration

  1. Read the current config:
cat ~/.config/nvim/lua/plugins/target-plugin.lua
  1. Make your changes using the Edit tool

  2. Test headlessly immediately:

nvim --headless -c "lua print('Config updated')" -c "quit" 2>&1
  1. If errors appear, fix them before launching nvim normally

  2. Launch nvim to verify behavior

Verification Workflows

After Plugin Changes

# Check plugin loads
nvim --headless -c "lua print('Plugins OK')" -c "sleep 2" -c "quit" 2>&1

# Check for errors (should be empty)
nvim --headless -c "quit" 2>&1 | grep -i error

After LSP Changes

# Verify LSP config loads
nvim --headless -c "lua print('LSP:', vim.inspect(vim.lsp))" -c "quit" 2>&1

# Test with actual file (after servers installed)
cd /tmp && echo 'def hello(): pass' > test.py && \
  nvim --headless test.py -c "sleep 3" -c "lua print('Clients:', #vim.lsp.get_clients())" -c "quit" 2>&1

After Keymap Changes

# Check specific keymap exists
nvim --headless -c "lua print('Leader key:', vim.g.mapleader)" -c "quit" 2>&1

Troubleshooting

Error: Failed to run config for plugin

Symptom: Error during plugin config function execution

Cause: Lua error in plugin's config = function() block

Solution:

  1. Look at the stack trace for file:line number
  2. Read the specific line in the config file
  3. Common issues:
    • Calling function that doesn't exist yet (dependency loading order)
    • Typo in module name
    • Missing require() statement

Fix dependency loading:

-- Add explicit dependencies and priority
{
  "my-plugin",
  dependencies = { "required-plugin" },
  priority = 900,  -- Lower number = loads later
}

Error: Module not found

Symptom: module 'xyz' not found

Cause: Plugin not installed or wrong name

Solution:

# Check if plugin directory exists
ls ~/.local/share/nvim/lazy/

# Launch nvim normally to let lazy.nvim install
nvim

# Or force plugin sync
nvim --headless -c "lua require('lazy').sync()" -c "sleep 5" -c "quit"

Error: Attempt to call field (a nil value)

Symptom: attempt to call field 'setup' (a nil value)

Cause:

  • Function doesn't exist in that module
  • Module not loaded yet
  • Wrong module name

Solution:

  1. Verify correct module name in plugin docs
  2. Check if module needs to be required first
  3. Ensure dependencies load before this plugin

LSP Not Attaching

Symptom: LSP features don't work, no diagnostics

Diagnosis:

# Check if LSP clients exist
nvim some-file.lua -c "lua vim.defer_fn(function() print(vim.inspect(vim.lsp.get_clients())) end, 2000)" -c "sleep 3" -c "quit"

Common causes:

  1. Server not installed - Run :Mason in nvim to install
  2. Server not configured - Add to lspconfig setup
  3. File type not detected - Check :set filetype? in nvim
  4. Server crashed - Check :LspInfo for errors

Solution:

# Verify mason installed servers
ls ~/.local/share/nvim/mason/bin/

# Check server executable works
~/.local/share/nvim/mason/bin/lua-language-server --version

Deprecation Warning: lspconfig framework deprecated

Symptom: Warning about require('lspconfig') being deprecated

Cause: Neovim 0.11+ has new LSP config API

Impact: Warning only - still works fine

Future fix: Migration guide will be available when nvim-lspconfig v3.0.0 releases

Performance: Slow Startup

Diagnosis:

# Profile startup time
nvim --startuptime startup.log -c "quit"
cat startup.log | tail -20

Common causes:

  • Too many plugins loading at startup
  • Heavy plugins not lazy-loaded
  • Expensive config functions

Solution:

-- Lazy load plugins
{
  "heavy-plugin",
  lazy = true,  -- Don't load at startup
  event = "VeryLazy",  -- Load after UI renders
  -- or
  cmd = "PluginCommand",  -- Load on command
  -- or
  ft = "python",  -- Load on filetype
}

Best Practices

Always Test Headlessly First

# GOOD: Test before manual launch
nvim --headless -c "echo 'OK'" -c "quit" 2>&1 && echo "Safe to launch nvim"

# BAD: Edit config, launch nvim, hit error, can't see anything
nvim  # Might crash immediately

Use Explicit Loading Order

-- For plugins that depend on each other, set priorities
{
  "base-plugin",
  priority = 1000,  -- Loads first
}
{
  "dependent-plugin",
  priority = 900,   -- Loads second
  dependencies = { "base-plugin" },
}

Keep Configs Modular

~/.config/nvim/
├── init.lua              # Entry point (minimal)
├── lua/
│   ├── config/
│   │   └── lazy.lua      # Plugin manager setup
│   ├── plugins/          # One file per plugin
│   │   ├── lsp.lua
│   │   ├── telescope.lua
│   │   └── treesitter.lua
│   └── user/             # User settings
│       ├── mappings.lua
│       └── settings.lua

Benefits:

  • Easy to find/edit specific plugin config
  • Can remove plugins by deleting one file
  • Clear separation of concerns

Document Your Configurations

-- Good: Explain why
{
  "williamboman/mason.nvim",
  priority = 1000,  -- Must load before mason-lspconfig
  lazy = false,     -- Required at startup for LSP
  config = function()
    require("mason").setup()
  end,
}

-- Bad: No context
{
  "williamboman/mason.nvim",
  config = function()
    require("mason").setup()
  end,
}

Test in Clean Environment

When debugging mysterious issues:

# Start nvim with no config
nvim -u NONE

# Start with minimal config
echo "vim.opt.number = true" > /tmp/minimal.lua
nvim -u /tmp/minimal.lua

Back Up Before Major Changes

# Before restructuring LSP config
cp ~/.config/nvim/lua/plugins/lsp.lua ~/.config/nvim/lua/plugins/lsp.lua.backup

# Or use git
cd ~/.config/nvim
git diff  # Review changes
git checkout -- file.lua  # Revert if needed

Common Patterns

Testing a Complete Config Change

# 1. Make your changes
# 2. Test headlessly
nvim --headless -c "quit" 2>&1

# 3. If errors, see the stack trace and fix
# 4. When clean, test with a file
cd /tmp && echo 'test' > test.txt && nvim --headless test.txt -c "quit" 2>&1

# 5. Launch normally
nvim

Adding a Language Server

# 1. Add to ensure_installed in lsp.lua
# 2. Add server config in lsp.lua
# 3. Test config loads
nvim --headless -c "quit" 2>&1

# 4. Launch nvim - mason auto-installs
nvim

# 5. Wait for mason to finish installing
# 6. Open a file of that language
# 7. Verify LSP attached: :LspInfo

Removing a Plugin

# 1. Delete plugin file
rm ~/.config/nvim/lua/plugins/unwanted-plugin.lua

# 2. Test config loads
nvim --headless -c "quit" 2>&1

# 3. Launch nvim
nvim

# 4. Clean up plugin directory
# In nvim: :Lazy clean

Quick Reference Commands

# Test config loads
nvim --headless -c "quit" 2>&1

# Test with delay for async operations
nvim --headless -c "sleep 2" -c "quit" 2>&1

# Test LSP configuration
nvim --headless -c "lua print(vim.inspect(vim.lsp))" -c "quit" 2>&1

# Test with specific file type
nvim --headless test.lua -c "quit" 2>&1

# Check plugin installation
ls ~/.local/share/nvim/lazy/

# Check LSP servers installed
ls ~/.local/share/nvim/mason/bin/

# Profile startup time
nvim --startuptime startup.log -c "quit" && tail -20 startup.log

# Launch with no config (debugging)
nvim -u NONE

# Force plugin sync
nvim --headless -c "lua require('lazy').sync()" -c "sleep 10" -c "quit"

Related Skills

  • Creating Claude Code Skills - For documenting reusable nvim patterns as skills
  • Git Workflows - For version controlling nvim config changes

Advanced Topics

Testing Specific Plugin Loading

# Test if specific plugin is loaded
nvim --headless -c "lua print('Telescope:', require('telescope') ~= nil)" -c "quit" 2>&1

Debugging Plugin Load Order

# Add prints in config functions to trace loading
config = function()
  print("Loading plugin X...")
  require("plugin").setup()
  print("Plugin X loaded")
end

Testing Keymaps Work

# Open file and try keymap programmatically
nvim --headless test.lua \
  -c "normal gd" \
  -c "echo 'Keymap executed'" \
  -c "quit" 2>&1

This pattern lets you verify keymaps trigger without manual testing.

Summary

The most important practice: always test nvim config changes headlessly before launching nvim manually. This catches errors immediately and shows you exactly where the problem is.

The basic workflow:

  1. Edit config
  2. nvim --headless -c "quit" 2>&1
  3. Fix any errors shown
  4. Repeat until clean
  5. Launch nvim normally

This saves enormous amounts of time and frustration compared to trial-and-error with manual launches.