preface

The design of plug-in system is the most important means for many open source projects to achieve community driven, which provides a strong scalability for the project. In the JavaScript ecosystem, there are many tools and libraries that implement plug-in mechanisms, such as Babel, Webpack, Vue, Vue Cli, and today’s hero VS Code.

What can the VS Code plug-in do?

  • Themes Themes include interface colors and file ICONS, such as material Theme and Material File Icon.
  • Custom UI extends vscode’s workbench by adding custom buttons & interfaces in the sidebar, bottom status bar.
  • Webview Opens a Webview in a new TAB, maximizing custom interface elements.
  • New Runtime Support provides support for a New runtime.
  • Language support provides support for a new language (such as pseudo-code for our custom rules).

Today we’ll focus on the last of those features, support for a new programming language. You may be wondering why VS Code supports a language in the form of plug-ins.

First of all, VS Code itself doesn’t come with built-in support for various programming languages, including JavaScript, as you might expect. VS Code provides a set of apis for plug-ins that enable a plug-in to implement language-specific support. HTML, for example, is supported by HTML plug-ins.

When you type console.log, VS Code notifies a plugin called Typescript Language Features, and the plugin responds by making the log appear in the completion prompt.

This means that we can customize our own programming language using these language apis. So let’s explore how to achieve this cool little goal.

One tip: The best position to read this article is to open it on your computer and follow it step by step.

Results show

First, let’s write a little pseudocode. Zz is an unsupported suffix in VS Code, so all characters are grey (zz stands for zizzle in zizzle).

By the end of the article, they will look like this:

Create a project

The first step in writing a plug-in is to create our directory structure. Here we use a scaffolding tool called Yo. Yo is a highly extensible, general-purpose scaffolding that allows plug-ins to implement different directory structures and initial options. VS Code officially provides a plugin called generator-code to create a plugin directory. First we need to install Yo and the plugin generator-code.

npm i -g yo generator-code
Copy the code

After the installation is complete, use the following command to create the directory structure.

yo code
Copy the code

After running Yo Code, it will ask you the following questions. I suggest that you keep the same input as me to avoid unexpected encounters. Here we name the pseudocode zhuanzhuan and tell VS Code to run our plugin when we encounter a file with a.zhuanzhuan or.zz extension.

After entering all the options, our plug-in directory is created. The structure looks like this:

├─ CHANGELOG. Md ├─ Language-Configuration. Json ├─ Package In tmLanguage. Custom in json syntax │ └ ─ ─ zhuanzhuan. TmLanguage. Json └ ─ ─ VSC - the extension - quickstart. MdCopy the code

To understand the Scope

Syntax highlighting involves taking a string of code, breaking it up into countless smaller pieces, and then assigning styles such as color to each of them. And these little pieces, they’re called tokens. The tokens in JWT are different from the tokens in JWT. They do not have the meaning of security, token, etc. Instead, they are more “symbolic”. Let’s look at a simple example 🌰 to understand this statement.

function sum(a: number, b: number) :number {
  return a + b;
}
Copy the code

In this TS code, we define a function for summing. At this point, press the VS Code shortcut, shift+ CMD + P, and type inspect Editor Tokens and scopes to see the corresponding type for each token. For example, sum is function and a and B are parameters.

As you can see from the bottom of the screenshot, each token also has a property called TextMate Scope. In layman’s terms, scope refers to the location of the token. For example, in the following code snippet, there are two A variables. The first is a variable declaration, while the second is one of the parameters of the function. Although they can be called variables in general, they are marked with different colors because they are in different scopes (that is, in different environments).

const a = 1;

function sum(a: number, b: number) :number {
  return a + b;
}
Copy the code

Support the annotation

Now that you’ve covered the basics, it’s time to actually modify the scaffolding creation code. Our first goal is to have the Zhuanzhuan language support annotations.

Json file in the root directory, find the Comments field and change lineComment from the default // to a comment:. When done, press F5 to start the Debug program and VS Code will open a new window where our plug-in will take effect. In the new window, we randomly open an empty folder, create a new folder named fakecode.zz, and type the following to test.

