@@ -22,69 +22,71 @@ import { ConvertDocumentationRequest } from "../sourcekit-lsp/extensions/Convert
22
22
export enum PreviewEditorConstant {
23
23
VIEW_TYPE = "swift.previewDocumentationEditor" ,
24
24
TITLE = "Preview Swift Documentation" ,
25
+ UNSUPPORTED_EDITOR_ERROR_MESSAGE = "The active text editor does not support Swift Documentation Live Preview" ,
25
26
}
26
27
27
28
export 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 (
40
34
path . join ( "assets" , "swift-docc-render" )
41
35
) ;
42
- // Create and hook up events for the WebviewPanel
43
- this . webviewPanel = vscode . window . createWebviewPanel (
36
+ const webviewPanel = vscode . window . createWebviewPanel (
44
37
PreviewEditorConstant . VIEW_TYPE ,
45
38
PreviewEditorConstant . TITLE ,
46
39
{ viewColumn : vscode . ViewColumn . Beside , preserveFocus : true } ,
47
40
{
48
41
enableScripts : true ,
49
42
localResourceRoots : [
50
43
vscode . Uri . file (
51
- this . extension . asAbsolutePath ( path . join ( "assets" , "documentation-webview" ) )
44
+ extension . asAbsolutePath ( path . join ( "assets" , "documentation-webview" ) )
52
45
) ,
53
46
vscode . Uri . file ( swiftDoccRenderPath ) ,
54
47
...context . folders . map ( f => f . folder ) ,
55
48
] ,
56
49
}
57
50
) ;
58
- const webviewBaseURI = this . webviewPanel . webview . asWebviewUri (
51
+ const webviewBaseURI = webviewPanel . webview . asWebviewUri (
59
52
vscode . Uri . file ( swiftDoccRenderPath )
60
53
) ;
61
- const scriptURI = this . webviewPanel . webview . asWebviewUri (
54
+ const scriptURI = webviewPanel . webview . asWebviewUri (
62
55
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" ) )
66
57
)
67
58
) ;
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 )
87
87
) ;
88
+ // Reveal the editor, but don't change the focus of the active text editor
89
+ webviewPanel . reveal ( undefined , true ) ;
88
90
}
89
91
90
92
/** An event that is fired when the Documentation Preview Editor is disposed */
@@ -117,18 +119,45 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
117
119
private receiveMessage ( message : WebviewMessage ) {
118
120
switch ( message . type ) {
119
121
case "loaded" :
120
- this . convertDocumentation ( vscode . window . activeTextEditor ) ;
122
+ if ( ! this . activeTextEditor ) {
123
+ break ;
124
+ }
125
+ this . convertDocumentation ( this . activeTextEditor ) ;
121
126
break ;
122
127
case "rendered" :
123
128
this . renderEmitter . fire ( ) ;
124
129
break ;
125
130
}
126
131
}
127
132
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 ;
132
161
}
133
162
134
163
const response = await this . context . languageClientManager . useLanguageClient (
@@ -137,7 +166,7 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
137
166
textDocument : {
138
167
uri : document . uri . toString ( ) ,
139
168
} ,
140
- position : editor . selection . start ,
169
+ position : textEditor . selection . start ,
141
170
} ) ;
142
171
}
143
172
) ;
0 commit comments