手摸手完成vscode插件开发

999 阅读4分钟

大家好!我是一个喜欢前端的~菜鸡H

项目背景:

在现有项目的开发中(小程序原生开发)常常在编写样式的时候会有那么一个问题困扰着我、当我在wxml 写好页面结构返回css 文件去编写样式的时候常常因为记不住当时自己写的类名而导致我还需要切回wxml去查看一下、反反复复我就厌烦了、冒出一个想法表示如果有个插件能够实时提醒我当前页面的class名称那就不用老切文件了还能提高效率!要是还能通过class名称点击进入到相对应的css文件位置那就更太好啦!大致的效果如下 css智能提醒

vscode文档

  • 安装vscode插件包

npm install -g yo generator-code
yo code //然后根据提示填写一些项目的基本信息和自己的编码习惯、喜欢typescript的可以选择他!
  • 把项目run起来

把项目拖入到vscode开发工具里面、然后按F5启动项目

windows用户按住shift+Ctrl+p 、mac按住command + p 启动命令输入行、然后输入hello World后回车、然后vscode正常会在右下角提醒Hello World from xxx 字样、那恭喜你你已经学会vscode的第一个插件了😀

  • 理解项目的目录结构

package.json

{
  	"name": "css-remind",
	"displayName": "css-remind",
	"description": "css提醒",
	"version": "0.0.1",
	"engines": { "vscode": "^1.54.0" },
	"categories": [ "Other" ],
	// 插件被激活的规则
	"activationEvents": [ "onCommand:css" ],
	//入口
	"main": "./out/extension.js",
    //扩展名的对象
	"contributes": {
		//命令
		"commands": [
			{ "command": "css", "title": "get class" }, 
                        { "command": "html", "title": "get html" }
		]
	},
    //用于搜索插件的关键字
    "keywords":[
        "css",
        "css-"
    ],
    //脚本
	"scripts": {
		"vscode:prepublish": "yarn run compile",
		"compile": "tsc -p ./",
		"watch": "tsc -watch -p ./",
		"pretest": "yarn run compile && yarn run lint",
		"lint": "eslint src --ext ts",
		"test": "node ./out/test/runTest.js"
	},
        //开发依赖
	"devDependencies": {
		"@types/vscode": "^1.54.0",
		"@types/glob": "^7.1.3",
		"@types/mocha": "^8.0.4",
		"@types/node": "^12.11.7",
		"eslint": "^7.19.0",
		"@typescript-eslint/eslint-plugin": "^4.14.1",
		"@typescript-eslint/parser": "^4.14.1",
		"glob": "^7.1.6",
		"mocha": "^8.2.1",
		"typescript": "^4.1.3",
		"vscode-test": "^1.5.0"
	}
}

extension.ts


import * as vscode from 'vscode';
/**
 * 插件被激活时触发,所有代码总入口
 * @param {*} context 插件上下文
 */
export function activate(context: vscode.ExtensionContext) {
	let disposable = vscode.commands.registerCommand('css', () => {
		vscode.window.showInformationMessage('css');
	});
	let disposable1 = vscode.commands.registerCommand('html', () => {
		vscode.window.showInformationMessage('html');
	});
	// 注册命令
	context.subscriptions.push(disposable,disposable1);
}

//插件被释放时触发
export function deactivate() {
	console.log('事件执行销毁!')
}

流程:在contributes.commands里面注册两个html css的命令、并在extension.js中去实现了它、还需要在activationEvents添加上onCommand:xxx用来告诉vscode,当用户执行了这个命令操作时我们定义的指令方法内容

现在我们对整个项目有个一个大概的认识、如果还想掌握更多的配置解读下方有vscode的文档介绍

activationEvents

Extension

API

  • 下面开始我们上方的GIF功能开发、如果不想看思路的直接拉到最低下方有GitHub仓库地址

功能分析

1、捕获在css、或者其他样式文件里面输入 . 然后匹配同级文件夹下的html里面所有的class名称
2、获取当前文件夹路径、读取路径下所有的文件并筛选出html文件路径(有可能没有html)
3、获取路径后读取html文件内容、并匹配出所有的class名称
4、当用户触发事件规则把筛选好的class名称提示出去

功能开发

    //先认识一个vscode自动补全的api https://code.visualstudio.com/api/references/vscode-api#CompletionItemProvider
    vscode.languages.registerCompletionItemProvider
    export function activate(context: vscode.ExtensionContext) {
      context.subscriptions.push(
        //在当前文件内触发事件
        vscode.languages.registerCompletionItemProvider(
            [
                { scheme: "file", language: "css" },
                { scheme: "file", language: "less" },
                { scheme: "file", language: "scss" },
                { scheme: "file", language: "sass" },
                { scheme: "file", language: "stylus" },
                { scheme: "file", language: "vue" },
            ],
            {
                provideCompletionItems,
                resolveCompletionItem,
            },
            //在上面这些文件输入 . 后触发事件
            "."
        ),
        //文件跳转部分
        vscode.languages.registerDefinitionProvider(
            .....
        );
    }
    
const extensionArray: string[] = ["htm", "html", "jsx", "tsx","wxml"];
const htmMatchRegex: RegExp = /class="[\w- ]+"/g;
const sxMatchRegex: RegExp = /className="[\w- ]+"/g;

function provideCompletionItems(
  document: vscode.TextDocument,
  position: vscode.Position,
  _token: vscode.CancellationToken,
  _context: vscode.CompletionContext
) {
  const typeText = document
    .lineAt(position)
    .text.substring(position.character - 1, position.character);
  if (typeText !== ".") { return; }
  // 获取当前文件路径
  const filePath: string = document.fileName;
  let classNames: string[] = [];
  // 在vue文件触发
  if (document.languageId === "vue") {
    // 读取当前文件
    classNames = getClass(filePath);
  }
  // 在css类文件触发
  else {
    // 获取当前文件夹路径
    const dir: string = path.dirname(filePath);
    // 读取当前文件夹下的文件名
    const files: string[] = fs.readdirSync(dir);
    // 筛选目标文件
    const target: string[] = files.filter((item: string) =>
      extensionArray.includes(item.split(".")[1])
    );
    // 读取目标文件,获取class
    target.forEach((item: string) => {
      const filePath: string = `${dir}/${item}`;
      classNames = getClass(filePath);
    });
  }

  classNames = classNames.reduce((arr, ele) => {
    const className: string = ele.split("=")[1];
    // 去掉引号
    const field: string = className.slice(1, className.length - 1);
    // 处理多class情况
    if (ele.includes(" ")) {
      return arr.concat(field.split(" "));
    } else {
      arr.push(field);
      return arr;
    }
  }, [] as string[]);

  return classNames.map( (ele: string) => {
    return new vscode.CompletionItem(
      // 提示内容要带上触发字符,https://github.com/Microsoft/vscode/issues/71662
      document.languageId === "vue" ? `${ele}` : `.${ele}`,
      vscode.CompletionItemKind.Text
    );
  });
}
    

这样我们就完成了一个自动补全的vscode插件、剩下的点击html跳转到css文件指定name名称在我的GitHub仓库、有兴趣的可以去看看!

仓库地址