美文网首页
vscode插件开发实践

vscode插件开发实践

作者: 程序员poetry | 来源:发表于2021-01-10 09:28 被阅读0次

    初次尝试了一下vscode插件开发,写了一个前端工具箱作为日常方便使用,记录一下

    image

    在vscode插件里面搜索:前端工具箱。或打开该网址安装即可 https://marketplace.visualstudio.com/items?itemName=poetries.fe-tools

    搭建开发环境

    我们先准备开发环境。我使用的操作系统:mac,首先确保安装了VS Code、Node.js 和 Git:

    code -v
    node -v
    npm -v
    git --version
    

    https://code.visualstudio.com/api/get-started/your-first-extension

    npm install -g yo generator-code
    

    使用yo code指令初始化VSCode插件项目,然后需要回答一些简单的配置问题

    yo code
    
    # What type of extension do you want to create? 
    # 创建那一种类型的扩展?
    
    # What's the name of your extension?
    # 扩展的名称?
    
    # What's the identifier of your extension?
    # 扩展的标示?
    
    # What's the description of your extension?
    # 扩展的描述
    
    # Initialize a git repository? 
    # 初始化git仓库
    
    # Which package manager to use? 
    # 使用那一种包管理器
    

    运行插件

    使用 VS Code 打开项目,在编辑器内部,按F5,编译并打开一个“扩展开发宿主机”窗口运行调试插件。为了叙述方便,把新打开的窗口称为运行窗口,旧窗口称为编辑窗口。在新窗口的命令面板(Ctrl+Shift+P) 运行 Hello World 命令。看到右下角的 Hello World 通知信息了吗?恭喜你已经运行了一个自己编写的插件!

    image

    调试插件

    使用 VS Code 调试扩展插件很容易。这里演示一下如何设置断点。在编辑窗口打开 extension.js 文件, 点击编辑器行号左侧的边栏设置断点。在运行窗口 命令面板输入 Hello World 命令运行插件,命中断点。

    image

    调试webview

    按F5打开调试模式,在webview页面,按command+shift+p选择open webview

    image
    image

    项目解析

    接下来我们来深入研究一下 helloworld 插件。 helloworld 的功能很简单,就是用户可以在命令面板执行 Hello World 命令,输出 Hello World 信息

    从实现的角度来看,helloworld 插件做了三件事:

    • 注册激活事件 onCommand:extension.helloWorld:插件在extension.helloWorld 触发时被激活。
    • 注册贡献点 contributes.commands:extension.helloWorld:在命令面板中使能 hello world 命令,并将其绑定到 extension.helloworld
    • 调用 VS Code API commands.registerCommand 给注册的命令 extension.helloWorld绑定处理函数。

    基本概念

    简单解释一下上面提到的三个概念:激活事件、贡献点和接口:

    • 激活事件,译自 Activation Events,在配置清单 package.json 中静态声明,其实就是 JSON 数组 activationEvents 的值。当激活事件发生时,声明的扩展将被激活。以下是目前所有可用激活事件:onLanguageonCommandonDebug:onDebugInitialConfigurationsonDebugResolveworkspaceContainsonFileSystemonViewonUrionWebviewPanel、*;
    • 贡献点,译自 Contribution Points,在配置清单 package.json 中静态声明,贡献点其实就是VS Code 的可以扩展的功能点。以下是目前所有可用的贡献点:configurationconfigurationDefaultscommandsmenuskeybindingslanguagesdebuggersbreakpointsgrammarsthemessnippetsjsonValidationviewsviewsContainersproblemMatchersproblemPatterns taskDefinitionscolorstypescriptServerPluginsresourceLabelFormatterscontributes.configur
    • VS Code 接口:可以在扩展代码中调用的一组 JavaScript API。链接中列举了所有可用的API,熟悉基本的,其他的用到的时候按需查找就行了。

    一般来说,插件都会使用到这三个概念:激活事件贡献点VS Code API。接下来我们分析一下 HelloWorld 示例的源代码,看看它是如何使用这些概念的。

    JavaScript 插件目录结构

    image

    VS Code 插件的目录结构很简单,根据命名大概就能知道作用。不同项目类型目录结构可能会有很大不同。对于 JavaScript 类型的项目来说,最重要的就是package.jsonextension.js

    清单文件:package.json

    每个 VS Code 插件都必须有一个用来描述插件的清单文件 package.json。VS Code 的清单文件是声明式的 JSON 格式,用于声明插件名(name)、插件展示名( displayName )、描述信息(description)、版本( version )、引擎( engines )、类别( categories )、依赖( devDependencies )、脚本 (scripts)、贡献点( contributes )、入口文件( main )、激活事件( activationEvents )等。

    下面是 helloworld 插件的清单文件内容,为了便于理解,我加了一些注释。

    {
        "name": "helloworld", //插件名
        "displayName": "helloworld", // 插件市场显示的插件名,支持中文
        "description": "demo", // 插件市场显示的描述信息
        "version": "0.0.1", // 版本号
        // 最低支持的 VS Code 版本
        "engines": {
            "vscode": "^1.38.0"
        },
        // 插件市场分类
        "categories": [
            "Other"
        ],
        // 激活事件
        "activationEvents": [
            "onCommand:extension.helloWorld"
        ],
        "main": "./extension.js", // 指定入口文件
        // 贡献点
        "contributes": {
            "commands": [{
                "command": "extension.helloWorld",
                "title": "Hello World"
            }]
        },
        // 脚本
        "scripts": {
            "test": "node ./test/runTest.js"
        },
        // 依赖,包含版本信息
        "devDependencies": {
            "@types/glob": "^7.1.1",
            "@types/mocha": "^5.2.6",
            "@types/node": "^10.12.21",
            "@types/vscode": "^1.38.0",
            "eslint": "^5.13.0",
            "glob": "^7.1.4",
            "mocha": "^6.1.4",
            "typescript": "^3.3.1",
            "vscode-test": "^1.2.0"
        }
    }
    

    插件入口文件:extension.js

    插件的入口文件是在清单文件中指定的,如果项目比较大,源文件比较多,也可以统一放在 src 目录里。

    // The module 'vscode' contains the VS Code extensibility API
    // Import the module and reference it with the alias vscode in your code below
    /* 导入 "vscode" 模块,这个模块包含 VS Code 的扩展接口。 */ 
    const vscode = require('vscode');
    
    // this method is called when your extension is activated
    // your extension is activated the very first time the command is executed
    // 第一次执行命令时,插件会被激活;插件被激活时,这个函数会被调用
    // 必须在入口中实现这个函数。
    
    /**
     * @param {vscode.ExtensionContext} context
     */
    function activate(context) {
    
        // Use the console to output diagnostic information (console.log) and errors (console.error)
        // This line of code will only be executed once when your extension is activated
        /* 输出诊断日志到控制台 */
        console.log('Congratulations, your extension "helloworld" is now active!');
    
        // The command has been defined in the package.json file
        // Now provide the implementation of the command with  registerCommand
        // The commandId parameter must match the command field in package.json
        /* 
         * 这个命令 extension.helloWorld 已经在 package.json 文件中定义了。
         * 现在我们使用 registerCommand 接口给这个命令绑定实现。
         */
        let disposable = vscode.commands.registerCommand('extension.helloWorld', function () {
            // The code you place here will be executed every time your command is executed
            /* 每一次执行命令,这儿的代码都会执行 */
    
            // Display a message box to the user
            vscode.window.showInformationMessage('Hello World!');
        });
    
        context.subscriptions.push(disposable);
    }
    exports.activate = activate;
    
    // this method is called when your extension is deactivated
    function deactivate() {}
    
    module.exports = {
        activate,
        deactivate
    }
    

    一个插件必须在其主模块实现并导出 activate()deactivate() 函数。

    • activate() 函数 初始化插件。当任何指定的激活事件发生时,VS Code 会调用并且只调用它一次。
    • deactivate() 函数 清理插件。如果清理过程是异步的, deactivate() 函数必须返回一个 Promise 对象。如果清理运行同步,则 deactivate() 函数返回 undefined

    Webview

    插件可以分为多种,比如主题样式类型的插件,图标插件,语言支持类型的插件。Webview类型的插件只是Vscode插件的一个大类。大致的实现大家可以参考文档,文档的示例使用的是html字符串,但这不适合复杂的Webview的开发。在GameNews这个插件中,模版部分,我使用了vue以及pug。

    // 静态资源的目录。绝对路径,并且使用了vscode-resource协议
    // vscode-resource:/Users/Desktop/game-news/views
    const webviewDir = path.join(context.extensionPath, 'views');
    
    // 创建一个Webview的面板
    const panel = vscode.window.createWebviewPanel(
        viewType,
        title,
        vscode.ViewColumn.One,
        {
            enableScripts: true, // 允许运行js脚本,默认是关闭的
            retainContextWhenHidden: true, // webview不可见时,脚本就会被挂起
            // 指定允许加载的本地资源的根目录
            localResourceRoots: [vscode.Uri.file(webviewDir)]
        }
    );
    
    // 模版文件
    const tpl = path.join(webviewDir, 'index.pug');
    
    // 通过pug渲染模版文件,到webview上
    panel.webview.html = pug.renderFile(tpl, options);
    

    本地资源的使用

    Webview中,我们会需要使用本地的css,js文件。虽然可以使用行间js或者行间样式,但是总归不太好。使用本地文件,就会涉及的静态文件路径的问题,在VScode中,我们需要使用绝对路径。并且是vscode-resource协议的路径。

    const webviewDir = path.join(context.extensionPath, 'views');
    // 静态资源的绝对目录
    let URI = vscode.Uri.file(path.join(webviewDir, 'js', 'vue.js'))
    // 使用vscode-resource协议头
    // 然后这个URL就可以使用在我们的webview的模版中了
    URI = URI.with({ scheme: 'vscode-resource' });
    

    Webview与插件通信

    Webview相当于一个网页,而网页是无法调用一些本地功能的。但是插件本身是运行在node环境的,而已我们可以通过插件实现一些在网页中无法完成的功能。Webview如果通知插件呢?这涉及到了Webview于网页的通信机制。

    下面是GameNews插件的例子,我通过事件将游戏新闻的url,发送给插件。插件会调用系统的命令,使用本地的浏览器打开url。

    // webview
    
    // webview中,一个内置的全局api
    const vscode = acquireVsCodeApi()
    
    vscode.postMessage({
        command: 'preview',
        text: url
    })
    
    // 插件
    panel.webview.onDidReceiveMessage(message => {
        switch (message.command) {
            case 'preview':
                // 打开浏览器
                open(message.text);
                return;
        }
    }, undefined, context.subscriptions);
    

    更多参考文档:https://code.visualstudio.com/api/extension-guides/webview

    打包、发布和升级

    如何让别人也能使用自己开发的插件呢?这和移动应用开发一样,有两种方式:

    • 把它发布到 VS Code 插件市场,这样其他人就可以找到、下载和使用你的插件。
    • 或者,可以将插件打包为可安装的vsix格式,并与其他用户共享。

    vsce,简写自 Visual Studio Code Extensions, 是用于打包、发布和管理 VS Code 插件的命令行工具

    先安装 Node.js ,然后运行 npm install -g vsce 安装 vsce。在插件的根目录下运行 vsce package 打包插件,运行 vsce publish 发布插件。

    npm install -g vsce
    
    vsce create-publisher poetry # 这一步先创建一个发布账号,需要用到token,看下面步骤获取token
    vsce package #打包插件 .vsix 格式
    vsce publish #发布到 MarketPlace
    

    发布插件到 VS Code 插件市场,需要注册开发者账号。有关如何发布插件的内容很简单,可以参考官方文档这一部分的内容:https://code.visualstudio.com/api/working-with-extensions/publishing-extension

    • 在Visual Studio Team Services 创建一个账号

    • 根据账号的名字访问主页,例如我的名字是bingou-ms,主页链接就是

    • 创建Personal Access Token

    image
    image

    需要将Accounts设置为All accessible accounts

    image

    那么如何升级已经发布到插件市场的插件呢?修改/增加版本号,然后再执行 vsce publish 即可。

    安装vsix文件

    可以直接安装vsce package的vsix文件,方便在本地进行调试。

    code --install-extension vsix文件名
    

    参考文档

    相关文章

      网友评论

          本文标题:vscode插件开发实践

          本文链接:https://www.haomeiwen.com/subject/ywlkaktx.html