提交 7cb8ac43 编写于 作者: J Johannes Rieken

allow to reuse the same function/context when listening to events

上级 be3e1c36
......@@ -6,47 +6,21 @@
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: Function[];
private _contexts: any[];
private _callbacks: LinkedList<[Function, 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 = [];
this._contexts = [];
this._callbacks = new LinkedList<[Function, any]>();
}
this._callbacks.push(callback);
this._contexts.push(context);
const remove = this._callbacks.insert([callback, context]);
if (Array.isArray(bucket)) {
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');
bucket.push({ dispose: remove });
}
return remove;
}
public invoke(...args: any[]): any[] {
......@@ -54,13 +28,12 @@ export default class CallbackList {
return undefined;
}
const ret: any[] = [],
callbacks = this._callbacks.slice(0),
contexts = this._contexts.slice(0);
const ret: any[] = [];
const elements = this._callbacks.toArray();
for (let i = 0, len = callbacks.length; i < len; i++) {
for (const [callback, context] of elements) {
try {
ret.push(callbacks[i].apply(contexts[i], args));
ret.push(callback.apply(context, args));
} catch (e) {
onUnexpectedError(e);
}
......@@ -68,19 +41,20 @@ 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.map((fn, index) => <[Function, any]>[fn, this._contexts[index]]);
return this._callbacks
? this._callbacks.toArray()
: [];
}
public isEmpty(): boolean {
return !this._callbacks || this._callbacks.isEmpty();
}
public dispose(): void {
this._callbacks = undefined;
this._contexts = undefined;
}
}
......@@ -82,7 +82,7 @@ export class Emitter<T> {
this._options.onFirstListenerAdd(this);
}
this._callbacks.add(listener, thisArgs);
const remove = this._callbacks.add(listener, thisArgs);
if (firstListener && this._options && this._options.onFirstListenerDidAdd) {
this._options.onFirstListenerDidAdd(this);
......@@ -97,7 +97,7 @@ export class Emitter<T> {
dispose: () => {
result.dispose = Emitter._noop;
if (!this._disposed) {
this._callbacks.remove(listener, thisArgs);
remove();
if (this._options && this._options.onLastListenerRemove && this._callbacks.isEmpty()) {
this._options.onLastListenerRemove(this);
}
......@@ -545,4 +545,4 @@ export class Relay<T> implements IDisposable {
this.disposable.dispose();
this.emitter.dispose();
}
}
\ No newline at end of file
}
/*---------------------------------------------------------------------------------------------
* 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<E> {
element: E;
next: Node<E>;
prev: Node<E>;
constructor(element: E) {
this.element = element;
}
}
export class LinkedList<E> {
private _first: Node<E>;
private _last: Node<E>;
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;
}
}
......@@ -179,6 +179,29 @@ 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();
......@@ -660,4 +683,4 @@ suite('Event utils', () => {
assert.deepEqual(result, [1, 2, 3, 4, 5]);
});
});
});
\ No newline at end of file
});
/*---------------------------------------------------------------------------------------------
* 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<E>(list: LinkedList<E>, ...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<number>();
list.insert(0);
list.insert(1);
list.insert(2);
assertElements(list, 0, 1, 2);
});
test('Insert/Remove', function () {
let list = new LinkedList<number>();
let disp = list.insert(0);
list.insert(1);
list.insert(2);
disp();
assertElements(list, 1, 2);
list = new LinkedList<number>();
list.insert(0);
disp = list.insert(1);
list.insert(2);
disp();
assertElements(list, 0, 2);
list = new LinkedList<number>();
list.insert(0);
list.insert(1);
disp = list.insert(2);
disp();
assertElements(list, 0, 1);
});
test('Insert/toArray', function () {
let list = new LinkedList<string>();
list.insert('foo');
list.insert('bar');
list.insert('far');
list.insert('boo');
assert.deepEqual(
list.toArray(),
[
'foo',
'bar',
'far',
'boo',
]
);
});
});
......@@ -37,12 +37,8 @@ export class ExtHostDocumentSaveParticipant implements ExtHostDocumentSavePartic
get onWillSaveTextDocumentEvent(): Event<vscode.TextDocumentWillSaveEvent> {
return (listener, thisArg, disposables) => {
this._callbacks.add(listener, thisArg);
const result = {
dispose: () => {
this._callbacks.remove(listener, thisArg);
}
};
const remove = this._callbacks.add(listener, thisArg);
const result = { dispose: remove };
if (Array.isArray(disposables)) {
disposables.push(result);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册