55 ViewColumn ,
66 Uri ,
77 WebviewPanelOptions ,
8- WebviewOptions
8+ WebviewOptions ,
99} from 'vscode' ;
1010import * as path from 'path' ;
1111
@@ -27,6 +27,8 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
2727 protected panelLoaded = false ;
2828 protected panelLoadedCallBacks : ( ( ) => void ) [ ] = [ ] ;
2929
30+ private panelResolves ?: Array < ( panel : WebviewPanel ) => void > ;
31+
3032 constructor (
3133 protected readonly ctx : ExtensionContext
3234 ) {
@@ -35,20 +37,36 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
3537
3638 public async restoreView ( panel : WebviewPanel ) : Promise < void > {
3739 this . panel = panel ;
38- this . setupPanel ( panel ) ;
40+ const config = await this . getPanelConfig ( ) ;
41+ this . setupPanel ( panel , config ) ;
3942 }
4043
4144 protected get isShowingPanel ( ) {
4245 return ! ! this . panel ;
4346 }
4447
45- protected getPanel ( ) : WebviewPanel {
48+ protected async getPanel ( ) : Promise < WebviewPanel > {
4649 if ( this . panel == undefined ) {
4750 const { ctx } = this ;
4851
49- const config = this . getPanelConfig ( ) ;
52+ // This is an async method, so in theory this method can be called concurrently. To ensure that we don't
53+ // create two panels, we use a promise that resolves when the panel is created. This way, if the panel is
54+ // being created, the promise will resolve when it is done.
55+ if ( this . panelResolves !== undefined ) {
56+ return new Promise ( ( resolve ) => {
57+ if ( this . panel !== undefined ) {
58+ resolve ( this . panel ) ;
59+ return ;
60+ }
61+
62+ this . panelResolves ?. push ( resolve ) ;
63+ } ) ;
64+ }
65+ this . panelResolves = [ ] ;
66+
67+ const config = await this . getPanelConfig ( ) ;
5068
51- this . panel = Window . createWebviewPanel (
69+ const panel = Window . createWebviewPanel (
5270 config . viewId ,
5371 config . title ,
5472 { viewColumn : config . viewColumn , preserveFocus : config . preserveFocus } ,
@@ -64,14 +82,17 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
6482 ] ,
6583 }
6684 ) ;
67- this . setupPanel ( this . panel ) ;
85+ this . panel = panel ;
86+
87+ this . setupPanel ( panel , config ) ;
88+
89+ this . panelResolves . forEach ( ( resolve ) => resolve ( panel ) ) ;
90+ this . panelResolves = undefined ;
6891 }
6992 return this . panel ;
7093 }
7194
72- protected setupPanel ( panel : WebviewPanel ) : void {
73- const config = this . getPanelConfig ( ) ;
74-
95+ protected setupPanel ( panel : WebviewPanel , config : WebviewPanelConfig ) : void {
7596 this . push (
7697 panel . onDidDispose (
7798 ( ) => {
@@ -101,7 +122,7 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
101122 ) ;
102123 }
103124
104- protected abstract getPanelConfig ( ) : WebviewPanelConfig ;
125+ protected abstract getPanelConfig ( ) : WebviewPanelConfig | Promise < WebviewPanelConfig > ;
105126
106127 protected abstract onPanelDispose ( ) : void ;
107128
@@ -123,8 +144,9 @@ export abstract class AbstractWebview<ToMessage extends WebviewMessage, FromMess
123144 this . panelLoadedCallBacks = [ ] ;
124145 }
125146
126- protected postMessage ( msg : ToMessage ) : Thenable < boolean > {
127- return this . getPanel ( ) . webview . postMessage ( msg ) ;
147+ protected async postMessage ( msg : ToMessage ) : Promise < boolean > {
148+ const panel = await this . getPanel ( ) ;
149+ return panel . webview . postMessage ( msg ) ;
128150 }
129151
130152 public dispose ( disposeHandler ?: DisposeHandler ) {
0 commit comments