需要的起因是英语渣在开发的过程中常常遇到一个变量晓得中文叫啥,然而英文单词可能就忘了,或者不晓得,这个时候,我之前做法是关上浏览器,关上谷歌翻译,输出中文,复制英文,而后切回vscode,粘贴后果。切实是太麻烦了,年老的时候还好,忘性好,英文单词大部分都能记住,但随着年纪越来越大,头发越来越少,忘性也是越来越差,下面的步骤反复的次数也越来越多,所以痛定思痛,就开发了这款插件。因为本人也是这几天从零开始学习的插件开发,所以本文齐全的记录了一个小白开发的插件开发之路,内容次要是实战类的介绍,次要从四个方面介绍来残缺的展现整个插件从功能设计到公布的残缺历程。

  1. 功能设计
  2. 环境搭建
  3. 插件性能开发
  4. 插件打包公布

功能设计

性能次要是两个性能,中译英,其余语言翻译成中文

  1. 将中文变量替换为翻译后的英文变量,多个单词须要主动驼峰,解决的场景就是日常开发常常碰到的“英语卡壳”
  2. 划词翻译,主动将各种语言翻译成中文,这解决的场景是有时候看国外我的项目源代码的正文碰到不会的单词不晓得啥意思影响效率

环境搭建

上手的第一步,环境搭建

  1. 装置脚手架, yogenerator-code,这两个工具能够帮忙咱们疾速构建我的项目,详情可见 Github

    //装置yarn global add yo generator-code
  2. 装置vsce,vsce可用来将开发的代码打包成.vsix后缀的文件,不便上传至微软插件商店或者本地装置

    yarn global add vsce
  3. 生成并初始化我的项目,初始化信息依据本人状况填写

    //初始化生成我的项目yo code

    到这一步后,抉择间接关上,Open with code

    关上后会主动建设一个工作区,并生成这些文件,可依据本人须要对文件进行删减,实现这步后,咱们能够间接进行开发与调试了

    如何进行调试?

    运行与调试面板点击Run Extention,或者快捷键F5,mac能够间接点击触控栏的调试按钮

    关上后会弹出一个新的vscode窗口,这个新的窗口就是你的测试环境(扩大开发宿主),你做的插件性能就是在这个新的窗口测试,打印的音讯在前一个窗口的调试控制台中,比方自带的例子,在咱们新窗口 cmd/ctrl+shift+p后输出Hello world后会在前一个窗口的控制台打印一些信息

    到这里,开发筹备环境就筹备好了,下一步就是开始正式的插件性能开发

插件性能开发

插件开发中,有两个重要的文件,一个是 package.json,一个是 extention.js

重要文件阐明

package.json

  • activationEvents用来注册激活事件,用来表明什么状况下会激活extention.js中的active函数。常见的有onLanguage,onCommand...更多信息可查看vscode文档activationEvents
  • main示意插件的主入口
  • contributes用来注册命令(commands),绑定快捷键(keybindings),配置设置项(configuration)等等,更多可配置项可看文档
extention.js

extention.js次要作用是作为插件性能的实现点,通过active,deactive函数,以及vscode提供的api以及一些事件钩子来实现插件性能的开发

实现翻译性能

