「教你用十分钟开发一款提升工作体验的vscode插件🌿 」console, debugger一键删除|自定义代码模板

5,149 阅读12分钟

大家好,我是寒草😈,一只草系码猿🐒。间歇性热血🔥,持续性沙雕🌟
如果喜欢我的文章,可以关注➕点赞,与我一同成长吧~

前奏🎵

我想读我文章的肯定是程序猿或者程序媛,不知道大家编码用的IDE是什么,反正我使用的一直是vscode,寒草和vscode也是亲如一家的好伙伴。

寒草🌿:“vscode,我们做兄弟吧”
vscode🪄:“给劳资滚”
寒草🌿:“好嘞,明儿见”

就这样,不打不相识,我和vscode成为了好伙伴,既然我们工作中很大一部分时间都是在和vscode打交道,并且vscode插件的能力是十分强大的,那么很自然浮现的想法就是:“我是不是可以掌握一些vscode插件开发知识,以达到优化我工作体验的目的?”,这个问题是显而易见的,大家的vscode应该都会装几个提高开发效率,优化开发体验的插件,比如:

  • veture
  • eslint
  • todo highlight
  • ...

这些插件对日常工作真的帮助很大,他们强大的功能也正印证着我上文的观点:

用vscode插件,可以优化我们工作体验

所以我们不妨亲手通过vscode插件开发以对我们朝夕相处的vscode进行能力增强,达到优化工作体验的目的。前一阵我做了一个vscode插件,名字叫做fe-file-rename,大家已经可以在插件商店搜索到,也专门为它写了一篇文章:我的第一次VS Code插件开发:fe-file-rename && 一些絮絮叨叨

fe-file-rename功能介绍:

  1. 批量修改文件名,支持下划线,连字符,大驼峰,小驼峰三种命名方式,并同步修改引用
  2. 当用户修改文件名或者修改文件路径时,自动将引用更新(有开关,可以关闭) -暂时只支持vue项目-

我想这个vscode插件解决了我修改文件名或者文件路径,不知道去哪修改文件引用,怕错改,怕漏改的问题,大大提高了我的工作体验。
优化工作体验这种事情会上瘾的,最近我又开发了两个vscode插件,来对我的工作体验进行优化,我也会一步一步的引导大家,挖掘工作中的隐藏需求,之后将其实现。

当然,当你的插件出现在vscode插件商店,也是成就感满满🔥~

间奏🎵

创建一个属于你的vscode-extension

官方文档:code.visualstudio.com/api
安装vscode插件开发脚手架

npm install -g yo generator-code

输入yo code初始化代码

image.png

新建一个项目就这么简单,后续我就通过我最近的两次实践陪大家一起完成两个简单却实用的vscode插件吧🌟~

console,debugger一键删除术🎋

image.png

插件名:invalid-code-remover,欢迎搜索下载

下面的对话改编自现实工作:
无辜的寒草:“我提mr了,你给我合一下~”
暴躁的leader:“好...诶!你这怎么有console和debugger啊。”
无辜的寒草:“啊啊啊,漏删了”
暴躁的leader:“这种代码不要带到release上来,去删了!”

console和debugger在我们日常调试中经常用到,用的多了漏删了也是家常便饭,我们有很多办法去解决console这个问题:

  • 代码提交时校验
  • 编译时删除
    我还没太接触过这两个东西,但是既然我们最好不要让consoledebugger出现在release分支(其实我也有强迫症,不太喜欢看到别人的console或者debugger),那么就需要一个手段去解决,去规避漏删consoledebugger的问题,那么我选择自己去解决,通过我的手段,我首先想到的就是通过vscode插件。
    首先我想梳理一下思路:
  1. 右键选择文件或者目录
  2. 读取选择的文件或者目录的所有子文件
  3. 通过正则替换掉之中的console以及debugger
  4. 将处理完成的内容写入文件

好的,既然思路已经有了,那么我们现在开始:

"activationEvents": [
    "onCommand:invalid-code-remover.removeConsole",
    "onCommand:invalid-code-remover.removeDebugger"
  ],
  "main": "./dist/extension.js",
  "contributes": {
    "commands": [
      {
        "command": "invalid-code-remover.removeConsole",
        "title": "remove console"
      },
      {
        "command": "invalid-code-remover.removeDebugger",
        "title": "remove debugger"
      }
    ],
    "menus": {
      "explorer/context": [
        {
          "command": "invalid-code-remover.removeConsole",
          "when": "filesExplorerFocus",
          "group": "navigation@1"
        },
        {
          "command": "invalid-code-remover.removeDebugger",
          "when": "filesExplorerFocus",
          "group": "navigation@2"
        }
      ]
    }
  }

首先,看一看,我在package.json里面的配置项,首先我有两个指令:

  • invalid-code-remover.removeConsole 删除console
  • invalid-code-remover.removeDebugger 删除debugger

并把他们的触发条件设置为filesExplorerFocus,这样这两个指令对应的title就会出现在你右键点击文件或者目录时出现的菜单上。

image.png

之后我们看一下入口文件extension.ts的内容:

import * as vscode from 'vscode';
import { removeInvalidCodeEntry } from './handlers';
import { INVALID_TYPE_MAP } from './configs';

export function activate(context: vscode.ExtensionContext) {
	
    const removeConsole = vscode.commands.registerCommand('invalid-code-remover.removeConsole', (params) => {
        const isSuccess = removeInvalidCodeEntry(params.fsPath, INVALID_TYPE_MAP.get('CONSOLE'));
        if(isSuccess) {
          vscode.window.showInformationMessage('remove console success!');
        } else {
          vscode.window.showErrorMessage('remove console failed!');
        }
    });

	const removeDebugger = vscode.commands.registerCommand('invalid-code-remover.removeDebugger', (params) => {
        const isSuccess = removeInvalidCodeEntry(params.fsPath, INVALID_TYPE_MAP.get('DEBUGGER'));
        if(isSuccess) {
            vscode.window.showInformationMessage('remove debugger success!');
        } else {
          vscode.window.showErrorMessage('remove debugger failed!');
        }
     });

    context.subscriptions.push(...[removeConsole, removeDebugger]);
}

export function deactivate() {}

大家可以把activate理解为一个入口方法。
我通过vscode.commands.registerCommand把上面两个时间注册,并把params.fsPath作为参数,传给了我定义的方法removeInvalidCodeEntry,当然我同时还传了一个类型INVALID_TYPE_MAP.get('CONSOLE'),用去确定用户是想删除console还是想删除debugger

这里也给大家看看我定义的常量,用于确定操作类型和不必要参与删除consoledebugger操作的目录或者文件:

export const UN_MATCH = 0;

export const INVALID_TYPE_MAP = new Map([
    ['CONSOLE', 1],
    ['DEBUGGER', 2]
]);

export const EXCLUDE_DIR_NAME:Set<string> = new Set([
    'public',
    'dist',
    'node_modules',
    'docs',
    'test',
]);
export const EXCLUDE_FILE_EXTNAME:Set<string> = new Set([
    'css',
    'sass',
    'less',
    'md',
    'lock',
    'json',
    'yarnrc',
    'svg',
    'png',
    'gitignore',
    'vscodeignore',
    ''
]);

下面我们进入我的重头戏吧,我把我想讲的内容放在注释里:

import { UN_MATCH, INVALID_TYPE_MAP, EXCLUDE_DIR_NAME, EXCLUDE_FILE_EXTNAME } from '../configs';
import { lstatSync, readFileSync, writeFileSync, existsSync, readdirSync } from 'fs';
import { join, basename, extname } from 'path';

// 封装isDir方法,用于判断是目录还是文件
function isDir(path: string): boolean {
  const stat = lstatSync(path);
  return stat.isDirectory();
}

