如何开发一款中二的vscode命名翻译插件

我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!

开局

  年中狗血的ppt工程师终于停下了内卷的脚步,闲暇思考(突发奇想),vscode已经是前端主流编译器了(其他编译器爱好者勿喷),那么我们日常用的插件是怎么开发的呢?脑洞大开,我就想做一个插件出来,既是学习,又能自己使用,然而很多工具都有了,一个中二的想法就在我脑子中出现了,项目开发功能时候一些专业名词,每次都得靠度娘翻译器折腾翻译一遍,要是命名就像写中文一样,会不会是一种很中二的体验?

  准备动手之前,先思考了这个插件的功能构想及面向群体;

定位

  • 有变量命名焦虑症的;
  • 英文词汇匮乏、或者写得久了就没有单词的困难户;
  • 老夫只用汉语拼音的用户;
  • 英文单词拼写错误大王;

功能构成

  • 前端项目文件中可以通过 const 变量名 or let 变量名 等命名关键词命名书写;
  • 插件监听文件,获取上述文本,匹配中文字符串;
  • 经过翻译api进行翻译(这里用的是百度翻译api,非广告);
  • 拿到翻译内容,组合成camel case 命名,替换代码中对应位置;

  想一想功能挺简单,那就开始我们的准备工作;

起步

第一步

  术业有专攻,我们得有具体插件开发的专业知识,我建议两种方法来学习:

  • 第一种: 文档学习,这种方法对于理解能力强的人来说会快速一点;
  • 第二种: 熟悉的插件源码 + 文档辅助,这种方法能快速上手(建议用这种方式);

  下面附上插件文档链接(翻译文档,照顾英文不好的同学);

第二步

  申请翻译工具提供的开放api

第三步

  插件功能执行流程,这里我大概画了一版插件功能构想图:

编译图片.png

开动

一、创建项目

  这块我的建议是参考 vscode 官方脚手架进行创建,参考官方文档,没有什么难度,照猫画虎即可~

  项目生成之后,我这边的进行了结构做了微小的调整,方便后续开发,具体如下:

结构.png

  这里我将结构目录按照功能块做了划分,extension.ts 更多的是作为功能注入的入口,方便我们后续对于其他功能的扩展;

二、插件开发过程

  从这里我们就开始进行插件核心功能开发,我们参考系统构想图进行一步步的代码实现:

  • 1、我们调整 extension.ts,具体如下:
import * as vscode from 'vscode';
import translateFun from './translate';

export function activate(context: vscode.ExtensionContext) {
    // add transtale method
    translateFun();
}
// this method is called when your extension is deactivated
export function deactivate() {
}
复制代码
  • 2、实现 onDidChangeTextDocument监听,这个事件主要用途是监听我们当前工作区内,正在编辑的文件发生写入删除操作的事件,举个不恰当的栗子,就如同前端经常监听 input onChange 事件;同时还需要注意
    • 这种密集触发的事件,防抖是第一要务,这里我们用的是 lodash 防抖工具函数;
    • 百度通用版翻译api,有吞吐量限制,同时也有翻译字符数限制;

const onTextDocumentChange = async (e: vscode.TextDocumentChangeEvent) => {
    console.log(e); 
};

const translateFun = () => {

  const configInfo = getConfiguration();

  vscode.workspace.onDidChangeTextDocument(debounce(onTextDocumentChange, 1500));
};

export default translateFun;

复制代码
  • 3、接下来我们就将核心流程伪代码进行备注,然后一一去实现,获取编辑行通过vscode.window.activeTextEditor.selections 这个方式拿到,在经 e.document.lineAt 获取文本行,大家可以去官方文档查阅相关用法,这里伪代码具体如下:

const onTextDocumentChange = async (e: vscode.TextDocumentChangeEvent) => {
    console.log(e); 
    
    for (const s of vscode.window.activeTextEditor.selections) {
        const lineNumber = s.start.line;
        const lineText = e.document.lineAt(lineNumber).text;

        // 判断是否是注释,如果注释就不往下执行
        if (isCheckComment(lineText)) {
            return;
        }
        // 判断是否是插件支持的翻译方式,并获取文本
        const filterText = getFilterText(lineText);
        if (!filterText) {
            return;
        }
        // 执行翻译 + 转化命名
        const text = await getTranslationInfo(filterText);

        // 如果翻译失败 or  转换失败 停止
        if (!text) {
            return;
        }
        // 替换选定内容
        replaceLines();
    }
};
复制代码
  • 4、按照上面的流程去实现代码,其中判断是否是注释 or 翻译文案的办法就是正则表达式,具体两个方法如下:
