我的这个 launch IDE 库,比尤雨溪写的和 react 团队写的都要好!

avatar
前端研发工程师 @快手

介绍

launch-ide 是一个能够根据当前用户正在运行的系统进程,自动判断用户所使用的 IDE(代码编辑器),打开 IDE 并将光标定位到用户指定的文件、行、列的库。

这种功能的库常用于组件、error 的源代码定位等场景,因此已经有一些类似的工具,例如比尤雨溪大佬写的 launch-editor(应用于 vite)、react 团队写的 react-dev-utils(应用于 CRA) 等等。

launch-ide 在功能、全面性和各种边界场景的兼容性上,都全方位超过了现有的这些库!

源码仓库:github.com/zh-lx/launc…

背景

肯定有人问,前面提到网上已经有不少和 launch-ide 类似的库了,为什么我还要重复造轮子呢?

看过我 点击页面元素就能打开IDE源码的开源提效工具——支持webpack/vite/rspack/react/vue等众多场景 文章的读者应该知道,我之前开源了 code-inspector-plugin 项目,其对于识别并打开 IDE 的功能有着强烈的依赖。

首先要向 react-dev-utilslaunch-editor 致敬,因为 code-inspector-plugin 前期分别借(zhào)鉴(bān)过他们的源码。后来不断有用户反馈过,在他们的电脑或者某个项目上存在 IDE 识别失败、报错、文件路径无法打开、IDE 唤起比较慢、无法打开 Cursor 编辑器等问题,我就在他们的基础上不断地修改源码去解决用户的问题。

在经历过了 n 次的迭代之后,我突然发现 launch-ide 的全面性已经远远超过 react-dev-utilslaunch-editor,并且到达了一个功能和兼容性都非常完备的阶段。所以我决定将这部分逻辑单独提取出来作为一个库,供有需要的用户使用。

原理

写文章的首要目的不是攀比,而是交流和传播知识,所以我们先讲一下 launch-ide 的主要原理:

  • 如何识别用户当前使用的 IDE 是什么
  • 如何打开 IDE 并将光标定位到指定的代码文件、行、列

如何识别 IDE

在不同的操作系统上执行对应的指令,可以获取到用户当前正在运行的进程,然后将进程的名称与市面上常用的 IDE 名称进行匹配。如果命中了特征,则判断用户正在使用这个 IDE。

以 MacOS 为例,可以通过 ps ax -o comm= 命令,获取当前正在运行的进程名称:

image.png

拿到用户的进程列表之后,与内置的 IDE 特征列表做一个双重 for 循环匹配,发现命中了 Cursor 的特征,我们可以判定为用户当前正在使用的 IDE 是 Cursor:

image.png

打开 IDE 并定位到对应位置

打开 IDE 和打开其他的应用程序是一样的,我们只需要拿到应用程序的启动路径(就是上一步通过进程列表识别出的进程路径),在 nodejs 中通过 child_process.execSync('你的进程路径'),就能够打开这个应用程序了。

而将光标定位到指定文件、行、列这个是依赖 IDE 内部的实现。以 Cursor 为例,它支持通过 /Applications/Cursor.app/Contents/MacOS/Cursor -g /Users/zlx/test.js:10:20 的命令将光标定位至 /Users/zlx/test.js 文件的第 10 行第 20 列。所以在识别到指定的 IDE 后,我们根据不同 IDE 代码定位的指令做一个适配,就能够打开 IDE 并定位到对应位置了。

以上述的 Cursor 为例,我们可以在 nodejs 中执行:

child_process.execSync('/Applications/Cursor.app/Contents/MacOS/Cursor -g /Users/zlx/test.js:10:20')

launch-ide 的优势

launch-ide 这类库的实现原理都差不多,那对比 launch-editorreact-dev-utils,做得更好的点有哪些呢?

Windows 中获取运行中进程

react-dev-utils 中,获取 Windows 中运行中进程的源码 是通过 wmic 获取的:

if (process.platform === 'drawin') {
  // ...
} else if (process.platform === 'win32') {
  // Some processes need elevated rights to get its executable path.
  // Just filter them out upfront. This also saves 10-20ms on the command.
  const output = child_process
    .execSync(
      'wmic process where "executablepath is not null" get executablepath'
    )
    .toString();
  const runningProcesses = output.split('\r\n');
  // ...others
} else {
  // ...
}

wmic 是 Windows 操作系统中的一个命令行工具,允许用户通过命令行界面访问和管理系统的管理信息。

  • 优点:性能很高,在我的 Windows 本上大概 200ms 左右就能够执行完毕
  • 缺点:是微软早在 2016 年就开始宣布将在 Windows 系统中弃用 wmic,并在 2021年H1 开始在 Windows10 中弃用,在 Windows11 系统中更是全部弃用。因此在比较系统的 Windows 系统中,react-dev-utils 的执行会失败。

image.png

尤雨溪的 launch-editor 中使用微软推荐的 powershell 代替了 wmic,对应的源码

if (process.platform === 'darwin') {
  // ...
} else if (process.platform === 'win32') {
  const output = childProcess
    .execSync(
      'powershell -NoProfile -Command "Get-CimInstance -Query \\"select executablepath from win32_process where executablepath is not null\\" | % { $_.ExecutablePath }"',
      {
        stdio: ['pipe', 'pipe', 'ignore']
      }
    )
    .toString()
  // ...
} else if (process.platform === 'linux') {
  // ...
}

powershell 是一个跨平台的命令行外壳和脚本语言,主要用于系统管理和自动化任务。

  • 优点:兼容性高,Windows7 及以上的版本都内置了
  • 缺点:性能差,我的 Windows 本执行基本需要 3s 左右

由此可知 wmicpowershell 各有优劣,所以我索性将其结合起来,性能和兼容性我都要!先尝试使用高性能的 wmic 去获取运行中进程,如果失败了再使用兼容性更高的 powershell 去获取。

C87B9E237F66FDE7C04E456B50A1C3FB.gif

对应的实现如下:

if (process.platform === 'win32') {
    try {
      output = child_process.execSync('wmic process where "executablepath is not null" get executablepath', { encoding: 'utf-8' });
    } catch (error) {
      output = child_process.execSync('powershell -NoProfile -Command "Get-CimInstance -Query \\"select executablepath from win32_process where executablepath is not null\\" | % { $_.ExecutablePath }"', { encoding: 'utf-8' });
    }
}

Windows 中对于中文字符的兼容

在大部分 Windows 系统中,node 进程默认不兼容中文字符集,因此无论是 react-dev-utils 还是 launch-editor 对于中文都会打印乱码。例如我的 webstorm 应用路径是 D:\chinese\中文\bin\webstorm64.exe,通过 child_process 获取的打印结果如下:

image.png

此时,如果拿着带乱码的 IDE 程序路径去启动应用程序,就会因找不到路径的应用程序而导致失败。

所以在 launch-ide 中,在执行获取运行中进程的命令之前,我先执行如下指令 child_process.execSync('chcp 65001') 将 node 终端的字符集切换至 utf-8,再去获取运行中进程就正常了:

image.png

启动应用程序的方式

react-dev-utilslaunch-editor 在识别到了用户所使用的 IDE 后,对于有自己的 command line tools 的 IDE 如 vscodeCursorAtom 等,会以 command line tools 的方式去打开。

还是以前面提到的 Cursor 为例,Cursor 可以选择安装 code 或者 cursor 两种 command line tools,在都安装的情况下:

child_process.execSync('/Applications/Cursor.app/Contents/MacOS/Cursor -g /Users/zlx/test.js:10:20')
// 等同于
child_process.execSync('code -g /Users/zlx/test.js:10:20')
// 等同于
child_process.execSync('cursor -g /Users/zlx/test.js:10:20')

command line tools 方式去打开 IDE 的缺点很明显,用户必须安装了 IDE 对应的 command line tools,否则会打开失败。所以 launch-ide 中全部以 IDE 的应用路径的方式去打开,就避免了这个问题。

对 vscode 和 Cursor 的混淆

前面提到 Cursor 可以选择安装 code 或者 cursor 两种 command line tools,而 vscode 的 command line tools 也是 code(Cursor 正是为了兼容 vscode 的用户所以提供了 code)。

因此在 react-dev-utilslaunch-editor 中,用户同时安装了 vscode 和 Cursor 的情况下,以 code 去打开 IDE 时,到底会打开哪个 IDE 可能就不如用户所愿了。而launch-ide 中全部以 IDE 的应用路径的方式去打开,也避免了 vscode 和 Cursor 的混淆的问题。

更多 IDE 的兼容

launch-ide 对于新流行的编辑器会比较迅速的支持,如 CursorClionZed 等,同时还支持了一些国产的 IDE 如 HBuilderX。在 IDE 的兼容范围方面,相比 react-dev-utilslaunch-editor 做得也更好。

总结

总结下来,我觉得 launch-ide 目前比 react-dev-utilslaunch-editor 更要优秀。这当然不是因为我的技术多强,而是因为 launch-ide 作为 code-inspector-plugin 的一大核心模块,而且目前我维护的开源项目没几个,所以会投入更多的精力在这上面。

当然,launch-ide 能够开源,首先要向 react-dev-utilslaunch-editor 致敬,因为 launch-ide 只是站在他们的肩膀上,做出了一些微不足道的改进。另外还要着重感谢 code-inspector-plugin 的用户们,在他们遇到问题时能够耐心的配合我进行问题定位,帮助 launch-ide 的质量提升。

最后,对于有需要 IDE 识别和打开功能的朋友,欢迎安装 launch-ide 进行体验!如果有帮助可以点个 star⭐️ 支持一下~