zoukankan      html  css  js  c++  java
  • VScode插件开发--M2D文档转换插件

    VSCode m2d插件开发

    VSCode结构

    VScode基于Electron构建的,分为三个部分

    • Electron: UI
      • Monaco Editor: 基于网页的编辑器,有符合LSP的插件就可以进行高亮,悬停,格式化等功能
      • Extension Host: VScode的主进程和插件进程是分开管理的,Extension Host就是管理插件进程,这也是VScode启动快速的原因
    • Language Server Protocol && Debug Adapter Protocol

    Language Server Protocol :即语言服务器协议,用于编辑器和编辑环境语言服务器之间的一种协议.就是当符合LSP的代码输入时会发送给语言服务器,语言服务器响应给客户端关于语法检测,自动补全之类的信息.在idea中,不同语言的检索需要不同的插件,这会导致极大的内存损耗,而LSP使得编程语言社区能专注于不断完善一个能提供语法检查、自动补全、跳转位置、引用查找等语言特性检查的高性能“语言服务器”.

    Debug Adapter Protocol: 一般而言,不同的编辑器对于不同的语言的调试模块都是不同的,这对于语言供应商和编辑器开发者而言都是一场灾难,因此,DAP便应运而生,此后各编辑器可以通过相同的协议与debugger通信,不同的编辑器通过统一的DAP(Debug Adapter Protocol)协议与各个debugger对应的适配器(Debug Adapter)通信.

    Extension Host: VScode的主进程和插件进程是分开管理的,Extension Host就是管理插件进程,这也是VScode启动快速的原因.它所管理的Extension实现了VScode的大部分功能,比如主题拓展,各种通用功能的拓展等.

    生命周期

    从生命周期上来看,插件编写有三大个部分:

    • Activation Event:设置插件激活的时机。位于 package.json 中。
    • Contribution Point:设置在 VSCode 中哪些地方添加新功能,也就是这个插件增强了哪些功能。位于 package.json 中。
    • Register:在 extension.ts 中给要写的功能用 vscode.commands.register...Activation EventContribution Point 中配置的事件绑定方法或者设置监听器。位于入口文件(默认是 extension.ts)的 activate() 函数中。

    activationEvents配置项配置插件的激活数组,即在什么情况下插件会被激活,目前支持以下8种配置:

    • onLanguage: 在打开对应语言文件时
    • onCommand: 在执行对应命令时
    • onDebug: 在 debug 会话开始前
    • onDebugInitialConfigurations: 在初始化 debug 设置前
    • onDebugResolve: 在 debug 设置处理完之前
    • workspaceContains: 在打开一个文件夹后,如果文件夹内包含设置的文件名模式时
    • onFileSystem: 打开的文件或文件夹,是来自于设置的类型或协议时
    • onView: 侧边栏中设置的 id 项目展开时
    • onUri: 在基于 vscode 或 vscode-insiders 协议的 url 打开时
    • onWebviewPanel: 在打开设置的 webview 时
    • *: 在打开 vscode 的时候,如果不是必须一般不建议这么设置

    contributes配置项是整个插件的贡献点,也就是说这个插件有哪些功能。contributes字段可以设置的key也基本显示了vscode插件可以做什么。

    • configuration:通过这个配置项我们可以设置一个属性,这个属性可以在vscodesettings.json中设置,然后在插件工程中可以读取用户设置的这个值,进行相应的逻辑。
    • commands:命令,通过cmd+shift+p进行输入来实现的。
    • menus:通过这个选项我们可以设置右键的菜单
    • keybindings:可以设置快捷键
    • languages:设置语言特点,包括语言的后缀等
    • grammars:可以在这个配置项里设置描述语言的语法文件的路径,vscode可以根据这个语法文件来自动实现语法高亮功能
    • snippets:设置语法片段相关的路径

    环境准备

    • yeoman脚手架工具

      npm install -g yo

    • generator-code VSCode代码生成器

    npm install -g generator -code

    创建项目

    1.执行代码创建项目

    yo code

    2.出现选项界面,按需选择

    创建完成会自动创建文件夹并帮助初始化完成文件,目录结构如下

    |-- src
       |-- test //插件单测文件
       |-- extension.js //插件入口文件
    |-- CHANGELOG.md //修改日志,发布后会展示
    |-- package-lock.json
    |-- package.json
    |-- README.md //插件说明 README,发布后会展示
    |-- tsconfig.json
    |-- tslint.json
    |-- vsc-extension-quickstart.md //插件开发说明
    

    项目内容

    需求: 将选定的内容进行文档转换,将markdown文件转换成符合公司语言规范样式的docx文件,方便公司内部使用,本人只负责编写插件,由后台传给jar包实现功能

    过程:

    1.在extension.ts中编写代码

    在插件激活activate中编写注册代码的命令,以及把注册的disposable对象推入subscription中,具体代码如下:

            //该命令是针对文件中所有内容进行转换
    vscode.commands.registerTextEditorCommand('helloworld.helloWorld', (textEditor,edit) => {
    
    		const doc = textEditor.document;
            const start = new vscode.Position(0, 0);
            const end = new vscode.Position(doc.lineCount - 1, doc.lineAt(doc.lineCount - 1).text.length);
            //获取全部文本区域
            const selection = new vscode.Range(start, end);
            let text = doc.getText(selection);
            //替换文件内容/
            //调用jar包方法,执行相应转换
            textEditor.edit(builder => {
                builder.replace(selection, "aaa");
            });
    		vscode.window.showInformationMessage(text);
        });
            //该注册命令针对选中内容
        let disposable = vscode.commands.registerTextEditorCommand('extension.px2rpxInSelection', (textEditor, edit) => {
            const doc = textEditor.document;
            let selection: vscode.Selection | vscode.Range = textEditor.selection;
            //获取选中区域
            if (selection.isEmpty) {
                const start = new vscode.Position(0, 0);
                const end = new vscode.Position(doc.lineCount - 1, doc.lineAt(doc.lineCount - 1).text.length);
                selection = new vscode.Range(start, end);
            }
            
            let text = doc.getText(selection);
            //替换文件内容
            textEditor.edit(builder => {
                builder.replace(selection, aaa();//执行相应转换文字的方法
            });
        });
    
    
    	context.subscriptions.push(disposable);
    

    之后需求改成了在资源管理器中选中文件或文件夹进行转换,之前写的压根没用上⊙﹏⊙∥

    //使用了fs,和path库
    // 注册命令,并获取URL
    	context.subscriptions.push(vscode.commands.registerCommand('extension.demo.getCurrentFilePath', (uri) => {
    
    		//获取文件的逻辑拉出
    		function fsReadDir(dir: string) {
    			return new Promise<string[]>((resolve, reject) => {
    				fs.readdir(dir, (err, files) => {
    					if (err) reject(err);
    					resolve(files);
    				});
    			});
    		}
    		// 获取fs.stats的逻辑拉出
    		function fsStat(path: string) {
    			return new Promise<fs.Stats>((resolve, reject) => {
    				fs.stat(path, (err, stat) => {
    					if (err) reject(err);
    					resolve(stat);
    				});
    			});
    		}
    
    
    		// 搜索文件主方法
    		async function fileSearch(dirPath: string) {
    			//获取文件路径
    			const files = await fsReadDir(dirPath);
    			const promises = files.map(file => {
    			//调用fsStat函数
    				return fsStat(path.join(dirPath, file));
    			});
                 //获取每一项文件绝对路径
    			const datas = await Promise.all(promises).then(stats => {
    				for (let i = 0; i < files.length; i += 1) files[i] = path.join(dirPath, files[i]);
    				return { stats, files };
    			});
                   //遍历每一项的stats,判断是否为文件夹
    			datas.stats.forEach(stat => {
    				const isFile = stat.isFile();
    				const isDir = stat.isDirectory();
    				//如果是文件夹,递归自身
    				if (isDir) {
    					fileSearch(datas.files[datas.stats.indexOf(stat)]);
    				}
    				//如果是文件,直接创建终端,执行命令
    				if (isFile) {
    					let terminalA = vscode.window.createTerminal({ name: "m2d" });
    					terminalA.show(true);
    			    //替换文件后缀为docx
    					let str = datas.files[datas.stats.indexOf(stat)].replace(/.w+$/, ".docx")
    				//终端执行命令
    					terminalA.sendText("m2d convert " + datas.files[datas.stats.indexOf(stat)] + " " + str); //输入命令
    					console.log(datas.files[datas.stats.indexOf(stat)]);
    				}
    			});
    		};
     
    		//获取执行命令的路径
    		let str = uri.path.substring(1);
    	    fs.stat(str, (error, stats) => {
    			if (error) {
    				console.log('fail')
    				return
    			}
    			const isFil = stats.isFile();
    			const isDi = stats.isDirectory();
    			if (isDi) {
    		// 当前选中的为文件夹
    				fileSearch(str);
    			}
    			if (isFil) {
            //当前选中的为文件
    				let terminalA = vscode.window.createTerminal({ name: "m2d" });
    				terminalA.show(true);
    				let path = str.replace(/.w+$/, ".docx")
    				terminalA.sendText("m2d convert " + str + " " + path);
    			}
    		})
    
    
    
    
    	}));
    
    

    2.在package.json中配置

    "contributes": {
    		"commands": [
    			{
    				"command": "helloworld.helloWorld",
    				"title": "M2D全部转换"
    			},
    			{
                    "command": "extension.px2rpxInSelection",
                    "title": "M2D选中转换"
                },
                	{
    				"command": "extension.demo.getCurrentFilePath",
    				"title": "M2D选中转换a"
    			}
    		],
    			"keybindings": [
    				{
    					"command": "helloworld.helloWorld",
    					"key": "ctrl+f10",
    					"mac": "cmd+f10",
    					"when": "editorTextFocus"
    				},
    				{
    					"command": "extension.px2rpxInSelection",
    					"key": "Alt+shift+p"
    				}
    			],
    				"menus": {
                        //在编辑器中显示
    					"editor/context": [
                            //获取所有文本内容
    						{
    							"when": "editorFocus",
    							"command": "helloworld.helloWorld",
    							"group": "navigation"
    						},
                            //当内容选中时
    						{
    							"when": "editorHasSelection",
    							"command": "extension.px2rpxInSelection",
    							"group": "6_px"
    						}
    					],
                            //资源管理器中显示
                            "explorer/context": [
                               {
                        "command": "extension.demo.getCurrentFilePath",
    					"group": "navigation"
    				
                                }
                          ]
    				}
    	},
    

    难点: 怎样在typescript中调用jar包中的方法

    个人认为,由于LSP的问题,在VScode中创建了ts文件,它便不会符合Java的语言服务器协议,无法使用Java的语法规范,完全和Java的规则不相同,便无法直接引用jar包.

    所以,重心变成了调用jar包中的方法,我想出的第一个方法是创建一个java文件,在Java文件中调用jar包中的方法,然后再从ts文件中调用Java里的方法,但这个问题在于处理ts和java文件通信的问题,这个问题在Android中很容易解决,通过webview的方法调用就能实现,pc端好像并不能用,所以我又想到了使用Ajax来传递,但也不太理想

    调用个鬼,创建bat文件,然后在typescipt中代码直接创建终端,输入命令调用bat文件就完事

    插件发布

    安装vsce

    npm i install vsce -g

    执行命令生成vsix文件

    vsce package

    问题:

    1.出现publisher is missing:直接在package.json中创建publisher即可

    2.make sure to edit the README.md file before you package or publish you extension:随便修改以下README.md,然后保存即可

    仅用来本人学习记录

    参考:官方api文档

    https://code.visualstudio.com/api/references/vscode-api

    ​ 张宇:VScode插件入门开发

    https://zhuanlan.zhihu.com/p/99198980

    ​ 雪山飞狐:vscode插件开发教程

    https://www.jianshu.com/p/e642856f6044

    ​ littleTommyTan:Typescript遍历文件

    https://segmentfault.com/a/1190000016841072

    ​ 小茗同学插件开发全攻略:(详细,强烈推荐)

    https://www.cnblogs.com/liuxianan/p/vscode-plugin-overview.html

  • 相关阅读:
    【poj1195】Mobile phones(二维树状数组)
    【2018年全国多校算法寒假训练营练习比赛(第五场)-E】情人节的电灯泡(二维树状数组单点更新+区间查询)
    【2018年全国多校算法寒假训练营练习比赛(第五场)-G】 送分啦-QAQ(斐波那契博弈)
    【Wannafly挑战赛10
    【2018年全国多校算法寒假训练营练习比赛(第四场)- E】通知小弟(强连通缩点)
    JS中的forEach、$.each、map方法推荐
    关于echarts3地图下钻省市
    Vue2.0总结———vue使用过程常见的一些问题
    我理解的关于Vue.nextTick()的正确使用
    一个用 vue 写的树层级组件 vue-ztree
  • 原文地址:https://www.cnblogs.com/dwfeng/p/14234270.html
Copyright © 2011-2022 走看看