使用 Vimspector 在 Vim 中调试 使用 Vimspector 在 Vim 中调试 下一步是什么?

2025-06-07

使用 Vimspector 在 Vim 中调试

使用 Vimspector 在 Vim 中调试

下一步是什么?

关注@learnvim获取更多 Vim 技巧和窍门!

使用 Vimspector 在 Vim 中调试

Vimspector 是一款功能强大的 Vim 图形调试器插件。不过,上手可能需要一些时间。在本文中,我将向您展示如何使用 Vimspector 进行调试:

  • 一个节点文件。
  • 使用 Chrome 的客户端应用程序。
  • 开玩笑地测试。
  • 一款 Express 应用程序。

他们的Github 页面官网都非常全面。建议你有机会的时候去看看!

本文中的许多内容都可以在 Vimspector 官网找到。然而,当我阅读这些内容时,我被海量的信息量所震撼。本文可以作为桥梁,帮助您尽早入门。我认为尽早动手实践是有益的。一旦开始使用,阅读文档就会变得更加清晰。虽然我以 JavaScript 生态系统(后端、前端、测试)为例,但您应该能够将相同的概念应用于您选择的任何语言。

要求

撰写本文时,我正在 MacOS Catalina 上使用 Vim 8.2。本指南适用于任何操作系统(需进行适当修改)。不过,Vimspector Github 页面建议你使用 Vim 8.2 或 NeoVim 0.4.3。你还需要 Python 3.6 或更高版本。

快速概览

那么 Vimspector 是什么?它是如何工作的?

Vimspector 并非通用语言调试器。从技术上讲,它本身并不负责调试。你可以把它想象成一个中间人,用于帮助你与所选语言的调试器进行通信。它依赖于特定的小工具,具体取决于你使用的语言。因此,如果你想调试 Node.js 应用,请使用vscode-node-debug2小工具。如果你想调试 Go 应用,请使用vscode-go小工具。如果你想调试客户端 JS 应用,请使用debugger-for-chrome小工具。

不同的语言有不同的调试器。有些语言内置调试器,有些则使用外部库进行调试。如果我们想将多种语言集成到我们常用的编辑器/IDE 中,可能会变得很混乱。Python 调试器的通信方式与 Node 调试器不同。Node 调试器的行为方式与通过 Chrome 浏览器的前端 JS 调试器不同。如果您是 Ruby/JavaScript 开发人员,理论上您可以在编辑器/IDE 中同时安装 Ruby 和 Node 调试器。但这两个调试器可能使用不同的协议,并表现出不同的行为。您处理的语言/环境越多,复杂性就越高。

因此,在不同的语言环境中调试会变得非常混乱。为了协调这些差异,我们需要在 X 语言调试器和我们的编辑器/IDE 之间架起一座桥梁——我们需要一个抽象协议。这个协议被称为调试适配器协议 (DAP)。DAP 最初是由 VSCode 团队创建的。但幸运的是,他们决定让它(在某种程度上)与编辑器无关,这样开发者就可以将它用于其他编辑器/IDE。Vimspector 就是基于 DAP 为 Vim 编辑器开发的成果。要查看支持 DAP 的工具列表,请查看此页面:支持 DAP 的工具

虽然不是必读内容,但当您有时间时,我强烈建议您查看这些页面以更好地了解 DAP:

好了,理论已经讲够了——让我们从一个实际的例子开始吧!

入门

首先,请安装 Vimspector(如果尚未安装)。请按照Vimspector 安装指南进行操作。

我使用的是vim-plug。我所做的就是在 vimrc 的插件列表中添加以下内容:

Plug 'puremourning/vimspector'
Enter fullscreen mode Exit fullscreen mode

然后我获取了我的 vimrc 并运行:PlugInstall

示例

学习一项新技能最快的方法就是立即投入其中,并边学边学。你现在可能有很多疑问,但一旦你了解了它的原理,读完这篇文章后,我希望你的一些疑问能够得到解答。

