门徒注册PRDUCTS DISPLAY

联系我们

联系人:张生

咨询热线:400-123-4657

传真:+86-123-4567

手机:13800000000

邮箱:admin@youweb.com

地址:广东省广州市天河区88号

在线咨询

公司动态

您现在的位置是: 首页 > 门徒动态 > 公司动态

VSCode插件开发实战

目前网络上可以找到很多VSCode插件开发教程,其中不乏一些优秀的文章。本篇将从实战的角度出发,带领大家开发一个好用、强大的插件。

Visual Studio Code 是一个运行在桌面上,并且可用于Windows,Mac OS X和Linux平台的的轻量级且功能强大的源代码编辑器。它配备了内置的JavaScript的,TypeScript和Node.js的支持,并具有其他语言(C ++,C#,Python和PHP)的扩展以及一个丰富的生态系统。

比sublime开源,比atom更快,比webstorm更轻。

下面来让我们一起开发一个强大的px转rpx插件。

npm install -g yo
npm install -g generator-code

执行下面代码

yo code

通过yo code生成插件开发项目,这里官方推荐使用TypeScript,当然我们更熟悉javascript,可根据自身实际情况选择。

执行后会提示几个问题,第一个问题我们选择 New Extension (TypeScript),创建使用TypeScript开发的扩展插件。(可以使用 yo code 来创建插件、主题、语言支持、代码片段、语言支持、键盘映射、插件包,本文我们只讨论插件,其他功能感兴趣的同学可以自行研究。)

填写完成后,会自动创建文件夹并帮助初始化完成文件,我们先来看下目录结构。

|-- src
   |-- test //插件单测文件
   |-- extension.js //插件入口文件
|-- CHANGELOG.md //修改日志,发布后会展示
|-- package-lock.json
|-- package.json
|-- README.md //插件说明 README,发布后会展示
|-- tsconfig.json
|-- tslint.json
|-- vsc-extension-quickstart.md //插件开发说明

通过VSCode打开刚生成的插件项目,在这些文件中,有两个重点:

extension.js 是插件的入口文件

package.json包含插件的配置信息(插件命令、快捷键、菜单均在此配置)

准备完成后,为了先验证下插件项目正常OK,在VSCode中F5运行(或Debug->start)如果你可以看到VSCode又启动了一个窗口运行插件项目,shift+ctrl+p 输入Hello World如果在右下角能看到Hello World的提示信息就OK 了。

如果在编辑插件时内容做了变更,在运行的窗口只需通过Ctrl+r 即可刷新,无需关闭重新运行

在开始正式编写插件之前,我们先来明确一下需求。因为笔者主要从事小程序开发,经常需要使用rpx来实现自适应布局,而px到rpx的转换却是一项繁琐的工作。那么如何才能在编写代码时快速的进行px到rpx的转换以提高开发效率呢?VSCode插件自然是最直接的一种方式。

下面我们就来看一下插件要实现的功能:

在css文件中输入px时自动给出px到rpx的转换提示,并且可以使用右键菜单的方式转换选定代码中的px。

下面就让我们开始动手实现我们的插件。

新建process.ts

export class CssRpxProcess {

    constructor(private config: any) {}

    private rePxReg: RegExp = /(\\d+(\\.\\d+)?)(px)?/;

    private rePxAllReg: RegExp = /(\\d+(\\.\\d+)?)px/g;

    /**
     * transform px value to rpx value
     *
     * @param{string}pxStr px value
     * @return{Object}transformed object
     */
    private pxToRpx(pxStr: string) {
        const px = parseFloat(pxStr);

        let rpxValue: number | string = +(px * (750 / this.config.baseWidth)).toFixed(this.config.fixedDigits);
        if (this.config.autoRemovePrefixZero) {
            if (rpxValue.toString().startsWith('0.'))
                rpxValue = rpxValue.toString().substring(1);
        }
        return {px: pxStr, pxValue: px, rpxValue, rpx: rpxValue + 'rpx'};
    }

    /**
     * transform px to rpx
     *
     * @param{string}code origin text
     * @return{string}transformed text
     */
    convert(text: string) {
        let match = text.match(this.rePxReg);
        if (!match) return null;

        return this.pxToRpx(match[1]);
    }

    /**
     * transform all px to rpx
     *
     * @param{string}code origin text
     * @return{string}transformed text
     */
    convertAll(code: string): string {
        if (!code) return code;

        return code.replace(this.rePxAllReg, (word: string) => {
            const res = this.pxToRpx(word);
            if (res) return res.rpx;
            return word;
        });

    }
}

新建provider.ts

import * as vscode from 'vscode';
import{ CssRpxProcess }from "https://zhuanlan.zhihu.com/p/process";
import * as nls from 'vscode-nls';

//国际化支持
const localize=nls.config({ messageFormat: nls.MessageFormat.both})();

//实现代码提示
export class CssRpxProvider implements vscode.CompletionItemProvider{

    constructor(private process: CssRpxProcess){ }

    provideCompletionItems (
        docume、nt: vscode.TextDocument, 
        position: vscode.Position, 
        token: vscode.CancellationToken
    ): Thenable<vscode.CompletionItem[]>{

        return new Promise((resolve, reject)=>{
			
	    let wordAtPosition=document.getWordRangeAtPosition(position);
            var currentWord='';
	    if (wordAtPosition && wordAtPosition.start.character < position.character){
	        var word=document.getText(wordAtPosition);
		currentWord=word.substr(0, position.character - wordAtPosition.start.character);
	    }
            const res=this.process.convert(currentWord);
            if (!res){
                return resolve([]);
            }
            
            //代码提示信息
            const item=new vscode.CompletionItem(`${res.pxValue}px -> ${res.rpx}`, vscode.CompletionItemKind.Snippet);
            // 要插入的文本
            item.insertText=res.rpx;
            item.detail='Value';
            //国际化提示信息
            let message=localize('px2rpx.description', 
            'Translate $0px to $1, you can set design base width in preferences settings.');
            message=message.replace('$0', res.pxValue + '')
            message=message.replace('$1', res.rpx + '')
            item.documentation=`${message}`;
            return resolve([item]);
        });
    }
}

修改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
'use strict';
import * as vscode from 'vscode';
import { CssRpxProcess } from 'https://zhuanlan.zhihu.com/p/process';
import { CssRpxProvider } from 'https://zhuanlan.zhihu.com/p/provider';

let config = null;
// 插件被激活时调用activate
export function activate(context: vscode.ExtensionContext) {
    config = vscode.workspace.getConfiguration("px-to-rpx");
    const process = new CssRpxProcess(config);
    let provider = new CssRpxProvider(process);
    const LANS = ['html', 'vue', "swan", "wxml", "axml", 'css', "wxss", "acss", 'less', 'scss', 'sass', 'stylus', 'wxss', 'acss'];
    for (let lan of LANS) {
        //为对应类型文件添加代码提示
        let providerDisposable = vscode.languages.registerCompletionItemProvider(lan, provider);
        context.subscriptions.push(providerDisposable);
    }
     
    //注册px2rpx命令
    vscode.commands.registerTextEditorCommand('extension.px2rpx', (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);
        //替换文件内容
        textEditor.edit(builder => {
            builder.replace(selection, process.convertAll(text));
        });
    });
    
    //注册px2rpxInSelection命令
    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, process.convertAll(text));
        });
    });

    context.subscriptions.push(disposable);
}

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

