@@ -103,4 +103,143 @@ suite('vscode API - tree', () => {
103103 assert . fail ( error . message ) ;
104104 }
105105 } ) ;
106+
107+ test ( 'TreeView - element already registered after refresh' , async function ( ) {
108+ this . timeout ( 60_000 ) ;
109+
110+ type ParentElement = { readonly kind : 'parent' } ;
111+ type ChildElement = { readonly kind : 'leaf' ; readonly version : number } ;
112+ type TreeElement = ParentElement | ChildElement ;
113+
114+ class ParentRefreshTreeDataProvider implements vscode . TreeDataProvider < TreeElement > {
115+ private readonly changeEmitter = new vscode . EventEmitter < TreeElement | undefined > ( ) ;
116+ private readonly rootRequestEmitter = new vscode . EventEmitter < number > ( ) ;
117+ private readonly childRequestEmitter = new vscode . EventEmitter < number > ( ) ;
118+ private readonly rootRequests : DeferredPromise < TreeElement [ ] > [ ] = [ ] ;
119+ private readonly childRequests : DeferredPromise < TreeElement [ ] > [ ] = [ ] ;
120+ private readonly parentElement : ParentElement = { kind : 'parent' } ;
121+ private childVersion = 0 ;
122+ private currentChild : ChildElement = { kind : 'leaf' , version : 0 } ;
123+
124+ readonly onDidChangeTreeData = this . changeEmitter . event ;
125+
126+ getChildren ( element ?: TreeElement ) : Thenable < TreeElement [ ] > {
127+ if ( ! element ) {
128+ const deferred = new DeferredPromise < TreeElement [ ] > ( ) ;
129+ this . rootRequests . push ( deferred ) ;
130+ this . rootRequestEmitter . fire ( this . rootRequests . length ) ;
131+ return deferred . p ;
132+ }
133+ if ( element . kind === 'parent' ) {
134+ const deferred = new DeferredPromise < TreeElement [ ] > ( ) ;
135+ this . childRequests . push ( deferred ) ;
136+ this . childRequestEmitter . fire ( this . childRequests . length ) ;
137+ return deferred . p ;
138+ }
139+ return Promise . resolve ( [ ] ) ;
140+ }
141+
142+ getTreeItem ( element : TreeElement ) : vscode . TreeItem {
143+ if ( element . kind === 'parent' ) {
144+ const item = new vscode . TreeItem ( 'parent' , vscode . TreeItemCollapsibleState . Collapsed ) ;
145+ item . id = 'parent' ;
146+ return item ;
147+ }
148+ const item = new vscode . TreeItem ( 'duplicate' , vscode . TreeItemCollapsibleState . None ) ;
149+ item . id = 'dup' ;
150+ return item ;
151+ }
152+
153+ getParent ( element : TreeElement ) : TreeElement | undefined {
154+ if ( element . kind === 'leaf' ) {
155+ return this . parentElement ;
156+ }
157+ return undefined ;
158+ }
159+
160+ getCurrentChild ( ) : ChildElement {
161+ return this . currentChild ;
162+ }
163+
164+ replaceChild ( ) : ChildElement {
165+ this . childVersion ++ ;
166+ this . currentChild = { kind : 'leaf' , version : this . childVersion } ;
167+ return this . currentChild ;
168+ }
169+
170+ async waitForRootRequestCount ( count : number ) : Promise < void > {
171+ while ( this . rootRequests . length < count ) {
172+ await asPromise ( this . rootRequestEmitter . event ) ;
173+ }
174+ }
175+
176+ async waitForChildRequestCount ( count : number ) : Promise < void > {
177+ while ( this . childRequests . length < count ) {
178+ await asPromise ( this . childRequestEmitter . event ) ;
179+ }
180+ }
181+
182+ async resolveNextRootRequest ( elements ?: TreeElement [ ] ) : Promise < void > {
183+ const next = this . rootRequests . shift ( ) ;
184+ if ( ! next ) {
185+ return ;
186+ }
187+ await next . complete ( elements ?? [ this . parentElement ] ) ;
188+ }
189+
190+ async resolveChildRequestAt ( index : number , elements ?: TreeElement [ ] ) : Promise < void > {
191+ const request = this . childRequests [ index ] ;
192+ if ( ! request ) {
193+ return ;
194+ }
195+ this . childRequests . splice ( index , 1 ) ;
196+ await request . complete ( elements ?? [ this . currentChild ] ) ;
197+ }
198+
199+ dispose ( ) : void {
200+ this . changeEmitter . dispose ( ) ;
201+ this . rootRequestEmitter . dispose ( ) ;
202+ this . childRequestEmitter . dispose ( ) ;
203+ while ( this . rootRequests . length ) {
204+ this . rootRequests . shift ( ) ! . complete ( [ ] ) ;
205+ }
206+ while ( this . childRequests . length ) {
207+ this . childRequests . shift ( ) ! . complete ( [ ] ) ;
208+ }
209+ }
210+ }
211+
212+ const provider = new ParentRefreshTreeDataProvider ( ) ;
213+ disposables . push ( provider ) ;
214+
215+ const treeView = vscode . window . createTreeView ( 'test.treeRefresh' , { treeDataProvider : provider } ) ;
216+ disposables . push ( treeView ) ;
217+
218+ const initialChild = provider . getCurrentChild ( ) ;
219+ const firstReveal = ( treeView . reveal ( initialChild , { expand : true } )
220+ . then ( ( ) => ( { error : undefined as Error | undefined } ) ) as Promise < { error : Error | undefined } > )
221+ . catch ( error => ( { error } ) ) ;
222+
223+ await provider . waitForRootRequestCount ( 1 ) ;
224+ await provider . resolveNextRootRequest ( ) ;
225+
226+ await provider . waitForChildRequestCount ( 1 ) ;
227+ const staleChild = provider . getCurrentChild ( ) ;
228+ const refreshedChild = provider . replaceChild ( ) ;
229+ const secondReveal = ( treeView . reveal ( refreshedChild , { expand : true } )
230+ . then ( ( ) => ( { error : undefined as Error | undefined } ) ) as Promise < { error : Error | undefined } > )
231+ . catch ( error => ( { error } ) ) ;
232+
233+ await provider . waitForChildRequestCount ( 2 ) ;
234+
235+ await provider . resolveChildRequestAt ( 1 , [ refreshedChild ] ) ;
236+ await delay ( 0 ) ;
237+ await provider . resolveChildRequestAt ( 0 , [ staleChild ] ) ;
238+
239+ const [ firstResult , secondResult ] = await Promise . all ( [ firstReveal , secondReveal ] ) ;
240+ const error = firstResult . error ?? secondResult . error ;
241+ if ( error && / E l e m e n t w i t h i d .+ i s a l r e a d y r e g i s t e r e d / . test ( error . message ) ) {
242+ assert . fail ( error . message ) ;
243+ }
244+ } ) ;
106245} ) ;
0 commit comments