VSCode插件开发全攻略(六)开发调试技巧
本文由 小茗同学 发表于 2018-10-07 浏览(15293)
最后修改 2018-10-14 标签:vscode 插件 开发 plugin

更多文章请戳VSCode插件开发全攻略系列目录导航

前言

在介绍完一些比较简单的内容点之后,我觉得有必要先和大家介绍一些开发中遇到的一些细节问题以及技巧,特别是后面一章节将要介绍WebView的知识,这个坑会比较多,避免大家走弯路。

开发方式

最理想的方式是准备双显示器,一个写代码,一个运行插件,实践证明这种方式开发效率会提升很多,每次修改完代码之后直接Ctrl+R重新加载即可,非常方便。

日志查看

就我目前遇到的情况来看,vscode日志主要有这5种:

旧窗口的调试控制台

扩展里的console.log()日志一般输出在这里,但是有很大的限制,结构稍微深一点的对象在这里了就显示不了:

Unable to log remote console arguments Output omitted for an object that cannot be inspected (Error: [sxei.vscode-plugin-demo]: Proposed API is only available when running out of dev or with the following command line switch: —enable-proposed-api sxei.vscode-plugin-demo)

这里只能看成是新窗口开发者控制台日志的一种快捷显示,以下是旧窗口调试控制台显示的内容:

而对应的内容在新窗口的开发者控制台显示如下:

可以看到,结构较深的对象即使在控制台也无法显示,目前发现的唯一比较好的方法就是在输出的地方打一个断点,然后运行的时候会自动卡在这里,鼠标悬停就可以查看对象的内容。

新窗口的调试控制台

一般没什么扩展相关日志会输出在这里。

旧窗口的开发者控制台

快捷键Ctrl+Alt+I,这里一般显示vscode本身一些日志,和扩展相关的不会显示在这里,所以这个也不用太多关心。

新窗口的开发者控制台

快捷键也是Ctrl+Alt+I,不记得的可以从帮助 -> 切换开发人员工具找到。这个控制台很重要,有时候如果发现你的代码莫名其妙没生效,很有可能是报错了,这种报错是不会显示在旧窗口调试控制台的,如果你不知道到这里来查看日志,那么你只能一脸懵逼的到处乱试了,调试控制台只打印常规日志,语法错误并不会显示在这里。

例如,我在跳转定义实现前人为制造一个错误:

function provideDefinition(document, position, token) {
	console.log(aaf);
	const fileName	= document.fileName;
	// 省略其它代码
}

运行后就会发现点击跳转不生效,但是也没有什么报错提示,此时只能打开控制台查看才能发现问题:

WebView控制台

WebView我们会在下一章节介绍,这里先提一下。Webview的控制台比较特殊,需要特殊的命令才能打开,按下Ctrl+Shift+P然后执行打开Webview开发工具,英文版应该是Open Webview Developer Tools

开发时我们把它当成一个普通的网页来看就好了。

调试

vscode插件的调试非常简单方便,只需要在需要调试的地方打个断点,然后按F5执行即可:

几个调试快捷键:

  • F5运行
  • Ctrl+F2停止运行
  • F6下一步跳过(类似于Chrome的F10
  • F5下一步跳入
  • F8跳过

如何快速找到我想找的内容

刚开始只能先大概对整个vscode的api有一个大概了解,了解了之后就大概清楚一般什么功能会怎么实现,该去什么地方找,所有的vscode的api都可以在vscode.d.ts文件里面找到:

不得不佩服,正规大型项目的注释写的真的不是一般的详细,官网的API文档肯定也是基于这个自动生成的,反正把这个ts文件吃透了,基本上你想实现什么功能要怎么实现都了如指掌了。

查看插件存放目录

插件安装后根据操作系统不同会放在如下目录:

  • Windows系统:%USERPROFILE%\.vscode\extensions
  • Mac/Linux:~/.vscode/extensions

想要学习查看其它插件的代码可以找到这个目录:

一些个人经验分享

调试控制台日志不可靠

vscode有一个很坑爹的地方,这里特别要注意,当require一个function进来并打印输出时,虽然打印在控制台显示为null,但其实是有值的,不知道的人很容易被误导,直接就是被这个现象骗了很久,切记切记

test-require-function.js:

function testRequireFunction(a, b) {
	console.log('进入testRequireFunction方法');
	console.log(a, b);
}
module.exports = testRequireFunction;

extension.js

exports.activate = function(context) {
	const testFn = require('./test-require-function');
	console.log(testFn); // vscode的日志输出不可靠,这里竟然会打印null?!
	testFn(1, 2); // 1, 2
};

输出结果:

null
进入testRequireFunction方法
1 2

代码为什么没生效

代码没生效一般从这几个地方去查找:

  • activationEvents里面添加了吗?开发的时候如果老是忘记可以直接设置成*
  • 代码是不是报错了?如前文所说,很多错误是不会暴露出来的,需要手动打开控制台查看;
  • 代码是不是忘记引入了?有时候拆分多个文件之后可能忘了引入;
  • 逻辑是不是写错了?最好的办法就是debug,这是找问题最快的方法;
  • 版本冲突

这里重点说一下最后面的版本冲突,这个甚至可以说是vscode本身的一些bug,经常发现代码莫名其妙地没生效,怎么调试都不对,后来发现运行的根本就不是我们正在开发的那个版本,特别是当你的插件已经发了一版到应用市场并安装后,本地再按F5运行,理论上说debug运行的会覆盖已安装的,但有时候还是会出现异常情况,所以为了以防万一,当出现这种情况时可以先把已经安装的给卸载。

还有一个问题就是,有时候明明安装了版本更加新的那个,结果运行的却是旧的,打开扩展目录会发现很多并存的同名不同版本插件,或者可能先是通过vsix方式安装了一个版本,然后又从应用市场安装一个,总之解决这类问题最好的方法就是:先卸载再安装,实在不行手动去插件目录删除之!

打开文件

打开文件是vscode.window.showTextDocument而不是vscode.workspace.openTextDocument,这个根据字面意思很容易搞错,原来老外也有命名不准确的时候啊,哈哈。

  • vscode.workspace.openTextDocument仅仅是加载文档并返回一个TextDocument对象,但是并不在vscode中打开;
  • vscode.window.showTextDocument则是在vscode中打开一个文档;

其实:

vscode.workspace.openTextDocument('someFilePath').then(document => {
	vscode.window.showTextDocument(document, editor => {
		// 可以操作文档的editor对象
	});
})

等价于:

vscode.window.showTextDocument(vscode.Uri.file('someFilePath'), editor => {
	// 可以操作文档的editor对象
});

工程根目录的获取

被这个问题踩过很多次坑,所有重点介绍一下。

有的人的vscode工作空间是这样的,每一个工程一个个地单独拖入:

也有的人是直接用打开文件夹的方式把存放代码的父文件夹给打开:

但是如果此时你点击将工作区另存为保存了工作区之后就变成这样了(请注意图标的变化):

所以,即便拿到了某个文件的完整路径也不好获取这个文件的工程路径,因为不知道工作区的这个文件夹名字是你的工程名还是存放工程的父文件夹的名字。

已知:

  • vscode以前有一个vscode.workspace.rootPath,由于后来vscode支持multipleRoot模式,所以这个字段已经过时作废了。
  • vscode.workspace.workspaceFolders可以获取当前工作区所有根文件夹数组;

之前我写了一个简单粗暴的获取工程目录方式:

/**
 * 获取当前所在工程根目录,有3种使用方法:<br>
 * getProjectPath(uri) uri 表示工程内某个文件的路径<br>
 * getProjectPath(document) document 表示当前被打开的文件document对象<br>
 * getProjectPath() 会自动从 activeTextEditor 拿document对象,如果没有拿到则报错
 * @param {*} document 
 */
getProjectPath(document) {
	if (!document) {
		document = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.document : null;
	}
	if (!document) {
		this.showError('当前激活的编辑器不是文件或者没有文件被打开!');
		return '';
	}
	const currentFile = (document.uri ? document.uri : document).fsPath;
	let projectPath = null;

	let workspaceFolders = vscode.workspace.workspaceFolders.map(item => item.uri.path);
	// 由于存在Multi-root工作区,暂时没有特别好的判断方法,先这样粗暴判断
	// 如果发现只有一个根文件夹,读取其子文件夹作为 workspaceFolders
	if (workspaceFolders.length == 1 && workspaceFolders[0] === vscode.workspace.rootPath) {
		const rootPath = workspaceFolders[0];
		var files = fs.readdirSync(rootPath);
		workspaceFolders = files.filter(name => !/^\./g.test(name)).map(name => path.resolve(rootPath, name));
		// vscode.workspace.rootPath会不准确,且已过时
		// return vscode.workspace.rootPath + '/' + this._getProjectName(vscode, document);
	}
	workspaceFolders.forEach(folder => {
		if (currentFile.indexOf(folder) === 0) {
			projectPath = folder;
		}
	})
	if (!projectPath) {
		this.showError('获取工程根路径异常!');
		return '';
	}
	return projectPath;
},

这种方式生效的前提是,如果是按照第一种方式存放工作空间的,工程的数目必须大于等于2,但是这种判断方式不用说肯定会不准确。

后来换成了另外一种方式,考虑到工作接触到的项目无论是node端还是前端都会有package.json文件在根目录,所以就根据哪个文件夹有这个文件来判断,也只能是这样了。