banner
isolcat

isolcat

I am not afraid of storms, for I am learning how to sail my ship
github

Try using VScode as your own RSS reader and develop your own VScode plugin.

Foreword#

RSS(RDF Site Summary) has a history of 24 years as a format specification for news sources, used to aggregate content updates from multiple websites and automatically notify website subscribers. RSS is also used on various blogs for convenient subscriptions. However, when choosing an RSS reader, it can often be a headache, not knowing which one to choose. After trying several well-known RSS readers, I still felt somewhat dissatisfied. Looking at VScode lying in the taskbar, a fun idea was born—I would turn VScode into my own RSS reader.

💡 Requirement Analysis#

Since there is this need, we need to analyze it:

  1. Add RSS Link: Users input or select an RSS link and save it in the plugin's configuration.
  2. Parse RSS: Use an RSS parsing library to parse the saved RSS link and extract blog information.
  3. Display Blog List: Show the blog list in the plugin, allowing users to select which blog to view.
  4. Select Article: Allow users to select an article from the blog.
  5. Display Article Content: Display the content of the selected article.

🛠️ Development Process#

Project Initialization#

As stated in the VScode Plugin Development Documentation, to develop our first VScode plugin, we need to install Yeoman and VS Code Extension Generator:

npm install -g yo generator-code

Run the command: yo code

image

Here, select New Plugin (JavaScript), then choose the extension name (which can be changed later). After running this command, our plugin will be initialized.

The project structure is as follows:

.
├── .vscode
│   ├── launch.json     // Configuration for launching and debugging the extension
│   └── tasks.json      // Build task configuration for compiling JavaScript
├── .gitignore          // Ignore build output and node_modules
├── README.md           // Readable description of the plugin's functionality
├── src
│   └── extension.js    // Plugin source code
├── package.json        // Plugin manifest
├── jsconfig.json       // JavaScript configuration

Running the Project#

We open extension.js and see code filled with comments. Here we only need to pay attention to two functions:

  1. activate

This is the function executed when the plugin is activated.

  1. deactivate

This is the method called when the plugin is destroyed, such as for releasing memory.

Most of our development will be executed in the activate function. Now let's run this project. On the extension.js page, we press F5 and select VScode Plugin Development. A new VScode window will open, where we can run our developing plugin. Press ctrl+Shift+P, enter the default execution command of the plugin Hello world, and we can see a small pop-up in the lower right corner.
image
This is the default command to run the plugin.

To better run our plugin, we enter package.json and modify the configuration:

  "activationEvents": [
    "onCommand:allblog.searchBlog"
  ],
  "main": "./extension.js",
  "contributes": {
    "commands": [
      {
        "command": "allblog.searchBlog",
        "title": "searchBlog"
      }
    ]
  },

Note that after modifying here, we should also synchronize the command to execute the plugin: vscode.commands.registerCommand binds the command ID to the handler function in the extension.

image

Now we run the command Reload window in the newly opened VScode window to reload it. When we run the plugin again, we can use our defined command: searchBlog.

Basic Functionality Implementation#

When we usually subscribe to RSS, we find that it is mostly in XML (Extensible Markup Language) format. To implement our functionality, we must parse it into a format that can be called with Js code. We introduce the utility library: fast-xml-parser, using Antfu's blog as a test example:

const vscode = require("vscode");
const xmlParser = require("fast-xml-parser");

async function activate(context) {
  // Get RSS feed and parse it
  const response = await axios.get("https://antfu.me/feed.xml");
  const articles = xmlParser.parse(response.data).rss.channel.item;
  
  // Save article list
  const articleList = articles.map((item, index) => ({
    label: item.title,
    description: item.description,
    index
  }));
...

To allow users to freely choose from the article list, we need to display the list of articles. At this point, we have achieved basic display:

image

However, when we click on an article, there is no response, as we haven't written the display method yet. 😂

Here we check the API provided by VScode and find that the Webview API is very suitable for implementing this functionality. We treat webview as an iframe that controls the content within VScode. Thus, we modify the code as follows:

    if (selectedArticle) {
      const { link } = selectedArticle;
      const webViewPanel = vscode.window.createWebviewPanel(
        'blogWebView',
        'Blog Article',
        vscode.ViewColumn.One,
        {
          enableScripts: true,
          retainContextWhenHidden: true
        }
      );

      // Set the HTML content of the Webview
      webViewPanel.webview.html = `
        <!DOCTYPE html>
        <html>
          <head>
            <style>
              body {
                font-family: Arial, sans-serif;
              }
            </style>
          </head>
          <body>
            <iframe src="${link}" width="100%" height="100%"></iframe>
          </body>
        </html>
      `;
    }
  }

