-
Notifications
You must be signed in to change notification settings - Fork 29
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Provide a reusable Disposable API similar to what Theia/VS Code uses Fixes eclipse-glsp/glsp#867
- Loading branch information
Showing
6 changed files
with
199 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/******************************************************************************** | ||
* Copyright (c) 2023 EclipseSource and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
|
||
import { expect } from 'chai'; | ||
import * as sinon from 'sinon'; | ||
import { Disposable, DisposableCollection } from './disposable'; | ||
|
||
describe('Disposable', () => { | ||
describe('DisposableCollection', () => { | ||
let disposableCollection: DisposableCollection; | ||
beforeEach(() => (disposableCollection = new DisposableCollection())); | ||
describe('push', () => { | ||
it('should add one disposable to the collection and remove it again', () => { | ||
const disposable = Disposable.empty(); | ||
const toRemove = disposableCollection.push(disposable); | ||
expect(disposableCollection['disposables'].length).to.be.equal(1); | ||
expect(disposableCollection['disposables'][0]).to.equal(disposable); | ||
toRemove.dispose(); | ||
expect(disposableCollection['disposables'].length).to.be.equal(0); | ||
}); | ||
it('should add multiple disposable to the collection and remove them again', () => { | ||
const disposable1 = Disposable.empty(); | ||
const disposable2 = Disposable.empty(); | ||
const disposable3 = Disposable.empty(); | ||
|
||
const toRemove = disposableCollection.push(disposable1, disposable2, disposable3); | ||
expect(disposableCollection['disposables'].length).to.be.equal(3); | ||
expect(disposableCollection['disposables'][0]).to.equal(disposable1); | ||
expect(disposableCollection['disposables'][1]).to.equal(disposable2); | ||
expect(disposableCollection['disposables'][2]).to.equal(disposable3); | ||
toRemove.dispose(); | ||
expect(disposableCollection['disposables'].length).to.be.equal(0); | ||
}); | ||
}); | ||
describe('dispose', () => { | ||
describe('should invoke dispose on all elements of the collection exactly once', () => { | ||
const disposable1 = Disposable.empty(); | ||
const disposable2 = Disposable.empty(); | ||
const disposable1Spy = sinon.spy(disposable1); | ||
const disposable2Spy = sinon.spy(disposable2); | ||
|
||
disposableCollection = new DisposableCollection(disposable1, disposable2); | ||
|
||
disposableCollection.dispose(); | ||
disposableCollection.dispose(); | ||
disposableCollection.dispose(); | ||
|
||
expect(disposable1Spy.dispose.calledOnce).to.be.true; | ||
expect(disposable2Spy.dispose.calledOnce).to.be.true; | ||
expect(disposableCollection['disposables'].length).to.be.equal(0); | ||
}); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/******************************************************************************** | ||
* Copyright (c) 2023 EclipseSource and others. | ||
* | ||
* This program and the accompanying materials are made available under the | ||
* terms of the Eclipse Public License v. 2.0 which is available at | ||
* http://www.eclipse.org/legal/epl-2.0. | ||
* | ||
* This Source Code may also be made available under the following Secondary | ||
* Licenses when the conditions for such availability set forth in the Eclipse | ||
* Public License v. 2.0 are satisfied: GNU General Public License, version 2 | ||
* with the GNU Classpath Exception which is available at | ||
* https://www.gnu.org/software/classpath/license.html. | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 | ||
********************************************************************************/ | ||
import { remove } from './array-util'; | ||
import { AnyObject, hasFunctionProp } from './type-util'; | ||
|
||
/** | ||
* Interface for objects that can or need to be disposed properly. | ||
*/ | ||
export interface Disposable { | ||
/** | ||
* Dispose this object. | ||
*/ | ||
dispose(): void; | ||
} | ||
|
||
export namespace Disposable { | ||
export function is(value: unknown): value is Disposable { | ||
return AnyObject.is(value) && hasFunctionProp(value, ''); | ||
} | ||
|
||
/** | ||
* Creates a new empty i.e. no-op {@link Disposable}. | ||
* @returns the newly created disposable | ||
*/ | ||
export function empty(): Disposable { | ||
// eslint-disable-next-line @typescript-eslint/no-empty-function | ||
return { dispose: () => {} }; | ||
} | ||
|
||
/** | ||
* Creates a new {@link Disposable} that delegates to the given callback. | ||
* @param cb The callback that should be invoked on dispose | ||
* @returns the newly created disposable | ||
*/ | ||
export function create(cb: () => void): Disposable { | ||
return { dispose: cb }; | ||
} | ||
} | ||
|
||
/** | ||
* Reusable base class to manage a collection of {@link Disposable}s. | ||
*/ | ||
export class DisposableCollection implements Disposable { | ||
protected readonly disposables: Disposable[] = []; | ||
errorHandler?: (err: any) => void; | ||
|
||
constructor(...toDispose: Disposable[]) { | ||
toDispose.forEach(d => this.push(d)); | ||
this.errorHandler = err => console.error(err); | ||
} | ||
|
||
dispose(): void { | ||
if (this.disposed) { | ||
return; | ||
} | ||
try { | ||
while (!this.disposed) { | ||
this.disposables.pop()?.dispose(); | ||
} | ||
} catch (err) { | ||
this.errorHandler?.(err); | ||
} | ||
} | ||
|
||
get disposed(): boolean { | ||
return this.disposables.length === 0; | ||
} | ||
|
||
/** | ||
* Pushes the given disposables to the collection. | ||
* @param disposables The disposables that should be added | ||
* @returns A disposable that removes the previously pushed values from the collection when invoked | ||
*/ | ||
push(...disposables: Disposable[]): Disposable { | ||
this.disposables.push(...disposables); | ||
return Disposable.create(() => remove(this.disposables, ...disposables)); | ||
} | ||
|
||
get isDisposed(): boolean { | ||
return this.disposed; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters