background

📢 blog launch: Hiro’s blog

📢 Team blog: SugarTurboS

🌈 repository source: learn-vscode-extension, ✨ star

Following the previous article [KT] VScode plug-in development example series (a), continue to complete some examples, the following examples are relatively complex and applicable, nonsense not to say, dry!

example

6. Code snippets

scenario

Type in a prefix, get one or more prompts, and return to the car with a lot of code.

The code shown

You need to modify the configuration of snippets in package.json

// package.json
{
  "contributes": {
    "snippets": [{"language": "html"."path": "./src/snippets/html.json"}}}]Copy the code

Then add an HTML. Json configuration

{
  "PDK": {
    "prefix": ["PDK"."PD"."PK"."DK"]."body": ["<PDK>"."${1}"."</PDK>"]."description": "Custom Snippets for Dokuan Peng"}}Copy the code

For each field, you can read the official documentation: create-your-own-Snippets

Above we set the language to: HTML, so before running the plugin, and ensure that the plugin is activated, in the specified language HTML, enter the relevant keyword prefix

Results show


The source code to show

  • Package configuration in Snippets

7. Customize the sidebar + panel

background

You need to customize the sidebar on the left to do some interactive logic

Code implementation

⚠️ Note: The sidebar button (Tree View Container) and panel View (Tree View) must be configured at the same time. Otherwise, the configuration does not take effect

Display of sidebar

First, let’s look at the official documentation and see how we can add our own custom content to the left sidebar

👉 contribution – points# contributes. ViewsContainers

