1

vscode 插件开发

事先说明

  • 该插件提供以下能力

    1. 在编辑器中选中的内容下一行添加 console.log('content', content)

      命令行操作: Insert Log
      快捷键: 
          win: ctrl+w
          mac: cmd+shift+w
    2. 删除当前文件中所有的console

      命令行操作: Del all Logs
      快捷键:
          win: ctrl+r
          mac: cmd+shift+r
    3. 删除当前文件所有注释

      命令行操作: Remove all comments
      快捷键:
          win: ctrl+t
          mac: cmd+shift+t
  • 该文档记录一下 从插件代码初始化, 到插件工程目录说明, 到package.json字段说明, 到extension.ts文件说明, 到功能实现, 到azure devops插件市场账号申请, 到插件发布的过程

工程初始化

// 事先全局安装yo(脚手架工具, 生成模板) generator-code(vscode拓展生成器, 配合yo使用构建项目)
npm install yo generator-code -g
yo code   // 找个目录初始化项目

image.png

? What type of extension do you want to create(您想要创建什么类型的扩展?)? New Extension (TavaScript)
? What's the name of your extension(你的分机名是什么,也就是项目名)? auto-log
? What's the identifier of your extension(你的扩展的标识符是什么)? auto-log
? What's the description of your extension(什么是您的扩展的描述)? auto-log
? Initialize a git repository(初始化一个git仓库)? Yes
? bundel the source code with webpack (是否用webpack打包源码)? No
? Which package manager to use(使用哪个包管理器)? npm

初始化工程目录说明

image.png

初始化package.json字段说明(后续新增的字段, 后续说明)

{
    "name": "auto-log",// 插件工程名称
    "displayName": "auto-log",    // 插件市场显示的名字
    "description": "auto-log",    // 插件描述
    "version": "0.0.1",    // 插件版本号
    "engines": {    // 表示插件最低支持的vscode版本
        "vscode": "^1.73.0"
    },
    "categories": [        // 插件应用市场分类
        "Other"
    ],
    "activationEvents": [        // 插件的被动激活事件,可配*来保持在后台持续运行
        "onCommand:auto-log.helloWorld"
    ],
    "main": "./out/extension.js",        // 插件功能的主入口文件
    "contributes": {    // 贡献点,整个插件最重要最多的配置项
        "commands": [
            {
                "command": "auto-log.helloWorld",
                "title": "Hello World"
            }
        ]
    },
    "scripts": {        // 执行命令
        "vscode:prepublish": "npm run compile",
        "compile": "tsc -p ./",
        "watch": "tsc -watch -p ./",
        "pretest": "npm run compile && npm run lint",
        "lint": "eslint src --ext ts",
        "test": "node ./out/test/runTest.js"
    },
    "devDependencies": {        // 依赖
        "@types/vscode": "^1.73.0",
        "@types/glob": "^7.2.0",
        "@types/mocha": "^9.1.1",
        "@types/node": "16.x",
        "@typescript-eslint/eslint-plugin": "^5.27.0",
        "@typescript-eslint/parser": "^5.27.0",
        "eslint": "^8.16.0",
        "glob": "^8.0.3",
        "mocha": "^10.0.0",
        "typescript": "^4.7.2",
        "@vscode/test-electron": "^2.1.3"
    }
}

在这package.json文件中,重点关注的主要有三部分内容:activationEvents、main以及contributes,其是整个文件中的重中之重。

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

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

onLanguage 打开解析为特定语言文件时被激活,例如"onLanguage:python"
onCommand 在调用命令时被激活
onDebug 在启动调试话之前被激活
onDebugInitialConfigurations
onDebugResolve
workspaceContains 每当打开文件夹并且该文件夹包含至少一个与 glob 模式匹配的文件时
onFileSystem 每当读取来自特定方案的文件或文件夹时
onView 每当在 VS Code 侧栏中展开指定 id 的视图
onUri 每当打开该扩展的系统范围的 Uri 时
onWebviewPanel
onCustomEditor
onAuthenticationRequest
只要一启动vscode,插件就会被激活
onStartupFinished
3、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

extension.ts文件说明

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
// 依赖导入
import * as vscode from 'vscode';

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed

// 总的来说该文件需要导出两个方法 activate和deactivate    
// 其中 activate 是插件被激活时执行的函数 
// deactivate 是插件被销毁时调用的方法,比如释放内存等    
export function activate(context: vscode.ExtensionContext) {
    
    // 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 "auto-log" 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
    let disposable = vscode.commands.registerCommand('auto-log.helloWorld', () => {
        // 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 from auto-log!');
    });

    context.subscriptions.push(disposable);
}

