diff --git a/src/vs/base/common/callbackList.ts b/src/vs/base/common/callbackList.ts index 96e0781ba87..9a27d3b844b 100644 --- a/src/vs/base/common/callbackList.ts +++ b/src/vs/base/common/callbackList.ts @@ -6,21 +6,47 @@ import { IDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { LinkedList } from 'vs/base/common/linkedList'; export default class CallbackList { - private _callbacks: LinkedList<[Function, any]>; + private _callbacks: Function[]; + private _contexts: any[]; - public add(callback: Function, context: any = null, bucket?: IDisposable[]): () => void { + public add(callback: Function, context: any = null, bucket?: IDisposable[]): void { if (!this._callbacks) { - this._callbacks = new LinkedList<[Function, any]>(); + this._callbacks = []; + this._contexts = []; } - const remove = this._callbacks.insert([callback, context]); + this._callbacks.push(callback); + this._contexts.push(context); + if (Array.isArray(bucket)) { - bucket.push({ dispose: remove }); + bucket.push({ dispose: () => this.remove(callback, context) }); + } + } + + public remove(callback: Function, context: any = null): void { + if (!this._callbacks) { + return; + } + + let foundCallbackWithDifferentContext = false; + for (let i = 0, len = this._callbacks.length; i < len; i++) { + if (this._callbacks[i] === callback) { + if (this._contexts[i] === context) { + // callback & context match => remove it + this._callbacks.splice(i, 1); + this._contexts.splice(i, 1); + return; + } else { + foundCallbackWithDifferentContext = true; + } + } + } + + if (foundCallbackWithDifferentContext) { + throw new Error('When adding a listener with a context, you should remove it with the same context'); } - return remove; } public invoke(...args: any[]): any[] { @@ -28,12 +54,13 @@ export default class CallbackList { return undefined; } - const ret: any[] = []; - const elements = this._callbacks.toArray(); + const ret: any[] = [], + callbacks = this._callbacks.slice(0), + contexts = this._contexts.slice(0); - for (const [callback, context] of elements) { + for (let i = 0, len = callbacks.length; i < len; i++) { try { - ret.push(callback.apply(context, args)); + ret.push(callbacks[i].apply(contexts[i], args)); } catch (e) { onUnexpectedError(e); } @@ -41,20 +68,19 @@ export default class CallbackList { return ret; } + public isEmpty(): boolean { + return !this._callbacks || this._callbacks.length === 0; + } + public entries(): [Function, any][] { if (!this._callbacks) { return []; } - return this._callbacks - ? this._callbacks.toArray() - : []; - } - - public isEmpty(): boolean { - return !this._callbacks || this._callbacks.isEmpty(); + return this._callbacks.map((fn, index) => <[Function, any]>[fn, this._contexts[index]]); } public dispose(): void { this._callbacks = undefined; + this._contexts = undefined; } } diff --git a/src/vs/base/common/event.ts b/src/vs/base/common/event.ts index 19ad6dd8657..9ca573aac02 100644 --- a/src/vs/base/common/event.ts +++ b/src/vs/base/common/event.ts @@ -82,7 +82,7 @@ export class Emitter { this._options.onFirstListenerAdd(this); } - const remove = this._callbacks.add(listener, thisArgs); + this._callbacks.add(listener, thisArgs); if (firstListener && this._options && this._options.onFirstListenerDidAdd) { this._options.onFirstListenerDidAdd(this); @@ -97,7 +97,7 @@ export class Emitter { dispose: () => { result.dispose = Emitter._noop; if (!this._disposed) { - remove(); + this._callbacks.remove(listener, thisArgs); if (this._options && this._options.onLastListenerRemove && this._callbacks.isEmpty()) { this._options.onLastListenerRemove(this); } @@ -545,4 +545,4 @@ export class Relay implements IDisposable { this.disposable.dispose(); this.emitter.dispose(); } -} +} \ No newline at end of file diff --git a/src/vs/base/common/linkedList.ts b/src/vs/base/common/linkedList.ts deleted file mode 100644 index e908f3c0e57..00000000000 --- a/src/vs/base/common/linkedList.ts +++ /dev/null @@ -1,103 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -class Node { - element: E; - next: Node; - prev: Node; - - constructor(element: E) { - this.element = element; - } -} - -export class LinkedList { - - private _first: Node; - private _last: Node; - - isEmpty(): boolean { - return !this._first; - } - - insert(element: E) { - const newNode = new Node(element); - if (!this._first) { - this._first = newNode; - this._last = newNode; - } else { - const oldLast = this._last; - this._last = newNode; - newNode.prev = oldLast; - oldLast.next = newNode; - } - - return () => { - - for (let candidate = this._first; candidate instanceof Node; candidate = candidate.next) { - if (candidate !== newNode) { - continue; - } - if (candidate.prev && candidate.next) { - // middle - let anchor = candidate.prev; - anchor.next = candidate.next; - candidate.next.prev = anchor; - - } else if (!candidate.prev && !candidate.next) { - // only node - this._first = undefined; - this._last = undefined; - - } else if (!candidate.next) { - // last - this._last = this._last.prev; - this._last.next = undefined; - - } else if (!candidate.prev) { - // first - this._first = this._first.next; - this._first.prev = undefined; - } - - // done - break; - } - }; - } - - iterator() { - let _done: boolean; - let _value: E; - let element = { - get done() { return _done; }, - get value() { return _value; } - }; - let node = this._first; - return { - next(): { done: boolean; value: E } { - if (!node) { - _done = true; - _value = undefined; - } else { - _done = false; - _value = node.element; - node = node.next; - } - return element; - } - }; - } - - toArray(): E[] { - let result: E[] = []; - for (let node = this._first; node instanceof Node; node = node.next) { - result.push(node.element); - } - return result; - } -} diff --git a/src/vs/base/test/common/event.test.ts b/src/vs/base/test/common/event.test.ts index c11572642b1..2e26cb50b0c 100644 --- a/src/vs/base/test/common/event.test.ts +++ b/src/vs/base/test/common/event.test.ts @@ -179,29 +179,6 @@ suite('Event', function () { } }); - test('reusing event function and context', function () { - let counter = 0; - function listener() { - counter += 1; - } - const context = {}; - - let emitter = new Emitter(); - let reg1 = emitter.event(listener, context); - let reg2 = emitter.event(listener, context); - - emitter.fire(); - assert.equal(counter, 2); - - reg1.dispose(); - emitter.fire(); - assert.equal(counter, 3); - - reg2.dispose(); - emitter.fire(); - assert.equal(counter, 3); - }); - test('Debounce Event', function (done: () => void) { let doc = new Samples.Document3(); @@ -683,4 +660,4 @@ suite('Event utils', () => { assert.deepEqual(result, [1, 2, 3, 4, 5]); }); }); -}); +}); \ No newline at end of file diff --git a/src/vs/base/test/common/linkedList.test.ts b/src/vs/base/test/common/linkedList.test.ts deleted file mode 100644 index 267eccaa806..00000000000 --- a/src/vs/base/test/common/linkedList.test.ts +++ /dev/null @@ -1,72 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -'use strict'; - -import * as assert from 'assert'; -import { LinkedList } from 'vs/base/common/linkedList'; - -suite('LinkedList', function () { - - function assertElements(list: LinkedList, ...elements: E[]) { - // first: assert toArray - assert.deepEqual(list.toArray(), elements); - - // second: assert iterator - for (let iter = list.iterator(), element = iter.next(); !element.done; element = iter.next()) { - assert.equal(elements.shift(), element.value); - } - assert.equal(elements.length, 0); - } - - test('Insert/Iter', function () { - const list = new LinkedList(); - list.insert(0); - list.insert(1); - list.insert(2); - assertElements(list, 0, 1, 2); - }); - - test('Insert/Remove', function () { - let list = new LinkedList(); - let disp = list.insert(0); - list.insert(1); - list.insert(2); - disp(); - assertElements(list, 1, 2); - - list = new LinkedList(); - list.insert(0); - disp = list.insert(1); - list.insert(2); - disp(); - assertElements(list, 0, 2); - - list = new LinkedList(); - list.insert(0); - list.insert(1); - disp = list.insert(2); - disp(); - assertElements(list, 0, 1); - }); - - test('Insert/toArray', function () { - let list = new LinkedList(); - list.insert('foo'); - list.insert('bar'); - list.insert('far'); - list.insert('boo'); - - assert.deepEqual( - list.toArray(), - [ - 'foo', - 'bar', - 'far', - 'boo', - ] - ); - }); -}); diff --git a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts index 3be0250cf70..f1adf5fdf56 100644 --- a/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts +++ b/src/vs/workbench/api/node/extHostDocumentSaveParticipant.ts @@ -37,8 +37,12 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic get onWillSaveTextDocumentEvent(): Event { return (listener, thisArg, disposables) => { - const remove = this._callbacks.add(listener, thisArg); - const result = { dispose: remove }; + this._callbacks.add(listener, thisArg); + const result = { + dispose: () => { + this._callbacks.remove(listener, thisArg); + } + }; if (Array.isArray(disposables)) { disposables.push(result); }