/*--------------------------------------------------------------------------------------------- * 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 { Event } from 'vscode'; import { dirname } from 'path'; import * as fs from 'fs'; export function log(...args: any[]): void { console.log.apply(console, ['git:', ...args]); } export interface IDisposable { dispose(): void; } export function dispose(disposables: T[]): T[] { disposables.forEach(d => d.dispose()); return []; } export function toDisposable(dispose: () => void): IDisposable { return { dispose }; } export function combinedDisposable(disposables: IDisposable[]): IDisposable { return toDisposable(() => dispose(disposables)); } export const EmptyDisposable = toDisposable(() => null); export function mapEvent(event: Event, map: (i: I) => O): Event { return (listener, thisArgs = null, disposables?) => event(i => listener.call(thisArgs, map(i)), null, disposables); } export function filterEvent(event: Event, filter: (e: T) => boolean): Event { return (listener, thisArgs = null, disposables?) => event(e => filter(e) && listener.call(thisArgs, e), null, disposables); } export function anyEvent(...events: Event[]): Event { return (listener, thisArgs = null, disposables?) => { const result = combinedDisposable(events.map(event => event(i => listener.call(thisArgs, i)))); if (disposables) { disposables.push(result); } return result; }; } export function done(promise: Promise): Promise { return promise.then(() => void 0); } export function once(event: Event): Event { return (listener, thisArgs = null, disposables?) => { const result = event(e => { result.dispose(); return listener.call(thisArgs, e); }, null, disposables); return result; }; } export function eventToPromise(event: Event): Promise { return new Promise(c => once(event)(c)); } // TODO@Joao: replace with Object.assign export function assign(destination: T, ...sources: any[]): T { for (const source of sources) { Object.keys(source).forEach(key => destination[key] = source[key]); } return destination; } export function uniqBy(arr: T[], fn: (el: T) => string): T[] { const seen = Object.create(null); return arr.filter(el => { const key = fn(el); if (seen[key]) { return false; } seen[key] = true; return true; }); } export function groupBy(arr: T[], fn: (el: T) => string): { [key: string]: T[] } { return arr.reduce((result, el) => { const key = fn(el); result[key] = [...(result[key] || []), el]; return result; }, Object.create(null)); } export function denodeify(fn: Function): (...args) => Promise { return (...args) => new Promise((c, e) => fn(...args, (err, r) => err ? e(err) : c(r))); } export function nfcall(fn: Function, ...args): Promise { return new Promise((c, e) => fn(...args, (err, r) => err ? e(err) : c(r))); } export async function mkdirp(path: string, mode?: number): Promise { const mkdir = async () => { try { await nfcall(fs.mkdir, path, mode); } catch (err) { if (err.code === 'EEXIST') { const stat = await nfcall(fs.stat, path); if (stat.isDirectory) { return; } throw new Error(`'${path}' exists and is not a directory.`); } throw err; } }; // is root? if (path === dirname(path)) { return true; } try { await mkdir(); } catch (err) { if (err.code !== 'ENOENT') { throw err; } await mkdirp(dirname(path), mode); await mkdir(); } return true; } export function uniqueFilter(keyFn: (t: T) => string): (t: T) => boolean { const seen: { [key: string]: boolean; } = Object.create(null); return element => { const key = keyFn(element); if (seen[key]) { return false; } seen[key] = true; return true; }; }