Vscode has been popular with developers in recent years due to its lightweight and unusual plug-in market. You can find plenty of powerful plug-ins in the plug-in market to help improve coding efficiency. But (there is always a but-🙄) in real development, there will be some special customization requirements that do not find a particularly suitable plugin, so we can roll up our sleeves and build our own wheel.

Plug-in function

Vscode gives plugins a number of capabilities that allow them to arm vscode in almost every way. The main capabilities of the plugins include:

  • Provide new themes (including editor color matching, file ICONS, etc.)
  • Syntax support for new programming languages (highlighting, code completion, error checking, and more)
  • Provides program debugging capabilities
  • Custom commands, shortcut keys, menus
  • Custom workspace Webview

What plug-ins are developed?

When I write unit tests, I often run into the following questions:

  1. You need to switch back and forth between the source file and the corresponding single test file, and vscode does not have webstorm’s quick switch between the source file and the test file. The whole switching experience is not good
  2. When creating a single test file for a source file, you often need to recursively create the test file if the principle is to separate the source file folder from the test file folder. Such assrc/module/child/a.ts, need to create layerstests/module/child/a.test.tsFiles, this is particularly troublesome 😫

Based on these pain points, I developed a vscode plug-in for quick switching between source test files and quick creation of single test files. Plugin effects:

Plug-in functions:

  • Find the test file corresponding to the source file. If only one possible file is found, switch directly; If more than one is found, a drop-down selection box is displayed for the user to select
  • Quickly create a single test file. Support for two modes, single test files and source files together, or all single test files in a specific folder and organized by source file directory structure
  • Provides rich configuration capabilities, users can customize single test file suffix specifications

You can click on this link, or search for ** Find Test File ** in vscode marketplace:

Environment to prepare

  • Nodejs, recommended stable version
  • git

Start by installing the yeoman scaffold tool and the official scaffolding tools provided by vscode:

npm install -g yo generator-code
Copy the code

Next, execute the following command to interactively create the plug-in project:

yo code
Copy the code

The project structure

Now that the plug-in project has been created, the project directory structure is as follows:

. ├ ─ ─ node_modules ├ ─ ─ CHANGELOG. Md ├ ─ ─ the README. Md ├ ─ ─ package. The json ├ ─ ─ the SRC │ ├ ─ ─ the extension. The ts │ └ ─ ─test│ ├─ ├─ ├─ exercises, ├─ exercises, exercises, exercises, exercises, exercises, exercises, exercises ├─.vsCode ├─ ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txt ├─ download.txtCopy the code

Among them:

  • .vscodeThe folder holds the program to run and debug related commands
  • vsc-extension-quickstart.mdThis document describes how to get started with plug-ins and how to run them. It is the first point of concern after a project is created
  • srcThis is where the plug-in source code is stored.src/testPlugins e2E test code, not to worry about)
  • package.jsonIn addition to containing regular Node project configuration properties, it also contains plug-in configuration properties, which should also be of great concern

By default, the project has been configured to Run the debug parameters, so press F5 to Run the Extension (which is to Run the Run Extension command in.vscode/launch.json) :

Let’s start with package.json. The code snippet related to plug-in configuration is as follows:

{
  "name": "test"."displayName": "test"."engines": {
		"vscode": "^ 1.57.0"
	},
	"categories": [
		"Other"]."activationEvents": [
        "onCommand:test.helloWorld"]."main": "./out/extension.js"."contributes": {
		"commands": [{"command": "test.helloWorld"."title": "Hello World"}}}]Copy the code
  • nameanddisplayNameNeedless to say, the name of the plugin and the display name (i.e. the name in the plugin market)
  • enginesIn thevscodeVersion refers to the version of vscode that the plug-in is compatible with
  • categoriesRepresents the classification of plug-ins, but it andkeywordsDifferences can only be made by choiceThe official listSelect the
  • mainRepresents the plug-in entry
  • activationEventsSaid whenActivate the plugin
  • contributesHow saidCustom plug-in function

Take a look at SRC /extension.ts again:

import * as vscode from "vscode";

export function activate(context: vscode.ExtensionContext) {
  console.log('Congratulations, your extension "test" is now active! ');
  // Customize the command
  let disposable = vscode.commands.registerCommand("test.helloWorld".() = > {
    // Triggers a pop-up
    vscode.window.showInformationMessage("Hello World from test!");
  });
	// Register the command in the execution context
  context.subscriptions.push(disposable);
}

export function deactivate() {}
Copy the code

Extension.ts exposes two methods:

  1. active: Runs when the plug-in is activated, usually registering custom commands in it
  2. deactive: Runs when the plug-in is disabled

These two parts are the core of plug-in development, so let’s walk through the process:

Roll up your sleeves and get to work

The plug-in configuration

First declare the command for the plug-in in package.json, along with the conditions under which the plug-in is activated:

{
 
  "activationEvents": [
    "onCommand:find-test-file.jumpToTest"."onCommand:find-test-file.createTestFile"]."contributes": {
    "commands": [{"command": "find-test-file.jumpToTest"."title": "Jump To Source/Test File"."category": "Find Test File"."icon": "$(preferences-open-settings)"."enablement": "resourceExtname =~ /.[jt]sx? /"
      },
      {
        "command": "find-test-file.createTestFile"."title": "Create Test File For Current"."category": "Find Test File"."icon": "$(file-add)"."enablement": "resourceExtname =~ /.[jt]sx? /"}}}]Copy the code

In the Commands configuration TAB, add the jumpToTest and createTestFile commands to add several configurations in addition to the general command and title configurations:

  • category: Used to group commands in the command panel

  • icon: Icon of the command (Follow-up proceduremenusFor configuration) in the format of$(name)Vscode bringingA set of ICONS
  • enablement: indicates when commands are displayed in the command panel. The above configuration indicates that only the currently open file ists(tsx),js(jsx) can be displayed in the command panel

ActivationEvents keeps the plug-in activated when the corresponding command is executed (see the official documentation for more activation conditions). If you want to save time, you can also set it to *, which is activated after vscode starts.

Of course, it’s a better practice to keep activating plug-ins only when necessary.

It’s not always convenient to select commands from the command palette. Shortcuts are king. You can add shortcut key configurations via the KeyBindings attribute (see the official documentation for more KeyBindings configurations) :

{
  "contributes": {
    "keybindings": [{"command": "find-test-file.jumpToTest"."key": "ctrl+shift+t"."mac": "cmd+shift+t"."when": "resourceExtname =~ /.[jt]sx? /"
      },
      {
        "command": "find-test-file.createTestFile"."key": "ctrl+alt+t"."mac": "cmd+alt+t"."when": "resourceExtname =~ /.[jt]sx? /"}}}]Copy the code
  • The Mac and Windows have different keyboard shortcutscmdThe key is the same as Window’sctrl, you need to configure them separately
  • whenWhen is the shortcut enabled, andcommandtheenablementConfiguration is similar to

Now that there are shortcuts, can there be shortcuts like the right mouse button? Can!!!! Add a configuration with the properties of menus (see the official documentation for more menus) :

{
  "contributes": {
    "menus": {
      "editor/context": [{"command": "find-test-file.jumpToTest"."group": "1_find-test-file"."when": "resourceExtname =~ /.[jt]sx? /"
        },
        {
          "command": "find-test-file.createTestFile"."group": "1_find-test-file"."when": "resourceExtname =~ /.[jt]sx? /"}]."editor/title": [{"command": "find-test-file.jumpToTest"."group": "navigation"."when": "resourceExtname =~ /.[jt]sx? /"
        },
        {
          "command": "find-test-file.createTestFile"."group": "navigation"."when": "resourceExtname =~ /.[jt]sx? /"}]}}}Copy the code

The icon property defined in the previous command is used in editor/title.

The plug-in also needs to provide the ability for user configuration. For example, different projects name test files with different suffixes, some are A.test.ts, some are A.spect.ts. This capability is provided by the Configuration configuration:

{
  "contributes": {
    "configuration": {
      "title": "Find Test File"."properties": {
        "findTestFile.basic.testSuffix": {
          "type": "string"."default": "\\.(spec|test)"."markdownDescription": "xxxxxx"
        },
        "findTestFile.basic.excludeFolder": {
          "type": "array"."default": [
            "node_modules"]."items": {
            "type": "string"
          },
          "uniqueItems": true."markdownDescription": "xxxxxx"
        },
        "findTestFile.createIfNotFind.enable": {
          "type": "boolean"."default": false."markdownDescription": "xxxxxx"
        },
        "findTestFile.createIfNotFind.preferStructureMode": {
          "type": "string"."default": "separate"."enum": [
            "separate"."unite"]."markdownEnumDescriptions": [
            "xxxxxx"."xxxxxx"]."markdownDescription": "xxxxxx"
        },
        "findTestFile.createIfNotFind.preferTestDirectory": {
          "type": "object"."default": {
            "separate": "__tests__"."unite": "__tests__"
          },
          "properties": {
            "separate": {
              "type": "string"
            },
            "unite": {
              "type": "string"}},"required": [
            "separate"."unite"]."additionalProperties": false."markdownDescription": "xxxxxx"
        }
      }
    }
  }
}
Copy the code

The following information is displayed on the Settings page:

Configuration items can be arrays, strings, booleans, enumerations, objects, see official documentation for details.

The plug-in code

The subsequent code will omit the business logic involved in this plug-in and only focus on the configuration and API use of vscode plug-in. After all, the business code is only for specific scenarios 😁. If you want to see the full code, check out the code repository!

SRC /extension.ts

  • Register two commands for the plug-in
  • Determine the file vscode is currently opening and the current working directory. Business logic needs to be executed only if a specific file is opened
import vscode from "vscode";

