nvim(LazyVim)配置C++调试环境(nvim-dap + gdb14) 2024年

时间:2024年1月6日

环境

ubuntu23.04
nvim 0.9.4,配置基于LazyVim


关于DAP

DAP(Debug Adapter Protocol)是一种将调试抽象化的协议,和LSP(Language Server Protocol)类似,实现了DAP协议的前端可以用统一的方式与实现了相同协议的各种不同后端进行通信。比如在nvim中,我们可以使用`nvim-dap`插件来实现调试功能。

调试器的选择

不管你使用什么语言,最好都去官网看一手资料,这样是最不容易被过时的教程误导的。Debug Adapter installation · mfussenegger/nvim-dap Wiki · GitHub

在我写下这篇文章时,C/C++的debuger有四种选择

其中vscode cpptools就是VSCode中的调试工具,配置啥的应该都一样,熟悉VSCode配置的可以考虑这个

这里我用的是gdb,因为我个人比较倾向于使用原有的生态

但是gdb14才有dap的支持,我当前的ubuntu23.04的apt只有gdb13,所以要多一个编译源码的麻烦;

(注:之后发现gdb13.1好像也支持)

编译安装gdb14

官网:

https://www.sourceware.org/gdb/

这里还遇到了一个小坑

不知道为什么download会跳转到上级目录的download,结果还404

正确的网址应该是这个https://www.sourceware.org/gdb/download/

我下载了这里的gdb-14.1.tar.xz

解压得到gdb-14.1的目录。

从源码编译gdb的教程网上有很多,也比较简单,我这里随便讲一下我的过程

  1. 在gdb14.1目录下mkdir build ; cd build
  2. 运行../configure [--参数] 它会根据你给的参数(比如安装路径前缀、是否生成tui版本(--enable-tui))在./下生成makefile
  3. 执行make,一堆输出信息,最后突然失败,找不到原因。于是尝试将标准输出重定向到临时文件。
    make -j32 >tmplog

    然后终端(应该是标准错误流的输出)提示makeinfo: not found。
    这种情况就要安装sudo apt install texinfo
    继续尝试发现还是缺少依赖,按照错误提示安装即可
    我这里运行了sudo apt install libncurses-dev libgmp-dev
    顺带一提,很多常见的依赖库都可以用apt install libxxx-dev的方式安装。
    比如C/C++连接mysql需要<mysql/mysql.h>,sudo apt install libmysqlclient-dev即可。它会在你的/usr/include下创建目录,你的C程序就看到他们了。

  4. 执行make install这个操作会改变默认的gdb,所以要考虑下你自己的需求。

  5. 检查gdb版本

  6. 可以试试gdb -i dap,gdb14才能正常运行

配置nvim-dap

~/.config/nvim/lua/plugins/dap.lua

我的配置,供参考

(注:之后发现一些快捷键冲突,其实不能使用)

return {
  {
    "mfussenegger/nvim-dap",
    event = "VeryLazy",
    keys = {
      -- add a keymap to browshttps://github.com/cmu-db/bustub.gite plhttps://github.com/cmu-db/bustub.gitugin files
      -- stylua: ignore
      {
        "<f5>",
        function() require("dap").continue() end,
        desc = "launch/continue gdb",
      },
      {
        "<f10>",
        function()
          require("dap").step_over()
        end,
        desc = "单步调试",
      },
      {
        "<C-f10>",
        function()
          require("dap").step_into()
        end,
        desc = "步入",
      },
      {
        "<C-f>",
        function()
          require("dap").step_out()
        end,
        desc = "步出",
      },
      {
        "<C-f5>",
        function ()
          require("dap").terminate()
        end,
        desc = "终止程序"
      }
    },
    config = function()
      local dap = require("dap")

      dap.adapters.gdb = {
        type = "executable",
        executable = {
          command = vim.fn.exepath("gdb"),
          args = { "-i", "dap" },
        },
      }
      dap.configurations.c = {
        name = "Launch file",
        type = "gdb",
        request = "launch",
        gdbpath = function()
          return "/usr/local/bin/gdb"
        end,
        cwd = "${workspaceFolder}",
      }
    end,
  },
  {
    "rcarriga/nvim-dap-ui",
    dependencies = {
      "mfussenegger/nvim-dap",
    },
    opts = function()
      local dap, dapui = require("dap"), require("dapui")
      dap.listeners.before.attach.dapui_config = function()
        dapui.open()
      end
      dap.listeners.before.launch.dapui_config = function()
        dapui.open()
      end
      dap.listeners.before.event_terminated.dapui_config = function()
        dapui.close()
      end
      dap.listeners.before.event_exited.dapui_config = function()
        dapui.close()
      end
      return {
        enabled = true, -- enable this plugin (the default)
        enabled_commands = true, -- create commands DapVirtualTextEnable, DapVirtualTextDisable, DapVirtualTextToggle, (DapVirtualTextForceRefresh for refreshing when debug adapter did not notify its termination)
        highlight_changed_variables = true, -- highlight changed values with NvimDapVirtualTextChanged, else always NvimDapVirtualText
        highlight_new_as_changed = false, -- highlight new variables in the same way as changed variables (if highlight_changed_variables)
        show_stop_reason = true, -- show stop reason when stopped for exceptions
        commented = false, -- prefix virtual text with comment string
        only_first_definition = true, -- only show virtual text at first definition (if there are multiple)
        all_references = false, -- show virtual text on all all references of the variable (not only definitions)
        filter_references_pattern = "<module", -- filter references (not definitions) pattern when all_references is activated (Lua gmatch pattern, default filters out Python modules)
        -- Experimental Features:
        virt_text_pos = "eol", -- position of virtual text, see `:h nvim_buf_set_extmark()`
        all_frames = false, -- show virtual text for all stack frames not only current. Only works for debugpy on my machine.
        virt_lines = false, -- show virtual lines instead of virtual text (will flicker!)
        virt_text_win_col = nil, -- position the virtual text at a fixed window column (starting from the first text column) ,
      }
    end,
  },
  {
    "theHamsta/nvim-dap-virtual-text",
    opts = {},
  },
}

