Vscode’s markdown preview is a feature we use all day long. Have you ever wondered how it works? Maybe one day you’ll get a request for a customized Markdown preview. What should you do?

Any ideas? Think for five seconds.

5

4

3

2

1

In fact, the overall idea is relatively simple, is to create a WebView panel, set the content to the HTML generated by MarkDown, and then synchronize the HTML modification of the WebView when markDown update.

Thought analysis

Through vscode. Window. Create a webview createWebviewPanel, specified in the open, after the panel object webview. The HTML attributes to set the HTML.

The HTML is generated from the editor’s Markdown content, which is retrieved from editor.document.gettext () and then generated by calling a third-party markdown to the HTML library.

This completes the markdown preview.

Preview needs to be updated, after listening vscode. Workspace. OnDidSaveTextDocument and vscode workspace. OnDidChangeTextDocument events, at the time of document update and save, get the content of the editor, Regenerate the HTML and set it to the WebView.

Support the webview webviewPanel. PostMessage (message); Supports a series of commands such as updateHTML that can be triggered by passing a message.

But how do you know which document updates which WebView?

You can maintain a map, which is recorded in the map when creating a webviewPanel. The key is the file path, so that the corresponding WebView can be found when updating.

This completes the markdown content update.

But the whole idea is pretty simple, so let’s write down the code

Code implementation

Let’s look at the code for vscode-Markdown-preview-enhanced plug-in. This is also a preview of MarkDown plug-in. The code is relatively simple and can be used to learn.

(The following code is simplified)

First, the plugin specifies the conditions to trigger, that is, activationEvents in package.json:

"activationEvents": [
    "onLanguage:markdown",
    "onCommand:markdown-preview-enhanced.openPreviewToTheSide"
],
Copy the code

In this case, one is activated when editing the markdown content, and one is activated when executing command.

The specific activation logic is in the active method:

export function activate(context: vscode.ExtensionContext) {

  const contentProvider = new MarkdownPreviewEnhancedView(context);

  context.subscriptions.push(
    vscode.commands.registerCommand(
      "markdown-preview-enhanced.openPreviewToTheSide",
      openPreviewToTheSide,
    ),
  );
  
  function openPreviewToTheSide(uri? : vscode.Uri) {
    let resource = uri;
    if(! (resourceinstanceof vscode.Uri)) {
      if (vscode.window.activeTextEditor) {
        resource = vscode.window.activeTextEditor.document.uri;
      }
    }
    contentProvider.initPreview(resource, vscode.window.activeTextEditor, {
      viewColumn: vscode.ViewColumn.Two,
      preserveFocus: true}); }}Copy the code

We registered that command, and executing that command will get the URL of the current editor, and then preview markdown.

Preview all the logic is defined in the MarkdownPreviewEnhancedView instance objects, in the command execution initPreivew when triggered.

public async initPreview(sourceUri: vscode.Uri, editor: vscode.TextEditor, viewOptions: { viewColumn: vscode.ViewColumn; preserveFocus? :boolean },
) {
    / / create the webview
    let previewPanel: vscode.WebviewPanel = vscode.window.createWebviewPanel(
        "markdown-preview-enhanced".`Preview ${path.basename(sourceUri.fsPath)}`,
        viewOptions
    );

    // Listen for webView messages
    previewPanel.webview.onDidReceiveMessage((message) = > {});

    // Record webView to map
    this.previewMaps[sourceUri.fsPath] = previewPanel;
    
    // Get the text from the editor and generate HTML
    const text = editor.document.getText();
    engine
      .generateHTMLTemplateForPreview({inputString: text})
      .then((html) = > {
        // Set HTML to previewPanel
        previewPanel.webview.html = html;
      });
}
Copy the code

Create a webviewPanel in initWebivew and save the webviewPanel to the map with key as the file path of the document. Get the editor text to generate the HTML, set it to webView.html, and complete the markDown preview.

Once this path has been taken, we are ready to preview markdown.

However, it is not enough to preview the document only once, since it needs to be updated automatically after updating the document, we continue to add event listeners to the active method:

  context.subscriptions.push(
    vscode.workspace.onDidSaveTextDocument((document) = > {
      if (isMarkdownFile(document)) {
        contentProvider.updateMarkdown(document.uri, true); }})); context.subscriptions.push( vscode.workspace.onDidChangeTextDocument((event) = > {
      if(isMarkdownFile(event.document)) { contentProvider.update(event.document.uri); }}));Copy the code

Call the Update method when listening for text changes and saves.

public updateMarkdown(sourceUri: Const previewPanel = this.previewMaps[sourceuri.fspath]; // Generate the latest HTML and pass it to webView const text = document.gettext (); engine .parseMD(text) .then(({ markdown, html }) => { previewPanel.webview.postMessage({ command: "updateHTML", html }); }}Copy the code

PostMessage is a webView. postMessage that sends the updateHTML command message to the HTML content, triggering an update of the HTML content.

Thus, we have achieved a synchronous refresh of markdown.

conclusion

Markdown preview is a common but easy feature to implement in VSCode. We have a look at the source code of vscode-Markdown-preview-enhanced plug-in to clear the overall process:

  • Through vscode. Window. CreateWebviewPanel create webviewPanel to display HTML
  • The HTML gets the text from editor.document.gettext () and is generated by a third-party package that is set to a webviewPanel
  • Listen to the workspace. OnDidSaveTextDocument and workspace. OnDidChangeTextDocument, to get the latest content, The HTML is then generated and updated to the WebView by passing udpateHTML messages through webView.postMessage.
  • Note that a map is needed to store the mapping between uri.fsPath and webviewPanel, and the corresponding WebView is updated by changing the text content

Markdown preview is a common but not difficult requirement. It is also suitable for starting vscode plug-in development. I hope this article will help you to clear your mind.