Note: When we use Chinese pseudocode to describe the execution process, note: whatever content is displayed as a gray string. Note: Our goal is to make them colorful. Note: Don't try to refactor this method or you'll lose the day. If ([a condition]) {do something that the condition holds} otherwise {when the condition does not hold... } End function [function name] {function contents}Copy the code

At this point, we press CMD +/, the shortcut key for annotation conversion, and will be surprised to find that VS Code automatically converts the content of the annotation for you.

That completes our Hello World project and lets us do something a little more complicated. Open/syntexes/zhuanzhuan. TmLanguage. Json file, the file all the content is replaced with the following contents:

{
	"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json"."name": "zhuanzhuan"."scopeName": "source.zz"."patterns": [{"include": "#comments"}]."repository": {
		"comments": {
			"name": "punctuation.comment"."begin": "Note:"."end": "\\n"."beginCaptures": {
				"0": {
					"name": "punctuation.comment.open"}},"endCaptures": {
				"0": {
					"name": "punctuation.comment.close"
				}
			}
		}
	}
}
Copy the code

It’s confusing at first glance, but let’s get a sense of what it means.

  • Patterns & Repository A repository is a repository of rules that specify how the rule identifies the objects to which it applies. Patterns, in turn, specifies which rules in the rules repository need to be in effect. So if we want to add a new rule, we need to add the rule to repository and include the rule in our Patterns. Otherwise, adding the rule to Repository won’t work.

  • Comments is the custom rule that we added, comments is the name of the rule.

  • Begin & End determine who this rule applies to. Here we use the characters at the beginning of the comment and the end of the carriage return as applicable objects.

  • BeginCapture & endCapture & Name represent the scope name we assign to the applicable object. A comment like “Comment: Don’t try to refactor this method or you’ll lose a day.” , corresponding to this rule, we’s comments: this part is endowed with punctuation.com ment. Open the scope, don’t try to refactor the method, or you will waste time of a day. Scope = punctuation.comment, the last carriage return character scope is punctuation.comment. Open.

Then switch to the Debug window we just opened using F5, press CMD + Shift + P, run the Reload Window to make our changes take effect, and you can see the scope name change:

Support keywords

Keyword is also edit the file/syntexes/zhuanzhuan tmLanguage. Json, the rules of new join keywords in the repository (remember to include it in at the top of the patterns) :

{
  "keywords": {
    "patterns": [{"match": "\ \ b (if | traversing the end | | | printing function) \ \ b"."name": "keyword.control.zhuanzhuan",}]}}Copy the code

To explain what I mean here:

  • Match this is a regular, if meet if | traversing the end | | | printing function is one of them, will it marked as a keyword.

  • What is the scope corresponding to the keywords name?

The effect is as follows:

From the figure, we can see that the characters (if, traversal, function, etc.) matched in the re have been highlighted one by one. Your VS Code doesn’t have to be blue, though, depending on the theme you’re currently using.

Support string

We then made the Zhuanzhuan language support strings, again modifying JSON files.

{
  "repository": {
    "strings": {
      "name": "string.quoted.book.zhuanzhuan"."begin": "" "."end": "" "."beginCaptures": {
        "0": {
          "name": "string.quoted.book.open"}},"endCaptures": {
        "0": {
          "name": "string.quoted.book.close"
        }
      }
    }
  }
}
Copy the code

Just as JS uses single and double quotation marks and template strings to mark strings, to reflect the differences between zhuanzhuan languages, we use book numbers instead of single and double quotation marks to mark a string. For example, XXXX is divided into three parts of XXXX, which have their respective scopes. The corresponding relationship is as follows:

  • string.quoted.book.open
  • xxxx string.quoted.book.zhuanzhuan
  • string.quoted.book.close

To see the changes, in the debug window, run CMD + Shift +p and run the Reload Window. Reloading looks like this:

Now line 13 changes, from black to green.

In-depth understanding of scope

Once you’ve seen it, look back at the JSON file and see what it really means.

First we specify the name of the top-level scope source.zz. That is, when we create a.zz file and start writing code, all of our code is in the top-level scope.

The Patterns property specifies how a subscope can be opened within the top-level scope. The Patterns array inclucde contains rules called strings and keywords, which are placed in a repository.

