/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; import { Repository } from './typings/git'; export class DisposableStore { private disposables = new Set(); add(disposable: vscode.Disposable): void { this.disposables.add(disposable); } dispose(): void { for (const disposable of this.disposables) { disposable.dispose(); } this.disposables.clear(); } } function decorate(decorator: (fn: Function, key: string) => Function): Function { return (_target: any, key: string, descriptor: any) => { let fnKey: string | null = null; let fn: Function | null = null; if (typeof descriptor.value === 'function') { fnKey = 'value'; fn = descriptor.value; } else if (typeof descriptor.get === 'function') { fnKey = 'get'; fn = descriptor.get; } if (!fn || !fnKey) { throw new Error('not supported'); } descriptor[fnKey] = decorator(fn, key); }; } function _sequentialize(fn: Function, key: string): Function { const currentKey = `__$sequence$${key}`; return function (this: any, ...args: any[]) { const currentPromise = this[currentKey] as Promise || Promise.resolve(null); const run = async () => await fn.apply(this, args); this[currentKey] = currentPromise.then(run, run); return this[currentKey]; }; } export const sequentialize = decorate(_sequentialize); export function groupBy(data: ReadonlyArray, compare: (a: T, b: T) => number): T[][] { const result: T[][] = []; let currentGroup: T[] | undefined = undefined; for (const element of data.slice(0).sort(compare)) { if (!currentGroup || compare(currentGroup[0], element) !== 0) { currentGroup = [element]; result.push(currentGroup); } else { currentGroup.push(element); } } return result; } export function getRepositoryFromUrl(url: string): { owner: string; repo: string } | undefined { const match = /^https:\/\/github\.com\/([^/]+)\/([^/]+?)(\.git)?$/i.exec(url) || /^git@github\.com:([^/]+)\/([^/]+?)(\.git)?$/i.exec(url); return match ? { owner: match[1], repo: match[2] } : undefined; } export function getRepositoryFromQuery(query: string): { owner: string; repo: string } | undefined { const match = /^([^/]+)\/([^/]+)$/i.exec(query); return match ? { owner: match[1], repo: match[2] } : undefined; } export function repositoryHasGitHubRemote(repository: Repository) { return !!repository.state.remotes.find(remote => remote.fetchUrl ? getRepositoryFromUrl(remote.fetchUrl) : undefined); } export function getRepositoryDefaultRemoteUrl(repository: Repository): string | undefined { const remotes = repository.state.remotes .filter(remote => remote.fetchUrl && getRepositoryFromUrl(remote.fetchUrl)); if (remotes.length === 0) { return undefined; } // upstream -> origin -> first const remote = remotes.find(remote => remote.name === 'upstream') ?? remotes.find(remote => remote.name === 'origin') ?? remotes[0]; return remote.fetchUrl; } export function getRepositoryDefaultRemote(repository: Repository): { owner: string; repo: string } | undefined { const fetchUrl = getRepositoryDefaultRemoteUrl(repository); return fetchUrl ? getRepositoryFromUrl(fetchUrl) : undefined; }