You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Neovim中LSP virtual_text异常:命令模式退出后符号重置

Neovim LSP虚拟文本符号重置问题修复

问题描述

在Neovim中配置LSP虚拟文本(virtual_text)后出现异常:进入命令模式再退出,自定义的虚拟文本前缀符号会恢复为默认样式。

系统环境

  • LSB Version: n/a
  • Distributor ID: Fedora
  • Description: Fedora Linux 39 (Workstation Edition)
  • Release: 39
  • Codename: n/a

用户配置代码

---@diagnosticjdisable: undefined-global
return {
  {
    "VonHeikemen/lsp-zero.nvim",
    branch = "v3.x",
    lazy = true,
    config = false,
    init = function()
      -- Disable automatic setup, we are doing it manually
      vim.g.lsp_zero_extend_cmp = 0
      vim.g.lsp_zero_extend_lspconfig = 0
    end,
  },

  -- Autocompletion
  {
    "hrsh7th/nvim-cmp",
    event = "InsertEnter",
    dependencies = {
      {
        "L3MON4D3/LuaSnip",
        "rafamadriz/friendly-snippets",
      },
    },
    config = function()
      -- Herk is where you configure the autocompletion settings.
      local lsp_zero = require("lsp-zero")
      lsp_zero.extend_cmp()

      -- And you can configure cmp even more, if you want to.
      local cmp = require("cmp")
      local cmp_action = lsp_zero.cmp_action()

      -- for vscode snippets
      require('luasnip.loaders.from_vscode').lazy_load()
      cmp.setup({
        formatting = lsp_zero.cmp_format({ details = true }),
        mapping = cmp.mapping.preset.insert({
          ["<C-b>"] = cmp.mapping.scroll_docs(-4),
          ["<C-f>"] = cmp.mapping.scroll_docs(4),
          ["<C-Space>"] = cmp.mapping.complete(),
          ["<C-e>"] = cmp.mapping.abort(),
          ["<CR>"] = cmp.mapping.confirm({
            behavior = cmp.ConfirmBehavior.Replace,
            select = true,
          }),
        }),
        snippet = {
          expand = function(args)
            require("luasnip").lsp_expand(args.body)
          end,
        },
        sources = cmp.config.sources({
          { name = "copilot" },
          { name = "nvim_lsp" },
          { name = "luasnip" },
        }, {
          { name = "buffer" },
        }),
      })
    end,
  },

  -- LSP
  {
    "neovim/nvim-lspconfig",
    cmd = "LspInfo",
    event = { "BufReadPre", "BufNewFile" },
    dependencies = {
      { "hrsh7th/cmp-nvim-lsp" },
    },
    config = function()
      -- This is where all the LSP shenanigans will live
      local lsp_zero = require("lsp-zero")
      lsp_zero.extend_lspconfig()

      lsp_zero.on_attach(function(client, bufnr)
        -- see :help lsp-zero-keybindings
        -- to learn the available actions
        lsp_zero.default_keymaps({ buffer = bufnr })
      end)

      lsp_zero.on_attach(function(client, bufnr)
        lsp_zero.default_keymaps({ buffer = bufnr })
      end)

      require("mason").setup({})
      require("mason-lspconfig").setup({
        -- Replace the language servers listed here
        -- with the ones you want to install
        ensure_installed = {
          "clangd",
          "rust_analyzer",
          "pyright",
          "tsserver",
          "html",
          "lua_ls",
          "ast_grep",
          "cssls",
          "gopls",
        },
        handlers = {
          lsp_zero.default_setup,
        },
        vim.diagnostic.config({
          virtual_text = {
            prefix = "--", -- Could be '■', '▎', 'x'
          },
        }),
      })

      -- (Optional) Configure lua language server for neovim
      local lua_opts = lsp_zero.nvim_lua_ls()
      require("lspconfig").lua_ls.setup(lua_opts)

      lsp_zero.on_attach(function(client, bufnr)
        lsp_zero.default_keymaps({ buffer = bufnr })
      end)

      lsp_zero.set_sign_icons({
        error = "",
        warn = "",
        hint = "",
        info = "",
      })
    end,
  },
}