这里使用了三个插件

  1. mfussenegger/nvim-dap 提供基本的dap支持
  2. rcarriga/nvim-dap-ui 提供调试的ui,类似VSCode 的调试界面那样
  3. theHamsta/nvim-dap-virtual-text 在调试的代码附近用虚显的文件显示变量信息

遇到的坑:

这一句

return {
  {
    ...
    ...

    config = function()
      local dap = require("dap")

      dap.adapters.gdb = {
        type = "executable",
        executable = {
          command = vim.fn.exepath("gdb"),
          args = { "-i", "dap" },
        },
      }
      dap.configurations.c = {
        name = "Launch file",
        type = "gdb",
        request = "launch",
        gdbpath = function()
          return "/usr/local/bin/gdb"
        end,
        cwd = "${workspaceFolder}",
      }
    end,


  },
    ...
    ...

之前遇到的大部分插件的选项都是通过opts字段来传递的

return {
  {
    "nvim-treesitter/nvim-treesitter",
    opts = {
      -- 增量选择
      incremental_selection = {
        enable = true,
        keymaps = {
          init_selection = "<CR>", -- set to `false` to disable one of the mappings
          node_incremental = "<CR>",
          scope_incremental = "<TAB>",
          node_decremental = "<BS>",
        },
      },
    },
  },
}

但是nvim-dap要用config字段,我也不知道为什么,可能还是不够了解Lua.
总之我在这花了很多时间,尝试了各种方法,它都一直报一个ENOENT即No such file or directory的错误,最后发现opts字段是不起作用的,得用config

官方仓库配置示例:

https://github.com/mfussenegger/nvim-dap/wiki/Debug-Adapter-installation#ccrust-via-vscode-cpptools

配置完成

正常情况下按f5(或者你自己的启动配置)或命令模式输入DapContinue会出现这个界面

Launch了已经编译好的程序会是这样的

至于打断点的行的标志和颜色是可以自己配置的

断点样式

下面是从网上抄来的配置,但是在标志那边出现了乱码问题,

首先要确定你已经安装了一种Nerd Font
然后把乱码的地方改成你喜欢的符号即可

-- 设置调试相关的字符和颜色
local dap_breakpoint_color = {
    breakpoint = {
        ctermbg=0,
        fg='#993939',
        bg='#31353f',
    },
    logpoing = {
        ctermbg=0,
        fg='#61afef',
        bg='#31353f',
    },
    stopped = {
        ctermbg=0,
        fg='#98c379',
        bg='#31353f'
    },
}

vim.api.nvim_set_hl(0, 'DapBreakpoint', dap_breakpoint_color.breakpoint)
vim.api.nvim_set_hl(0, 'DapLogPoint', dap_breakpoint_color.logpoing)
vim.api.nvim_set_hl(0, 'DapStopped', dap_breakpoint_color.stopped)

-- 输入unicode的方法:ctrl + v + u 再输入unicode码
-- 可在https://www.nerdfonts.com/cheat-sheet查询想要的字符
local dap_breakpoint = {
    error = {
        text = "",
        texthl = "DapBreakpoint",
        linehl = "DapBreakpoint",
        numhl = "DapBreakpoint",
    },
    condition = {
        text = '',
        texthl = 'DapBreakpoint',
        linehl = 'DapBreakpoint',
        numhl = 'DapBreakpoint',
    },
    rejected = {
        text = "",
        texthl = "DapBreakpoint",
        linehl = "DapBreakpoint",
        numhl = "DapBreakpoint",
    },
    logpoint = {
        text = '',
        texthl = 'DapLogPoint',
        linehl = 'DapLogPoint',
        numhl = 'DapLogPoint',
    },
    stopped = {
        text = '',
        texthl = 'DapStopped',
        linehl = 'DapStopped',
        numhl = 'DapStopped',
    },
}

vim.fn.sign_define('DapBreakpoint', dap_breakpoint.error)
vim.fn.sign_define('DapBreakpointCondition', dap_breakpoint.condition)
vim.fn.sign_define('DapBreakpointRejected', dap_breakpoint.rejected)
vim.fn.sign_define('DapLogPoint', dap_breakpoint.logpoint)
vim.fn.sign_define('DapStopped', dap_breakpoint.stopped)
-- end dap

自定义符号

首先要确定你已经安装了一种Nerd Font(下载地址Nerd Fonts - Iconic font aggregator, glyphs/icons collection, & fonts patcher)
然后把乱码的地方改成你喜欢的符号即可

我这里使用的是JetBrainsMono字体

Nerd Fonts - Iconic font aggregator, glyphs/icons collection, & fonts patcher

可以查询特殊符号的Unicode值,或者你可以直接复制它们

在nvim中输入unicode字符的方法:ctrl + v + u 再输入unicode码
 

顺便一提,我在使用symboloutline这个插件时也遇到了乱码问题,虽然用的已经是Nerd Font了。可能是因为字体有更新,某些Unicode字符被废弃,而插件没有跟上

甚至插件官方给出的配置也是有乱码的
https://github.com/simrat39/symbols-outline.nvim
去上面给出的网站找到自己想要的字符进行替换即可

正常显示

  • 21
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值