{
  "strings": {
    "name": "string.quoted.book.zhuanzhuan"."begin": "" "."end": "" "}}Copy the code

​ 在repositoryIn order tostringsRules, for example, when the VS Code parsing engine encounters a Code that begins with “” and ends with” “tokenThe content in the middle is considered a string. In other words, we have given the same functionality as single and double quotation marks in JS. A string ofscopeIt becomes our rulestring.quoted.book.zhuanzhuan. We can get throughinspect editor tokens and scopesCommand to verify this.

Pictures, XXXX’s scope has two, one is the constant. The character. The escape. Zhuanzhuan, another is the root of scope. A token tends to have multiple scopes, like a string, in both the root scope and the one created by the book name.

In the JSON file above, there is also a property called keywords, which is considered a keyword when a string meets the regular expression in the match field.

In fact, as we extend and nest the JSON rules above, we get closer and closer to other popular languages, with countless nesting. A token can belong to an infinite number of scopes.

Then the question arises, what are the functions of these scopes? We took a lot of effort to define the JSON format so that tokens in different locations have different scopes. So we have something like a CSS selector where we can specify different styles for different scopes to highlight our own language.

Using the Scope

Next we will use the scope defined above. Since so far we have only redefined scope names for some scenarios in zhuanzhuan language, we can use these custom scopes to make more detailed highlighting configurations.

The way to use scope is to create a plug-in of type Theme (yes, we will write a second plug-in). This time we need a CD to the.vs Code/Extensions folder in the user folder so that our theme can appear directly in the VS Code theme list without installation.

Open the project with VS Code and edit the theme/zhuanzhuan-lang-theme-color-theme.json file. The structure of the file looks like this:

{
 "name": "zhuanzhuan-lang-theme"."type": "light"."colors": {
  "editor.background": "#f5f5f5"."editor.foreground": "# 333333"
 },
 "tokenColors": [{"name": "Comments"."scope": [
    "comment"."punctuation.definition.comment"]."settings": {
    "fontStyle": "italic"."foreground": "#AAAAAA"}}}]Copy the code

The tokenColors field is the one we need to care about, specifying different styles for different scopes. Name is the name of this rule, which can be arbitrarily named to ensure that it is unique. A scope is similar to a CSS selector and is an object to which rules are applied. Settings is the concrete style.

Then we add our custom styles in tokenColors.

{
  "tokenColors": [
    // Omit other existing rules and list only new rules.
    {
      "name": "quotedBookOpen".Quote.letter. Open token; // Use scope as string.
      // Set the color to #33ec0e.
      "scope": "string.quoted.book.open"."settings": {
        "foreground": "#33ec0e"}}// omit a large section of the same configuration.
    // As long as scope is the same as scope in our Zhuanzhuan language definition,
    // You can highlight the corresponding token.]}Copy the code

Then in the debug window of zhuanzhuan-lang, open the theme selection list and select zhuanzhuan-lang-theme to see that the above three rules are in effect for XXXX.

The text and its corresponding scope and color are as follows:

  • "
    • scope: string.quoted.book.open
    • Color: # 33 ec0e
  • "
    • scope: string.quoted.book.close
    • Color: # 33 ec0e
  • xxxx
    • scope: string.quoted.book.open
    • Color: # eb8837

results

After a series of efforts above, and then adding billions of details, the final result is the image below.

conclusion

After reading the article, we created two VS Code plug-ins in total. One is the language support plug-in, which, through simple configuration, enables the Zhuanzhuan language to support Chinese keywords, book name strings, and variables represented by brackets. The second is the theme plug-in, which provides highlighting rules for custom scopes in the Zhuanzhuan language. Scope name, which is the hub connecting the two plug-ins.

However, zhuanzhuan language still needs a lot of work to be a complete language. We need to define more scope rules, and there are often complex nested relationships between rules. This article only covers the tip of the iceberg above sea level. If you want to learn more about this, you still need to refer to VS Code’s official documentation and learn about how compilation works.

Also attached is the final code of the two plug-ins in the article:

Github.com/inkyMountai…

Github.com/inkyMountai…

Reference: code.visualstudio.com/api#vscode

The level is limited, if there are mistakes, please correct.