@pkerschbaum/code-oss-file-service
Version:
VS Code ([microsoft/vscode](https://github.com/microsoft/vscode)) includes a rich "`FileService`" and "`DiskFileSystemProvider`" abstraction built on top of Node.js core modules (`fs`, `path`) and Electron's `shell` module. This package allows to use that
353 lines • 11.9 kB
JavaScript
"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.disposeOnReturn = exports.ImmortalReference = exports.AsyncReferenceCollection = exports.ReferenceCollection = exports.RefCountedDisposable = exports.MutableDisposable = exports.Disposable = exports.DisposableStore = exports.toDisposable = exports.combinedDisposable = exports.dispose = exports.isDisposable = exports.MultiDisposeError = exports.markAsSingleton = exports.setDisposableTracker = void 0;
const functional_1 = require("../../base/common/functional");
const iterator_1 = require("../../base/common/iterator");
/**
* Enables logging of potentially leaked disposables.
*
* A disposable is considered leaked if it is not disposed or not registered as the child of
* another disposable. This tracking is very simple an only works for classes that either
* extend Disposable or use a DisposableStore. This means there are a lot of false positives.
*/
const TRACK_DISPOSABLES = false;
let disposableTracker = null;
function setDisposableTracker(tracker) {
disposableTracker = tracker;
}
exports.setDisposableTracker = setDisposableTracker;
if (TRACK_DISPOSABLES) {
const __is_disposable_tracked__ = '__is_disposable_tracked__';
setDisposableTracker(new class {
trackDisposable(x) {
const stack = new Error('Potentially leaked disposable').stack;
setTimeout(() => {
if (!x[__is_disposable_tracked__]) {
console.log(stack);
}
}, 3000);
}
setParent(child, parent) {
if (child && child !== Disposable.None) {
try {
child[__is_disposable_tracked__] = true;
}
catch (_a) {
// noop
}
}
}
markAsDisposed(disposable) {
if (disposable && disposable !== Disposable.None) {
try {
disposable[__is_disposable_tracked__] = true;
}
catch (_a) {
// noop
}
}
}
markAsSingleton(disposable) { }
});
}
function trackDisposable(x) {
disposableTracker === null || disposableTracker === void 0 ? void 0 : disposableTracker.trackDisposable(x);
return x;
}
function markAsDisposed(disposable) {
disposableTracker === null || disposableTracker === void 0 ? void 0 : disposableTracker.markAsDisposed(disposable);
}
function setParentOfDisposable(child, parent) {
disposableTracker === null || disposableTracker === void 0 ? void 0 : disposableTracker.setParent(child, parent);
}
function setParentOfDisposables(children, parent) {
if (!disposableTracker) {
return;
}
for (const child of children) {
disposableTracker.setParent(child, parent);
}
}
/**
* Indicates that the given object is a singleton which does not need to be disposed.
*/
function markAsSingleton(singleton) {
disposableTracker === null || disposableTracker === void 0 ? void 0 : disposableTracker.markAsSingleton(singleton);
return singleton;
}
exports.markAsSingleton = markAsSingleton;
class MultiDisposeError extends Error {
constructor(errors) {
super(`Encountered errors while disposing of store. Errors: [${errors.join(', ')}]`);
this.errors = errors;
}
}
exports.MultiDisposeError = MultiDisposeError;
function isDisposable(thing) {
return typeof thing.dispose === 'function' && thing.dispose.length === 0;
}
exports.isDisposable = isDisposable;
function dispose(arg) {
if (iterator_1.Iterable.is(arg)) {
let errors = [];
for (const d of arg) {
if (d) {
try {
d.dispose();
}
catch (e) {
errors.push(e);
}
}
}
if (errors.length === 1) {
throw errors[0];
}
else if (errors.length > 1) {
throw new MultiDisposeError(errors);
}
return Array.isArray(arg) ? [] : arg;
}
else if (arg) {
arg.dispose();
return arg;
}
}
exports.dispose = dispose;
function combinedDisposable(...disposables) {
const parent = toDisposable(() => dispose(disposables));
setParentOfDisposables(disposables, parent);
return parent;
}
exports.combinedDisposable = combinedDisposable;
function toDisposable(fn) {
const self = trackDisposable({
dispose: (0, functional_1.once)(() => {
markAsDisposed(self);
fn();
})
});
return self;
}
exports.toDisposable = toDisposable;
class DisposableStore {
constructor() {
this._toDispose = new Set();
this._isDisposed = false;
trackDisposable(this);
}
/**
* Dispose of all registered disposables and mark this object as disposed.
*
* Any future disposables added to this object will be disposed of on `add`.
*/
dispose() {
if (this._isDisposed) {
return;
}
markAsDisposed(this);
this._isDisposed = true;
this.clear();
}
/**
* Returns `true` if this object has been disposed
*/
get isDisposed() {
return this._isDisposed;
}
/**
* Dispose of all registered disposables but do not mark this object as disposed.
*/
clear() {
try {
dispose(this._toDispose.values());
}
finally {
this._toDispose.clear();
}
}
add(o) {
if (!o) {
return o;
}
if (o === this) {
throw new Error('Cannot register a disposable on itself!');
}
setParentOfDisposable(o, this);
if (this._isDisposed) {
if (!DisposableStore.DISABLE_DISPOSED_WARNING) {
console.warn(new Error('Trying to add a disposable to a DisposableStore that has already been disposed of. The added object will be leaked!').stack);
}
}
else {
this._toDispose.add(o);
}
return o;
}
}
exports.DisposableStore = DisposableStore;
DisposableStore.DISABLE_DISPOSED_WARNING = false;
class Disposable {
constructor() {
this._store = new DisposableStore();
trackDisposable(this);
setParentOfDisposable(this._store, this);
}
dispose() {
markAsDisposed(this);
this._store.dispose();
}
_register(o) {
if (o === this) {
throw new Error('Cannot register a disposable on itself!');
}
return this._store.add(o);
}
}
exports.Disposable = Disposable;
Disposable.None = Object.freeze({ dispose() { } });
/**
* Manages the lifecycle of a disposable value that may be changed.
*
* This ensures that when the disposable value is changed, the previously held disposable is disposed of. You can
* also register a `MutableDisposable` on a `Disposable` to ensure it is automatically cleaned up.
*/
class MutableDisposable {
constructor() {
this._isDisposed = false;
trackDisposable(this);
}
get value() {
return this._isDisposed ? undefined : this._value;
}
set value(value) {
var _a;
if (this._isDisposed || value === this._value) {
return;
}
(_a = this._value) === null || _a === void 0 ? void 0 : _a.dispose();
if (value) {
setParentOfDisposable(value, this);
}
this._value = value;
}
clear() {
this.value = undefined;
}
dispose() {
var _a;
this._isDisposed = true;
markAsDisposed(this);
(_a = this._value) === null || _a === void 0 ? void 0 : _a.dispose();
this._value = undefined;
}
/**
* Clears the value, but does not dispose it.
* The old value is returned.
*/
clearAndLeak() {
const oldValue = this._value;
this._value = undefined;
if (oldValue) {
setParentOfDisposable(oldValue, null);
}
return oldValue;
}
}
exports.MutableDisposable = MutableDisposable;
class RefCountedDisposable {
constructor(_disposable) {
this._disposable = _disposable;
this._counter = 1;
}
acquire() {
this._counter++;
return this;
}
release() {
if (--this._counter === 0) {
this._disposable.dispose();
}
return this;
}
}
exports.RefCountedDisposable = RefCountedDisposable;
class ReferenceCollection {
constructor() {
this.references = new Map();
}
acquire(key, ...args) {
let reference = this.references.get(key);
if (!reference) {
reference = { counter: 0, object: this.createReferencedObject(key, ...args) };
this.references.set(key, reference);
}
const { object } = reference;
const dispose = (0, functional_1.once)(() => {
if (--reference.counter === 0) {
this.destroyReferencedObject(key, reference.object);
this.references.delete(key);
}
});
reference.counter++;
return { object, dispose };
}
}
exports.ReferenceCollection = ReferenceCollection;
/**
* Unwraps a reference collection of promised values. Makes sure
* references are disposed whenever promises get rejected.
*/
class AsyncReferenceCollection {
constructor(referenceCollection) {
this.referenceCollection = referenceCollection;
}
acquire(key, ...args) {
return __awaiter(this, void 0, void 0, function* () {
const ref = this.referenceCollection.acquire(key, ...args);
try {
const object = yield ref.object;
return {
object,
dispose: () => ref.dispose()
};
}
catch (error) {
ref.dispose();
throw error;
}
});
}
}
exports.AsyncReferenceCollection = AsyncReferenceCollection;
class ImmortalReference {
constructor(object) {
this.object = object;
}
dispose() { }
}
exports.ImmortalReference = ImmortalReference;
function disposeOnReturn(fn) {
const store = new DisposableStore();
try {
fn(store);
}
finally {
store.dispose();
}
}
exports.disposeOnReturn = disposeOnReturn;
//# sourceMappingURL=lifecycle.js.map