记录一下我自学开发vscode插件的过程。实现一个以颜色代码提示的方式,获取中国传统色的visual studio code扩展。
参考资料官方文档:code.visualstudio.com/api
官方提供的各类插件示例:github.com/microsoft/v…
需求我在写css时,经常会有颜色选择困难症,虽然vs code内置的插件提供了取色器,但在256^3的颜色中去选取,未必能找到符合期望的颜色。于是我想要是有个颜色提示插件就好了,只要我输入# + 颜色名,就能以代码提示的方式,将对应的颜色列出来供我选择。【推荐学习:《vscode入门教程》】
我在vs code插件市场搜了一圈,没找到类似的插件,最终决定自己写一个,本着学习的态度,我将学习过程记录下来。
这是最终效果:
演示是使用拼音,直接用汉字也是可行的。
在vs code插件市场搜索chinese colors或者“中国色”即可找到我写的这个插件。
仓库地址:taiyuuki/chinese-colors
获取颜色首先第一件事就是要有现成的颜色代码,很快我就找到了这个网站:中国色
这个网站提供了500多种颜色的rgb值以及hex值,打开浏览器控制台,输入colorsarray就能全部拿到,这就是我想要的。
从网站底部的信息来看,这个网站是山寨自日本色,颜色据称来自中科院科技情报编委会名词室编写、科学出版社1957年出版的《色谱》。
这个颜色来源是否可信我无从考证,随便百度一下“中国传统色”,就可以找到很多版本的所谓中国色,我在github上还找到了另一个接近2k star的中国色项目:中国传统颜色手册,这个网站使用的颜色与前者完全不同,是来自于一篇现在已经无法查看的新浪博客,颜色数量我没有统计,粗略估计在200以内。
初始化项目安装开发工具
npm i -g yo generator-code
新建项目
yo code
各项配置如下:
hello world初始项目中有个hello world,用vs code打开项目,然后按f5(或者点击“运行-->启动调试”)可以开启调试窗口。
然后在调试窗口下 ctrl + shift + p (或者点击“设置-->命令面板”),输入并选择 hello world 命令,就会在编辑器右下角弹出一个消息提示。
extension.ts是插件的入口文件:
import * as vscode from 'vscode'; // activate方法会在插件被激活时调用 export function activate(context: vscode.extensioncontext) { // 注册命令,第一个参数是命令名称,第二参数是回调 let disposable = vscode.commands.registercommand('chinese-colors.helloworld', () => { // 弹出消息提示 vscode.window.showinformationmessage('hello world from chinese colors!'); }); // 添加到插件上下文 context.subscriptions.push(disposable); } // deactivate方法会在插件失活时调用 export function deactivate() {}
package.json查看package.json,其中比较重要的两项:
{ "activationevents": [ "oncommand:chinese-colors.helloworld" ], "contributes": { "commands": [ { "command": "chinese-colors.helloworld", "title": "hello world" } ] }, }
activationevents是插件的激活配置,它是一个数组,每一项对应一个激活插件的条件,格式为“<类型>:<名称>”,oncommand是调用命令(也就是上面的输入hello world)。
contributes:一般翻译为贡献点,配置了一个“chinese-colors.helloworld”,与activationevents配置项对应。
其他packege.json配置见下表:
名称必要类型说明
name 是 string 插件名称,必须为小写且不能有空格。
version 是 string 插件版本
publisher 是 string 发布者
engines 是 object 一个至少包含vscode键值对的对象,该键表示的是本插件可兼容的vs code的版本,其值不能为*。比如 ^0.10.5 表示插件兼容vs code的最低版本是0.10.5。
license 否 string 授权。如果有授权文档license.md,可以把license值设为"see license in license.md"。
displayname 否 string 插件市场中显示的名字。
description 否 string 描述,说明本插件是什么以及做什么。
categories 否 string[] 插件类型:[languages, snippets, linters, themes, debuggers, other]
keywords 否 array 一组 关键字 或者 标记,方便在插件市场中查找。
gallerybanner 否 object 插件市场中横幅的样式。
preview 否 boolean 在市场中把本插件标记为预览版本。
main 否 string 插件的入口文件。
contributes 否 object 一个描述插件 贡献点 的对象。
activationevents 否 array 一组用于本插件的激活事件。
dependencies 否 object 生产环境node.js依赖项。
devdependencies 否 object 开发环境node.js依赖项。
extensiondependencies 否 array 一组本插件所需的其他插件的id值。格式 ${publisher}.${name}。比如:vscode.csharp。
scripts 否 object 和 npm的 scripts一样,但还有一些额外vs code特定字段。
icon 否 string 一个128x128像素图标的路径。
用户自定配置项颜色代码有多种表示方式,比较常用的是16进制(#ffffff)和rgb(255,255,255)这两种,因此我需要让用户自己选择采用哪种方式。
在contributes中有个configuration,允许用户对插件进行一些自定义配置。
package.json:
{ // ... "contributes": { "configuration": [{ "title": "color mode",// 配置项名称 "properties": { // 配置属性 "rgb": { "type": "boolean", // 属性值类型 "default": false, // 属性默认值 "description": "控制预设的中国色采用rgb格式" // 属性描述 } } }] }, }
这样就可以在扩展设置中进行一些自定义的设置:
我们可以通过workspace.getconfiguration()获取用户的配置。
import { workspace } from "vscode"; const configuration = workspace.getconfiguration(); const isrgb = configuration.rgb;
代码补全api代码补全api:
vscode.languages.registercompletionitemprovider(selector, provider, …triggercharacters)
该api的文档:code.visualstudio.com/api/referen…
该方法有三个参数:
参数description
selector: string/string[] 选择编程语言,比如python
provider 供应者配置对象
triggercharacters: string/string[] 触发字符, 比如 . 或 :
register completion item provider(注册完成件供应者),这个provider也是比较费解的一个词,直译是供应者,我猜:代码补全就相当于插件给我们供应了代码,所以叫provider。
provider是一个对象,要求必须包含一个叫providecompletionitems的方法,该方法需要返回一个数组,数组的每一项是一个completionitem对象,规定了代码提示和补全的规则。
官方示例完整示例:github.com/microsoft/v…
import * as vscode from 'vscode'; export function activate(context: vscode.extensioncontext) { // 注册供应者:languages.registercompletionitemprovider const provider2 = vscode.languages.registercompletionitemprovider( 'plaintext',// plaintext,表示对txt文档激活该插件 { // 实现providecompletionitems方法 // document的内容见下文,position为当前光标的位置 providecompletionitems(document: vscode.textdocument, position: vscode.position) { // 获取当前这行代码 const lineprefix = document.lineat(position).text.substr(0, position.character); // 如果这行代码不是以console.结尾,返回undefined,表示不会弹出代码提示 if (!lineprefix.endswith('console.')) { return undefined; } // 返回completionitem对象组成的数组,补全代码列表:log、warn、error // completionitem对象可以自己创建,也可以像下面这样new vscode.completionitem的方式创建 // vscode.completionitem()有两个参数: // 第一个是补全的代码,第二个是代码类型,用于控制显示在每一行提示前的图标 // vscode.completionitemkind.method表示该代码是一个方法 return [ new vscode.completionitem('log', vscode.completionitemkind.method), new vscode.completionitem('warn', vscode.completionitemkind.method), new vscode.completionitem('error', vscode.completionitemkind.method), ]; } }, '.' // 以.作为触发 ); context.subscriptions.push(provider2); }
providecompletionitems参数:
position:当前光标所处的位置。
document:用于获取、控制文档的内容或状态,这里列举几个常用的方法和属性:
方法:
getwordrangeatposition(position): range:获取指定位置单词的范围(起始位置)gettext(range):string:获取指定范围的文本lineat(position):string:获取指定位置的文本validateposition(position):position:获取鼠标停留的位置属性
linecount:总代码行数languageid:语言名称isclosed:当前文件是否关闭isdirty:当前文件的代码是否更改未保存completionitem对象completionitem对象可以通过new vscode.completionitem()的方式创建,但它默认只能补全代码,不能自定义替换,并不能满足我的需求,因此需要自己创建。
completionitem对象包含的属性:
属性说明
detail: string 语义化描述
documentation: string 语义化描述
filtertext: string 代码过滤。匹配输入的内容,没有设置时,使用label
inserttext: string 插入、补全的代码。没有设置时,使用label
label: string 默认的匹配代码、补全代码
kind 代码类型,控制显示代码提示前的图标
sorttext: string 排序文本,与sorttext匹配的提示代码会排在靠前的位置
textedit 对补全代码进行编辑,如果设置了textedit,inserttext会失效
kind的取值:
classcolorconstructorenumfieldfilefunctioninterfacekeywordmethodmodulepropertyreferencesnippettextunitvaluevariable简单的示例 import * as vscode from "vscode"; import { completionitemkind } from "vscode"; export function activate(context: vscode.extensioncontext) { const cc = vscode.languages.registercompletionitemprovider( "css", { providecompletionitems() { return [ { detail: '#66ccff', documentation: '天依蓝', kind: completionitemkind.color, filtertext: `#66ccff天依蓝`, label: '天依蓝', inserttext: '#66ccff' }, { detail: '#39c5bb', documentation: '初音绿', kind: completionitemkind.color, filtertext: `#39c5bb初音绿`, label: '初音绿', inserttext: '#39c5bb' } ]; }, }, "#" ); context.subscriptions.push(cc); } export function deactivate() {}
记得要在package.json里配置激活:
"activationevents": [ "onlanguage:css" ]
中国色插件package.json关键配置:
{ "activationevents": [ "onlanguage:css", "onlanguage:scss", "onlanguage:sass", "onlanguage:less", "onlanguage:stylus", "onlanguage:html", "onlanguage:xml", "onlanguage:json", "onlanguage:javascript", "onlanguage:typescript", "onlanguage:javascriptreact", "onlanguage:typescriptreact", "onlanguage:vue", "onlanguage:vue-html" ], "contributes": { "configuration": [{ "title": "chinese colors", "properties": { "rgb": { "type": "boolean", "default": false, "description": "控制预设的中国色采用rgb格式" } } }] }, }
颜色列表colors.ts:
// 声明color类型 export type color = { rgb: number[]; hex: string; name: string; phonics: string; }; // 这里只列两个颜色 export const colors: color[] = [ { rgb: [92, 34, 35], hex: "#5c2223", name: "暗玉紫", phonics: "anyuzi", }, { rgb: [238, 162, 164], hex: "#eea2a4", name: "牡丹粉红", phonics: "mudanfenhong", }, // ... ]
extensions.ts
import * as vscode from "vscode"; import { workspace, completionitemkind } from "vscode"; import { colors, color } from "./colors"; const isrgb = workspace.getconfiguration().rgb; export function activate(context: vscode.extensioncontext) { const cc = vscode.languages.registercompletionitemprovider( [ "css", "scss", "sass", "less", "stylus", "html", "xml", "json", "javascript", "typescript", "javascriptreact", "typescriptreact", "vue", "vue-html", ],// activationevents { providecompletionitems() { const list = [] as completionitemkind[]; colors.foreach((color: color) => { list.push({ detail: isrgb ? rgb : hex, documentation: color.name, kind: completionitemkind.color, filtertext: "#" + color.name + color.phonics, label: color.name, inserttext: isrgb ? rgb : hex, }); }); return list; }, }, "#" ); context.subscriptions.push(cc); } export function deactivate() {}
如此,代码补全的功能已经基本实现,实际开发时,为了便于维护,需要将这部分逻辑抽离出来。
颜色预览接下来,需要实现颜色的预览,虽然vs code内置的插件已经实现了这项功能,但我的需求是:不仅能预览颜色,还得显示颜色名称。
api实现颜色预览需要用到装饰效果,涉及以下这些api:
window.createtexteditordecorationtype(options):创建装饰效果的类型
window.activetexteditor.setdecorations(decorationtype, decorations):添加装饰效果至文档
window.ondidchangeactivetexteditor:文档内容变化事件
workspace.ondidchangetextdocument:切换文档事件
官方示例首先来看一下官方提供的示例片段
完整实例: github.com/microsoft/v…
import * as vscode from 'vscode';// 插件激活时调用export function activate(context: vscode.extensioncontext) { console.log('decorator sample is activated'); let timeout: nodejs.timer | undefined = undefined; // 为small numbers创建装饰效果类型 const smallnumberdecorationtype = vscode.window.createtexteditordecorationtype({ // 以下是装饰效果的样式 borderwidth: '1px', borderstyle: 'solid', overviewrulercolor: 'blue', overviewrulerlane: vscode.overviewrulerlane.right, light: { // 亮色主题下的边框颜色 bordercolor: 'darkblue' }, dark: { // 暗色主题下的边框颜色 bordercolor: 'lightblue' } }); // 为large numbers创建装饰效果类型 const largenumberdecorationtype = vscode.window.createtexteditordecorationtype({ cursor: 'crosshair', // 设置装饰的背景颜色, 在package.json中可以配置该名称对应的颜色 backgroundcolor: { id: 'myextension.largenumberbackground' } }); // activeeditor是当前活跃(展示)的文档编辑器实例 let activeeditor = vscode.window.activetexteditor; // updatedecorations方法,在每次文档被更新或切换文档时调用。 function updatedecorations() { if (!activeeditor) { return; } // 匹配数字的正则 const regex = /\d+/g; // 获取文档的文本 const text = activeeditor.document.gettext(); // 装饰效果数组,用于归集每一个decoration对象 const smallnumbers: vscode.decorationoptions[] = []; const largenumbers: vscode.decorationoptions[] = []; let match; while ((match = regex.exec(text))) { // 获取匹配结果的起始位置 const startpos = activeeditor.document.positionat(match.index);// 开始位置 const endpos = activeeditor.document.positionat(match.index + match[0].length);// 结束位置 // decoration对象 const decoration = { // 装饰效果的位置 range: new vscode.range(startpos, endpos), // 鼠标悬停(hover)的提示信息 hovermessage: 'number **' + match[0] + '**' }; // 将符合的结果归集 if (match[0].length < 3) { smallnumbers.push(decoration); } else { largenumbers.push(decoration); } } // 添加装饰效果 activeeditor.setdecorations(smallnumberdecorationtype, smallnumbers); activeeditor.setdecorations(largenumberdecorationtype, largenumbers); } // 给方法节流 function triggerupdatedecorations(throttle = false) { if (timeout) { cleartimeout(timeout); timeout = undefined; } if (throttle) { timeout = settimeout(updatedecorations, 500); } else { updatedecorations(); } } // 打开文档时调用一次 if (activeeditor) { triggerupdatedecorations(); } // 切换文档时调用 vscode.window.ondidchangeactivetexteditor(editor => { // 这一步赋值是必须的,确保activeeditor是当前打开的文档编辑器实例 activeeditor = editor; if (editor) { triggerupdatedecorations(); } }, null, context.subscriptions); // 文档内容发送改变时调用 vscode.workspace.ondidchangetextdocument(event => { if (activeeditor && event.document === activeeditor.document) { triggerupdatedecorations(true); } }, null, context.subscriptions);}
效果如下:
decorationtypedecorationtype是通过window.createtexteditordecorationtype(options)创建的对象,它主要用来设置装饰效果的样式,其实就是css样式,比如border、color、backgroundcolor等等。
如果要在匹配结果之前或之后添加装饰,可以添加before/after字段进行设置,还可以分别给dark、light模式配置不同的样式。
const decorationtype = window.createtexteditordecorationtype({ // 在匹配位置之前添加装饰效果: before: { color: '#eee', backgroundcolor: '#fff', width: 'fit-content' }})
由于该方法支持的样式字段有限,有些样式(比如line-height)无法在options里直接添加,但我们可以在任意字段后添加分号,将这些样式写在后面,比如:
const decorationtype = window.createtexteditordecorationtype({ // 在匹配位置之后添加装饰效果: after: { color: '#333', backgroundcolor: '#fff', width: 'fit-content', height: '0.8em', // fontsize: '0.6em', 这么设置是无效的,因为并不支持fontsize字段, // 但我们可以将其添加在任意字段后面 fontstyle: 'normal;font-size:0.6em;line-height:0.8em' }})
具体支持哪些字段,可以查看此api的官方文档:
vs code api | visual studio code extension api
decoration对象decoration对象有三个属性:
range:装饰效果的位置,range对象可以通过new vscode.range(start, end)创建
hovermessage:鼠标悬停时的提示信息
renderoptions:和decorationtype类似,可以单独对每一个装饰效果设置样式。但只支持before、after、dark、light四个字段,也就是说,无法再对匹配的内容本身设置样式。
示例由于实现的代码比较长,和上述官方示例其实差不多,这里就不再贴出来了,感兴趣的可以我去文章开头的仓库地址查看。
值得一提的是,为了颜色的名称在不同的颜色背景下都能清晰的显现,我这里用到了一个计算对比色的方法,贴出来供参考:
// 通过hex值计算应该使用的字体颜色function getcontrastcolor(hexcolor: string) { const r = parseint(hexcolor.substring(1, 2), 16) const g = parseint(hexcolor.substring(3, 4), 16) const b = parseint(hexcolor.substring(5, 6), 16) const yiq = (r * 299 + g * 587 + b * 114) / 1000 return yiq >= 8 ? 'black' : 'white'}
插件打包打包命令:
vsce package
如果打包失败,可能的原因:
packege.json缺少上文表格中必要的配置项vs code版本与packege.json里设置的版本不兼容根目录下缺少readme.md文件根目录下缺少license.md文件更多关于vscode的相关知识,请访问:vscode教程!
以上就是带你开发一个提示颜色代码的vs code插件的详细内容。