// package.json
{
  "contributes": {
    "viewsContainers": {
      "activitybar": [{"id": "sugar"."title": "Sugar-BeeHive"."icon": "./src/logo/sugar.svg"}},"views": {
      "sugar": [{"id": "BeeHive-Command"."name": "01. Command Set"
        },
        {
          "id": "BeeHive-PackageAnalysis"."name": "02. Packet Analysis"}]}}}Copy the code

⚠️ Note: The key in views must be consistent with the attribute ID in activityBar. For example, the sugar in views is consistent with the sugar in activityBar

Run our plugin: Run Extension, and you’ll see our custom sidebar on the left

On the icon SVG this we pay attention to the path is good, I focus on the icon is not ha ~

After we’ve configured package.json above, we’ll go back to the document and see this: tree-view#activationEvents

If necessary, you can add the following code

{
  "activationEvents": ["onView:BeeHive-Command"]}Copy the code

How do YOU define panel content

It shows the sidebar, but we need to show the content. How do we do that? You can use the official document: tree-data-provider to implement a small demo. The following code is also based on the official document

// beehive-sidebar.ts
// Demo7 custom sidebar entry and panel
import * as vscode from 'vscode'

const scripts = [
  {
    script: 'webpack:dev'}, {script: 'webpack:prod'}, {script: 'server:dev'}, {script: 'server:test'}, {script: 'server:test-1'}, {script: 'server:test-2',},]/ * * *@description Rewrite each node */
export class SideBarEntryItem extends vscode.TreeItem {
  constructor(
    private version: string.public readonly label: string.public readonly collapsibleState: vscode.TreeItemCollapsibleState
  ) {
    super(label, collapsibleState)
    this.tooltip = `The ${this.label}-The ${this.version}`
    // this.description = `${this.version}-${Math.ceil(Math.random() * 1000)}`}}/ * * *@description Import file */
export class SideBarBeeHiveCommand
  implements vscode.TreeDataProvider<SideBarEntryItem> {
  constructor(privateworkspaceRoot? :string) {}
  getTreeItem(element: SideBarEntryItem): vscode.TreeItem {
    returnelement } getChildren( element? : SideBarEntryItem ): vscode.ProviderResult<SideBarEntryItem[]> {if (element) {
      / / child nodes
      var childrenList = []
      for (let index = 0; index < scripts.length; index++) {
        var item = new SideBarEntryItem(
          '1.0.0',
          scripts[index].script,
          vscode.TreeItemCollapsibleState.None
        )
        item.command = {
          command: 'BeeHive-Command.openChild'./ / command id
          title: scripts[index].script,
          arguments: [scripts[index].script], // Parameters received by the command
        }
        childrenList[index] = item
      }
      return childrenList
    } else {
      / / the root node
      return [
        new SideBarEntryItem(
          '1.0.0'.'Item 1',
          vscode.TreeItemCollapsibleState.Collapsed
        ),
        new SideBarEntryItem(
          '1.0.0'.'Item 2',
          vscode.TreeItemCollapsibleState.Collapsed
        ),
      ]
    }
  }
}

export class SideBarBeeHivePackageAnalysis
  implements vscode.TreeDataProvider<SideBarEntryItem> {
  constructor(privateworkspaceRoot? :string) {}
  getTreeItem(element: SideBarEntryItem): vscode.TreeItem {
    returnelement } getChildren( element? : SideBarEntryItem ): vscode.ProviderResult<SideBarEntryItem[]> {if (element) {
      / / child nodes
      var childrenList = []
      for (let index = 0; index < scripts.length; index++) {
        var item = new SideBarEntryItem(
          '1.0.0',
          scripts[index].script,
          vscode.TreeItemCollapsibleState.None
        )
        item.command = {
          command: 'BeeHive-PackageAnalysis.openChild'./ / command id
          title: scripts[index].script,
          arguments: [index], // Parameters received by the command
        }
        childrenList[index] = item
      }
      return childrenList
    } else {
      / / the root node
      return [
        new SideBarEntryItem(
          '1.0.0'.'Button Group',
          vscode.TreeItemCollapsibleState.Collapsed
        ),
      ]
    }
  }
}

module.exports = function (context: vscode.ExtensionContext) {
  // Register the sidebar panel
  const sidebarBeeHiveCommand = new SideBarBeeHiveCommand()
  const sidebarBeeHivePackageAnalysis = new SideBarBeeHivePackageAnalysis()
  vscode.window.registerTreeDataProvider(
    'BeeHive-Command',
    sidebarBeeHiveCommand
  )
  vscode.window.registerTreeDataProvider(
    'BeeHive-PackageAnalysis',
    sidebarBeeHivePackageAnalysis
  )

  // Register the command
  vscode.commands.registerCommand('BeeHive-Command.openChild'.(args) = > {
    console.log('[beehive-command-openChild] currently selected :', args)
    vscode.window.showInformationMessage(args)
  })
  vscode.commands.registerCommand(
    'BeeHive-PackageAnalysis.openChild'.(args) = > {
      console.log('[BeeHive - PackageAnalysis openChild] the currently selected is:', args)
      vscode.window.showInformationMessage(args)
    }
  )
}
Copy the code

Then add the file in the entry file extension.ts

import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('your extension "sugar-demo-vscode" is now active! ')
  require('./beehive-sidebar')(context) // Demo7 custom sidebar entry and panel
}

export function deactivate() {}
Copy the code

If you need to trigger content when you click on the node in the left sidebar, just post back some content in Arguments and do the corresponding business action

Results show

The source code to read

  • beehive-sidebar.ts
  • Package. The viewsContainers in json
  • Package. The views in json

8. Read folder directory + content copy clipboard

scenario

Previous example: Customizing the sidebar + panel does what we do, but there are some drawbacks, as we need to dynamically generate our panel content based on the file path.

Take the following example as an example: according to the VScode working directory, read the scripts field of package.json file in the directory, screen out the script commands that meet the specification, and dynamically generate our button

First of all need to be clear that vscode a vscode. Workspace. RootPath, because later vscode support multipleRoot mode, so this field have been out of date invalid. We can only through vscode. Workspace. WorkspaceFolders for current workspace all root folder array;

Note that you get the folder array path, that is, in the following case, you get the path content like this

├ ─ ─ A_Folder │ ├ ─ ─ B_Folder │ │ ├ ─ ─ D_Folder │ │ └ ─ ─ │ │ │ ├ ─ ─ C_Folder │ └ ─ ─ └ ─ ─Copy the code

The above will only get the: A_Folder path, not the B, C, and D paths below.

Another point to note: It’s hard to tell whether this folder belongs to A front-end project or Node project. Here, I classify it by whether package.json exists under this folder.

Do you really want all the folders under “A”?

If you really want to, my idea is that you can only use Node’s FS module to retrieve the A folder and recurse all the way down. There is always A way.

OK, let’s get to the general idea

The code shown

Register the sidebar panel first

// extension.ts
import * as vscode from 'vscode'

export function activate(context: vscode.ExtensionContext) {
  console.log('Plug-in started, go ahead.')
  require('./container/commandSet')(context)
}

export function deactivate() {}
Copy the code

Don’t forget that package.json also needs to be added

{
  "contributes": {
      "commands": []."viewsWelcome": [{"view": "BeeHive-Command"."contents": "Increase your productivity and free up your hands."}]."viewsContainers": {
        "activitybar": [{"id": "sugar"."title": "Sugar-BeeHive"."icon": "./assets/logo_default.svg"}]."panel": [{"id": "sugar"."title": "Package Explorer"."icon": "./assets/logo_default.svg"}},"views": {
        "sugar": [{"id": "BeeHive-Command"."name": "01. Command Set"
          },
          {
            "id": "BeeHive-Package"."name": "02. Packet Analysis"}}},}Copy the code

CommandSet = commandSet = commandSet = commandSet = commandSet

// commandSet.ts
import * as vscode from 'vscode'
import SideBarCommand from './SideBarCommand'
import { PREFIX } from '.. /.. /constants'
import { ShellType } from '.. /.. /type/common'
import { getWorkSpaceFolderList } from '.. /.. /utils'

module.exports = function (context: vscode.ExtensionContext) {
  // 1. Get all vscode projects
  const folderList = getWorkSpaceFolderList()

  // 2. Register sidebar panel
  const sideBar = new SideBarCommand(folderList)
  vscode.window.registerTreeDataProvider('BeeHive-Command', sideBar)

  // 3. Register commands
  vscode.commands.registerCommand(
    'BeeHive-Command.openChild'.(args: { title: string; shell: ShellType; [key: string] :any }) = > {
      const { title, shell = null, path = ' ' } = args
      const reg = new RegExp(`${PREFIX}`)
      if (reg.test(title)) {
        vscode.window.showInformationMessage(title)
      } else {
        // 4. Copy to clipboard
        vscode.env.clipboard.writeText(`cd ${path} \n npm run ${shell? .key}`)
        vscode.window.showInformationMessage(
          `ok, fine ! shell copied to clipboard ~`); }})}Copy the code

The next big thing is the SideBarCommand that we implemented, which basically overwrites the getChildren method to dynamically generate the panel content

// SideBarCommand.ts
/ * * *@description Command set sidebar instance */
import * as vscode from 'vscode'
import { PREFIX } from '.. /.. /constants'
import { FolderType, ShellType } from '.. /.. /type/common'
import { isExist, read, getShellFromScripts } from '.. /.. /utils/package'
import { SideBarEntryItem, SideBarEntryListImplements,} from '.. /.. /factory/SideBar'

function getNode(
  title: string, description? :string, args? : { [key:string] :any }
) {
  let node = new SideBarEntryItem(title, vscode.TreeItemCollapsibleState.None, description)
  node.command = {
    command: 'BeeHive-Command.openChild'./ / command id
    title: title,
    arguments: [{ title, ...args }], // Parameters received by the command
  }
  return node
}

export default class SideBarCommand extends SideBarEntryListImplements {
  constructor(private folderPathList: FolderType[] | undefined) {
    super()
  }
  getChildren(
    element: SideBarEntryItem | undefined
  ): vscode.ProviderResult<SideBarEntryItem[]> {
    if (element) {
      var childrenList: any = []
      if (isExist(`${element.path}/package.json`)) {
        const packageValues = read(`${element.path}/package.json`)
        if (packageValues && packageValues.scripts) {
          const eggShell = getShellFromScripts(packageValues.scripts, 'server')
          const webpackShell = getShellFromScripts(packageValues.scripts, 'webpack')
          const shellList = [...webpackShell, ...eggShell]
          if(!!!!! shellList.length) { shellList.forEach((shell: ShellType, index: number) = > {
              const node = getNode(shell.key, ` [${shell.environment}] `, { shell, path: element.path })
              childrenList[index] = node
            })
          } else {
            const noneNode = getNode(` [${PREFIX}]: scripts script command does not comply with the rule)
            childrenList = [noneNode]
          }
        } else {
          const noneNode = getNode(` [${PREFIX}]: scripts command 'does not exist)
          childrenList = [noneNode]
        }
      } else {
        const noneNode = getNode(` [${PREFIX}]: The project does not exist package.json ')
        childrenList = [noneNode]
      }
      return childrenList
    } else {
      const folderNode = this.folderPathList? .map((folder: FolderType) = > {
        return new SideBarEntryItem(
          folder.name,
          vscode.TreeItemCollapsibleState.Collapsed,
          ' ',
          folder.path
        )
      })
      return folderNode
    }
  }
}
Copy the code

The above example is from practice: vscode-Beehive-extension

The source code to read

  • SideBarCommand
  • commandSet

9. Customize the plug-in preference configuration and perform different logic according to the configuration

scenario

Each plug-in can add its own configuration of preferences. When vsCode is opened, different logic is executed based on the configuration selected by the preferences

The code shown

Let’s now configure our preference parameters in package.json

{
   "contributes": {
      "configuration": {
        "title": "sugar-demo-vscod"."properties": {
          "sugar-demo-vscode.matchConfig": {
            "type": "string"."description": "Sugar-demo-vscod configuration, low version by default"."enum": [
              "lowMatch"."middleMatch"."highMatch"]."default": "lowMatch"."scope": "window"
          }
        }
      }
  }
}
Copy the code

Note that the name sugar-demo-vscode must be consistent!

We have implemented the preference configuration above, and look at the effect

We then get the configuration and execute the different logic


    // Get the version Settings configured by the user
    const matchConfig = vscode.workspace.getConfiguration().get('vscode-beehive-extension.matchConfig')
    if (matchConfig === MATCH_CONFIG_MAPS.LOW) {
       console.log('low match')}else if (matchConfig === MATCH_CONFIG_MAPS.MIDDLE) {
        console.log('in the match')}else if (matchConfig === MATCH_CONFIG_MAPS.HIGH) {
        console.log('high-end')}else {
      vscode.window.showErrorMessage(`unknown error`)}Copy the code

If you want to modify the matchConfig content in code, you can go through

// The last parameter, true to write global configuration, false to write workspace configuration only
vscode.workspace.getConfiguration().update('vscode-beehive-extension.matchConfig'.'middleMatch, true);
Copy the code

The source code to show

  • beehive-customUserConfig.ts

Package and publish

You don’t need me to teach you that, do you? Search or have this aspect of the article, I am not the porter, interested in their own to search, or wait for my follow-up actual combat article to see?

Be a few words

So far, most of the applicable VScode demo examples are in: learn-vscode-extension, if you want to see the source code, please go to 👉 vscode-beehive-extension

A link to the

  • Hiro’s blog
  • SugarTurboS
  • vscode-gitlens
  • Official Documentation API
  • Xiao Ming -vscode plug-in development overview
  • 【KT】 VScode plug-in development example series (1)

Kua small Ming students article, although I did not seriously look at the content, I am looking at the figure, and then go to see the corresponding source of the figure, but in the early development of VScode plug-in, can be said to help or relatively large ~