function doPrepare() {
  // Gets the currently edited file object, empty if no files are open
  const activeEditor = vscode.window.activeTextEditor;
  if(! activeEditor) {return;
  }
	// Get the file name of the edit file
  const activeFilePath = activeEditor.document.fileName;
	// Business code, don't worry
  const result = createValidFileReg().exec(getBasename(activeFilePath));

  if(! result) {// A warning message is displayed
    vscode.window.showWarningMessage(INVALID_FILE_WARNING_MESSAGE);
    return;
  }
	// get the address of the workspace directory currently open with vscode
  constworkspaceFilePath = vscode.workspace.getWorkspaceFolder( activeEditor.document.uri )! .uri.fsPath;// omit the business code
}

export function activate(context: vscode.ExtensionContext) {
  Register the jumpToTest command
  const jumpToTestCommand = vscode.commands.registerCommand(
    "find-test-file.jumpToTest".() = > {
     // omit the business code});// Register createTestFile
  const createTestFileCommand = vscode.commands.registerCommand(
    "find-test-file.createTestFile".() = > {
      // omit the business code});// Register the command in the execution context
  context.subscriptions.push(jumpToTestCommand, createTestFileCommand);
}

export function deactivate() {}

Copy the code

SRC /config.ts main functions:

  • Get plug-in configuration information
// src/config.js
import vscode from "vscode";

constgetCfgByKey = <K1 extends keyof Config, K2 extends keyof Config[K1]>( primary: K1, key: K2) => {// Select findTestFile, findTestFile, findTestFile, findTestFile, findTestFile Such as findTestFile. Basic. TestSuffix const config. = vscode workspace. GetConfiguration (" findTestFile "); TestSuffix const CFG = config.get< config [K1][K2]>(' ${primary}.${key} ')! ; Const defaultCfg = config.inspect< config [K1][K2]>(' ${primary}.${key} ')! .defaultValue! ; return [cfg, defaultCfg]; }; // omit the business codeCopy the code

SRC/jumptofile. ts Main functions:

  • If a possible target file is found, switch directly to the target file
  • If multiple possible target files are found, a dialog box is displayed for users to select
import vscode, { QuickPickItem } from "vscode";

export const openFile = async (filePath: string) = > {// Open the file at the corresponding address. Note that this operation is asynchronous
  const document = await vscode.workspace.openTextDocument(filePath);
  // Switch the current window to the file. Note that this operation is also asynchronous
  await vscode.window.showTextDocument(document);
};

export const jumpToPossibleFiles = async (
  current: string.relativeFiles: string[].isJumpToTestFile: boolean.createTestFileOption: CreateTestFileOption
) => {
  
  // Display a selection box to get the result of the user's selection. Note that this is an asynchronous operation
  const select = await vscode.window.showQuickPick(pickItems);
	// Empty is returned when the selection is deselected
  if(! select) {return;
  }
  // omit the business code
};
Copy the code

SRC/createTestfile. ts

  • If the user is not satisfied with the search result or cannot find the test file, the input box is displayed to help the user quickly create the test file
import vscode, { QuickPickItem } from "vscode";

export const createTestFile = async (
  { basename, ext, parent, root }: CreateTestFileOption,
  manualCreate: boolean = true) = > {// Displays the input box
  const userInputPath = await vscode.window.showInputBox({
    prompt: NEW_TEST_FILE_PROMPT,
    value: filePath,
    valueSelection: [filePath.length, filePath.length],
    // Verify that the input is valid. Null is returned indicating that the verification is successful
    validateInput(value) {
      return isValidFile(basename, ext, value, true)?null: INVALID_TEST_FILE_WARNING_MESSAGE; }});// Cancel input and return empty result
  if(! userInputPath) {return; }};Copy the code

See the official documentation for more vscode API usage.

Release the plugin

It’s just a snap. It’s a snap. Next you need to package the plug-in and publish it.

To package plug-ins, install the VSCE library:

npm i -g vsce
Copy the code

Then execute the package command:

vsce package
Copy the code

After the package is complete, the find-test-file-x.x.x. vi file is generated. At this point, you can directly install the plug-in through the plug-in market to verify the effect:

Once the functionality is verified, it can be released to the plug-in market:

  1. Vscode’s plug-in marketplace is based on Microsoft’s Azure DevOps, where plug-ins are authenticated, hosted, and managed. So before publishing, you need to sign up for an Azure Devops account, just like NPM:

  1. After registration is complete, create Personal Access Token:

Copy the created token for future use.

  1. Next you need to create a new Publisher in the plug-in market to publish the plug-in:

  1. Log in to the VSCE account and use the previously registered Publisher and token:
vsce login publisher-name
Copy the code

Don’t forget to add publisher, icon, categories to package.json:

{
  "publisher": "xxxx"."icon": "xxxx/icon.png"."categories": [
    "Other"],}Copy the code
  1. Once login is complete, you can happily publish:
vsce publish
Copy the code

Once successfully published, the plug-in can be searched in the plug-in market (which usually takes a few minutes), and you can also check the usage of the plug-in in the web management side.

conclusion

So far is the author in the development of plug-ins used in vscode knowledge, I hope this article can help you, but also welcome everyone to use the author developed plug-ins and issue.