// this method is called when your extension is deactivated
export function deactivate() {}

功能实现

package.json

{
    "name": "auto-log",
    "displayName": "auto-log",
    "description": "auto-log",
    "version": "0.0.2",
    "publisher": "jianting-wu",
    "engines": {
        "vscode": "^1.73.0"
    },
    "categories": [
        "Other"
    ],
    "activationEvents": [ // 注册命令
        "onCommand:auto-log.insert",
        "onCommand:auto-log.del",
        "onCommand:auto-log.remove-comments"
    ],
    "main": "./out/extension.js",
    "contributes": {
        "commands": [
            {
                "command": "auto-log.insert",
                "title": "Insert Log"    // cmd+shift+p 命令行中输入的命令, 也是右键显示的命令
            },
            {
                "command": "auto-log.del",
                "title": "Del all Logs"
            },
            {
                "command": "auto-log.remove-comments",
                "title": "Remove all comments"
            }
        ],
        "keybindings": [    // 绑定快捷键
            {
                "command": "auto-log.insert",
                "key": "ctrl+w",
                "mac": "cmd+shift+w",
                "when": "editorTextFocus"
            },
            {
                "command": "auto-log.del",
                "key": "ctrl+r",
                "mac": "cmd+shift+r",
                "when": "editorTextFocus"
            },
            {
                "command": "auto-log.remove-comments",
                "key": "ctrl+t",
                "mac": "cmd+shift+t",
                "when": "editorFocus"
            }
        ],
        "menus": {
            "editor/context": [        // 配置右键显示
                {
                    "when": "editorTextFocus",
                    "command": "auto-log.insert",
                    "group": "navigation"
                },
                {
                    "when": "editorTextFocus",
                    "command": "auto-log.del",
                    "group": "navigation"
                },
                {
                    "when": "editorFocus",
                    "command": "auto-log.remove-comments",
                    "group": "navigation"
                }
            ]
        }
    },
    "scripts": {
        "vscode:prepublish": "npm run compile",
        "compile": "tsc -p ./",
        "watch": "tsc -watch -p ./",
        "pretest": "npm run compile && npm run lint",
        "lint": "eslint src --ext ts",
        "test": "node ./out/test/runTest.js"
    },
    "devDependencies": {
        "@types/vscode": "^1.73.0",
        "@types/glob": "^7.2.0",
        "@types/mocha": "^9.1.1",
        "@types/node": "16.x",
        "@typescript-eslint/eslint-plugin": "^5.27.0",
        "@typescript-eslint/parser": "^5.27.0",
        "eslint": "^8.16.0",
        "glob": "^8.0.3",
        "mocha": "^10.0.0",
        "typescript": "^4.7.2",
        "@vscode/test-electron": "^2.1.3"
    },
    "repository": {
        "type": "git",
        "url": "https://github.com/ulahala/auto-log.git"
    }
}

src/extension.ts

import * as vscode from 'vscode';
import insert from './insert';
import del from './del';
import remove from './remove';

const commandList = [
    {command: 'auto-log.insert', handler: insert},
    {command: 'auto-log.del',handler: del},
    {command: 'auto-log.remove-comments',handler: remove}
];

export function activate(context: vscode.ExtensionContext) {
    console.log('activated inter');

    commandList.forEach(({command, handler}) => {
        context.subscriptions.push(vscode.commands.registerCommand(command, handler));
    });

}

export function deactivate() {}

src/insert.ts

import * as vscode from 'vscode';

function insertText(str: string) {
    const editor = vscode.window.activeTextEditor;

    if(!editor) {return vscode.window.showErrorMessage('Can\'t insert log becase no document is opened');};

    const selection = editor.selection;

    const lineOfSelectionVar = selection.active.line;

    editor.edit(editBuilder => {
        // todo: 插入位置和选中行 头部对齐
        editBuilder.insert(new vscode.Position(lineOfSelectionVar + 1, 0), str);
    });
}

export default () => {
    const editor = vscode.window.activeTextEditor;

    if(!editor) {return;}

    const selection = editor.selection;

    const text = editor.document.getText(selection);

    const logToInsert = `console.log('${text}', ${text});\n`;

    text ? insertText(logToInsert) : insertText('console.log();\n');
};

