VSCODE插件开发体验

221 阅读4分钟

在日常使用vscode开发中,总会碰到自己想使用的插件功能但是苦苦找不到合适的插件,这时我们不妨自己开发一个插件来使用

1.生成插件脚手架

要开发VSCode插件,首先要知道开发需要哪些文件、目录如何组织、文件都包含什么内容,官方提供了交互式工具来自动生成插件的脚手架,一键命令生成即可

npm install -g yo generator-code

yo code
     _-----_     ╭──────────────────────────╮
    |       |    │   Welcome to the Visual  │
    |--(o)--|    │   Studio Code Extension  │
   `---------´   │        generator!        │
    ( _´U`_ )    ╰──────────────────────────╯
    /___A___\   /
     |  ~  |
   __'.___.'__
 ´   `  |° ´ Y `

**生成后,我们只需要关注 extension.js和package.json即可,了解这两个文件基本上就可以入门开发一个vscode插件了**

image.png

2.extension.js

该文件是其入口文件,即package.json中main字段对应的文件(不一定叫extension.js这个名字),该文件中将导出两个方法:activate和deactivate,两个方法的执行时机如下所示:

activate

这是插件被激活时执行的函数

deactivate

这是插件被销毁时调用的方法,比如释放内存等。

3.package.json

该文件是vscode扩展的清单文件,里面有很多字段,官方对每个字段都进行了详细阐述,本次我们重点阐述以下初始化后期清单文件

{
  "publisher": "CC",// 开发插件人
  "name": "clear-git", // 插件名
  "displayName": "clear-git", // 显示在插件市场的名称
  "description": "一键清理git本地分支插件", // 插件描述
  "version": "0.0.1", // 版本号
  "engines": {
    "vscode": "^1.83.0" // 最低支持的vscode版本
  },
  "repository": { // 仓库链接
    "type": "git",
    "url": "https://gitee.com/cc-2018_admin/clean-git.git"
  },
  "categories": [
    "Other" // 扩展类别
  ],
  "activationEvents": [], // 激活事件组,在那些事件情况下被激活 
  "main": "./extension.js", // 入口文件
  "icon": "icon.png", // 插件icon
  // 命令列表
  "contributes": {
    "commands": [
      {
      // 命令名称,与extension.js里名称对应
        "command": "cleanLocalBranch",
        // 显示在标题的名称
        "title": "一键清理本地git"
      }
    ],
    // 右键菜单列表
    "menus": {
      "editor/context": [
        {
        // 命令名称,与extension.js里名称对应
          "command": "cleanLocalBranch",
          // 分组
          "group": "group1"
        }
      ],
      // 上下文列表
      "explorer/context": [
        {
        // 命令名称,与extension.js里名称对应
          "command": "cleanLocalBranch",
          // 分组
          "group": "group1"
        }
      ]
    }
  },
  "scripts": {
    "lint": "eslint .",
    "pretest": "pnpm run lint",
    "test": "node ./test/runTest.js"
  },
  "devDependencies": {
    "@types/vscode": "^1.83.0",
    "@types/mocha": "^10.0.2",
    "@types/node": "18.x",
    "eslint": "^8.50.0",
    "glob": "^10.3.3",
    "mocha": "^10.2.0",
    "typescript": "^5.2.2",
    "@vscode/test-electron": "^2.3.4"
  }
}

重点关注的主要有三部分内容:activationEvents、main以及contributes,其是整个文件中的核心模块

main

指明了该插件的主入口在哪,只有找到主入口整个项目才能正常的运转

activationEvents

指明该插件在何种情况下才会被激活,因为只有激活后插件才能被正常使用,官网已经指明了激活的时机,这样我们就可以按需设置对应时机。(具体每个时机用的时候详细查看即可)

  • onLanguage 打开解析为特定语言文件时被激活,例如"onLanguage:JavaScript"

  • onCommand 在调用命令时被激活

  • onDebug 在启动调试话之前被激活

  • workspaceContains 每当打开文件夹并且该文件夹包含至少一个与 glob 模式匹配的文件时

  • onFileSystem 每当读取来自特定方案的文件或文件夹时

  • onView 每当在 VS Code 侧栏中展开指定 id 的视图

  • onUri 每当打开该扩展的系统范围的 Uri 时

  • onWebviewPanel

  • onCustomEditor

  • onAuthenticationRequest

    • 只要一启动vscode,插件就会被激活
  • onStartupFinished

contributes

