安利介绍
在使用Vscode编辑器摸鱼时,难免会遇到一些功能,标准版是没有的,但是有关系嘛,没有关系。
我们可以利用Vscode强大的插件功能,去扩展自己定制的功能嘛,只要思想不滑坡,困难总比办法多。利用vscode插件我们大概可以做以下功能。
- **主题:**界面和文本主题色、图标样式。
- 通用功能:添加自定义命令,添加右键菜单,获取文本内容,添加自定义快捷键,自定义代码片段等。
- 工作区扩展:活动栏项目,自定义提示,添加左侧菜单栏功能,打开webview等。
- ....
开始安装
可以下载官方的脚手架进行快速开发:Github地址 最重要的两个文件为以下两个
package.json部分关键内容如下:
// 扩展的激活事件
"activationEvents": [
"onCommand:extension.sayHello"
],
// 入口文件
"main": "./src/extension",
// 贡献点,vscode插件大部分功能配置都在这里
"contributes": {
// 代码片段
"snippets": [
{
"language": "javascript",
"path": "./snippets/snippets.json"
},
],
// 自定义命令
"commands": [
{
"command": "template-code.clearCD",
"title": "🤓清空console/debugger"
},
],
// 自定义快捷键
"keybindings": [
{
"command": "FolderView.add",
"key": "ctrl+q",
"mac": "ctrl+q",
"when": "editorTextFocus"
},
{
"command": "template-code.clearCD",
"key": "ctrl+d",
"mac": "ctrl+d",
"when": "editorTextFocus"
}
],
}
}
extension.js 主要为添加的命令进行激活,在package.json里配置了命令还不行,必须要激活后才会触发。
const vscode = require('vscode');
/**
* 插件被激活时触发,所有代码总入口
* @param {*} context 插件上下文
*/
exports.activate = function(context) {
console.log('恭喜,您的扩展已被激活!');
// 注册命令
let disposableCreateFile = vscode.commands.registerCommand("template-code.createFilePath", async (uri) => {
createTemplateFile(uri);
});
context.subscriptions.push(disposableCreateFile);
};
/**
* 插件被释放时触发
*/
exports.deactivate = function() {
console.log('您的扩展已被释放!')
};
实战编码篇
看下效果
- 首先按下@review 会出现设置好的代码片段。
- 在reviewType.时会触发三个类型,Bug 、Perf(更好的)Formate(格式问题)
- 选择不同的类型会触发不同的颜色
- 高亮review人的名字跟review内容
- 跟左侧工作区面板的review列表做联动效果
设置代码片段
在插件里自定义代码片段,首先需要在package.json里定义一个代码片段的字段,指定代码片段的文件地址
"snippets": [
{
"language": "javascript",
"path": "./snippets/snippets.json"
},
{
"language": "typescriptreact",
"path": "./snippets/snippets.json"
},
{
"language": "typescript",
"path": "./snippets/snippets.json"
}
],
- language代表在哪种语言生效
- path代表存放的路径
snippets.json
{
"@review": {
"prefix": "@review",
"body": ["/** $CURRENT_YEAR年/$CURRENT_MONTH_NAME/$CURRENT_DATE日/$CURRENT_DAY_NAME", "*@reviewType.Perf", "*@reviewContent By Name", "1.","*/"],
"description": "review template"
}
}
这个应该一目了然。
自定义触发列表
在前缀为reviewType. 时会触发三个自定义列表。这个需要vscode以下api
- vscode.languages.registerCompletionItemProvider()
- 官方文档介绍registerCompletionItemProvider
大致的意思在指定的文件类型中,要想触发前缀,可以通过 new vscode.CompletionItem()来为这个语言提供指定的item。
const vscode = require("vscode");
const Decoration = require("./decoration");
/**
* 自动提示实现,
* @param {*} document
* @param {*} position
* @param {*} context
*/
function provideCompletionItems(document, position, context) {
const line = document.lineAt(position);
// 只截取到光标位置为止,防止一些特殊情况
const lineText = line.text.substring(0, position.character);
if (/.*@reviewType\.$/g.test(lineText)) {
console.log("字符串注册", lineText);
//const json = require(`${projectPath}/package.json`);
const dependencies = ["Perf", "Format", "Bug"]; //Object.keys(json.dependencies || {}).concat(Object.keys(json.devDependencies || {}));
return dependencies.map((dep) => {
// vscode.CompletionItemKind 表示提示的类型
console.log(dep);
let Com = new vscode.CompletionItem(dep, vscode.CompletionItemKind.Field);
Com.command = {
command: "review.click.item",
title: "review Type",
};
return Com;
});
}
}
/**
* 光标选中当前自动补全item时触发动作,一般情况下无需处理
* @param {*} item
* @param {*} token
*/
function resolveCompletionItem(item, token) {
return null;
}
const TYPES = ["vue", "css", "less", "scss", "sass", "stylus", "javascript", "javascriptreact", "typescriptreact", "typescript"];
module.exports = function (context) {
// 注册代码建议提示,只有当按下“.”时才触发
new Decoration();
TYPES.forEach((el) => {
let providerDisposable = vscode.languages.registerCompletionItemProvider(
{
scheme: "file",
language: el,
},
{
provideCompletionItems,
resolveCompletionItem,
},
"."
);
context.subscriptions.push(providerDisposable);
});
};
注册完成后就可以实现前缀为 reviewType.时 触发三个自定义的itme啦
实现文字背景高亮
- 准备每一个类型对应的颜色数据
- 获取当前编辑器的文本内容
- 通过正则找到符合要求的文本区域
- 拿到符合要求的文本位置信息
- 将所有文本的位置信息给他加上背景颜色。
1.首先定一个包含颜色类型的对象,这里可以扩展出去给到用户自己去设置。
/**
* 每一个主题的所对应的颜色
*/
const DecorationTypes = {
Perf: {
border: "1px",
borderColor: "#08AEEA",
backgroundColor: "#08AEEA",
color: "white",
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
},
Bug: {
border: "1px",
borderColor: "#F76B1C",
backgroundColor: "#F76B1C",
color: "white",
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
},
Format: {
border: "1px",
borderColor: "#FBAB7E",
backgroundColor: "#ffcc00",
color: "#1f1f1f",
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed,
},
};
2.获取当前编辑里的文本内容:
// 获取当前文档的全部信息
const editor = window.activeTextEditor // 当前活动的编辑器
const doc = editor.document;
const text = doc.getText()
text就是当前编辑的内容。
3.匹配对应代码片段里的正则为
this.regType = [/(?<=\*@reviewType)\.(Perf|Bug|Format)?/g, /(?<=\*@reviewContent(\s\S)?)([\S\s]*?)(?=\*\/)/g];
数组的第一项为寻找 类型的正则,数组的第二项为找内容的正则
通过while循环找到每一个符合要求的文本信息,并记录行和列的信息,方便拿到位置信息设置背景
// 根据正则指定的内容
findEditeContent() {
// 获取当前文档的全部信息
let doc = this.editor.document;
let text = doc.getText();
let match;
let empty = true;
this.regType.forEach((reg, index) => {
let type = "Perf"; // 默认类型
while ((match = reg.exec(text))) {
// 获取数字开始和结束的位置
const startPos = doc.positionAt(match.index + 1);
let endLength = match.index + match[0].length;
empty = false;
const endPos = doc.positionAt(endLength);
const line = new Range(startPos, endPos);
if (index === 0) type = doc.getText(line);
const decoration = {
range: line,
hoverMessage: "主题颜色" + type,
};
if (type) {
const review = {
type: type,
decoration,
};
this.reviewDecoration.push(review);
}
}
});
if (empty) this.reviewDecoration = [];
console.log("收集的review", this.reviewDecoration);
}
得到的review数组里的内容:
主要是拿到range里的信息,才知道该给哪个区域里加背景颜色。
最后就是根据所有的位置信息,去设置背景颜色,需要用到vscode以下api:
TexTeditor就是上面说的 window.activeTextEditor, 第一个参数需要 vscode.TextEditorDecorationType 类型,那么在上面push到reviewDecoration数组的时候 需要通过 一个循环去遍历一次,为每个item添加decorationType属性,并生成新的一个数组专门用来存放背景的decorationList。
for (let index = 0; index < this.reviewDecoration.length; index ++) {
try {
const element = this.reviewDecoration[index];
const next = this.reviewDecoration[index + 1];
const tempObj = {
type: element.type,
decoration: [element.decoration, next.decoration],
decorationType: window.createTextEditorDecorationType(DecorationTypes[element.type]),
};
this.decorationList.push(tempObj.decorationType);
tempArray.push(tempObj);
} catch (error) {
console.log(error,'错误');
}
}
这里用新的数组decorationList来存放位置信息是因为,每一次文本改动的时候都会去重新找一次,那么就需要在找之前,将上一次设置的背景,给清除掉,这点很重要,不然会导致一个文字区域有多层的背景。
clearDecorationToData() {
this.reviewDecoration = [];
this.decorationList.forEach((el) => {
this.editor.setDecorations(el, []);
});
this.decorationList = [];
}
- 清空之前的背景,需要设置一个空数组来完成。
最后在找完之后遍历每一项进行背景的设置。
setDecorationToData() {
let tempArray = this.ConversionData();
tempArray.forEach((el) => {
this.editor.setDecorations(el.decorationType, el.decoration);
});
this.updateTreeReview(tempArray);
}
这样根据代码片段高亮背景颜色就完成了,还有一个需要跟左侧扩展区联动的效果,篇幅很长,下篇在讲。 后续还扩展了四个实用的小功能,都是对个人或者前端团队适用的,年末了准备晋升的可以拿去用用
总结
- 设置代码片段的需要在package.json里配置
- window.activeTextEditor为当前编辑器的对象,可以拿到跟当前文本的所有信息,比如获取选中内容,清空内容等
- window.createTextEditorDecorationType()创建文本类型装饰器
- editor.setDecorations()给指定的文本rang范围设置装饰器 第二篇地址