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

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。