Building a VSCode Extension: Part Four

Search for a command to run...

No comments yet. Be the first to comment.
I will be documenting my journey as I slowly implement a vscode extension. Things covering - The planning - How I implement the features. - The struggles and challenges I will come across. - and more
I have been looking for a fun side project to work on in my free time. Building projects is the best way to actually improve your skills as a developer. I decided that I will try to document my journey publicly to help share my thoughts and decisions...
Have you ever wondered how to set a hotkey to focus an input field in your React application? Follow along to learn how. We'll even handle showing different hotkeys based on whether the user is on a Mac or Windows system. Implementation const SearchI...

As a software engineer, choosing and understanding your text editor is important part of your work, as it impacts your productivity and workflow efficiency. It's like choosing the perfect tool for any trade - you need to know what tool to use and how...

Design systems are essential tools for software engineers as they provide a structured framework for maintaining consistency and coherence in product design. By offering clear guidelines and standards, design systems streamline the development proces...

Let's Build our own real-time page view tracker using Next.js as the frontend framework and a Postgres database hosted by Supabase. One of the best ways to understand how your blog posts are performing is by tracking page views. You can begin to unde...

One of the best ways to drive traffic to your website is to have strong Search Engine Optimization (SEO). You can provide search engines with all the URLs for your website using a Sitemap. This allows for easier indexing and more efficient crawling b...

One of the of most important things to make this extension function is to figure out the best way to have the React.js app communicate with the extension framework. After reading the docs and playing around, it was fairly simple using VS Codes message API.
VS Code offers a special API Object inside their webview by calling acquireVsCodeApi inside your JavaScript. The API object has a postMessage() function that can be used to send messages back to the extension's backend. You can subscribe to messages in the backend using the panel.webview.onDidReceiveMessage() function.
Example of sending a message when script is loaded in Webview App.tsx
// Add typedef for acquireVsCodeApi
declare const acquireVsCodeApi: Function;
// Fetch the api object
export const vscodeApi = acquireVsCodeApi();
vscodeApi.postMessage('React App Loaded')
You can then verify your extension caught the message using:
panel.webview.onDidReceiveMessage((message) => console.log('MESSAGE', message))
Now that we can send messages to the VS Code backend, we need to figure out how to send messages back to the webview and catch it. You can easily send a message using panel.webview.postMessage() function which is similar to how we sent the message from the webview. Instead of using the VSCodeAPI to catch the message in the webview, you actually add an event listener on the window object for message.
Sending the message from the VS Code backend after react app loads:
panel.webview.onDidReceiveMessage((message) => {
if (message === 'React App Loaded') {
panel.webview.postMessage('Extension Knows React is ready');
}
})
Webview listening for a message from the VS Code backend in App.tsx:
window.addEventListener('message', (message) => console.log('CAUGHT THE MESSAGE', message));
You should now see a console.log() with the caught message.
I decided to create a lib service that wraps the VS Code API. I can add more type checking to the API and simplify cleanup of eventListeners.
declare const acquireVsCodeApi: Function;
interface VSCodeApi {
getState: () => any;
setState: (newState: any) => any;
postMessage: (message: any) => void;
}
class VSCodeWrapper {
private readonly vscodeApi: VSCodeApi = acquireVsCodeApi();
/**
* Send message to the extension framework.
* @param message
*/
public postMessage(message: any): void {
this.vscodeApi.postMessage(message);
}
/**
* Add listener for messages from extension framework.
* @param callback called when the extension sends a message
* @returns function to clean up the message eventListener.
*/
public onMessage(callback: (message: any) => void): () => void {
window.addEventListener('message', callback);
return () => window.removeEventListener('message', callback);
}
}
// Singleton to prevent multiple fetches of VsCodeAPI.
export const VSCodeAPI: VSCodeWrapper = new VSCodeWrapper();
I can now subscribe to messages using useEffect inside of my App.tsx:
import React, { useEffect } from 'react';
import './App.css';
import { VSCodeAPI } from './lib/VSCodeAPI';
export default function App() {
useEffect(() => {
return VSCodeAPI.onMessage((message) => console.log('app', message));
});
return (
<h1>Hello World</h1>
);
}
Now that we can pass data between the view and the backend, we can start working on actual functionality. I need to look through the VS Code documentation on how to create a custom editor to generate and modify the todo.md file. I want to add Tailwind CSS to the front end for styles and create views for displaying and submitting todos.