添加gulpfile.js

/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

const gulp=require('gulp');
const path=require('path');

const ts=require('gulp-typescript');
const typescript=require('typescript');
const sourcemaps=require('gulp-sourcemaps');
const del=require('del');
const runSequence=require('run-sequence');
const es=require('event-stream');
const vsce=require('vsce');
const nls=require('vscode-nls-dev');

const tsProject=ts.createProject('https://zhuanlan.zhihu.com/p/tsconfig.json',{ typescript });

const inlineMap=true;
const inlineSource=false;
const outDest='out';

// If all VS Code langaues are support you can use nls.coreLanguages
const languages=[{ folderName: 'zh-cn', id: 'zh-cn' }];

const cleanTask=function(){
	return del(['out/**', 'package.nls.*.json', 'i18n-sample*.vsix']);
}

const internalCompileTask=function(){
	return doCompile(false);
};

const internalNlsCompileTask=function(){
	return doCompile(true);
};

const addI18nTask=function(){
	return gulp.src(['package.nls.json'])
		.pipe(nls.createAdditionalLanguageFiles(languages, 'i18n'))
		.pipe(gulp.dest('.'));
};

const buildTask=gulp.series(cleanTask, internalNlsCompileTask, addI18nTask);

const doCompile=function (buildNls){
	var r=tsProject.src()
		.pipe(sourcemaps.init())
		.pipe(tsProject()).js
		.pipe(buildNls ? nls.rewriteLocalizeCalls() : es.through())
		.pipe(buildNls ? nls.createAdditionalLanguageFiles(languages, 'i18n', 'out') : es.through());

	if (inlineMap && inlineSource){
		r=r.pipe(sourcemaps.write());
	}else{
		r=r.pipe(sourcemaps.write("https://zhuanlan.zhihu.com/out",{
			// no inlined source
			includeContent: inlineSource,
			// Return relative source map root directories per file.
			sourceRoot: "https://zhuanlan.zhihu.com/src"
		}));
	}

	return r.pipe(gulp.dest(outDest));
}

