深入了解vscode中markdown預覽的實現原理

深入了解vscode中markdown預覽的實現原理

vscode 的 markdown 預覽是我們整天都在用的功能,有沒有想過它是怎么實現的。或許有一天你會接到個定制 markdown 預覽的需求,應該怎么做呢?【推薦學習:《vscode》】

其實整體思路比較簡單,就是創建一個 webview panel,設置內容為 markdown 生成的 html,之后在 markdown 更新的時候同步修改 webview 的 html 就可以了。

思路分析

通過 vscode.window.createWebviewPanel 創建一個 webview,指定在側邊打開,之后通過該 ?panel 對象的 webview.html 屬性來設置 html。

html 是通過編輯器的 markdown 內容生成的, 編輯器內容通過 editor.document.getText() 拿到,然后調用第三方的 markdown 轉 html 的庫來生成。

這樣就完成了 markdown 的預覽。

預覽之后需要更新,監聽 vscode.workspace.onDidSaveTextDocument 和 vscode.workspace.onDidChangeTextDocument 的事件,在文檔更新和保存的時候,拿到編輯器的內容,重新生成 html,然后設置到 webview。

webviewPanel 支持 webview.postMessage(message); 的方式傳遞消息,支持 updateHTML 等一系列 command,可以通過傳遞消息來觸發。

但是怎么知道哪個文檔更新哪個 webview 呢?

可以維護一個 map,在創建 webviewPanel 的時候記錄到 map 中,key 為文件路徑,這樣更新的時候就能查找到對應的 webview 進行更新。

這樣,就完成了 markdown 內容的更新。

其實整體思路還是比較簡單的,下面我們來寫下代碼

代碼實現

我們看下 vscode-markdown-preview-enhanced 的插件的代碼,這也是一個預覽 markdown 的插件,代碼還算簡潔,可以用來學習。

(以下代碼是簡化后的代碼)

首先,插件要指定觸發的條件,也就是在 package.json 里面指定 activationEvents:

"activationEvents":?[ ????"onLanguage:markdown", ????"onCommand:markdown-preview-enhanced.openPreviewToTheSide" ],

這里一個是編輯 markdown 內容的時候激活,一個是執行 command 的時候激活。

具體激活的邏輯在 active 方法里:

export?function?activate(context:?vscode.ExtensionContext)?{  ??const?contentProvider?=?new?MarkdownPreviewEnhancedView(context);  ??context.subscriptions.push( ????vscode.commands.registerCommand( ??????"markdown-preview-enhanced.openPreviewToTheSide", ??????openPreviewToTheSide, ????), ??); ?? ??function?openPreviewToTheSide(uri?:?vscode.Uri)?{ ????let?resource?=?uri; ????if?(!(resource?instanceof?vscode.Uri))?{ ??????if?(vscode.window.activeTextEditor)?{ ????????resource?=?vscode.window.activeTextEditor.document.uri; ??????} ????} ????contentProvider.initPreview(resource,?vscode.window.activeTextEditor,?{ ??????viewColumn:?vscode.ViewColumn.Two, ??????preserveFocus:?true, ????}); ??} }

我們注冊了那個 command,執行 command 會拿到當前 editor 的 url,然后進行 markdown 的 preview。

preview 的所有邏輯都集中定義在了 MarkdownPreviewEnhancedView 的實例對象中,在 command 觸發時執行 initPreivew。

public?async?initPreview( ????sourceUri:?vscode.Uri, ????editor:?vscode.TextEditor, ????viewOptions:?{?viewColumn:?vscode.ViewColumn;?preserveFocus?:?boolean?}, )?{ ????//?創建?webview ????let?previewPanel:?vscode.WebviewPanel?=?vscode.window.createWebviewPanel( ????????"markdown-preview-enhanced", ????????`Preview?${path.basename(sourceUri.fsPath)}`, ????????viewOptions ????);  ????//?監聽?webview?的消息 ????previewPanel.webview.onDidReceiveMessage((message)?=>?{});  ????//?記錄?webview?到?map?中 ????this.previewMaps[sourceUri.fsPath]?=?previewPanel; ???? ????//?拿到編輯器的文本,生成?html ????const?text?=?editor.document.getText(); ????engine ??????.generateHTMLTemplateForPreview({inputString:?text}) ??????.then((html)?=>?{ ????????//?設置?html?到?previewPanel ????????previewPanel.webview.html?=?html; ??????}); }

在 initWebivew 里面創建 webviewPanel,同時把 webviewPanel 保存到 map 中,key 為文檔的文件路徑。拿到編輯器文本來生成 html,設置到 webview.html,這樣就完成了 markdown 的預覽。

這條路徑走通之后,我們就實現了 markdown 的預覽。

但是只預覽一次不行,更新文檔之后需要自動更新,我們繼續在 active 方法里添加事件監聽:

??context.subscriptions.push( ????vscode.workspace.onDidSaveTextDocument((document)?=>?{ ??????if?(isMarkdownFile(document))?{ ????????contentProvider.updateMarkdown(document.uri,?true); ??????} ????}), ??);  ??context.subscriptions.push( ????vscode.workspace.onDidChangeTextDocument((event)?=>?{ ??????if?(isMarkdownFile(event.document))?{ ????????contentProvider.update(event.document.uri); ??????} ????}), ??);

監聽文本修改和保存的時候,調用 update 方法來更新。

public?updateMarkdown(sourceUri:?Uri)?{  ????//?從?map?中根據文件路徑取出對應的?webviewPanel ????const?previewPanel?=?this.previewMaps[sourceUri.fsPath]; ???? ????//?生成最新的?html?傳遞給?webview ????const?text?=?document.getText(); ????engine ????????.parseMD(text) ????????.then(({?markdown,?html?})?=>?{ ????????????previewPanel.webview.postMessage({ ??????????????command:?"updateHTML", ??????????????html ????????????}); ????????}  }

這里是通過 webview.postMessage 給 html 內容傳遞 updateHTML 的 command 消息,觸發 html 內容的更新。

這樣,我們就實現了 markdown 的同步刷新。

總結

vscode 里面 markdown 的預覽是一個常用但實現起來并不難的功能,我們看了下 vscode-markdown-preview-enhanced 插件的源碼,理清了整體的流程:

  • 通過 vscode.window.createWebviewPanel 創建 webviewPanel 來顯示 html
  • html 通過 editor.document.getText() 拿到文本內容之后通過第三方包生成,設置到 webviewPanel
  • 監聽 workspace.onDidSaveTextDocument 和 workspace.onDidChangeTextDocument,來拿到最新內容,之后生成 html 通過 webview.postMessage 傳遞 udpateHTML 的消息來更新到 webview。
  • 要注意的是,需要記錄一個 map 來保存 uri.fsPath 和 webviewPanel 的對應關系,實現文本內容改變更新對應的 webview

markdown 的預覽是一個常見但是并不難的需求,也比較適合入門 vscode 插件的開發,希望這篇文章能夠幫大家理清思路。

更多編程相關知識,請訪問:vscode!!

以上就是深入了解

? 版權聲明
THE END
喜歡就支持一下吧
點贊10 分享