让我们看一下节点示例。

调试节点应用程序

Vimspector 插件本身已经自带示例。请前往 Vim 保存 Vimspector 插件的目录。我使用的是 vim-plugged,所以我的插件安装在该plugged/目录中。就我而言,该目录位于~/.vim/plugged/vimspector/。您的插件可能位于其他位置,具体取决于您的插件管理器和系统。找到它们后,从vimspector/目录转到/support/test/目录。在该目录中,您会发现不同的示例,我强烈建议您在读完本文后查看一下。

由于本节是关于调试节点应用程序,让我们检查一下节点目录。

cd node/
Enter fullscreen mode Exit fullscreen mode

在撰写本文时,您应该在其中找到一个名为的目录simple/。转到那里,您将看到一个simple.js文件。

在我们调试这个文件之前,使用 Vimspector 进行调试至少有两个要求:

  1. 相关的小工具。
  2. Vimspector 配置文件。

小工具是一种调试适配器(类似于微软的调试适配器页面中列出的适配器)。由于我们正在调试 Node 应用,因此需要一个 Node 适配器。该 Node 适配器将在 NodeJS 和抽象协议 DAP 之间中继消息。

(顺便说一句,在安装节点适配器之前,根据Vimspector 的站点,您需要使用 6 到 12 之间的 Node 版本)。

对于第一个要求,要安装 Node 适配器,请在 Vim 中运行:VimspectorInstall vscode-node-debug2。调试不同的语言/环境时,需要不同的小工具。如果您需要调试 Python 文件,则必须安装 Python 小工具。如果您正在调试 Go 文件,则必须安装 Go 小工具。由于我们正在调试 Node 应用,因此需要安装 Node 小工具。有关小工具列表,请查看 Vimspector Github 页面中的以下部分:支持的语言

对于第二个要求,该.vimspector.json文件恰好位于项目的根目录(内部simple/)。如果您检查目录的隐藏文件vimspector/support/test/simple/,应该已经有一个.vimspector.json可用的文件,因此您无需执行任何操作。请记住,在检查自己的项目时,请记得创建自己的 vimspector 文件。

满足这两个要求后,我们就可以进行调试了。

Vimspector 提供了许多命令和快捷键。新手入门时,要全部用上可能会让人不知所措。我发现以下这些命令和快捷键足以让你上手。以下是我在 vimrc 中设置的一些 Vimspector 快捷键:

nnoremap <Leader>dd :call vimspector#Launch()<CR>
nnoremap <Leader>de :call vimspector#Reset()<CR>
nnoremap <Leader>dc :call vimspector#Continue()<CR>

nnoremap <Leader>dt :call vimspector#ToggleBreakpoint()<CR>
nnoremap <Leader>dT :call vimspector#ClearBreakpoints()<CR>

nmap <Leader>dk <Plug>VimspectorRestart
nmap <Leader>dh <Plug>VimspectorStepOut
nmap <Leader>dl <Plug>VimspectorStepInto
nmap <Leader>dj <Plug>VimspectorStepOver
Enter fullscreen mode Exit fullscreen mode

你可以随意借鉴我的经验,或者自己创造一些。Vimspector 还附带一组称为“人机模式映射”的快捷键。如果你习惯使用 VSCode 的调试快捷键,你可能会觉得它们更适合你。

最后,让我们打开simple.js。它看起来应该是这样的:

var msg = 'Hello, world!'

var obj = {
  test: 'testing',
  toast: function() {
    return 'toasty' + this.test;
  }
}

console.log( "OK stuff happened " + obj.toast() )
Enter fullscreen mode Exit fullscreen mode

