@@ -19,6 +19,8 @@ import { RenderNode, WebviewContent, WebviewMessage } from "./webview/WebviewMes
19
19
import { WorkspaceContext } from "../WorkspaceContext" ;
20
20
import { DocCDocumentationRequest , DocCDocumentationResponse } from "../sourcekit-lsp/extensions" ;
21
21
import { LSPErrorCodes , ResponseError } from "vscode-languageclient" ;
22
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
23
+ import throttle = require( "lodash.throttle" ) ;
22
24
23
25
export enum PreviewEditorConstant {
24
26
VIEW_TYPE = "swift.previewDocumentationEditor" ,
@@ -94,6 +96,7 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
94
96
}
95
97
96
98
private activeTextEditor ?: vscode . TextEditor ;
99
+ private activeTextEditorSelection ?: vscode . Selection ;
97
100
private subscriptions : vscode . Disposable [ ] = [ ] ;
98
101
99
102
private disposeEmitter = new vscode . EventEmitter < void > ( ) ;
@@ -108,11 +111,11 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
108
111
this . subscriptions . push (
109
112
this . webviewPanel . webview . onDidReceiveMessage ( this . receiveMessage , this ) ,
110
113
vscode . window . onDidChangeActiveTextEditor ( this . handleActiveTextEditorChange , this ) ,
114
+ vscode . window . onDidChangeTextEditorSelection ( this . handleSelectionChange , this ) ,
111
115
vscode . workspace . onDidChangeTextDocument ( this . handleDocumentChange , this ) ,
112
116
this . webviewPanel . onDidDispose ( this . dispose , this )
113
117
) ;
114
- // Reveal the editor, but don't change the focus of the active text editor
115
- webviewPanel . reveal ( undefined , true ) ;
118
+ this . reveal ( ) ;
116
119
}
117
120
118
121
/** An event that is fired when the Documentation Preview Editor is disposed */
@@ -125,7 +128,8 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
125
128
onDidRenderContent = this . renderEmitter . event ;
126
129
127
130
reveal ( ) {
128
- this . webviewPanel . reveal ( ) ;
131
+ // Reveal the editor, but don't change the focus of the active text editor
132
+ this . webviewPanel . reveal ( undefined , true ) ;
129
133
}
130
134
131
135
dispose ( ) {
@@ -161,82 +165,98 @@ export class DocumentationPreviewEditor implements vscode.Disposable {
161
165
return ;
162
166
}
163
167
this . activeTextEditor = activeTextEditor ;
168
+ this . activeTextEditorSelection = activeTextEditor . selection ;
164
169
this . convertDocumentation ( activeTextEditor ) ;
165
170
}
166
171
172
+ private handleSelectionChange ( event : vscode . TextEditorSelectionChangeEvent ) {
173
+ if (
174
+ this . activeTextEditor !== event . textEditor ||
175
+ this . activeTextEditorSelection === event . textEditor . selection
176
+ ) {
177
+ return ;
178
+ }
179
+ this . activeTextEditorSelection = event . textEditor . selection ;
180
+ this . convertDocumentation ( event . textEditor ) ;
181
+ }
182
+
167
183
private handleDocumentChange ( event : vscode . TextDocumentChangeEvent ) {
168
184
if ( this . activeTextEditor ?. document === event . document ) {
169
185
this . convertDocumentation ( this . activeTextEditor ) ;
170
186
}
171
187
}
172
188
173
- private async convertDocumentation ( textEditor : vscode . TextEditor ) : Promise < void > {
174
- const document = textEditor . document ;
175
- if (
176
- document . uri . scheme !== "file" ||
177
- ! [ "markdown" , "tutorial" , "swift" ] . includes ( document . languageId )
178
- ) {
179
- this . postMessage ( {
180
- type : "update-content" ,
181
- content : {
182
- type : "error" ,
183
- errorMessage : PreviewEditorConstant . UNSUPPORTED_EDITOR_ERROR_MESSAGE ,
184
- } ,
185
- } ) ;
186
- return ;
187
- }
189
+ private convertDocumentation = throttle (
190
+ async ( textEditor : vscode . TextEditor ) : Promise < void > => {
191
+ const document = textEditor . document ;
192
+ if (
193
+ document . uri . scheme !== "file" ||
194
+ ! [ "markdown" , "tutorial" , "swift" ] . includes ( document . languageId )
195
+ ) {
196
+ this . postMessage ( {
197
+ type : "update-content" ,
198
+ content : {
199
+ type : "error" ,
200
+ errorMessage : PreviewEditorConstant . UNSUPPORTED_EDITOR_ERROR_MESSAGE ,
201
+ } ,
202
+ } ) ;
203
+ return ;
204
+ }
188
205
189
- try {
190
- const response = await this . context . languageClientManager . useLanguageClient (
191
- async ( client ) : Promise < DocCDocumentationResponse > => {
192
- return await client . sendRequest ( DocCDocumentationRequest . type , {
193
- textDocument : {
194
- uri : document . uri . toString ( ) ,
195
- } ,
196
- position : textEditor . selection . start ,
197
- } ) ;
206
+ try {
207
+ const response = await this . context . languageClientManager . useLanguageClient (
208
+ async ( client ) : Promise < DocCDocumentationResponse > => {
209
+ return await client . sendRequest ( DocCDocumentationRequest . type , {
210
+ textDocument : {
211
+ uri : document . uri . toString ( ) ,
212
+ } ,
213
+ position : textEditor . selection . start ,
214
+ } ) ;
215
+ }
216
+ ) ;
217
+ this . postMessage ( {
218
+ type : "update-content" ,
219
+ content : {
220
+ type : "render-node" ,
221
+ renderNode : this . parseRenderNode ( response . renderNode ) ,
222
+ } ,
223
+ } ) ;
224
+ } catch ( error ) {
225
+ // Update the preview editor to reflect what error occurred
226
+ let livePreviewErrorMessage = "An internal error occurred" ;
227
+ const baseLogErrorMessage = `SourceKit-LSP request "${ DocCDocumentationRequest . method } " failed: ` ;
228
+ if ( error instanceof ResponseError ) {
229
+ if ( error . code === LSPErrorCodes . RequestCancelled ) {
230
+ // We can safely ignore cancellations
231
+ return undefined ;
232
+ }
233
+ switch ( error . code ) {
234
+ case LSPErrorCodes . RequestFailed :
235
+ // RequestFailed response errors can be shown to the user
236
+ livePreviewErrorMessage = error . message ;
237
+ break ;
238
+ default :
239
+ // We should log additional info for other response errors
240
+ this . context . outputChannel . log (
241
+ baseLogErrorMessage + JSON . stringify ( error . toJson ( ) , undefined , 2 )
242
+ ) ;
243
+ break ;
244
+ }
245
+ } else {
246
+ this . context . outputChannel . log ( baseLogErrorMessage + `${ error } ` ) ;
198
247
}
199
- ) ;
200
- this . postMessage ( {
201
- type : "update-content" ,
202
- content : {
203
- type : "render-node" ,
204
- renderNode : this . parseRenderNode ( response . renderNode ) ,
205
- } ,
206
- } ) ;
207
- } catch ( error ) {
208
- // Update the preview editor to reflect what error occurred
209
- let livePreviewErrorMessage = "An internal error occurred" ;
210
- const baseLogErrorMessage = `SourceKit-LSP request "${ DocCDocumentationRequest . method } " failed: ` ;
211
- if ( error instanceof ResponseError ) {
212
- if ( error . code === LSPErrorCodes . RequestCancelled ) {
213
- // We can safely ignore cancellations
214
- return undefined ;
215
- }
216
- switch ( error . code ) {
217
- case LSPErrorCodes . RequestFailed :
218
- // RequestFailed response errors can be shown to the user
219
- livePreviewErrorMessage = error . message ;
220
- break ;
221
- default :
222
- // We should log additional info for other response errors
223
- this . context . outputChannel . log (
224
- baseLogErrorMessage + JSON . stringify ( error . toJson ( ) , undefined , 2 )
225
- ) ;
226
- break ;
227
- }
228
- } else {
229
- this . context . outputChannel . log ( baseLogErrorMessage + `${ error } ` ) ;
248
+ this . postMessage ( {
249
+ type : "update-content" ,
250
+ content : {
251
+ type : "error" ,
252
+ errorMessage : livePreviewErrorMessage ,
253
+ } ,
254
+ } ) ;
230
255
}
231
- this . postMessage ( {
232
- type : "update-content" ,
233
- content : {
234
- type : "error" ,
235
- errorMessage : livePreviewErrorMessage ,
236
- } ,
237
- } ) ;
238
- }
239
- }
256
+ } ,
257
+ 100 /* 10 times per second */ ,
258
+ { trailing : true }
259
+ ) ;
240
260
241
261
private parseRenderNode ( content : string ) : RenderNode {
242
262
const renderNode : RenderNode = JSON . parse ( content ) ;
0 commit comments