Vscode plug-in development

As a free, lightweight, scalable editor, vscode is becoming more and more popular in development. Vscode was developed based on TS using the Electron framework. As a front-end developer, it is very convenient to develop an extension on vscode.

What can vscode plug-ins do

  • Change VS Code’s look-theme using color or file icon theme

  • Add custom components and views to the UI – Extend workbench

  • Create a Webview to display a custom web page built using HTML/CSS/JS

  • Support for a new programming language

  • Support for debugging specific runtimes

    .

Initial attempt at extension plug-in development

Install the Yeoman

First, VS Code officially gave us Yeoman and the VS Code extension generator

Yo Generator-code is installed globally

npm install -g yo generator-code
Copy the code

Start building the plug-in project

Run first after installation is complete

yo code
Copy the code

You can choose the type of plug-in you want to create (extensions, color themes, code language support, etc.) and you can choose TS or JS depending on your personal development habits

Okay, next

? What type of extension do you want to create? New Extension (TypeScript) // Let's select the first item, New Extension (TypeScript), and proceed to the next step. What's the name of your extension? HelloWorld // Plugin name, simply call HelloWorld? What's the identifier of your extension? Hello-world // Plugin identifier (unique ID). This is the name of the plugin after you publish it. What's the description of your extension? // Plug-in description, can be directly empty? Initialize a git repository? Yes // Do you want to initialize git? Bundle the source code with webpack? No // Do you want to use Webpack to build your project, default No? Which package manager to use? NPM // What package manager is used here? There are two options: NPM and YARNCopy the code

After the selection is completed, the project is set up

The project to try

Open the generated project with vscode. After opening the project (sometimes you need to run NPM I to install the dependencies), press the f5 key or click on the red box from the image below

Ps: If there is no reaction, you can try to press CTRL + S several times on the page to save the reaction, the pro test is effective, I don’t know why, ha ha

Normally, another instance of vscode will pop up at this point, called the extension development host, where we can try out our plug-in,

On the extension development host we press Shift + CTRL + P to bring up the command panel and type Hello World

At the bottom, HelloWorld from HelloWorld! The notice of

At this point, our initial project is a success.

Analyze the project structure

Focus on the extension entry file extension.ts and configuration file package.json

Extension entry file 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
import * as vscode from 'vscode';

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
export function activate(context: vscode.ExtensionContext) {
	
	// Use the console to output diagnostic information (console.log) and errors (console.error)
	// This line of code will only be executed once when your extension is activated
	console.log('Congratulations, your extension "hello-world" is now active! ');

	// The command has been defined in the package.json file
	// Now provide the implementation of the command with registerCommand
	// The commandId parameter must match the command field in package.json
	let disposable = vscode.commands.registerCommand('hello-world.helloWorld'.() = > {
		// The code you place here will be executed every time your command is executed
		// Display a message box to the user
		vscode.window.showInformationMessage('Hello World from HelloWorld! ');
	});

	context.subscriptions.push(disposable);
}

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

The file exports two methods activate and deactivate

  • Activate: When the plug-in is activated, the activate function is triggered
  • Deactivate: A handler that destroys instances registered with activate when an extension is deactivated. This is the way to do this if the extension needs to be closed or disabled or uninstalled.

Configuration file package.json