// 可以通过正则表达式判定是否是注释
const isCheckComment = (text: string) => {
  const reg = /(\/\/|\*)\s*(const|var|let)\s+[\u4e00-\u9fa5]+\s*/;

  const result = reg.exec(text);

  return !!result;
};
复制代码
const getFilterText = (text: string) => {
  let result = "";
  const reg = /(const|var|let)\s+[\u4e00-\u9fa5]+\s*/;
  const regText = /[\u4e00-\u9fa5]+/;

  const list = reg.exec(text);

  if (list === null || !list.length) {
    return result;
  }

  const matchInfo = regText.exec(list[0]);

  result = matchInfo !== null ? matchInfo[0] : "";

  return result;
};
复制代码
  • 5、在翻译之前我们需要获取插件配置,vscode插件配置项可以通过 package.json 方式配置,在插件项目中可以通过 vscode.workspace.getConfiguration拿到所有的配置,然后筛选出我们需要的配置项,这里关于 time 配置项我做了一个兜底值 1000ms ,防止配置太小的值,输入文案后触发多次翻译请求,下面是代码实现部分:
{
    ......
    "contributes": {
		"configuration": {
			"title": "transform helper",
			"properties": {
				"transform-helper.appid": {
					"type": "string",
					"description": "Baidu translation private account appid",
					"default": ""
				},
				"transform-helper.key": {
					"type": "string",
					"description": "Baidu translation private account key",
					"default": ""
				},
				"transform-helper.time": {
					"type": "number",
					"description": "Stop editing and trigger translation after a few seconds (ms)",
					"default": 1500
				}
			}
		}
	},
    ......
}
复制代码
const getConfiguration = () => {
  const configuration = vscode.workspace.getConfiguration();

  const appid = configuration.get<string>('transform-helper.appid');
  const key = configuration.get<string>('transform-helper.key');
  const time = configuration.get<number>('transform-helper.time');

  return {
    appid,
    key,
    time: time && time > 1000 ? time : 1000
  };
};
复制代码
  • 6、接下来就是翻译与生成命名了,这块由 翻译Api请求 + 生成 camel case 命名 两个部分,但是需要注意一点,我们插件程序是运行在node环境中的,所以选择可以在node环境中使用的请求库,这里我们使用的是 got 请求工具库,当然也可以选择 axiosnode-fetch这些,随自己的喜好而定,下面附上我的代码:
const getTranslationInfo = async (q: string) => {

  const configInfo = getConfiguration();

  const url = "https://fanyi-api.baidu.com/api/trans/vip/translate";

  const salt = Math.floor(Math.random() * 1000000);
  const appid = configInfo.appid || "xxxxxxxx";
  const key = configInfo.key || "xxxxxx";
  const sign = md5(appid + q + salt + key);

  try {

    const data: TranstionResult = await got.post(url, {
      form: { from: 'auto', to: 'en',  salt, sign, q, appid },
    }).json();
    // return camel case info
    if (data && data.trans_result && data.trans_result.length) {
      return createCamelCase(data.trans_result[0]);
    }
  } catch (e) {
    console.log(e);
  }
  vscode.window.showWarningMessage('Translation failed, please edit again');
  return '';
};
复制代码
const createCamelCase = (t: TranstionItem) => {
  const words = t.dst.split(' ').map((s: string, i: number) => {
    // 百度翻译,有时候翻译的内容第一个单词首字母会大写,需要排除
    if (i === 0) {
      return s.toLowerCase();
    }
    return s.slice(0, 1).toUpperCase() + s.slice(1);
  });

  return words.join('');
};
复制代码
  • 7、最后一步,将翻译后的内容应用到工作区;思路很好理解,就像 如何把大象装进冰箱(开门,塞进去,关门)~,这里我们需要找到替换内容 vscode.Range( startPosition, endPosition), 准备好替换的内容,通过 new vscode.Workspace().replace() 进行替换内容,最后通过 vscode.workspace.applyEdit() 将替换好得内容应用到工作区,实现代码如下:
const replaceLines = async (oldStr: string, translateText: string, uri: vscode.Uri, lineNum: number) => {
  const edit = new vscode.WorkspaceEdit();

  const newStr = oldStr.replace(/[\u4e00-\u9fa5]+/, translateText);
  const startPosition = new vscode.Position(lineNum, 0);
  const endPosition = new vscode.Position(lineNum, oldStr.length);

  edit.replace(uri, new vscode.Range(
    startPosition,
    endPosition
  ), newStr);
  await vscode.workspace.applyEdit(edit);
};
复制代码

  下面是插件核心代码,请参考github仓库(github仓库地址);插件名为 transform helper已发布;

发布

  最后插件做了出来,测试没问题了,我们就需要发布上线(提前准备好自己的windows平台的账号),具体的步骤如下:

  • 1、安装发布工具 npm i vsce -g

  • 2、去 azure devops 平台申请秘钥(地址),网上搜一下具体教程;

  • 3、vscode 插件平台,创建 publisher, 同时在插件的 package.json 写入 "publisher": "xxxxxxxx"

  • 4、项目中执行 vsce publish 输入秘钥就可以发布成功了;

不足与展望

  • 首先项目中暂时还没有完善单元测试,这个是不好的一方面,一个好项目得有单测保驾护航(后续完善);

  • 使用翻译工具api这块选择太单一,更好的办法就是改造加入多个平台配置,用户可以自有选择,目前是写死,有点扯;

  展望一下,将来是否可以将前端 console.log 打印日志操作简化,或者其他方便开发的想法加入插件中来;

  文末,阅读的朋友不妨试一试这个中二的功能,好评差评通通接受~

猜你喜欢

转载自juejin.im/post/7125710087250771998
今日推荐