Describes the process of implementing a VS Code extension, and from this example, you can easily write other similar extensions.

GitHub project address

References

  • Example – Hello World
  • Visual Studio Code Extensions: Editing the Document
  • pangu
  • text-autospace.js
  • vscode-alignment

Note

Like many people, I have a kind of obsessive-compulsive disorder. When I see typesetting between Chinese and English without Spaces, I feel uncomfortable and have to correct it. When taking notes, we often have to modify the content copied from the Internet, and the Spaces are manually added one by one. We got tired of it, and then we thought, there should be a plug-in to help us do this, so Google “VScode plug-in Chinese and English Spaces”, did not find valuable content, Google “Chinese and English Spaces”, “Why am I so adept at adding Spaces?” Then I learned about the Pangu project, a JavaScript implementation that automatically places Spaces between half-corner and full-corner characters, which was exactly what I wanted. But on its presentation page, there were all kinds of editor plug-ins based on it, except for VS Code, and I thought, No, we just masturbate one by ourselves. It’s not so difficult. The core implementation is already there.

(Postscript: If I had known to search for “Pangu” in the first place, I would not have built the wheel. After I wrote the Code, I uploaded it to VS Code Marketplace and searched for “VScode-Pangu” and found the same implementation: Halfcrazy /vscode-pangu, disrespect!)

The Pangu library provides a core method for converting strings (the internal implementation is done through regular matching), as follows:

Const refinedText = pangu.spacing(originText) // example const refinedText = pangu.spacing(" This is a VS Code extension ") // refinedText: "This is a VS Code extension"Copy the code

So all the plug-in has to do is get all the text in the current editor, call this method, get the text with Spaces, and replace the original text. So the key is, how do you get the text in the editor, how do you replace it. This, of course, requires VS Code’s Extension API.

This is the first time I’ve seen the VS Code Extension API Document cram all the apis into one page, and the INTRODUCTION of the API is too concise.

VS Code has a Trailing Whitespace command. Press CMD + Shift + P. In the displayed command window, enter trim, select trim Trailing Whitespace, and press Enter.

The plugin we want to implement is similar to this command, press CMD + Shift + P and type something like Add Space in the command window that pops up, except instead of clearing extra Spaces, we add Spaces, but essentially the same, modifying the text in the editor.

The VS Code Extension documentation provides a Hello World example, which fortunately is exactly what we need. The example works like this. Typing Hello World in a command window will bring up a prompt window with code like this:

// extension.js function activate(context) { let disposable = vscode.commands.registerCommand('extension.sayHello', function () { // 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! '); }); context.subscriptions.push(disposable); }Copy the code

Other we can all do not need to understand, only need to put the vscode. Window. ShowInformationMessage (‘ Hello World! ‘); This line of code is replaced with our own logic, which is to get the text, add a space, replace the original text, and the plug-in is almost complete.

First, let’s see how to get the text. In the Hello World sample article, we can look briefly at the following objects:

  • Window object – Represents the entire current VS Code Window, usingvscode.windowI get this Window object.
  • TextEditor object – VS Code may have multiple tabs open throughout the window, and each TAB is a TextEditor object, but we only need the currently active TAB, which we usewindow.activeTextEditorProperty to get the current working TAB, the TextEditor object.
  • TextDocument Object – Each TextEditor has a document, and that document is the TextDocument object that we useeditor.documentProperty to get the TextDocument object in the TextEditor object. The TextDocument object has onegetText()Method to get all the text in it.

Finally, we passed

const originText = vscode.window.activeTextEditor.document.getText()
Copy the code

Gets all the text of the document currently being edited.

Now that I have the original text, I’m all set (ignoring the process of installing Pangu via NPM) :

const refinedText = pangu.spacing(originText)
Copy the code

It’s time to replace the old text with the new text. If we have the document.gettext () method, we should have a matching document.settext (string) method.

vscode.window.activeTextEditor.document.setText(refinedText)
Copy the code

TextDocument does not have such a method, and there is no similar method, confused, exactly how to manipulate the original text?

Find one of the few articles on the web that explains how to edit a Document using a plug-in – Visual Studio Code Extensions: Editing the Document. In this article, get to the heart of VS Code plug-in Editing text content.

This idea is embodied in an object – a TextEdit object (note, not a TextEditor). A TextEdit object represents an operation on text.

There are no more than three operations on text: add, delete, and replace, but in essence, add and delete are also substitution operations. Increment, replace the empty string with a new string; Delete and replace the original string with an empty string.

For the object to be replaced, the original string, we need to know its position in the document. This position includes the start position and the end position. Each position should include its line number and the line number within it, which form an interval.

VS Code uses the Position object to represent the Position of a character in a document, which has two properties:

  • The line – the line number
  • Character – The number of the row

A starting Position and an ending Position form the Range object, which represents a series of consecutive characters.

