VSCode插件开发流程兼【代码注释阅读小说】插件发布
VSCode插件开发流程兼【代码注释阅读小说】插件发布一、前言二、需求三、成果演示四、插件开发流程1、安装环境2、运行生成器3、文件介绍package.json 插件配置extension.js 插件程序入口4、调试5、本地打包和安装6、上线发布五、附【代码注释阅读小说】核心代码六、参考资料一、前言很早以前就想写vscode相关的插件,最近有时间于是研究一下。二、需求定一个需求:编辑器里面可以用代
VSCode插件开发流程兼【代码注释阅读小说】插件发布
一、前言
很早以前就想写vscode相关的插件,最近有时间于是研究一下。
二、需求
定一个需求:编辑器里面可以用代码注释的形式看小说,达到写代码,看小说两不耽误,老板还会夸你认真工作呢,这是摸鱼的最高境界!
三、成果演示
插件地址:https://marketplace.visualstudio.com/items?itemName=DaHuaZhuXi.read-novel
或者在 vscode 插件栏搜索“小说”
,就能找到 ReadNovel 插件了。
代码注释阅读小说插件:读取txt小说文件,并在当前编辑器的文档第一行使用代码注释的方式插入小说文字,用于阅读。
四、插件开发流程
1、安装环境
npm install -g yo generator-code
2、运行生成器
执行 yo code
,按提示填写内容,然后会初始化一个最简单的demo案例
? What type of extension do you want to create? New Extension (JavaScript) //选择语言
? What's the name of your extension? helloworld //插件名称
? What's the identifier of your extension? helloworld //插件标识符,只能小写
? What's the description of your extension? a simple demo //插件描述
? Enable JavaScript type checking in 'jsconfig.json'? Yes //js类型检测
? Initialize a git repository? No //初始化git存储库
? Which package manager to use? npm //包管理器
3、文件介绍
生成插件目录后,需要注意的几个文件有:
package.json // 插件配置
extension.js // 插件程序入口
README.md // 插件描述文件
CHANGELOG.md // 版本历史描述
package.json 插件配置
{
// 插件的名字,应全部小写,不能有空格
"name": "vscode-plugin-demo",
// 插件的友好显示名称,用于显示在应用市场,支持中文
"displayName": "VSCode插件demo",
// 描述
"description": "VSCode插件demo集锦",
// 关键字,用于应用市场搜索
"keywords": ["vscode", "plugin", "demo"],
// 版本号
"version": "1.0.0",
// 发布者,如果要发布到应用市场的话,这个名字必须与发布者一致
"publisher": "sxei",
// 表示插件最低支持的vscode版本
"engines": {
"vscode": "^1.27.0"
},
// 插件应用市场分类,可选值: [Programming Languages, Snippets, Linters, Themes, Debuggers, Formatters, Keymaps, SCM Providers, Other, Extension Packs, Language Packs]
"categories": [
"Other"
],
// 插件图标,至少128x128像素
"icon": "images/icon.png",
// 扩展的激活事件数组,可以被哪些事件激活扩展,后文有详细介绍
"activationEvents": [
"onCommand:extension.sayHello"
],
// 插件的主入口
"main": "./src/extension",
// 贡献点,整个插件最重要最多的配置项
"contributes": {
// 插件配置项
"configuration": {
"type": "object",
// 配置项标题,会显示在vscode的设置页
"title": "vscode-plugin-demo",
"properties": {
// 这里我随便写了2个设置,配置你的昵称
"vscodePluginDemo.yourName": {
"type": "string",
"default": "guest",
"description": "你的名字"
},
// 是否在启动时显示提示
"vscodePluginDemo.showTip": {
"type": "boolean",
"default": true,
"description": "是否在每次启动时显示欢迎提示!"
}
}
},
// 命令
"commands": [
{
"command": "extension.sayHello",
"title": "Hello World"
}
],
// 快捷键绑定
"keybindings": [
{
"command": "extension.sayHello",
"key": "ctrl+f10",
"mac": "cmd+f10",
"when": "editorTextFocus"
}
],
// 菜单
"menus": {
// 编辑器右键菜单
"editor/context": [
{
// 表示只有编辑器具有焦点时才会在菜单中出现
"when": "editorFocus",
"command": "extension.sayHello",
// navigation是一个永远置顶的分组,后面的@6是人工进行组内排序
"group": "navigation@6"
},
{
"when": "editorFocus",
"command": "extension.demo.getCurrentFilePath",
"group": "navigation@5"
},
{
// 只有编辑器具有焦点,并且打开的是JS文件才会出现
"when": "editorFocus && resourceLangId == javascript",
"command": "extension.demo.testMenuShow",
"group": "z_commands"
},
{
"command": "extension.demo.openWebview",
"group": "navigation"
}
],
// 编辑器右上角图标,不配置图片就显示文字
"editor/title": [
{
"when": "editorFocus && resourceLangId == javascript",
"command": "extension.demo.testMenuShow",
"group": "navigation"
}
],
// 编辑器标题右键菜单
"editor/title/context": [
{
"when": "resourceLangId == javascript",
"command": "extension.demo.testMenuShow",
"group": "navigation"
}
],
// 资源管理器右键菜单
"explorer/context": [
{
"command": "extension.demo.getCurrentFilePath",
"group": "navigation"
},
{
"command": "extension.demo.openWebview",
"group": "navigation"
}
]
},
// 代码片段
"snippets": [
{
"language": "javascript",
"path": "./snippets/javascript.json"
},
{
"language": "html",
"path": "./snippets/html.json"
}
],
// 自定义新的activitybar图标,也就是左侧侧边栏大的图标
"viewsContainers": {
"activitybar": [
{
"id": "beautifulGirl",
"title": "美女",
"icon": "images/beautifulGirl.svg"
}
]
},
// 自定义侧边栏内view的实现
"views": {
// 和 viewsContainers 的id对应
"beautifulGirl": [
{
"id": "beautifulGirl1",
"name": "国内美女"
},
{
"id": "beautifulGirl2",
"name": "国外美女"
},
{
"id": "beautifulGirl3",
"name": "人妖"
}
]
},
// 图标主题
"iconThemes": [
{
"id": "testIconTheme",
"label": "测试图标主题",
"path": "./theme/icon-theme.json"
}
]
},
// 同 npm scripts
"scripts": {
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "node ./node_modules/vscode/bin/test"
},
// 开发依赖
"devDependencies": {
"typescript": "^2.6.1",
"vscode": "^1.1.6",
"eslint": "^4.11.0",
"@types/node": "^7.0.43",
"@types/mocha": "^2.2.42"
},
// 后面这几个应该不用介绍了
"license": "SEE LICENSE IN LICENSE.txt",
"bugs": {
"url": "https://github.com/sxei/vscode-plugin-demo/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/sxei/vscode-plugin-demo"
},
// 主页
"homepage": "https://github.com/sxei/vscode-plugin-demo/blob/master/README.md"
}
extension.js 插件程序入口
这里只介绍常用的一些方法,具体API请看官网:https://code.visualstudio.com/api/references/vscode-api
// 引入 vscode 模块
const vscode = require('vscode');
/**
* @description:扩展被激活时执行,只会执行一次
* @param {vscode.ExtensionContext} context
*/
function activate(context) {
//注册事件,此事件为:变更当前编辑文档
vscode.window.onDidChangeActiveTextEditor(() => {
vscode.window.showInformationMessage('变更当前编辑文档');
})
//注册命令helloWorld,命令激活时执行代码(命令要和package.json里面的对应,可以注册多个命令)
let helloWorld = vscode.commands.registerCommand('helloworld.helloWorld', function () {
// vscode的消息框
vscode.window.showInformationMessage('普通消息');
vscode.window.showWarningMessage('警告消息');
});
//注册命令showError
let showError = vscode.commands.registerCommand('helloworld.showError', function () {
vscode.window.showErrorMessage('错误消息');
});
//订阅命令
context.subscriptions.push(helloWorld);
context.subscriptions.push(showError);
}
exports.activate = activate;
// 扩展停用时执行(例如关闭vscode)
function deactivate() {
console.log("扩展停用");
}
module.exports = {
activate,
deactivate
}
4、调试
按F5
,就能进入调试模式,此时会新开一个vscode窗口,按 ctrl + shift + i
会打chrome的开发者工具,进行更详细的调试。
ps:主编辑器的文件必须以根目录形式打开插件目录,否则按F5无法打开调试窗口
此时在“ 扩展编辑器”上按 ctrl + shift + p
,输入Hello World
则会弹出提示窗口
5、本地打包和安装
- 安装打包工具
npm i vsce -g
- 执行打包
vsce package
执行后会在根目录生成 helloworld.vsix 文件 - 本地安装
本地安装只能在插件菜单中选择“从VSIX安装…”
6、上线发布
除了本地安装的方式,也可以把你的插件发布到vscode平台,让全世界所有人都可以使用你的插件。
如果你有GitHub账号,可以直接进入第2步,用GitHub账号登录,它会同时创建一个默认组织
-
微软账号注册:https://login.live.com/
-
创建令牌:并记下令牌
-
创建开发者
回到命令行,输入vsce create-publisher
,填入刚才生成的token。
注意:publisher名称要和package.json里的publisher一致!
-
发布插件
vsce publish
发布插件后会提供一个插件地址,要过一段时间才能看到你的插件。 -
更新插件
只需更新 package.json 里的 version 版本号,然后执行vsce publish
就可以更新了。
五、附【代码注释阅读小说】核心代码
/*
* @Author: DaHuaZhuXi
* @Date: 2020-11-02 16:47:19
* @LastEditTime: 2020-11-05 20:00:41
* @LastEditors: DaHuaZhuXi
* @Description: 主程序文件
*/
const vscode = require('vscode');
const fs = require("fs");
let fileData = ""; //读取文件后的字符数据
let datas = []; //通过lineBreak分割后的字符数组
let curPage = 0; //当前datas索引(分页记录)
const tipTxt = "【阅读进度,勿删!Reading progress, do not delete!】"; //文件第一行存储阅读进度文字
const lineBreak = "\r\n"; //分页使用的分割字符
let filePath = ""; //文件路径
let replaceMark = "/*$*/"; //替换标记
/**
* @param {vscode.ExtensionContext} context
* @Description: 插件激活时调用
*/
function activate(context) {
//变更当前激活文件时触发(用于记录阅读进度)
vscode.window.onDidChangeActiveTextEditor(() => {
saveReadProgress();
})
//读取文件并插入注释
let disposable = vscode.commands.registerCommand('ReadNovel.ReadNovel', function () {
fileData = "";
datas = [];
curPage = 0;
filePath = vscode.workspace.getConfiguration().get('ReadNovel.filePath').replace(/(^\s*)|(\s*$)/g, "");
replaceMark = vscode.workspace.getConfiguration().get('ReadNovel.replaceMark');
//文件存在检测
if (filePath === "") {
vscode.window.showErrorMessage('文件不能存在,请到设置里添加文件地址');
return;
};
//文件类型检测
if (getFileType(filePath) != "txt") {
vscode.window.showErrorMessage('文件类型不正确,只支持utf-8编码的txt文件');
return;
};
//打开文件
try {
fileData = fs.readFileSync(filePath, 'utf-8');
} catch (error) {
vscode.window.showErrorMessage(error.toString());
return
}
curPage = getSavePage(fileData);
datas = fileData.split(lineBreak);
console.log("过滤前段落长度:", datas.length);
datas = datas.filter(str => isEmptyLine(str))
console.log("过滤后段落长度:", datas.length);
//在当前文档第一行插入注释
handleTxt({ action: "insert", position: [0, 0], data: datas[curPage] })
});
//下一页
let nextPage = vscode.commands.registerCommand('ReadNovel.ReadNovel_nextPage', function () {
handleTxt({ action: "replace", position: [1, 0], data: datas[++curPage] })
});
//上一页
let prevPage = vscode.commands.registerCommand('ReadNovel.ReadNovel_prevPage', function () {
handleTxt({ action: "replace", position: [1, 0], data: datas[--curPage] })
});
//清空注释(老板键)
let clear = vscode.commands.registerCommand('ReadNovel.ReadNovel_clear', function () {
handleTxt({ action: "replace", position: [1, 0], data: "" })
});
context.subscriptions.push(disposable);
context.subscriptions.push(nextPage);
context.subscriptions.push(prevPage);
context.subscriptions.push(clear);
}
/**
* @description: 编辑器第一行文字处理
* @param {Object} option
* @param {String} option.action //操作
* @param {Array} option.position //位置
* @param {String} option.data //数据
*/
function handleTxt(option) {
if (!isLegal()) return;
const editor = vscode.window.activeTextEditor;
editor.edit(editBuilder => {
let result = replaceMark.replace("$", option.data) + "\r\n";
switch (option.action) {
case "insert":
editBuilder.insert(new vscode.Position(option.position[0], option.position[1]), result)
break;
default:
editBuilder.replace(new vscode.Range(new vscode.Position(0, 0), new vscode.Position(option.position[0], option.position[1])), result)
break;
}
})
}
//获取文件类型
function getFileType(filePath) {
var startIndex = filePath.lastIndexOf(".");
if (startIndex != -1) {
return filePath.substring(startIndex + 1, filePath.length).toLowerCase();
} else {
return "";
}
}
//存储阅读进度
function saveReadProgress() {
if (!isLegal()) return;
let datas = fileData.split("\r\n");
if (datas.length === 0) return;
//如果没有记录进度则添加进度
if (datas[0].indexOf(tipTxt) === -1) {
let newData = curPage + tipTxt + "\r\n" + fileData;
writeFile(newData)
} else { //如果有进度则变更进度
if (curPage === 0) return;
let savePage = Number(datas[0].split(tipTxt)[0]);
if (curPage === savePage) return;
let oldLine = savePage + tipTxt + "\r\n";
let newLine = curPage + tipTxt + "\r\n";
let newData = fileData.replace(oldLine, newLine);
writeFile(newData)
}
}
//检测合法性
function isLegal() {
if (datas.length === 0 || curPage < 0 || curPage > datas.length) return false;
return true;
}
//写入文件
function writeFile(data) {
fs.writeFile(filePath, data, { encoding: 'utf8' }, function (err) {
if (err) return console.error(err);
});
}
//获取阅读进度
function getSavePage(fileData) {
const datas = fileData.split("\r\n")
if (datas[0].indexOf(tipTxt) === -1) return curPage;
let savePage = Number(datas[0].split(tipTxt)[0]);
return savePage
}
//检测是否空行
function isEmptyLine(str) {
return str.replace(/[ ]|[\r\n]/g, "") != "";
}
exports.activate = activate;
//插件销毁时调用
function deactivate() {
saveReadProgress();
}
module.exports = {
activate,
deactivate
}
六、参考资料
- VSCode插件开发全攻略 https://www.cnblogs.com/liuxianan/p/vscode-plugin-overview.html
- VSCode官方文档 https://code.visualstudio.com/api
- VScode API https://code.visualstudio.com/api/references/vscode-api
兄弟,点个赞再走!
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)