const vscePublishTask=function(){
	return vsce.publish();
};

const vscePackageTask=function(){
	return vsce.createVSIX();
};

gulp.task('default', buildTask);

gulp.task('clean', cleanTask);

gulp.task('compile', gulp.series(cleanTask, internalCompileTask));

gulp.task('build', buildTask);

gulp.task('publish', gulp.series(buildTask, vscePublishTask));

gulp.task('package', gulp.series(buildTask, vscePackageTask));

gulp.task('watch-build', function (){
    buildTask();
    gulp.watch('*.ts', gulp.series(buildTask));
    gulp.watch('https://zhuanlan.zhihu.com/p/i18n/**/*.json', gulp.series(buildTask));
    gulp.watch('https://zhuanlan.zhihu.com/p/package.json', gulp.series(buildTask));
});

修改package.json

{
    "name": "px-to-rpx",
    "displayName": "px to rpx",
    "description": "This is an extension for Visual Studio Code that allows you to convert px to rpx.",
    "version": "0.0.2",
    "publisher": "zhengjiaqi",
    // 仓库地址
    "repository": "https://github.com/zhengjiaqi/vscode-px-to-rpx",
    "engines":{
        "vscode": "^1.35.0"
    },
    // 插件类别
    "categories":[
        "Formatters",
        "Snippets",
        "Other"
    ],
    // 搜索关键字
    "keywords":[
        "px2rpx",
        "rpx",
        "px",
        "px to rpx"
    ],
    // 插件图标
    "icon": "imgs/pxToRpxIcon.png",
    // 插件激活事件
    "activationEvents":[
        "onLanguage:html",
        "onLanguage:vue",
        "onLanguage:swan",
        "onLanguage:wxml",
        "onLanguage:axml",
        "onLanguage:css",
        "onLanguage:wxss",
        "onLanguage:acss",
        "onLanguage:less",
        "onLanguage:scss",
        "onLanguage:sass",
        "onLanguage:stylus",
        "onLanguage:tpl"
    ],
    // 插件入口
    "main": "https://zhuanlan.zhihu.com/p/out/extension.js",
    "contributes":{
        // 注册命令
        "commands":[
{
                "command": "extension.px2rpx",
                "title": "%extension.px2rpx.title%"
            },
{
                "command": "extension.px2rpxInSelection",
                "title": "%extension.px2rpxInSelection.title%"
            }
        ],
        // 注册快捷键
        "keybindings":[
{
                "command": "extension.px2rpx",
                "key": "cmd+Alt+p"
            },
{
                "command": "extension.px2rpxInSelection",
                "key": "Alt+shift+p"
            }
        ],
        // 注册菜单
        "menus":{
            "editor/context":[
{
                    "when": "editorFocus",
                    "command": "extension.px2rpx",
                    "group": "6_px"
                },
{
                    "when": "editorHasSelection",
                    "command": "extension.px2rpxInSelection",
                    "group": "6_px"
                }
            ]
        },
        // 注册插件设置项
        "configuration":{
            "type": "object",
            "title": "px-to-rpx configuration",
            "properties":{
                "px-to-rpx.baseWidth":{
                    "type": "number",
                    "default": 1242,
                    "description": "%extension.baseWidth.title%"
                },
                "px-to-rpx.fixedDigits":{
                    "type": "number",
                    "default": 3,
                    "description": "%extension.fixedDigits.title%"
                },
                "px-to-rpx.autoRemovePrefixZero":{
                    "type": "boolean",
                    "default": false,
                    "description": "%extension.autoRemovePrefixZero.title%"
                }
            }
        }
    },
    "scripts":{
        "compile": "tsc -p https://zhuanlan.zhihu.com/p/",
        "watch": "tsc -watch -p https://zhuanlan.zhihu.com/p/",
        "postinstall": "node https://zhuanlan.zhihu.com/p/node_modules/vscode/bin/install",
        "test": "npm run compile && node https://zhuanlan.zhihu.com/p/node_modules/vscode/bin/test",
        "package": "gulp package",
        "build": "gulp build",
        "publish": "gulp publish",
        "dev": "gulp watch-build"
    },
    "devDependencies":{
        "vscode": "^1.1.28",
        "@types/node": "^10.12.21",
        "@types/mocha": "^2.2.42",
        "del": "^4.1.1",
        "event-stream": "^4.0.1",
        "gulp": "^4.0.2",
        "gulp-filter": "^5.1.0",
        "gulp-sourcemaps": "^2.6.5",
        "gulp-typescript": "^5.0.1",
        "run-sequence": "^2.2.1",
        "tslint": "^5.16.0",
        "typescript": "^3.5.1",
        "vsce": "^1.63.0",
        "vscode-nls-dev": "^3.2.6"
    },
    "dependencies":{
        "vscode-nls": "^4.1.1"
    }
}