要启动 vimspector,请运行 launch 命令。按下<Leader>dd( :call vimspector#Launch())。你应该会看到一个 Vimspector 窗口。太酷了!

Vimspector 布局

屏幕上应该会显示 6 个不同的窗口——根据 Vim 的方向,它们的显示顺序可能会有所不同。如果您以前从未使用过调试器,也不必担心。使用一段时间后,您会逐渐习惯其中一些功能。要退出 Vimspector,请按<Leader>de( :call vimspector#Reset())。

顺便说一下,启动 vimspector 时,窗口底部会出现一些提示,要求输入类似 的内容...Break on Uncaught Exceptions?。我通常会按N。如果你不想一直收到提示,请在 vimspector.json 文件的块中添加以下内容"run": { ...

  ...
  "breakpoints": {
    "exception": {
      "all": "N",
      "uncaught": "N"
    }
  },
  ...
Enter fullscreen mode Exit fullscreen mode

该文件的完整 vimspector json(因此您只需复制粘贴即可)如下所示:

{
  "configurations": {
    "run": {
      "adapter": "vscode-node",
      "breakpoints": {
        "exception": {
          "all": "N",
          "uncaught": "N"
        }
      },
      "configuration": {
        "request": "launch",
        "protocol": "auto",
        "stopOnEntry": true,
        "console": "integratedTerminal",
        "program": "${workspaceRoot}/simple.js",
        "cwd": "${workspaceRoot}"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

回到调试,再次启动 vimspector ( <Leader>dd)。由于我们必须stepOnEntrytrueVimspector json 文件中,所以即使你没有标记断点,Vimspector 也会在第一行停止。

要遍历文件,您可以:

  1. 走出(走出范围)
  2. 步入(步入函数范围)
  3. 跨过(在范围内跨到下一行)

以下是我使用的按键映射。请注意,我使用的hlj按键与 Vim 的移动键类似。

nmap <Leader>dh <Plug>VimspectorStepOut
nmap <Leader>dl <Plug>VimspectorStepInto
nmap <Leader>dj <Plug>VimspectorStepOver
Enter fullscreen mode Exit fullscreen mode

如果您不确定什么是走出去、走进去和跨过,我发现这些简短的视频教程非常有帮助:

回到我们的调试——现在我们位于代码的第一行,按下<Leader>dj( <Plug>VimspectorStepOver)。注意,高亮会移动到变量声明处。如果再按<Leader>dj一次,它会再次向下移动。如果继续单步执行,最终会到达simple.js文件末尾。遗憾的是,您无法“回退”到上一行。一旦进入下一步,您就会继续前进,直到到达末尾。

如果您不小心跳过了重要的一行,只需重启调试器即可。要重启,请运行<Leader>dk( <Plug>VimspectorRestart)。重启后,Vimspector 会从头开始运行。或者,您也可以重置并重新启动 Vimspector。

您可以在文件中设置断点。回到主simple.js文件,在要添加断点的行上运行<Leader>dt( :call vimspector#ToggleBreakpoint())(在断点所在的行再次运行该命令即可移除断点)。

在整个文件中添加断点后,再次启动 Vimspector。按下<Leader>dc( :call vimspector#Continue()) 键,Vimspector 就会跳转到下一个断点。太酷了!

Vimspector 继续

要清除断点,请运行<Leader>dT:call vimspector#ClearBreakpoints())。

在进入下一部分之前,请花 10-15 分钟尝试一下simple.js。更改中的代码simple.js。四处走动。玩一玩。

在我们进入下一部分之前,让我们简单回顾一下 6 个 Vimspector 窗口的功能。

变量窗口

变量窗口包含相对于当前范围的可用变量(及其当前值)。

- Scope: Local
  + this (Object): Object
  - __dirname (string): "/Users/iggy/.vim/plugged/vimspector/support/test/node/simple"
  - __filename (string): "/Users/iggy/.vim/plugged/vimspector/support/test/node/simple/simple.js"
  + exports (Object): Object {}
  + module (Object): Module {id: ".", path: "/Users/iggy/.vim/plugged/vimspector/support/test/n…", exports: Object, …}
 *- msg (undefined): undefined
 *- obj (undefined): undefined
  + require (Function): function require(path) { … }
+ Scope: Global
Enter fullscreen mode Exit fullscreen mode

在上面的例子中,在 Local 作用域内,我有一些常见的 Node 变量,例如this__dirname,以及写成__filename的变量。当你跨过下一个变量时,请注意观察它们如何从 undefined 变为有值。msgobj

尝试一下:跨过并进入不同的函数作用域。找到一种方法进入函数内部,查看可用的变量。然后跳出函数外部并进行比较。同时检查函数内部的内容Scope: Global。为什么它有这些变量?这能告诉你关于 Node 的什么信息?

监视窗口

在“监视”窗口中,您可以监视特定的值。最初它是空白的。如果您想监视msg变量的值,请在“监视”窗口中输入该变量msg。当您位于文件开头时,该值为undefined。然后,当您单步执行时,该值将变为'Hello, world!'

堆栈跟踪窗口

Stack Trace 窗口显示节点文件执行的调用堆栈。

控制台窗口

在控制台窗口中,您可以输入定义的变量,例如msg。您还可以计算表达式的值。

终端窗口

终端窗口显示整个调试会话中的所有输出。

在浏览器中调试

让我们探索如何使用 Chrome 调试器调试客户端应用程序。幸运的是,Vimspector 目录中也有一个示例。在该~/.vim/plugged/vimspector/support/test/chrome/目录中,您会找到一个名为run_server、atest.js和 a 的文件.vimspector.json

让我们看看 vimspector 配置文件里面有什么:

{
  "configurations": {
    "launch": {
      "adapter": "chrome",
      "configuration": {
        "request": "launch",
        "url": "http://localhost:1234/",
        "webRoot": "${workspaceRoot}/www"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

这个配置和我们之前看到的 Node Vimspector 配置确实有所不同。其中很重要的一行是"adapter": "chrome"——它表明我们将使用 Chrome 适配器。URL 被定义为 on,localhost:1234因为这是我们服务器运行的端口。

顺便说一句,稍后启动 Vimspector 时,它会再次提示你是否要在未捕获的异常等情况下中断。如果你不想处理这些提示,请.vimspector.json像之前在 Node 应用程序中那样,在里面添加以下几行代码。

"breakpoints": {
  "exception": {
    "all": "N",
    "uncaught": "N"
  }
},
Enter fullscreen mode Exit fullscreen mode

我们需要安装正确的小工具。在前面的部分,我们用 安装了一个小工具:VimspectorInstall vscode-node-debug2。这次我们需要为 Chrome 安装一个。从 Vim 运行以下命令:

:VimspectorInstall debugger-for-chrome
Enter fullscreen mode Exit fullscreen mode

完成后,运行服务器(确保已安装 PHP):

./run_server
Enter fullscreen mode Exit fullscreen mode

查看localhost:1234。您应该会看到一个带有一些弹出模式的简单应用程序。

现在打开www/js/test.js。在任何你喜欢的地方添加断点。运行 Vimspector 启动命令<Leader>dd。运行它,它会自动启动 Chrome 浏览器。Vimspector 会在断点处暂停 Chrome 浏览器。单步执行并进入断点。观察一些变量。修改代码。尽情体验吧!

注意:请查看“变量”窗口。同时检查“本地”和“全局”作用域。与使用 Node 调试时相比,你在“全局”作用域中发现了什么不同吗?这能说明客户端和后端代码执行的哪些方面?

注2:请注意,这次启动 Vimspector 时,它会启动一个 Chrome 浏览器。如果你查看.vimspector.json,你会看到 ,而不是之前 Node 调试时显示"request": "launch","request": "attach",。它们有什么不同?调试应用程序的方法有两种:将其附加到已运行的进程或启动新进程。

调试 Jest 测试

现在让我们学习如何调试 Jest 测试。在本节中,我将使用vscode-recipes仓库。首先,访问网站并克隆仓库。然后,进入debugging-jest-tests/目录。您将看到两个目录:lib/test/。这是我们的工作区根目录。

首先,安装依赖项:npm i

确保 Jest 测试正在运行并且全部通过:npm run test

调试 Jest 测试需要 Node 调试器。如果您一直在输入,那么您应该已经拥有之前安装的 Node 调试器。使用 Vimspector,您只需安装一次小工具(Vimspector 会将所有已安装的小工具保存在 Vimspector 目录中 - 在我的情况下,它们存储在 中~/.vim/plugged/vimspector/)。

在目录中debugging-jest-tests/,添加一个.vimspector.json文件。在其中:

  "configurations": {
    "my awesome jest test": {
      "adapter": "vscode-node",
      "breakpoints": {
        "exception": {
          "all": "N",
          "uncaught": "N"
        }
      },
      "configuration": {
        "request": "launch",
        "name": "Jest debugger",
        "type": "node",
        "console": "integratedTerminal",
        "program": "${workspaceRoot}/node_modules/.bin/jest",
        "skipFiles": ["*/<node_internals>/**/*.js", "node_modules/**/*.js"],
        "cwd": "${workspaceRoot}"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

该配置看起来很熟悉,新添加了:"program": " ${workspaceRoot}/node_modules/.bin/jest"。以下是详细信息:

  • workspaceRoot当前目录(debugging-jest-tests/目录)。
  • node_modules/.bin/jest是来自的 Jest 可执行文件node_modules/(您应该在之后获得它npm i)。

这次运行调试器时,Vimspector 需要运行 Jest 可执行文件。你可以在node_modules/目录内找到 Jest 可执行文件。你也可以通过全局 Jest 命令来运行它,但我喜欢将其隔离开来(如果我从容器中运行它,并且不能保证有全局 Jest 命令怎么办?通过全局命令.bin/jest,我可以保证有全局命令——但这只是我的个人偏好)。

太棒了!让我们在测试文件中设置一些断点,然后启动 Vimspector ( <Leader>dd)。瞧!你的测试套件将暂停,现在你可以调试代码了。

如果将断点放在调用该函数的行上,如add()下面的函数:

...
it('Should return correct result', () => {
  const result = add(1, 2); // put a breakpoint here
  expect(result).toEqual(3);
});
...
Enter fullscreen mode Exit fullscreen mode

当你单步执行 ( <Leader>dl) 时,它会进入 内部的原始函数声明lib/calc.js,让你可以调查源代码。这太棒了!有了它,你可以调试错误的测试,直到函数的起源!

有一个问题。使用当前的 Vimspector 配置,它会在启动时运行所有测试。这很好,但在实际应用中,你的应用可能有数百个测试(如果你一直在实践 TDD……眨眨眼)。运行所有测试可能不是最好的生活方式。如果你想一次只运行一个特定的测试怎么办?

你一定可以!

在 Jest 中,你可以通过将文件名(或文件名的一部分)作为参数传递来运行特定文件。如果你只想运行add.spec.js,可以运行命令jest add。Jest 足够智能,会匹配add.spec.js而不是subtract.spec.js

如果您想匹配文件中的特定测试,Jest 也足够智能,可以匹配您使用该-t选项传递的任何关键字。

假设add.spec.js我有两个测试(请注意,我修改了测试描述):

const { add } = require('../lib/calc');

describe('When adding numbers', () => {
  it('one should return correct result', () => {
    const result = add(1, 2);
    expect(result).toEqual(3);
  });

  it('two should not return correct result', () => {
    const result = add(1, 5);
    expect(result).not.toEqual(3);
  });
});
Enter fullscreen mode Exit fullscreen mode

我只想运行第二个测试。为此,我可以从 CLI 运行test add -t two。Jest 足够智能,可以只运行add.spec.js 测试'two should not return correct result'试试看。

有了这些知识,我们需要在运行 Vimspector 时传递这些参数。事实证明,Vimspector 有一个属性args,你可以用它来向程序传递参数。

{
  "configurations": {
    "jest": {
      "adapter": "vscode-node",
      "breakpoints": {
        "exception": {
          "all": "N",
          "uncaught": "N"
        }
      },
      "configuration": {
        "request": "launch",
        "name": "Jest debugger",
        "type": "node",
        "console": "integratedTerminal",
        "program": "${workspaceRoot}/node_modules/.bin/jest",
        "skipFiles": ["*/<node_internals>/**/*.js", "node_modules/**/*.js"],
        "cwd": "${workspaceRoot}",
        "args": [
                "${FileName}",
                "-t",
                "${TestName}"
            ]
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

我们的新属性args是一个包含 3 个元素的数组。那些看起来很奇怪的变量(${FileName}${TestName})是 Vimspector 的命名参数。这样,​​我就可以在启动 Vimspector 时传递FileName和变量了。TestName

让我们再次启动 Vimspector。这次它会提示:“输入 FileName 的值”(在其中输入“add”)。之后,Vimspector 会提示:“输入 TestName 的值”(在其中输入“two”)。然后观察它是否只运行了该文件中的那个测试。成功!

现在你没有理由不去实践 TDD!:D

调试 Express 应用

在接下来的示例中,我们尝试调试一个简单的 Express 应用。由于 Express 是一个 Node 库,因此需要一个 Node 小工具。

创建一个目录 ( mkdir express-debug) 并进入其中。运行npm init -y以初始化 NPM 项目。安装 express ( npm i express)。然后创建一个app.js。在其中:

const express = require('express');
const app = express();
const port = 3000;

const helloFunc = () => {
  const hello = 'hello';
  return hello;
};

app.get('/', (req, res) => {
  const msg = helloFunc();
  res.send(msg);
});

app.listen(port, () => {
  console.log(`Example app listening on port ${port}!`)
});

Enter fullscreen mode Exit fullscreen mode

在该目录中创建一个.vimspector.json。至少应该包含:

{
  "configurations": {
    "run": {
      "adapter": "vscode-node",
      "default": true,
      "configuration": {
        "type": "node",
        "request": "attach",
        "processId": "${processId}"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

但是,如果我们启用一些默认配置可能会更好:

{
  "configurations": {
    "run": {
      "adapter": "vscode-node",
      "default": true,
      "breakpoints": {
        "exception": {
          "all": "N",
          "uncaught": "N"
        }
      },
      "configuration": {
        "name": "Attaching to a process ID",
        "type": "node",
        "request": "attach",
        "skipFiles": ["node_modules/**/*.js", "<node_internals>/**/*.js"],
        "processId": "${processId}"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

然后在检查模式下运行 express 应用程序:

node --inspect app.js
Enter fullscreen mode Exit fullscreen mode

你应该会看到你的应用在 上运行localhost:3000。接下来,在 中添加几个断点app.js,然后启动 Vimspector(它也会要求processId,但我发现不给它任何值,只需按下 Return/Enter 键就可以了)。

最后,刷新页面,你会看到调试器在第一个断点处暂停。从那里,你可以单步退出、单步进入和单步跳过你的代码。

Vimspector Express

恭喜!您已成功调试 Express 应用。

下一步是什么?

本文只是粗略地介绍了一下调试器的功能。Vimspector 还有很多其他功能。希望通过从不同角度的了解,你能更深入地理解这个插件。

您的调试需求可能与我在本文中给出的示例不同,但我真诚地相信,如果您了解其背后的原理,您应该能够实现 Vimspector 来满足您的需求。

感谢你读到这里。祝你 Vim 愉快!

文章来源:https://dev.to/iggredible/debugging-in-vim-with-vimspector-4n0m
PREV
通过构建一个极简电商购物应用来了解 React Context API 的工作原理
NEXT
使用 HTML、CSS 和 JavaScript 制作动画推荐卡。