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:
- Add RSS Link: Users input or select an RSS link and save it in the plugin's configuration.
- Parse RSS: Use an RSS parsing library to parse the saved RSS link and extract blog information.
- Display Blog List: Show the blog list in the plugin, allowing users to select which blog to view.
- Select Article: Allow users to select an article from the blog.
- 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
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:
- activate
This is the function executed when the plugin is activated.
- 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.
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.
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:
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!
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.
🧐 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!
🐱 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.