chain events

fixes #11297
This commit is contained in:
Joao Moreno 2016-09-12 15:33:14 +02:00
parent dde6f243fa
commit ea45b3873b
8 changed files with 96 additions and 63 deletions

View file

@ -10,7 +10,7 @@ import * as DOM from 'vs/base/browser/dom';
import { EventType as TouchEventType } from 'vs/base/browser/touch';
import { KeyCode } from 'vs/base/common/keyCodes';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import Event, { Emitter, mapEvent, EventBufferer, filterEvent } from 'vs/base/common/event';
import Event, { Emitter, EventBufferer, chain, mapEvent } from 'vs/base/common/event';
import { domEvent } from 'vs/base/browser/event';
import { IDelegate, IRenderer, IListMouseEvent, IFocusChangeEvent, ISelectionChangeEvent } from './list';
import { ListView, IListViewOptions } from './listView';
@ -134,13 +134,14 @@ class Controller<T> implements IDisposable {
this.disposables.push(view.addListener('click', e => this.onPointer(e)));
this.disposables.push(view.addListener(TouchEventType.Tap, e => this.onPointer(e)));
const onRawKeyDown = domEvent(view.domNode, 'keydown');
const onKeyDown = mapEvent(onRawKeyDown, e => new StandardKeyboardEvent(e));
filterEvent(onKeyDown, e => e.keyCode === KeyCode.Enter)(this.onEnter, this, this.disposables);
filterEvent(onKeyDown, e => e.keyCode === KeyCode.UpArrow)(this.onUpArrow, this, this.disposables);
filterEvent(onKeyDown, e => e.keyCode === KeyCode.DownArrow)(this.onDownArrow, this, this.disposables);
filterEvent(onKeyDown, e => e.keyCode === KeyCode.PageUp)(this.onPageUpArrow, this, this.disposables);
filterEvent(onKeyDown, e => e.keyCode === KeyCode.PageDown)(this.onPageDownArrow, this, this.disposables);
const onKeyDown = chain(domEvent(view.domNode, 'keydown'))
.map(e => new StandardKeyboardEvent(e));
onKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUpArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDownArrow, this, this.disposables);
}
private onMouseDown(e: IListMouseEvent<T>) {

View file

@ -208,14 +208,6 @@ export function once<T>(event: Event<T>): Event<T> {
};
}
export function mapEvent<I,O>(event: Event<I>, map: (i:I)=>O): Event<O> {
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
}
export function filterEvent<T>(event: Event<T>, filter: (e:T)=>boolean): Event<T> {
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
}
export function any(...events: Event<any>[]): Event<void> {
let listeners = [];
@ -309,6 +301,44 @@ export class EventBufferer {
}
}
export interface IChainableEvent<T> {
event: Event<T>;
map<O>(fn: (i: T) => O): IChainableEvent<O>;
filter(fn: (e: T) => boolean): IChainableEvent<T>;
on(listener: (e: T) => any, thisArgs?: any, disposables?: IDisposable[]): IDisposable;
}
export function mapEvent<I,O>(event: Event<I>, map: (i:I)=>O): Event<O> {
return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables);
}
export function filterEvent<T>(event: Event<T>, filter: (e:T)=>boolean): Event<T> {
return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables);
}
class ChainableEvent<T> implements IChainableEvent<T> {
get event(): Event<T> { return this._event; }
constructor(private _event: Event<T>) {}
map(fn) {
return new ChainableEvent(mapEvent(this._event, fn));
}
filter(fn) {
return new ChainableEvent(filterEvent(this._event, fn));
}
on(listener, thisArgs, disposables) {
return this._event(listener, thisArgs, disposables);
}
}
export function chain<T>(event: Event<T>): IChainableEvent<T> {
return new ChainableEvent(event);
}
export function stopwatch<T>(event: Event<T>): Event<number> {
const start = new Date().getTime();
return mapEvent(once(event), _ => new Date().getTime() - start);

View file

@ -5,7 +5,7 @@
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc';
import Event, { mapEvent, filterEvent } from 'vs/base/common/event';
import Event, { chain } from 'vs/base/common/event';
import { fromEventEmitter } from 'vs/base/node/event';
import { Server as IPCServer, Client as IPCClient, IServer, IClient, IChannel } from 'vs/base/parts/ipc/common/ipc';
@ -73,9 +73,10 @@ export class Server implements IServer, IDisposable {
}
private createScopedEvent(eventName: string, senderId: string) {
const onRawMessageEvent = fromEventEmitter<IIPCEvent>(this.ipc, eventName, (event, message) => ({ event, message }));
const onScopedRawMessageEvent = filterEvent<IIPCEvent>(onRawMessageEvent, ({ event }) => event.sender.getId() === senderId);
return mapEvent<IIPCEvent,string>(onScopedRawMessageEvent, ({ message }) => message);
return chain(fromEventEmitter<IIPCEvent>(this.ipc, eventName, (event, message) => ({ event, message })))
.filter(({ event }) => event.sender.getId() === senderId)
.map(({ message }) => message)
.event;
}
dispose(): void {

View file

@ -15,7 +15,7 @@ import { SignatureHelp, SignatureInformation, SignatureHelpProviderRegistry } fr
import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser';
import { RunOnceScheduler } from 'vs/base/common/async';
import { onUnexpectedError } from 'vs/base/common/errors';
import Event, {Emitter, filterEvent} from 'vs/base/common/event';
import Event, {Emitter, chain} from 'vs/base/common/event';
import {domEvent, stop} from 'vs/base/browser/event';
import { ICommonCodeEditor, ICursorSelectionChangedEvent } from 'vs/editor/common/editorCommon';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@ -238,8 +238,9 @@ export class ParameterHintsWidget implements IContentWidget, IDisposable {
updateFont();
const onFontInfo = filterEvent(this.editor.onDidChangeConfiguration.bind(this.editor), (e: IConfigurationChangedEvent) => e.fontInfo);
onFontInfo(updateFont, null, this.disposables);
chain<IConfigurationChangedEvent>(this.editor.onDidChangeConfiguration.bind(this.editor))
.filter(e => e.fontInfo)
.on(updateFont, null, this.disposables);
this.disposables.push(this.editor.onDidLayoutChange(e => this.updateMaxHeight()));
this.updateMaxHeight();

View file

@ -8,7 +8,7 @@
import 'vs/css!./suggest';
import * as nls from 'vs/nls';
import * as strings from 'vs/base/common/strings';
import Event, { Emitter, filterEvent } from 'vs/base/common/event';
import Event, { Emitter, chain } from 'vs/base/common/event';
import { TPromise } from 'vs/base/common/winjs.base';
import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors';
import { IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
@ -89,8 +89,9 @@ class Renderer implements IRenderer<ICompletionItem, ISuggestionTemplateData> {
configureFont();
const onFontInfo = filterEvent(this.editor.onDidChangeConfiguration.bind(this.editor), (e: IConfigurationChangedEvent) => e.fontInfo);
onFontInfo(configureFont, null, data.disposables);
chain<IConfigurationChangedEvent>(this.editor.onDidChangeConfiguration.bind(this.editor))
.filter(e => e.fontInfo)
.on(configureFont, null, data.disposables);
return data;
}
@ -196,8 +197,9 @@ class SuggestionDetails {
this.configureFont();
const onFontInfo = filterEvent(this.editor.onDidChangeConfiguration.bind(this.editor), (e: IConfigurationChangedEvent) => e.fontInfo);
onFontInfo(this.configureFont, this, this.disposables);
chain<IConfigurationChangedEvent>(this.editor.onDidChangeConfiguration.bind(this.editor))
.filter(e => e.fontInfo)
.on(this.configureFont, this, this.disposables);
}
get element() {

View file

@ -5,7 +5,7 @@
'use strict';
import Event, {mapEvent,filterEvent} from 'vs/base/common/event';
import Event, {chain} from 'vs/base/common/event';
import {fromEventEmitter} from 'vs/base/node/event';
import {IURLService} from 'vs/platform/url/common/url';
import product from 'vs/platform/product';
@ -21,17 +21,18 @@ export class URLService implements IURLService {
constructor() {
const rawOnOpenUrl = fromEventEmitter(app, 'open-url', (event: Electron.Event, url: string) => ({ event, url }));
const uriEvent = mapEvent(rawOnOpenUrl, ({ event, url }) => {
event.preventDefault();
this.onOpenURL = chain(rawOnOpenUrl)
.map(({ event, url }) => {
event.preventDefault();
try {
return URI.parse(url);
} catch(e) {
return null;
}
});
this.onOpenURL = filterEvent(uriEvent, uri => !!uri);
try {
return URI.parse(url);
} catch(e) {
return null;
}
})
.filter(uri => !!uri)
.event;
app.setAsDefaultProtocolClient(product.urlProtocol);
}

View file

@ -11,7 +11,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import { marked } from 'vs/base/common/marked/marked';
import { always } from 'vs/base/common/async';
import * as arrays from 'vs/base/common/arrays';
import Event, { Emitter, once, fromEventEmitter, filterEvent, mapEvent } from 'vs/base/common/event';
import Event, { Emitter, once, fromEventEmitter, chain } from 'vs/base/common/event';
import Cache from 'vs/base/common/cache';
import { Action } from 'vs/base/common/actions';
import { isPromiseCanceledError } from 'vs/base/common/errors';
@ -179,10 +179,10 @@ export class ExtensionEditor extends BaseEditor {
this.extensionActionBar = new ActionBar(extensionActions, { animated: false });
this.disposables.push(this.extensionActionBar);
let onActionError = fromEventEmitter<{ error?: any; }>(this.extensionActionBar, 'run');
onActionError = mapEvent(onActionError, ({ error }) => error);
onActionError = filterEvent(onActionError, error => !!error);
onActionError(this.onError, this, this.disposables);
chain(fromEventEmitter<{ error?: any; }>(this.extensionActionBar, 'run'))
.map(({ error }) => error)
.filter(error => !!error)
.on(this.onError, this, this.disposables);
const body = append(root, $('.body'));
this.navbar = new NavBar(body);

View file

@ -13,7 +13,7 @@ import { isPromiseCanceledError, onUnexpectedError, create as createError } from
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Builder, Dimension } from 'vs/base/browser/builder';
import { assign } from 'vs/base/common/objects';
import EventOf, { mapEvent, filterEvent } from 'vs/base/common/event';
import EventOf, { mapEvent, chain } from 'vs/base/common/event';
import { IAction } from 'vs/base/common/actions';
import { domEvent } from 'vs/base/browser/event';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
@ -76,8 +76,9 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet {
super(VIEWLET_ID, telemetryService);
this.searchDelayer = new ThrottledDelayer(500);
const onOpenExtensionUrl = filterEvent(urlService.onOpenURL, uri => /^extension/.test(uri.path));
onOpenExtensionUrl(this.onOpenExtensionUrl, this, this.disposables);
chain(urlService.onOpenURL)
.filter(uri => /^extension/.test(uri.path))
.on(this.onOpenExtensionUrl, this, this.disposables);
this.disposables.push(viewletService.onDidViewletOpen(this.onViewletOpen, this, this.disposables));
}
@ -99,29 +100,25 @@ export class ExtensionsViewlet extends Viewlet implements IExtensionsViewlet {
const renderer = this.instantiationService.createInstance(Renderer);
this.list = new PagedList(this.extensionsBox, delegate, [renderer]);
const onRawKeyDown = domEvent(this.searchBox, 'keydown');
const onKeyDown = mapEvent(onRawKeyDown, e => new StandardKeyboardEvent(e));
const onEnter = filterEvent(onKeyDown, e => e.keyCode === KeyCode.Enter);
const onEscape = filterEvent(onKeyDown, e => e.keyCode === KeyCode.Escape);
const onUpArrow = filterEvent(onKeyDown, e => e.keyCode === KeyCode.UpArrow);
const onDownArrow = filterEvent(onKeyDown, e => e.keyCode === KeyCode.DownArrow);
const onPageUpArrow = filterEvent(onKeyDown, e => e.keyCode === KeyCode.PageUp);
const onPageDownArrow = filterEvent(onKeyDown, e => e.keyCode === KeyCode.PageDown);
const onKeyDown = chain(domEvent(this.searchBox, 'keydown'))
.map(e => new StandardKeyboardEvent(e));
onEnter(this.onEnter, this, this.disposables);
onEscape(this.onEscape, this, this.disposables);
onUpArrow(this.onUpArrow, this, this.disposables);
onDownArrow(this.onDownArrow, this, this.disposables);
onPageUpArrow(this.onPageUpArrow, this, this.disposables);
onPageDownArrow(this.onPageDownArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.Enter).on(this.onEnter, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.Escape).on(this.onEscape, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.UpArrow).on(this.onUpArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.DownArrow).on(this.onDownArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.PageUp).on(this.onPageUpArrow, this, this.disposables);
onKeyDown.filter(e => e.keyCode === KeyCode.PageDown).on(this.onPageDownArrow, this, this.disposables);
const onSearchInput = domEvent(this.searchBox, 'input') as EventOf<SearchInputEvent>;
onSearchInput(e => this.triggerSearch(e.target.value, e.immediate), null, this.disposables);
this.onSearchChange = mapEvent(onSearchInput, e => e.target.value);
const onSelectedExtension = filterEvent(mapEvent(this.list.onSelectionChange, e => e.elements[0]), e => !!e);
onSelectedExtension(this.openExtension, this, this.disposables);
chain(this.list.onSelectionChange)
.map(e => e.elements[0])
.filter(e => !!e)
.on(this.openExtension, this, this.disposables);
return TPromise.as(null);
}