通过扩展注册contributes用来扩展Visual Studio Code中的各项技能,其有多个配置,如下所示:

  • breakpoints 断点
  • colors 主题颜色
  • commands 命令
  • configuration 配置
  • configurationDefaults 默认的特定于语言的编辑器配置
  • customEditors 自定义编辑器
  • debuggers
  • grammars
  • iconThemes
  • jsonValidation
  • keybindings 快捷键绑定
  • languages
  • menus
  • problemMatchers
  • problemPatterns
  • productIconThemes
  • resourceLabelFormatters
  • snippets 特定语言的片段
  • submenus
  • taskDefinitions
  • themes 颜色主题
  • typescriptServerPlugins
  • views
  • viewsContainers
  • viewsWelcome
  • walkthroughs

4.实战开发

这是一个帮你一键管理本地git分支的vscode插件,当你在开发时,可能会有需要迭代更新,迭代更新完毕后,有许多不需要的本地分支,不需要执行复杂的git branch -D 命令清除,只需点击一键即可清除

插件开发官方示例库:github.com/microsoft/v…

插件开发官方API文档:code.visualstudio.com/api/referen…

  • extension.js

const vscode = require("vscode");
const { checkGitFile } = require("./fs-service/operaFile.js");
    
/**
 * @param {vscode.ExtensionContext} context
 */
function activate(context) {
  const cleanGit = vscode.commands.registerCommand(
    "cleanLocalBranch",
    async () => {
      const isDefaultBranch = await vscode.window.showInformationMessage(
        "当前分支是否为默认分支?",
        "是",
        "否"
      );
      if (isDefaultBranch === "否") {
        vscode.window.showErrorMessage("请先切换到默认分支,例如master");
      } else {
        const projectRoot =
          vscode.workspace.workspaceFolders[0].uri.fsPath || "";
        checkGitFile(projectRoot, vscode);
      }
    }
  );

  context.subscriptions.push(cleanGit);
}

function deactivate() {}

module.exports = {
  activate,
  deactivate,
};
  • fs-service/operaFile.js
const fs = require("fs");
const path = require("path");

/**
 * @param {string} url
 * @param {{window:any}} vscode
 */
function checkGitFile(url, vscode) {
  let files = fs.readdirSync(url); //返回文件和子目录的数组
  if (!files.includes(".git")) {
    vscode.window.showErrorMessage("您还未初始化git,请先初始化");
    return;
  } else {
    const gitFolderPath = path.join(url, ".git");
    readRefsGitFile(gitFolderPath, vscode);
  }
}

/**
 * @param {string} url
 * @param {{window: any}} vscode
 */
function readRefsGitFile(url, vscode) {
  fs.readdir(url, (err, files) => {
    if (err) {
      vscode.window.showErrorMessage(err);
      return;
    }
    if (files.includes("refs")) {
      for (const file of files) {
        if (file === "refs") {
          const filePath = path.join(url, file);
          fs.stat(filePath, (err, stats) => {
            if (err) {
              vscode.window.showErrorMessage(err);
              return;
            }
            if (stats.isDirectory()) {
              // 递归调用 readGitFile 函数,继续读取子目录
              readGitFile(filePath, vscode);
            }
          });
        }
      }
    } else {
      vscode.window.showErrorMessage("git文件夹配置出错,请重新初始化");
      return;
    }
  });
}

/**
 * @param {string} url
 * @param {{window: any}} vscode
 */
function readGitFile(url, vscode) {
  fs.readdir(url, (err, files) => {
    if (err) {
      vscode.window.showErrorMessage(err);
      return;
    }
    for (const file of files) {
      const filePath = path.join(url, file);
      fs.stat(filePath, (err, stats) => {
        if (err) {
          vscode.window.showErrorMessage(err);
          return;
        }
        if (stats.isDirectory()) {
          // 递归调用 readGitFile 函数,继续读取子目录
          readGitFile(filePath, vscode);
        } else {
          deleteGitFile(filePath, vscode);
        }
      });
    }
  });
}

/**
 * @param {string} filePath
 * @param {{ window: any; commands?: any; }} vscode
 */
function deleteGitFile(filePath, vscode) {
  if (!["master", "HEAD"].some((item) => filePath.endsWith(item))) {
    fs.unlink(filePath, (err) => {
      if (err) {
        vscode.window.showErrorMessage(err);
        return;
      }
    });
    // 在 checkGitFile 完成后执行 git pull 命令
    vscode.commands.executeCommand("git.pull");
  }
}

module.exports = {
  checkGitFile,
};