banner
isolcat

isolcat

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

试试将VScode作为自己的RSS阅读器,开发自己的VScode插件

前言#

RSS(RDF Site Summary)作为一种消息来源格式规范,用以聚合多个网站更新的内容并自动通知网站订阅者,距今已经 24 年了的历史了,RSS 也用于各个博客上,方便用于的订阅,但是在选择 RSS 阅读器的时候往往会因此犯愁,不知道选择什么好,在尝试了几个知名的 RSS 阅读器后,我总觉得仍有些许的不满意,望着任务栏里躺着的 VScode,一个自认为好玩的想法诞生了 —— 将 VScode 打造成自己的 RSS 阅读器

💡需求分析#

既然有了这个需求,那就要对这个需求进行分析:

  1. 添加 RSS 链接: 用户输入或选择 RSS 链接,并将其保存在插件的配置中
  2. 解析 RSS: 使用 RSS 解析库解析保存的 RSS 链接,提取博客信息
  3. 显示博客列表: 在插件中展示博客列表,允许用户选择要查看的博客
  4. 选择文章: 允许用户选择博客中的一篇文章
  5. 显示文章内容: 将选定的文章内容展示出来

🛠️开发过程#

项目初始化#

正如VScode 插件开发文档所言,要开发我们的第一个 VScode 插件,要先安装好安装YeomanVS Code Extension Generator

npm install -g yo generator-code

运行命令:yo code

image

这里选择新插件 (JavaScript) 即可,然后选择扩展名称 (后续可以更改),在跑完这个命令后,我们的插件也便初始化完成了

项目结构如下:

.
├── .vscode
│   ├── launch.json     // 用于启动和调试扩展的配置
│   └── tasks.json      // 用于编译 JavaScript 的构建任务配置
├── .gitignore          // 忽略构建输出和 node_modules
├── README.md           // 插件功能的可读描述
├── src
│   └── extension.js    // 插件源代码
├── package.json        // 插件清单
├── jsconfig.json       // JavaScript 配置

运行项目#

我们点开extension.js,可以看到充满注释的代码,这里我们只需要注意两个函数:

  1. activate

这是插件被激活时执行的函数

  1. deactivate

这是插件被销毁时调用的方法,比如释放内存等

我们这次主要开发均在activate函数中执行,现在让我们来运行这个项目,在extension.js页面,我们按下 F5,选择 VScode 插件开发,这时候便会打开一个新的 VScode 窗口,这里新开的窗口便可以运行我们的开发中的插件了,按下ctrl+Shift+P,输入插件默认的执行命令Hello world,便可以看见右下角的小弹窗
image
这便是默认的运行插件的命令

为了更好的运行我们的插件,我们进入package.json,修改配置:

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

注意,修改这里后我们应同步修改执行插件的命令:vscode.commands.registerCommand将命令 ID 绑定到扩展中的处理函数

image

现在我们在新开的 VScode 窗口运行命令Reload window,重新加载窗口,再次运行插件的时候,就可以使用我们自己定义的命令:searchBlog

基础功能实现#

平常我们订阅 RSS 的时候不难发现,基本上都是 XML (可扩展标记语言) 格式,为了实现我们的功能,就必须将其解析成可用 Js 代码调用的格式,我们引入工具库:fast-xml-parser,这里用 antfu 老师的博客作为测试例子:

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

async function activate(context) {
  // 获取RSS feed并解析
  const response = await axios.get("https://antfu.me/feed.xml");
  const articles = xmlParser.parse(response.data).rss.channel.item;
  
  // 保存文章列表
  const articleList = articles.map((item, index) => ({
    label: item.title,
    description: item.description,
    index
  }));
...

为了实现用户可以自由选择文章的列表,我们要将文章的列表展现出来,此时已经实现了基础的展示:

image

但此时我们点击文章,是没有任何的反应的,毕竟我们还没有写好展示的方式嘛😂

这里我们查看 VScode 提供的 API,发现Webview API很适合我们这个功能的实现,将webview视为iframe您的扩展控制的 VSCode 内的内容,于是我们修改如下代码:

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

      // 设置Webview的HTML内容
      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>
      `;
    }
  }

成功打开!
image

再稍微优化一下?让他能够自由的设置窗口的高度:

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>
      `;
		}

到此为止,我们基本的功能就做好了

优化一下?#

这时候我们再次运行命令的时候,会发现已经订阅的 RSS 展示出来的是之前的链接,这可很伤脑筋,想想一下订阅的 RSS 多了之后,只看链接或许不能记忆的很清楚,那这时候我们不如试试给他新增个自定义名称,让我们订阅的 RSS 这些可以用我们习惯的方式记忆,来修改一下代码:

...
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;
            }
            // 添加新的RSS链接和对应的自定义名字
            rssLinks[newLink] = customName;
            vscode.workspace.getConfiguration().update(RSS_LINKS_CONFIG_KEY, rssLinks, vscode.ConfigurationTarget.Global);
            await getArticleContent(newLink, customName);
          }

ok,现在便是自定义的名称了
image

🧐发现问题并解决#

接着便是选择发布插件了,这里可以自己去查找相关文章,本文就不过多解释了,当插件发布成功后,我们下载完插件,添加了自己感兴趣的 RSS 博客,当关掉后再次打开查看,完蛋!之前已经订阅的 RSS 没有了!😭
问题不大,发现了问题就去解决它,我们查阅先前的代码可以发现之前的存在一个巨大的问题就是忘记了将已经订阅好的 RSS 进行一个数据持久化的处理,我们查阅 VScode 文档发现,它一共提供了四种数据存储的方案:

这里我们使用ExtensionContext.globalState进行全局的存储,将数据存储在存储在用户的本地配置文件中,这样即使关掉 VScode 再重新打开,也不会存在数据丢失的情况了,修改代码:

...
// 添加新的RSS链接和对应的自定义名字
      rssLinks[newLink] = customName;
      // 保存订阅信息到用户的本地
      context.globalState.update(RSS_LINKS_CONFIG_KEY, rssLinks);
      await getArticleContent(newLink, customName);
...

再重新发布插件,搞定!现在一个简陋的 RSS 阅读器插件便完成了!

image

🐱总结#

目前插件已经上线了 (虽然还不是正式版),可以在 VScode 上进行下载:下载链接,或者直接在 VScode 扩展里搜索RSS-searchBlog,目前功能还不够完善,比如没法提供订阅后有更新的情况会进行小弹窗提示之类的,后续会通过更新来完善这个插件,之前看过一个有意思的开源项目是RSSHub,可以给任何奇奇怪怪的内容生成 RSS 订阅源,打算之后将插件接入 RSSHub,并不断完善这个插件的功能,相信正式版插件会让大家满意的,感谢你的阅读,该项目源码:RSS-searchBlog

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