{
	"name": "hello-world".// Plug-in name
	"displayName": "HelloWorld".// The unique id of the plug-in, which is also the name of the plug-in marketplace
	"description": ""./ / description
	"version": "0.0.1"./ / version number
	"engines": {
		"vscode": "^ 1.61.0" // The lowest version of VS Code that the extension depends on.
	},
	"categories": [ // category, the category displayed after Posting to the market
		"Other"]."activationEvents": [ // Activate events
		"onCommand:hello-world.helloWorld"]."main": "./out/extension.js".// Expand entry points
	"contributes": { / / contribution points
		"commands": [ // Command, CTRL + Shift + P activates the command panel input command
			{
				"command": "hello-world.helloWorld"."title": "Hello World"}},"scripts": {
		"vscode:prepublish": "npm run compile"."compile": "tsc -p ./"."watch": "tsc -watch -p ./"."pretest": "npm run compile && npm run lint"."lint": "eslint src --ext ts"."test": "node ./out/test/runTest.js"
	},
	"devDependencies": {
		"@types/vscode": "^ 1.61.0"."@types/glob": "^ 7.1.3." "."@types/mocha": "^ 8.2.2"."@types/node": "14.x"."eslint": "^ 7.27.0"."@typescript-eslint/eslint-plugin": "^ 4.26.0"."@typescript-eslint/parser": "^ 4.26.0"."glob": "^ 7.1.7"."mocha": "^ 8.4.0"."typescript": "^ 4.3.2." "."vscode-test": "^ 1.5.2." "}}Copy the code

Combined with the above we can find

The entire “Enter HelloWorld command to the pop-up HelloWorld from HelloWorld! Is produced by the process of

For each item that is contributed by package.json, the title is the corresponding command title in the command palette, and the command Id is the corresponding command Id (hello-world. HelloWorld). Command id corresponds to the trigger event is by the extension. The ts file vscode.com mands. RegisterCommand registered the same command id, and bind the corresponding event (i.e., pop-up prompt box).

It is worth mentioning that activationEvents in package.json are events that identify the activation of the extender. The activation event in the above code is onCommand:hello-world.helloWorld, which means that the extension is activated when the hello-world.helloWorld command is called.

Activation events are

  • OnLanguage: when opening the corresponding language file
  • OnCommand: when the corresponding command is executed
  • OnDebug: before starting the debug session
    • OnDebugInitialConfigurations: in the callprovideDebugConfigurationsMethod before firing
    • OnDebugResolve:onDebugResolve:typeIn the call of the specified typeresolveDebugConfigurationMethod before firing
  • WorkspaceContains: Whenever a folder is opened and the folder contains at least one file that matches the Glob pattern
  • OnFileSystem: Opens a file or folder of a specified type or protocol
  • OnView: when the view with the specified ID is opened in the sidebar
  • OnUri: when a url based on the vscode or vscode-insiders protocol is opened
  • OnWebviewPanel: Opens the corresponding Webview
  • OnCustomEditor: When the specified custom editor is opened
  • OnAuthenticationRequest: Whenever the request authentication session is extended
  • * : when starting vscode, this setting is generally not recommended unless necessary
  • OnStartupFinished: This activation event willinVS Code to startAfter a period of timeIssue, and*Activation events are similar, but do not slow down VS Code startup

The whole process is roughly shown below

Practical example

Here’s an example of a plug-in I developed earlier

The name of the plug-in developed by the author is footstep-mark. You can install it by searching footstep-mark in the plug-in market

The main function of the plug-in is to mark the position in the code editing place through shortcut keys. A sidebar can be opened on the right side to display the marked text information. Click the corresponding mark record to jump to the corresponding mark position.

The functions of the plug-in are shown below

The first step in implementing this plugin is to learn how to create a custom Webview(the sidebar above)

Create a custom Webview

The WebView API allows extensions to create fully customizable views in Visual Studio Code. You can use fully custom HTML, CSS, and JS in this custom view.

Webview can’t get vscode data directly; it communicates with extensions via messaging.

Create a Webview, through the API (vscode. Window. CreateWebviewPanel)

const webView = window.createWebviewPanel(
      "footstepMark.extensionWebView"./ / the webview
      "Action bar".// Extend the page title
      { 
        preserveFocus: false.// Whether to focus on the newly created page
       	viewColumn: 1.// The line number displayed on the page});Copy the code

The webview set HTML

First, a brief introduction to Uri objects in vscode

Uri: a generic resource identifier that can represent a file or other resource on a disk. It can be interpreted as a specific resource path that vscode can load

Set the HTML content of the webView with webView.html

export async function activate(context: ExtensionContext) {
  	// Store the context to store
  	state.context = context;
	const webView = window.createWebviewPanel(
      "footstepMark.extensionWebView"./ / the webview
      "Action bar".// Extend the page title
      { 
        preserveFocus: false.// Whether to focus on the newly created page
       	viewColumn: 1.// The line number displayed on the page}); webView.webview.html = getHtmlForWebview();/ / set the HTML
    webView.webview.options = {
      enableScripts: true.// Scripts are allowed
      localResourceRoots: [
        Uri.joinPath(state.context.extensionUri, "media")],// Allow access to other local resources and define the root URI for loading local content
    };
}

function getHtmlForWebview() {
    const scriptPathOnDisk = Uri.joinPath(
      state.context.extensionUri,
      "media"."main.js"
    );
    const scriptUri = scriptPathOnDisk.with({ scheme: "vscode-resource" });

    const styleMainPath = Uri.joinPath(
      state.context.extensionUri,
      "media"."main.css"
    );
    const stylesMainUri = webview.asWebviewUri(styleMainPath);

    return ` <! DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, Initial - scale = 1.0 "> < link href ="${stylesMainUri}" rel="stylesheet">
      </head>
      <body>
        <div class='mark-list'></div>
        <script src="${scriptUri}"></script>
      </body>
    </html>`;
  }
Copy the code

Webview communicates with extensions

1. The extension passes data to the WebView

In order to avoid the impact of customized webview on vscode, webview cannot directly access vscode information, and the communication between webview and extension needs to be through API, similar to webwork.

The extension can use webView.postMessage () to pass data to our custom WebView. For example, in this plug-in, we need to pass the location data of the code edit area marked by shortcut keys to the WebView, so that the HTML of the WebView can add a tag message

this.FMwebView.webview.postMessage(
     ... // Tagged data
);
/ / this. FMwebView is above the window. The createWebviewPanel create custom webview
// postMessage The content in parentheses is a custom content, which can send arbitrary data information according to the situation
Copy the code

In the custom webView js code can be window.addEventListener(‘message’, event => {… }) to accept data from extended postMessage

  window.addEventListener("message".(event) = > {
    const data = event.data;
    // data is marked data
  });
Copy the code

2. Webview passes data to the extension

Webviews can also pass messages back to its extender. For example, in this plug-in, when clicking on the tag data of the webView of the action class, the code editor will jump to the corresponding tag location

Specific data transfer, in the custom WebView JS logic Code using a special VS Code API object, namely

const vscode = acquireVsCodeApi();  // acquireVsCodeApi can be called directly

vscode.postMessage(
	... // The location data to jump to
);
Copy the code

While vscode extend through the webview. OnDidReceiveMessage to accept data

this.FMwebView.webview.onDidReceiveMessage((message) = >{...// message is the location data passed above
});
Copy the code

Next, how to register the shortcut keys

Register shortcut key

The shortcuts for this plugin are:

  • Ctrl+Alt+M: This shortcut will highlight the background color at the current document focus (cursor position) and record the location, text information, if the side action bar is open, it will render the side action bar; If the focus is marked, the mark is unmarked and all relevant information is deleted

  • Ctrl + Alt + N: This shortcut will focus in the current document (i.e., the cursor position), if the position has been Ctrl + Alt + M mark, will pop up box, enter after input note information, will remark in the location information, and to the information shown in this location, if the side the action bar is open, will be in the action bar synchronous rendering

First of all, contribute to the entire package.json file by writing the corresponding key shortcuts and the bound command ID, where the WHEN field represents the effective time (here is when the editor focuses)

  "contributes": {
    "keybindings": [{"command": "footstepMark.locationMark"."key": "ctrl+alt+m"."mac": "cmd+alt+m"."when": "editorTextFocus"
      },
      {
        "command": "footstepMark.markRecord"."key": "ctrl+alt+n"."mac": "cmd+alt+n"."when": "editorTextFocus"}},Copy the code

Next, the plug-in code registers the binding’s corresponding command ID event,

    this.LocationMarkDisposable = commands.registerCommand(
      'footstepMark.locationMark'.() = > {
        this.locationMark(); });Copy the code
    this.MarkRecordDisposable = commands.registerCommand(
      'footstepMark.markRecord'.async() = > {this.markRecord(); });Copy the code

The following is the detailed operation of events corresponding to shortcut keys

A brief introduction to some objects in vscode

  • Position: Indicates the Position of lines and characters, such as the cursor
  • Indicates two ordered pairs of positions, ensuring that the end position is greater than or equal to the beginning position

Ctrl+Alt+M

Ctrl + Alt + M (order ID: footstepMark locationMark) events corresponding code

 import {
  commands,
  window,
  Range,
  Position,
  OverviewRulerLane,
  Disposable,
  Uri,
  ExtensionContext
} from "vscode";

 async locationMark() {
    // Get the currently active document
    const activeEditor = window.activeTextEditor;
    try {
      // Get mouse focus position information
      letstartLine = activeEditor? .selection.start.line;letendLine = activeEditor? .selection.end.line;// Get the current active file name, which can be used to jump to the file after clicking the mark position
      letfileName = activeEditor? .document.fileName;if(startLine ! = =undefined&& endLine ! = =undefined && fileName) {
          // If the location is already marked, return if it is
        if (this.calculateRange(startLine, endLine, fileName)) {
          return;
        }
        // Get the final text width
        letendPosition = activeEditor? .document.lineAt(endLine).text.length;if (endPosition) {
          // Calculate the range
          let range = new Range(
            new Position(startLine, 0),
            new Position(endLine, endPosition)
          );
            
          const imgUri = Uri.joinPath(
            (state.context as ExtensionContext).extensionUri,
            "media"."icon.svg"
          );
          let textEditorDecorationType = window.createTextEditorDecorationType({
            overviewRulerColor: "Rgba (208,2,27,1)".backgroundColor: "Rgba (208,2,27,0.1)".// Right cursor
            isWholeLine: true.overviewRulerLane: OverviewRulerLane.Full,
            gutterIconPath: imgUri,
            gutterIconSize: 'contain'
          });
          // Code editor style renderactiveEditor? .setDecorations(textEditorDecorationType, [{ range }]); }}}catch (error: any) {
      const message = error.message ?? error;
      window.showErrorMessage(message); }}Copy the code

By getting the position of the focal point of information, and use Windows. CreateTextEditorDecorationTypeapi created can be used in the text editor to add decoration instance,

Then use setDecorations to render the decoration instance to the corresponding mark position in the editor. Here, the background of the mark is highlighted.

Ctrl+Alt+M will also pass location data to the webView (the custom webView created above)

Ctrl+Alt+N

Function: Add custom remarks to the position data marked with Ctrl+Alt+M

Ctrl+Alt+N (command ID: footstepmark.markRecord) event code

  async markRecord() {
    // The currently active document
    const activeEditor = window.activeTextEditor;
    try {
      letstartLine = activeEditor? .selection.start.line;letendLine = activeEditor? .selection.end.line;letfileName = activeEditor? .document.fileName;if (startLine && endLine && fileName) {
        let markData = state.markData[fileName].markDetails; // Find the corresponding tag data in the position information of all tags
        if (markData) {
          let target = markData.find((e) = > {
            let start = e.range[0];
            let end = e.range[1];
            return startLine && endLine && start <= startLine && end >= endLine;
          }); // Find the corresponding data information that has been marked
          if (target && target.textEditorDecorationType) {
            let record = await window.showInputBox({
              placeHolder: 'Please enter marking remarks'
            });
            target.record = record;
            let range = new Range(
              new Position(target.range[0].0),
              new Position(target.range[1], target.range[2])
            );
            activeEditor?.setDecorations(target.textEditorDecorationType, [
              {
                range,
                renderOptions: {
                  after: {
                    contentText: record,
                    color: "Rgba (208,2,27,0.4)".margin: "0 0 0 20px",},},},]);// Communicate with webView script messages. SendMessage is the encapsulated postMessage message passing methodFmWebViewPanel.currentPanel? .sendMessage( ...// record,); }}}}catch (error: any) {
      const message = error.message ?? error;
      window.showErrorMessage(message); }}Copy the code

ShowInputBox opens an input box using API window.showinputBox that asks the user to enter remarks and then adds the input information to the location data and postMessage to the WebView for display

Extension table

“Workbench” the entire Visual Studio Code UI, including the following sections

  • The title bar
  • Activity bar
  • The sidebar
  • Control panel
  • The editorial team
  • The status bar

VS Code provides various apis that allow you to add your own components to the workbench.

The workspace we will add in this plug-in is the Status Bar Item area, adding two buttons

  • On/OFF: Custom WebView on or off button

  • Clear: clears data

createStatusBarItem

Through the window. The createStatusBarItem button to add the corresponding to the bottom of the lower position,

On/Off button related code

import { window, StatusBarItem, StatusBarAlignment, commands  } from "vscode";
let statusBar = window.createStatusBarItem(StatusBarAlignment.Left); 
// StatusBarAlignment is a built-in vsCode object that indicates the alignment of status bar items. It has two values: left and right.
statusBar.command = 'extension.statusBarShowWebView'; // Bind command ID
statusBar.text = `$(repo-clone)off`;
Copy the code

The button event is then registered by registering the corresponding command ID

commands.registerCommand(
      'extension.statusBarShowWebView'.async() = > {// Extend webView on or off});Copy the code

The same goes for the Clear button, which I won’t go into here.

The whole author’s footstep-mark plug-in roughly realizes the logic as above, of course, there are many complex details, including the jump across files and mark the location after editing the corresponding file when the location of the confusion problem processing, and so on, specific corresponding code

Making: github.com/chenkai77/f…

Packaging and publishing

How do I publish a written plug-in to the vscode marketplace

The first step is to have a Microsoft account

In marketplace.visualstudio.com/ register and login, then click on the diagram below

Then create a new organization

Click agree to continue

Enter organization name

After the organization is created, click on the following image

Create a token

Next, select the scopes in the following image, and select Full Access for scopes

Click create and copy the token. The token website won’t save it for you, so you’d better save it yourself, so you won’t forget it.

vsce

Install vsCE (Visual Studio Code Extensions)

npm install -g vsce
Copy the code

Create a publisher name from create-Publisher, and login can be used for those that have already been created

Vsce create-publisher <publisher name>Copy the code

Then the terminal will ask you to input the corresponding token, the above token copy input

Finally, run it in the corresponding plug-in project folder

  • packaging

    vsce package
    Copy the code
  • release

    vsce publish
    Copy the code

Pay attention to

  • You need to enter publisher and version in package.json
  • The contents of the readme.md file are displayed in the plug-in details section of the marketplace
  • The changelog. md file corresponds to the version History of the plug-in

Reference: vscode code.visualstudio.com/api official address