当前位置: 首页 > 科技观察

Vscode的Markdown预览是如何实现的

时间:2023-03-16 19:09:43 科技观察

vscode的markdown预览是我们整天使用的功能。你有没有想过它是如何实现的?也许有一天你会收到自定义markdown预览的请求,你该怎么办?你有什么想法?思考五秒钟。54321其实整体思路还是比较简单的。就是创建一个webview面板,设置内容为markdown生成的html,然后在markdown更新时同步修改webview的html。思路分析通过vscode.window.createWebviewPanel创建一个webview,指定在侧面打开,然后通过panel对象的webview.html属性设置html。html是通过编辑器的markdown内容生成的。通过editor.document.getText()获取编辑器内容,然后调用第三方markdown-to-html库生成。这样就完成了降价预览。预览后需要更新,监听vscode.workspace.onDidSaveTextDocument和vscode.workspace.onDidChangeTextDocument的事件,当文档更新保存时,获取编辑器的内容,重新生成html,然后设置为网络视图。webviewPanel支持webview.postMessage(message);传递消息,支持updateHTML等一系列命令,可以通过传递消息触发。但是你怎么知道哪个文档更新了哪个webview呢?可以维护一张图,在创建webviewPanel的时候记录在图中,key是文件路径,这样更新的时候就可以找到对应的webview进行更新。这样就完成了markdown内容的更新。其实整体思路比较简单,先把代码写下来:代码实现先看vscode-markdown-preview-enhanced插件的代码,也是一个预览markdown的插件。代码比较简单,可以用来学习。(以下代码为简化代码)首先,插件需要指定触发条件,即在package.json中指定activationEvents:"activationEvents":["onLanguage:markdown","onCommand:markdown-preview-enhanced.openPreviewToTheSide"],这里一个在编辑markdown内容时激活,另一个在执行命令时激活。具体的激活逻辑在active方法中:),);functionopenPreviewToTheSide(uri?:vscode.Uri){letresource=uri;if(!(resourceinstanceofvscode.Uri)){if(vscode.window.activeTextEditor){resource=vscode.window.activeTextEditor.document.uri;}}contentProvider.initPreview(resource,vscode.window.activeTextEditor,{viewColumn:vscode.ViewColumn.Two,preserveFocus:true,});}}我们注册了命令,执行命令会得到当前编辑器的url,以及然后执行降价预览。预览的所有逻辑都集中定义在MarkdownPreviewEnhancedView的实例对象中,触发命令时执行initPreivew。publicasyncinitPreview(sourceUri:vscode.Uri,editor:vscode.TextEditor,viewOptions:{viewColumn:vscode.ViewColumn;preserveFocus?:boolean},){//创建webviewletpreviewPanel:vscode.WebviewPanel=vscode.window.createWebviewPanel("markdown-preview-enhanced",`Preview${path.basename(sourceUri.fsPath)}`,viewOptions);//监听webview消息previewPanel.webview.onDidReceiveMessage((message)=>{});//记录webview映射这个.previewMaps[sourceUri.fsPath]=previewPanel;//获取编辑器的文本并生成htmlconsttext=editor.document.getText();engine.generateHTMLTemplateForPreview({inputString:text}).then((html)=>{//SethtmltopreviewPanelpreviewPanel.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方法进行更新。publicupdateMarkdown(sourceUri:Uri){//根据文件路径从地图中获取对应的webviewPanelconstpreviewPanel=this.previewMaps[sourceUri.fsPath];//生成最新的html传给webviewconsttext=document.getText();engine.parseMD(text).then(({markdown,html})=>{previewPanel.webview.postMessage({command:"updateHTML",html});}}这里是updateHTML的命令消息传递给html内容通过webview.postMessage,触发html更新内容,这样我们就实现了markdown的同步刷新。综上所述,vscode中的markdown预览是一个常用的功能,但实现起来并不难。我们查看了vscode-markdown-preview-enhanced插件的源码,理清了整体流程:通过vscode.window.createWebviewPanel创建一个webviewPanel来显示htmlhtml通过editor.document.getText()获取文本内容和通过第三方包生成。设置为webviewPanel监听workspace.onDidSaveTextDocument和workspace.onDidChangeTextDocument获取最新内容,然后生成html并通过webview.postMessage传递udpateHTML消息更新到webview。需要注意的是需要记录一个map,保存uri.fsPath和webviewPanel的对应关系。实现webviewmarkdown对应文本内容变化更新的预览,是一个常见但不难的需求。也比较适合入门vscode插件的开发。希望这篇文章可以帮助你理清思路。