翻译这里次要是应用了两个服务,谷歌和百度翻译。

  1. 谷歌翻译参考了他人的做法,应用google-translate-token获取到token,而后结构申请url,再解决返回的body,拿到返回后果。这里还有一个没搞懂的中央就是申请url的生成很迷,不晓得这一块是啥意思。

    const qs = require('querystring');const got = require('got');const safeEval = require('safe-eval');const googleToken = require('google-translate-token');const languages = require('../utils/languages.js');const config = require('../config/index.js');// 获取申请urlasync function getRequestUrl(text, opts) {    let token = await googleToken.get(text);    const data = {        client: 'gtx',        sl: opts.from,        tl: opts.to,        hl: opts.to,        dt: ['at', 'bd', 'ex', 'ld', 'md', 'qca', 'rw', 'rm', 'ss', 't'],        ie: 'UTF-8',        oe: 'UTF-8',        otf: 1,        ssel: 0,        tsel: 0,        kc: 7,        q: text    };    data[token.name] = token.value;    const requestUrl = `${config.googleBaseUrl}${qs.stringify(data)}`;    return requestUrl;}//解决返回的bodyasync function handleBody(url, opts) {    const result = await got(url);    let resultObj = {        text: '',        from: {            language: {                didYouMean: false,                iso: ''            },            text: {                autoCorrected: false,                value: '',                didYouMean: false            }        },        raw: ''    };    if (opts.raw) {        resultObj.raw = result.body;    }    const body = safeEval(result.body);    // console.log('body', body);    body[0].forEach(function(obj) {        if (obj[0]) {            resultObj.text += obj[0];        }    });    if (body[2] === body[8][0][0]) {        resultObj.from.language.iso = body[2];    } else {        resultObj.from.language.didYouMean = true;        resultObj.from.language.iso = body[8][0][0];    }    if (body[7] && body[7][0]) {        let str = body[7][0];        str = str.replace(/<b><i>/g, '[');        str = str.replace(/<\/i><\/b>/g, ']');        resultObj.from.text.value = str;        if (body[7][5] === true) {            resultObj.from.text.autoCorrected = true;        } else {            resultObj.from.text.didYouMean = true;        }    }    return resultObj;}//翻译async function translate(text, opts) {    opts = opts || {};    opts.from = opts.from || 'auto';    opts.to = opts.to || 'en';    opts.from = languages.getCode(opts.from);    opts.to = languages.getCode(opts.to);    try {        const requestUrl = await getRequestUrl(text, opts);        const result = await handleBody(requestUrl, opts);        return result;    } catch (error) {        console.log(error);    }}// 获取翻译后果const getGoogleTransResult = async(originText, ops = {}) => {    const { from, to } = ops;    try {        const result = await translate(originText, { from: from || config.defaultFrom, to: to || defaultTo });        console.log('谷歌翻译后果', result.text);        return result;    } catch (error) {        console.log(error);        console.log('翻译失败');    }}module.exports = getGoogleTransResult;
  1. 百度翻译,百度翻译的比较简单,申请服务,取得appid和key,而后结构申请url间接申请就行,不晓得如何申请的,可查看我之前的一篇文章Electron+Vue从零开始打造一个本地文件翻译器进行申请

    const md5 = require("md5");const axios = require("axios");const config = require('../config/index.js');axios.defaults.withCredentials = true;axios.defaults.crossDomain = true;axios.defaults.headers.post["Content-Type"] =    "application/x-www-form-urlencoded";// 百度翻译async function getBaiduTransResult(text = "", opt = {}) {    const { from, to, appid, key } = opt;    try {        const q = text;        const salt = parseInt(Math.random() * 1000000000);        let str = `${appid}${q}${salt}${key}`;        const sign = md5(str);        const query = encodeURI(q);        const params = `q=${query}&from=${from}&to=${to}&appid=${appid}&salt=${salt}&sign=${sign}`;        const url = `${config.baiduBaseUrl}${params}`;        console.log(url);        const res = await axios.get(url);        console.log('百度翻译后果', res.data.trans_result[0]);        return res.data.trans_result[0];    } catch (error) {        console.log({ error });    }}module.exports = getBaiduTransResult;

获取选中的文本

应用事件钩子onDidChangeTextEditorSelection,获取选中的文本

    onDidChangeTextEditorSelection(({ textEditor, selections }) => {        text = textEditor.document.getText(selections[0]);    })

配置项的获取更新

通过vscode.workspace.getConfiguration获取到工作区的配置项,而后通过事件钩子onDidChangeConfiguration监听配置项的变动。

获取更新配置项

const { getConfiguration } = vscode.workspace;const config = getConfiguration();//留神get外面的参数其实就是package.json配置项外面的contributes.configuration.properties.xxxconst isCopy = config.get(IS_COPY);const isReplace = config.get(IS_REPLACE);const isHump = config.get(IS_HUMP);const service = config.get(SERVICE);const baiduAppid = config.get(BAIDU_APPID);const baiduKey = config.get(BAIDU_KEY);//更新应用update办法,第三个参数为true代表利用到全局config.update(SERVICE, selectedItem, true);

监听配置项的变动

const { getConfiguration, onDidChangeConfiguration } = vscode.workspace;const config = getConfiguration();//监听变动const disposeConfig = onDidChangeConfiguration(() => {  config = getConfiguration();})

监听个别配置项的变动

const disposeConfig = onDidChangeConfiguration((e) => {    if (e && e.affectsConfiguration(BAIDU_KEY)) {        //干些什么    }})

获取以后关上的编辑器对象

vscode.window.activeTextEditor代表以后关上的编辑器,如果切换标签页,而没有设置监听,那么这个这个对象不会自动更新,所以须要应用onDidChangeActiveTextEditor来监听,并替换之前的编辑器对象

const { activeTextEditor, onDidChangeActiveTextEditor } = vscode.window;let active = activeTextEditor;const edit = onDidChangeActiveTextEditor((textEditor) => {  console.log('activeEditor扭转了');  //更换关上的编辑器对象  if (textEditor) {      active = textEditor;  }})

划词翻译悬浮提醒

通过vscode.languages.registerHoverProvider注册一个Hover,而后通过activeTextEditor拿到选中的词语进行翻译,而后再通过new vscode.Hover将翻译后果悬浮提醒

// 划词翻译检测const disposeHover = vscode.languages.registerHoverProvider("*", {    async provideHover(document, position, token) {        const service = config.get(SERVICE);        const baiduAppid = config.get(BAIDU_APPID);        const baiduKey = config.get(BAIDU_KEY);        let response, responseText;        const selected = document.getText(active.selection);        // 谷歌翻译        if (service === 'google') {            response = await getGoogleTransResult(selected, { from: 'auto', to: 'zh-cn' });            responseText = response.text;        }        // 百度翻译        if (service === 'baidu') {            response = await getBaiduTransResult(selected, { from: "auto", to: "zh", appid: baiduAppid, key: baiduKey });            responseText = response.dst;        }        // 悬浮提醒        return new vscode.Hover(`${responseText}`);    }})

替换选中的文本

获取到activeTextEditor,调用他的edit办法,而后应用回调中的replace

//是否替换原文if (isReplace) {  let selectedItem = active.selection;  active.edit(editBuilder => {    editBuilder.replace(selectedItem, result)  })}

复制到剪贴板

应用vscode.env.clipboard.writeText;

// 是否复制翻译后果if (isCopy) {  vscode.env.clipboard.writeText(result);}

驼峰解决

function toHump(str) {    if (!str) {        return    }    const strArray = str.split(' ');    const firstLetter = [strArray.shift()];    const newArray = strArray.map(item => {        return `${item.substring(0,1).toUpperCase()}${item.substring(1)}`;    })    const result = firstLetter.concat(newArray).join('');    return result;}module.exports = toHump;

快捷键绑定

通过vscode.commands.registerCommand注册绑定之前package.json中设置的keybindings,须要留神的是registerCommand的第一个参数须要与keybindings的command保持一致能力绑定

registerCommand('translateVariable.toEN', async() => {  //do something})//package.json"keybindings": [{  "key": "ctrl+t",  "mac": "cmd+t",  "when": "editorTextFocus",  "command": "translateVariable.toEN"}],

插件打包公布

打包

vsce package

打包后会在目录下生成.vsix后缀的插件

公布

插件公布次要是把打包的vsix后缀插件,传入微软vscode插件商店,当然也能本地装置应用。

传入商店

公布到线上须要到微软插件商店治理页面,创立发布者信息,如果没有微软账号,须要去申请。

创立实现后,抉择公布到vscode商店

本地装置

本地是能够间接装置.vsix后缀插件的,找到插件菜单

抉择从VSIX装置,装置下面打包的插件就好了

最初

vscode的中文材料有点少,这次开发大多数工夫都在看英文文档,以及上外网寻找材料,真的英语太重要了,前面得多学点英语了,心愿前面我应用本人做的这个插件的次数会越来越少,我的项目已开源,应用阐明与源码传送门