问题原因

  1. 诊断配置位置错误:vim.diagnostic.config被错误嵌套在mason-lspconfig.setup的参数表中,该位置的配置无法被正确执行,且LSP启动时会被默认配置覆盖。
  2. 重复绑定回调:多次调用lsp_zero.on_attach导致回调重复注册,可能触发不必要的配置重置逻辑。

修复方案

  1. 将vim.diagnostic.config从mason-lspconfig.setup中移出,单独调用以确保全局生效。
  2. 移除重复的lsp_zero.on_attach绑定,仅保留一次即可。
  3. 确保诊断配置在LSP服务启动前完成设置,避免被后续操作覆盖。

修改后的配置代码

---@diagnostic disable: undefined-global
return {
  {
    "VonHeikemen/lsp-zero.nvim",
    branch = "v3.x",
    lazy = true,
    config = false,
    init = function()
      -- Disable automatic setup, we are doing it manually
      vim.g.lsp_zero_extend_cmp = 0
      vim.g.lsp_zero_extend_lspconfig = 0
    end,
  },

  -- Autocompletion
  {
    "hrsh7th/nvim-cmp",
    event = "InsertEnter",
    dependencies = {
      "L3MON4D3/LuaSnip",
      "rafamadriz/friendly-snippets",
    },
    config = function()
      local lsp_zero = require("lsp-zero")
      lsp_zero.extend_cmp()

      local cmp = require("cmp")
      local cmp_action = lsp_zero.cmp_action()

      -- 加载vscode风格代码片段
      require('luasnip.loaders.from_vscode').lazy_load()
      
      cmp.setup({
        formatting = lsp_zero.cmp_format({ details = true }),
        mapping = cmp.mapping.preset.insert({
          ["<C-b>"] = cmp.mapping.scroll_docs(-4),
          ["<C-f>"] = cmp.mapping.scroll_docs(4),
          ["<C-Space>"] = cmp.mapping.complete(),
          ["<C-e>"] = cmp.mapping.abort(),
          ["<CR>"] = cmp.mapping.confirm({
            behavior = cmp.ConfirmBehavior.Replace,
            select = true,
          }),
        }),
        snippet = {
          expand = function(args)
            require("luasnip").lsp_expand(args.body)
          end,
        },
        sources = cmp.config.sources({
          { name = "copilot" },
          { name = "nvim_lsp" },
          { name = "luasnip" },
        }, {
          { name = "buffer" },
        }),
      })
    end,
  },

  -- LSP
  {
    "neovim/nvim-lspconfig",
    cmd = "LspInfo",
    event = { "BufReadPre", "BufNewFile" },
    dependencies = {
      "hrsh7th/cmp-nvim-lsp",
    },
    config = function()
      local lsp_zero = require("lsp-zero")
      lsp_zero.extend_lspconfig()

      -- 仅绑定一次on_attach回调
      lsp_zero.on_attach(function(client, bufnr)
        lsp_zero.default_keymaps({ buffer = bufnr })
      end)

      require("mason").setup({})
      require("mason-lspconfig").setup({
        ensure_installed = {
          "clangd",
          "rust_analyzer",
          "pyright",
          "tsserver",
          "html",
          "lua_ls",
          "ast_grep",
          "cssls",
          "gopls",
        },
        handlers = {
          lsp_zero.default_setup,
        },
      })

      -- 单独设置诊断配置,确保全局生效
      vim.diagnostic.config({
        virtual_text = {
          prefix = "--", -- 可替换为'■', '▎', 'x'等符号
        },
      })

      -- 配置lua语言服务器
      local lua_opts = lsp_zero.nvim_lua_ls()
      require("lspconfig").lua_ls.setup(lua_opts)

      lsp_zero.set_sign_icons({
        error = "",
        warn = "",
        hint = "",
        info = "",
      })
    end,
  },
}

额外优化

如果修改后问题仍存在,可在诊断配置中添加update_in_insert = false,避免插入模式下的配置干扰:

vim.diagnostic.config({
  virtual_text = {
    prefix = "--",
  },
  update_in_insert = false,
})

内容的提问来源于stack exchange,提问作者Yossef Sabry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 08:32:05