src/del.ts

import * as vscode from 'vscode';

function getAllLogs() {
    const editor = vscode.window.activeTextEditor;

    if(!editor) {return [];};

    const document = editor.document;

    const documentText = document.getText();

    const allLogs = [];

    const logRegex = /console.(log|debug|info|warn|error|assert|dir|dirxml|trace|group|groupEnd|time|timeEnd|profile|profileEnd|count)\((.*)\);?/g;

    let match;
    while(match = logRegex.exec(documentText)) {
        let matchRange = new vscode.Range(document.positionAt(match.index), document.positionAt(match.index + match[0].length));

        if(!matchRange.isEmpty) {allLogs.push(matchRange);};
    }

    return allLogs;
}


export default () => {
    const editor = vscode.window.activeTextEditor;

    if(!editor) {return;};

    const workspaceEdit = new vscode.WorkspaceEdit();

    const document = editor.document;

    const allLogs = getAllLogs();

    allLogs.forEach(log => {
        workspaceEdit.delete(document.uri, log);
    });

    vscode.workspace.applyEdit(workspaceEdit).then(() => {
        vscode.window.showInformationMessage(`${allLogs.length} console deleted`);
    });

};

src/remove.ts

import * as vscode from 'vscode';


export default () => {
    const editor = vscode.window.activeTextEditor;

    if(!editor) {return;}

    editor.edit(editBuilder => {
        let text= editor.document.getText();

        text = text.replace(/((\/\*([\w\W]+?)\*\/)|(\/\/(.(?!"\)))+)|(^\s*(?=\r?$)\n))/gm, '')
                    .replace(/(^\s*(?=\r?$)\n)/gm, '')
                    .replace(/\\n\\n\?/gm, '');

        const end = new vscode.Position(editor.document.lineCount + 1, 0);

        editBuilder.replace(new vscode.Range(new vscode.Position(0, 0), end), text);

        vscode.commands.executeCommand('editor.action.formatDocument');

    });
};

azure devops插件市场账号申请

简单说明一下:
Visual Studio Code的应用市场基于微软自己的Azure DevOps,插件的身份验证、托管和管理都是在这里。

  • 要发布到应用市场首先得有应用市场的publisher账号;
  • 而要有发布账号首先得有Azure DevOps组织;
  • 而创建组织之前,首先得创建Azure账号;
  • 创建Azure账号首先得有Microsoft账号;

而且

  • 一个Microsoft账号可以创建多个Azure组织;
  • 一个组织可以创建多个publisher账号;
  • 同时一个组织可以创建多个PAT(Personal Access Token,个人访问令牌);
  1. 申请Microsoft账号
    登录Microsoft , 没有的话注册一个
    image.png
    image.png
  2. 创建Azure DevOps组织

https://aka.ms/SignupAzureDevOps

image.png
点击continue 创建组织

  1. 创建令牌

进入组织的主页后,点击右上角的Security,点击创建新的个人访问令牌,这里特别要注意Organization要选择all accessible organizations,Scopes要选择Full access,否则后面发布会失败。

image.png

image.png

点击Create创建token, token要自己存一下, 在发布的时候要用的, 没存的话发布的时候得重新生成

  1. 创建发布账号

访问https://aka.ms/vscode-create-publisher

image.png
把name 和 id 这俩必填的填上就行 这个name要和你插件的package.json中的 'publisher' 字段保持一致

点击下方Create创建发布账号, 这块如果你用的google浏览器会有个 无法加载recaptcha 的报错, 这是google的人机验证, 头铁的多试几次, 头肿了了用火狐搞, 解决方法的话查一下 无法加载recaptcha 不细讲了, 反正我头铁过去了

到这了账号的问题基本搞完了, 接下来就是搞发布的事了

  1. 发布到应用市场

先全局安装vsce, 这个是vscode插件的打包工具

npm install vsce -g

然后本地创建一个发布者

vsce create-publisher jianting-wu 
// 这个名字要和创建发布账号的那个name一致, 和package.json中的 'publisher' 字段一致

然后在项目根目录下执行

vsce publish

第一次执行会让你输入上面生成的token, 还遇到提示package.json中没有repository字段, 根目录下没有LICENSE...爱补就补上

然后等个几分钟, 在应用市场上就能看到你发布的应用了

image.png

image.png

完事, 先到这...


UlaHalal
29 声望0 粉丝

主要用于笔记整理