@@ -22,69 +22,71 @@ import { ConvertDocumentationRequest } from "../sourcekit-lsp/extensions/Convert
2222export enum PreviewEditorConstant {
2323 VIEW_TYPE = "swift.previewDocumentationEditor" ,
2424 TITLE = "Preview Swift Documentation" ,
25+ UNSUPPORTED_EDITOR_ERROR_MESSAGE = "The active text editor does not support Swift Documentation Live Preview" ,
2526}
2627
2728export class DocumentationPreviewEditor implements vscode . Disposable {
28- private readonly webviewPanel : vscode . WebviewPanel ;
29- private subscriptions : vscode . Disposable [ ] = [ ] ;
30-
31- private disposeEmitter = new vscode . EventEmitter < void > ( ) ;
32- private renderEmitter = new vscode . EventEmitter < void > ( ) ;
33- private updateContentEmitter = new vscode . EventEmitter < WebviewContent > ( ) ;
34-
35- constructor (
36- private readonly extension : vscode . ExtensionContext ,
37- private readonly context : WorkspaceContext
38- ) {
39- const swiftDoccRenderPath = this . extension . asAbsolutePath (
29+ static async create (
30+ extension : vscode . ExtensionContext ,
31+ context : WorkspaceContext
32+ ) : Promise < DocumentationPreviewEditor > {
33+ const swiftDoccRenderPath = extension . asAbsolutePath (
4034 path . join ( "assets" , "swift-docc-render" )
4135 ) ;
42- // Create and hook up events for the WebviewPanel
43- this . webviewPanel = vscode . window . createWebviewPanel (
36+ const webviewPanel = vscode . window . createWebviewPanel (
4437 PreviewEditorConstant . VIEW_TYPE ,
4538 PreviewEditorConstant . TITLE ,
4639 { viewColumn : vscode . ViewColumn . Beside , preserveFocus : true } ,
4740 {
4841 enableScripts : true ,
4942 localResourceRoots : [
5043 vscode . Uri . file (
51- this . extension . asAbsolutePath ( path . join ( "assets" , "documentation-webview" ) )
44+ extension . asAbsolutePath ( path . join ( "assets" , "documentation-webview" ) )
5245 ) ,
5346 vscode . Uri . file ( swiftDoccRenderPath ) ,
5447 ...context . folders . map ( f => f . folder ) ,
5548 ] ,
5649 }
5750 ) ;
58- const webviewBaseURI = this . webviewPanel . webview . asWebviewUri (
51+ const webviewBaseURI = webviewPanel . webview . asWebviewUri (
5952 vscode . Uri . file ( swiftDoccRenderPath )
6053 ) ;
61- const scriptURI = this . webviewPanel . webview . asWebviewUri (
54+ const scriptURI = webviewPanel . webview . asWebviewUri (
6255 vscode . Uri . file (
63- this . extension . asAbsolutePath (
64- path . join ( "assets" , "documentation-webview" , "index.js" )
65- )
56+ extension . asAbsolutePath ( path . join ( "assets" , "documentation-webview" , "index.js" ) )
6657 )
6758 ) ;
68- fs . readFile ( path . join ( swiftDoccRenderPath , "index.html" ) , "utf-8" ) . then (
69- documentationHTML => {
70- documentationHTML = documentationHTML
71- . replaceAll ( "{{BASE_PATH}}" , webviewBaseURI . toString ( ) )
72- . replace ( "</body>" , `<script src="${ scriptURI . toString ( ) } "></script></body>` ) ;
73- this . webviewPanel . webview . html = documentationHTML ;
74- this . subscriptions . push (
75- this . webviewPanel . webview . onDidReceiveMessage ( this . receiveMessage . bind ( this ) ) ,
76- vscode . window . onDidChangeActiveTextEditor ( editor => {
77- this . convertDocumentation ( editor ) ;
78- } ) ,
79- vscode . window . onDidChangeTextEditorSelection ( event => {
80- this . convertDocumentation ( event . textEditor ) ;
81- } ) ,
82- this . webviewPanel . onDidDispose ( this . dispose . bind ( this ) )
83- ) ;
84- // Reveal the editor, but don't change the focus of the active text editor
85- this . webviewPanel . reveal ( undefined , true ) ;
86- }
59+ let doccRenderHTML = await fs . readFile (
60+ path . join ( swiftDoccRenderPath , "index.html" ) ,
61+ "utf-8"
62+ ) ;
63+ doccRenderHTML = doccRenderHTML
64+ . replaceAll ( "{{BASE_PATH}}" , webviewBaseURI . toString ( ) )
65+ . replace ( "</body>" , `<script src="${ scriptURI . toString ( ) } "></script></body>` ) ;
66+ webviewPanel . webview . html = doccRenderHTML ;
67+ return new DocumentationPreviewEditor ( context , webviewPanel ) ;
68+ }
69+
70+ private activeTextEditor ?: vscode . TextEditor ;
71+ private subscriptions : vscode . Disposable [ ] = [ ] ;
72+
73+ private disposeEmitter = new vscode . EventEmitter < void > ( ) ;
74+ private renderEmitter = new vscode . EventEmitter < void > ( ) ;
75+ private updateContentEmitter = new vscode . EventEmitter < WebviewContent > ( ) ;
76+
77+ private constructor (
78+ private readonly context : WorkspaceContext ,
79+ private readonly webviewPanel : vscode . WebviewPanel
80+ ) {
81+ this . activeTextEditor = vscode . window . activeTextEditor ;
82+ this . subscriptions . push (
83+ this . webviewPanel . webview . onDidReceiveMessage ( this . receiveMessage , this ) ,
84+ vscode . window . onDidChangeActiveTextEditor ( this . handleActiveTextEditorChange , this ) ,
85+ vscode . workspace . onDidChangeTextDocument ( this . handleDocumentChange , this ) ,
86+ this . webviewPanel . onDidDispose ( this . dispose , this )
8787 ) ;
88+ // Reveal the editor, but don't change the focus of the active text editor
89+ webviewPanel . reveal ( undefined , true ) ;
8890 }
8991
9092 /** An event that is fired when the Documentation Preview Editor is disposed */
@@ -117,18 +119,45 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
117119 private receiveMessage ( message : WebviewMessage ) {
118120 switch ( message . type ) {
119121 case "loaded" :
120- this . convertDocumentation ( vscode . window . activeTextEditor ) ;
122+ if ( ! this . activeTextEditor ) {
123+ break ;
124+ }
125+ this . convertDocumentation ( this . activeTextEditor ) ;
121126 break ;
122127 case "rendered" :
123128 this . renderEmitter . fire ( ) ;
124129 break ;
125130 }
126131 }
127132
128- private async convertDocumentation ( editor : vscode . TextEditor | undefined ) : Promise < void > {
129- const document = editor ?. document ;
130- if ( ! document || document . uri . scheme !== "file" ) {
131- return undefined ;
133+ private handleActiveTextEditorChange ( activeTextEditor : vscode . TextEditor | undefined ) {
134+ if ( this . activeTextEditor === activeTextEditor || activeTextEditor === undefined ) {
135+ return ;
136+ }
137+ this . activeTextEditor = activeTextEditor ;
138+ this . convertDocumentation ( activeTextEditor ) ;
139+ }
140+
141+ private handleDocumentChange ( event : vscode . TextDocumentChangeEvent ) {
142+ if ( this . activeTextEditor ?. document === event . document ) {
143+ this . convertDocumentation ( this . activeTextEditor ) ;
144+ }
145+ }
146+
147+ private async convertDocumentation ( textEditor : vscode . TextEditor ) : Promise < void > {
148+ const document = textEditor . document ;
149+ if (
150+ document . uri . scheme !== "file" ||
151+ ! [ "markdown" , "tutorial" , "swift" ] . includes ( document . languageId )
152+ ) {
153+ this . postMessage ( {
154+ type : "update-content" ,
155+ content : {
156+ type : "error" ,
157+ errorMessage : PreviewEditorConstant . UNSUPPORTED_EDITOR_ERROR_MESSAGE ,
158+ } ,
159+ } ) ;
160+ return ;
132161 }
133162
134163 const response = await this . context . languageClientManager . useLanguageClient (
@@ -137,7 +166,7 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
137166 textDocument : {
138167 uri : document . uri . toString ( ) ,
139168 } ,
140- position : editor . selection . start ,
169+ position : textEditor . selection . start ,
141170 } ) ;
142171 }
143172 ) ;
0 commit comments