Now that we have an object to replace and a new string, we can define a TextEdit object to represent such a replacement.

const aTextReplace = new vscode.TextEdit(range, newText)
Copy the code

For example, if we want to delete line 2, character 3, through line 5, character 6, and replace it with an empty string, the code looks like this:

const start = new vscode.Position(2, 3)
const end = new vscode.Position(5, 6)
const range = new vscode.Range(start, end)
const aTextDel = new vscode.TextEdit(range, '')
Copy the code

The first three lines of code can be simplified to

const range = new vscode.Range(2, 3, 5, 6)
Copy the code

The TextEdit object can be generated using the textedit.delete (range) static method:

const aTextDel = vscode.TextEdit.delete(range)
Copy the code

Range and TextEdit, I think, are the core concepts of manipulating text, understanding these two objects, the rest is not that hard.

However, so far, TextEdit has only defined an action that will be applied, but it has not yet been applied to text, so how can this action actually be performed?

A new object is involved here – the WorkspaceEdit object. WorkspaceEdit can be understood as a container for TextEdit. TextEdit is a single operation on the text. If we need to perform multiple operations on the text at the same time, such as global substitution, we define multiple TextEdit objects and place these objects in an array, which we then place in a WorkspaceEdit object.

Even more powerful, WorkspaceEdit supports multiple simultaneous operations on multiple documents, so each TextEdit array must correspond to a document object. WorkspaceEdit uses URIs to represent a document, The URI can be obtained from the document.uri attribute.

We’ve got the Document object, and we’ve defined some TextEdit objects, which we put into the WorkspaceEdit object:

let textEdits = []
textEdits.push(aTextDel)
// push more TextEdit
// textEdits.push(...)

let workspaceEdit = new vscode.WorkspaceEdit()
workspaceEdit.set(document.uri, textEdits)
Copy the code

At last, we can really do this, use vscode. Workspace. ApplyEdit () method to make these actions take effect:

vscode.workspace.applyEdit(workspaceEdit)
Copy the code

Let’s see how our plugin works:

const editor = vscode.window.activeTextEditor if (! editor) { return // No open text editor } const document = editor.document const lineCount = document.lineCount let textEdits = [] for (let i=0; i<lineCount; i++) { const textLine = document.lineAt(i) const oriTrimText = textLine.text.trimRight() if (oriTrimText.length === 0) {  textEdits.push(new vscode.TextEdit(textLine.range, '')) } else { const panguText = pangu.spacing(oriTrimText) textEdits.push(new vscode.TextEdit(textLine.range, panguText)) } } let workspaceEdit = new vscode.WorkspaceEdit() workspaceEdit.set(document.uri, textEdits) vscode.workspace.applyEdit(workspaceEdit)Copy the code

It’s a little bit more code because I’m doing some extra work to remove extra trailing whitespace, but the overall logic is pretty simple, just iterating through each line, fetching each line object through document.lineat (I), and each line is a TextLine object, This object has the contents of all the text in this line, and their Range. If it is an empty line, a TextEdit object is generated that replaces the original content with blank text, otherwise, a TextEdit object is generated that replaces the original text with whittled text. These TextEdit objects are placed as an array into the WorkspaceEdit object, and all operations in this object are performed.

WorkspaceEdit is designed to perform multiple operations on multiple documents at the same time. If we just want to edit the current document, using WorkspaceEdit is a bit of an overkill. As you can see from above, there are too many layers to wrap around.

If you edit only the text of the current TAB, the TextEditor object, you can use the Edit () method of the TextEditor object. The code is similar, but the TextEdit object is not explicitly generated. Just look at the code:

editor.edit(builder => { for (let i=0; i<lineCount; i++) { const textLine = document.lineAt(i) const oriTrimText = textLine.text.trimRight() if (oriTrimText.length === 0) {  builder.replace(textLine.range, Delete (textline.range)} else {const panguText = Pangu.spacing (oriTrimText) builder.replace(textLine.range, panguText) } } })Copy the code

Builder.repalce (Textline.range, panguText) is equivalent to executing a TextEdit(Textline.range, panguText) object. By comparison, the code is a little cleaner than above.

Be careful not to write loops outside editor.edit(), which is what I did in the beginning, – Extension API Texteditoredit. replace only works on primary selection? .

There is also a more common object Selection, which inherits from the Range object and therefore represents a Range. It represents the area selected by the cursor in the TAB, and can be obtained through editor.Selection.

Finally, to summarize, text manipulation in VS Code Extension mainly uses the following objects, attributes, and methods:

  • Window
    • activeTextEditor
  • TextEditor
    • document
    • selection
    • edit()
  • TextDocument
    • uri
    • lineCount
    • lineAt()
    • getText()
  • WorkspaceEdit
  • TextEdit
  • Range
  • Selection
  • Position