export function removeInvalidCodeEntry(path: string, type: number = UN_MATCH): boolean {
  // 如果传入的操作类型不在我接受的类型里,我直接return false;
  if (type === UN_MATCH) {
    return false;
  }
  // 如果传入文件路径不存在,我直接return false;
  if (!existsSync(path)) {
    return false;
  }
  // 异常处理,如果发生异常,我直接return false;
  try {
    if (isDir(path)) {
      // 上文常量中有的目录不需要处理
      if(EXCLUDE_DIR_NAME.has(basename(path))) {
        return true;
      }
      // 是目录的话,去获取其子文件,进行递归
      const files = readdirSync(path);
      files.forEach((file: any) => {
        const subPath = join(path, file);
        removeInvalidCodeEntry(subPath, type);
      });
    } else {
      // 如果该文件类型不需要处理,直接return
      if(EXCLUDE_FILE_EXTNAME.has(`.${extname(path)}`)) {
        return true;
      }
      const fileOptions = { encoding: 'utf-8' as BufferEncoding };
      const content = readFileSync(path, fileOptions);
      // 我采用读取文件之后逐行进行替换的方式,原因我在后续中会进行介绍
      const lines = content.split('\n');
      const handledLines = [];
      // 逐行删除console和debugger
      for (const line of lines) {
        let handledLine = '';
        switch (type) {
          case (INVALID_TYPE_MAP.get("CONSOLE")):
            handledLine = line.replace(/console\..*\(.*\)( )*;?/g, '');
            break;
          case (INVALID_TYPE_MAP.get("DEBUGGER")):
            handledLine = line.replace(/debugger( )*;?/g, '');
            break;
          default:
            handledLine = line;
            break;
        }
        if (handledLine === line || !/^( )*$/.test(handledLine)) {
          handledLines.push(handledLine);
        }
      }
      let handledContent = handledLines.join('\n');
      // 文件写入,当然如果文件没有发生变更,则不需要写入
      if (handledContent !== content) {
        writeFileSync(path, handledContent, fileOptions);
      }
    }
    return true;
  } catch(err) {
    return false;
  }
}

那么我们为什么会采用逐行进行遍历删除console或者debugger的操作呢?

我想多数工程师都会有代码强迫症,如果用了我的插件,之后consoledebugger删除了,却在那里留下了一个空行,岂不是十分的逼死强迫症。所以我使用逐行处理,如果替换后的行变成了空行,不进行push操作,这样就保持了原文件的格式。

下面我们看一下效果:

image.png 格式丝毫没有影响~完美✨~
大家赶快去下载吧🔥

配置属于你的代码段🎋

image.png

插件名:trantor-md-snippets,欢迎搜索下载

最近我一直在写公共组件的文档和系统重构的设计,里面有大量的公共格式,但是每次要去别的地方复制粘贴很不爽。

// 比如这种公共的表格
#### Attributes
| 参数 | 说明 | 类型 | 默认值 |
| ---- | ---- | ---- | ---- |
|  |  |  |  |

// 比如这种uml
@startuml
top to bottom direction
component 组件名 [
组件中文描述
组件名连字符表示
--props--

--events--

--methods--

--slots--

]
 子组件 ->  父组件
@enduml

所以我就想能不能借助vscode插件的代码段来解决这个问题,我简单的打两个字母,在按一个Tab,夸嚓,模板就出来了,我直接在上面写就好了~那么说干就干,为了我的工作幸福度,说干就干!

这次就比较简单了,我们先看package.json:

"categories": [
    "Snippets"
],
"contributes": {
    "snippets": [
        {
            "language": "markdown",
            "path": "./snippets.json"
        }
     ]
},

我限制这个插件只在markdown中生效。之后大家去看看我的snippets.json吧

{
"设计文档: 视图组件图": {
        "prefix": "Components",
		"body": [
            "@startuml",
            "top to bottom direction",
            "component ${1:组件名} [",
            "${2:组件中文描述}",
            "${3:组件名连字符表示}",
            "--props--",
            "$4",
            "--events--",
            "$5",
            "--methods--",
            "$6",
            "--slots--",
            "$7",
            "]", 
            "${8: 子组件} -> ${9: 父组件}",
            "@enduml"
		],
		"description": "设计文档: 视图组件图"
    },
    ...
}

这里我只举了一个例子,当我输入Components的时候就会有这样的提示:

image.png

按下Tab,这个代码段就出来了:

image.png

当然如果安装了plantUML的话,可以直接试一下:

image.png

后续我也会给大家分享如何去做前端设计,敬请期待~

尾奏🎵

把彩蛋写在七夕🎋

image.png

今天又到了七夕,又是一个人过的,窗外的北京下起了雨🌧️,窗内的小屋独自写文。本文的代码写于七夕,文章于七夕的雨夜开始码字,其实这一个章节是我在写正文前弄的,想不到吧哈哈哈,我想了想不知道拿什么内容放在这一章,但是总想在这个平凡而特殊的日子写一点特殊的东西。正巧我最近其实也在写一点我们前端团队的介绍文,有人就说我:“这文章都能写好几千字,感觉你有写小说的天赋!”,诶?那我在这个章节写个小作文好了,之后大家就知道了,我其实并没有写小说的天赋,那么现在开始吧,寒草的睡前即兴小故事:

雨一直下个不停,雨滴打在窗外的挡板上噼里啪啦。男孩坐在自己的桌子前发呆,也不知道在想写什么,突然手机震动,“嘟嘟嘟”,吓了男孩一激灵,但也算是把他的思绪唤回了现实世界。
“谁会给我发消息啊,又是广告推销吧”,男孩心想。打开手机一看,是陌生号码,正常的手机号是11位的,这个号码居目测有20几位...男孩看了看内容也是觉得莫名其妙,短信上充斥着莫名其妙的字符,中间穿插着几个中文汉字:
你,未,四百,验,尝试,话
男孩不自主的跟着读短信中少数认识的字,“莫名其妙,啥玩意啊,又是个垃圾短信吧”,随后也没多想便把手机放在一旁,走向窗户,手按在玻璃上,仿佛能感觉到雨水击打在自己的肌肤,他离世界只有一层玻璃的距离,但是似乎无比遥远。叹了口气,男孩回到床边,恣意的躺了下去,直勾勾地望着氧化有些许发黄的天花板,轻叹了口气,合上双眼陷入了梦乡。
...
第二天清晨,男孩被一阵吵闹声惊醒,只听楼道里嚷嚷着,“反了你了!TMD今天是你能出门的日子么!下次再想往外面闯,我先打断你的腿,再多关你三个月”,声音中透露出威严,令人不寒而栗。
“又是那兄弟要逃吧...也不知道外面有什么好”,男孩囔囔着,转头望了望窗外,也就比夜晚亮那么一点点,空气中仿佛洒满了灰尘,隐约可见外面的摩天大楼散发着魔幻诡异的光,“也不想想,外面这样的世界有什么吸引人的,切”,男孩抓了抓头发,拿起一旁的手机,眼前的一幕让他惊呆了。
99+!
天啊,男孩被关在这里五个月了,都没有收到99个消息吧。消息都是昨天那个陌生号码发送来的,男孩开始只能看到重复的:
收到请回话
收到请回话
收到请回话
往上翻了好久,终于看到了最初的几条消息:
诶呀,我忘记切换成你们那个年代的编码格式了,现在应该好了,接下来请听我说
男孩咽了口口水,继续往下读:
你好,菜狗子,我来自未来,没错,四百年后的未来,我在进行一项实验,尝试拯救世界的伟大实验,需要你的帮助,让我们来一场穿越时空的救援行动吧~
怎么还不回话,拯救世界还不积极?
果然是菜狗子
臭弟弟
emmm,收到请回话
...
收到请回话
男孩有一点无法理解对方的话,甚至觉得对方或许是个精神病,但是在这个日复一日枯燥的生活待久了突然感觉到了一丝丝惊喜,握紧手机,仔细的打下几个字:
我在,刚在睡觉...有什么需要我做吗?”,男孩儿仔细检查打在屏幕上的话,轻按发送。
发送中...
发送中...
loading转了几圈,终于在屏幕上显示出一个绿色的对号
发送成功
手机瞬间震动,没错还是那个号码的消息,文字简短,又充满元气:
“终于回复啦,哈哈哈,那接下来,我们的故事再次开始了🌟”
“再...再次开始?!”
-如果有人期待,那么未完待续-

流星雨.gif

还有,七夕定制不仅有寒草即兴发挥的小说,还有七夕定制彩蛋——流星雨🌠

在这里祝大家(读到这里的观众)愿望成真,闪闪发光~

诶呀,单身的我是把这流星雨送给谁呀

写在最后的最后🎋

image.png

那么本篇文章到此就结束了,vscode插件能力很强,但是文档确阅读体验不是很好,我每次去写vscode插件总要经历一次重新学习的过程,大家可以期待洛竹关于vscode插件开发的新作,我也打算尝试陪洛竹去做vscode插件api文档的本地化,大家敬请期待。

如果大家喜欢我的文章📖,不妨点赞 ➕ 关注,为我这个草系码猿积攒魔力,你们的支持是我最大的动力🌟

各位读者,愿我们未来可期,愿我们来日方长,下一篇文章再见🔥~

加我微信:hancao97,邀你进群,了解寒草🌿 的github小组现状,一起学习前端,成为更优秀的工程师~(二维码在这里->交流群