Successfully opened!
image

Shall we optimize it a bit? Let's allow the user to freely set the height of the window:

if (selectedArticle) {
			const { link } = selectedArticle;

			const heightInput = await vscode.window.showInputBox({
				prompt: 'Enter the height of the window (in px)',
				placeHolder: '500'
			});

			const webViewPanel = vscode.window.createWebviewPanel(
				'blogWebView',
				customName, // Use the custom name as the title
				vscode.ViewColumn.One,
				{
					enableScripts: true,
					retainContextWhenHidden: true
				}
			);

			let height = parseInt(heightInput) || 500;
			webViewPanel.webview.html = `
        <!DOCTYPE html>
        <html>
          <head>
            <style>
              body {
                font-family: Arial, sans-serif;
              }
            </style>
          </head>
          <body>
            <iframe src="${link}" width="100%" height="${height}px"></iframe>
          </body>
        </html>
      `;
		}

At this point, we have completed the basic functionality.

Optimize a Bit?#

When we run the command again, we find that the already subscribed RSS displays the previous links, which can be quite troublesome. Considering that after subscribing to many RSS feeds, just looking at the links may not be very memorable, we might as well try adding a custom name so that we can remember the subscribed RSS in a way we are used to. Let's modify the code:

...
if (customName) {
            // Check for duplicate custom names
            const duplicateCustomName = Object.values(rssLinks).includes(customName);
            if (duplicateCustomName) {
              vscode.window.showErrorMessage('This custom name is already in use. Please choose a different name.');
              return;
            }
            // Add new RSS link and corresponding custom name
            rssLinks[newLink] = customName;
            vscode.workspace.getConfiguration().update(RSS_LINKS_CONFIG_KEY, rssLinks, vscode.ConfigurationTarget.Global);
            await getArticleContent(newLink, customName);
          }

Ok, now we have custom names.
image

🧐 Discover Problems and Solve Them#

Next, it's time to choose to publish the plugin. You can look for related articles yourself; this article will not explain too much. After the plugin is successfully published, we downloaded the plugin and added our favorite RSS blogs. However, when we closed and reopened it, oh no! The previously subscribed RSS was gone! 😭
No big deal; once we discover a problem, we need to solve it. We reviewed the previous code and found a huge issue: we forgot to handle data persistence for the already subscribed RSS. We checked the VScode documentation and found that it provides four data storage options:

Here, we use ExtensionContext.globalState for global storage, saving data in the user's local configuration file. This way, even if VScode is closed and reopened, there will be no data loss. We modify the code:

...
// Add new RSS link and corresponding custom name
      rssLinks[newLink] = customName;
      // Save subscription information to the user's local storage
      context.globalState.update(RSS_LINKS_CONFIG_KEY, rssLinks);
      await getArticleContent(newLink, customName);
...

After republishing the plugin, it's done! Now a simple RSS reader plugin is complete!

image

🐱 Summary#

The plugin is now online (though not yet a formal version) and can be downloaded on VScode: Download Link, or you can directly search for RSS-searchBlog in the VScode extension. Currently, the functionality is not yet perfect, such as not providing a small pop-up prompt when there are updates after subscribing. Future updates will improve this plugin. I previously saw an interesting open-source project called RSSHub, which can generate RSS feeds for any strange content. I plan to integrate the plugin with RSSHub and continuously improve its functionality. I believe the official version of the plugin will satisfy everyone. Thank you for reading. The source code for this project is: RSS-searchBlog.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.