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

原文地址:https://www.cnblogs.com/dwfeng/p/14234270.html