然后执行

npm run dev

默认情况下,工程已经帮我们配置好了调试相关参数(有兴趣的可以查看.vscode/launch.json文件的写法),只需要到调试面板选中要调试的项目(仅仅是第一次需要,后续会自动记住上次调试的项目),然后按下F5就会弹出一个新的vscode窗口:

进入到插件运行环境下就可以测试px转rpx功能了。

插件编写好之后,我们可以把开发好的插件打包成.vsix文件,在本地安装测试,或者提供给身边的小伙伴试用一下。

执行

npm run package

此命令会调用vsce模块的createVSIX方法,在项目根目录创建一个.vsix插件包

在VSCode扩展中选择从VSIX安装即可安装本地插件包。重启VSCode,即可使用新的插件功能。

Visual Studio Code的应用市场基于微软自己的Azure DevOps,插件的身份验证、托管和管理都是在这里。要发布到应用市场首先得有应用市场的publisher账号

  1. 注册账号login.live.com/
  2. 访问aka.ms/SignupAzureDevOp,如果你从来没有使用过Azure,那么会看到如下提示:

点击继续,默认会创建一个以邮箱前缀为名的组织。

3.默认进入组织的主页后,点击右上角的Security

点击创建新的个人访问令牌,这里特别要注意Organization要选择all accessible organizationsScopes要选择Full access,否则后面发布会失败。

创建令牌成功后你需要本地记下来,因为网站是不会帮你保存的。

4. 获得个人访问令牌后,使用vsce以下命令创建新的发布者:

vsce create-publisher your-publisher-name  

创建成功后会默认登录这个账号,接下来你可以直接发布了,当然,如果你是在其它地方创建的,可以试用vsce login your-publisher-name来登录。

5. 执行发布命令

npm run pubilsh

此命令调用vsce publish 进行发布。
发布成功后,可以访问marketplace.visualstudio.com 查看

在VSCode中也可以搜索进行安装

github.com/zhengjiaqi/v

blog.haoji.me/vscode-pl

github.com/microsoft/vs

在线客服

关注我们 在线咨询 投诉建议 返回顶部

平台注册入口