/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { QuickPickItem, window, QuickPick } from 'vscode'; import * as nls from 'vscode-nls'; import { RemoteSourceProvider, RemoteSource } from './api/git'; import { Model } from './model'; import { throttle, debounce } from './decorators'; const localize = nls.loadMessageBundle(); async function getQuickPickResult(quickpick: QuickPick): Promise { const result = await new Promise(c => { quickpick.onDidAccept(() => c(quickpick.selectedItems[0])); quickpick.onDidHide(() => c(undefined)); quickpick.show(); }); quickpick.hide(); return result; } class RemoteSourceProviderQuickPick { private quickpick: QuickPick; constructor(private provider: RemoteSourceProvider) { this.quickpick = window.createQuickPick(); this.quickpick.ignoreFocusOut = true; if (provider.supportsQuery) { this.quickpick.placeholder = localize('type to search', "Repository name (type to search)"); this.quickpick.onDidChangeValue(this.onDidChangeValue, this); } else { this.quickpick.placeholder = localize('type to filter', "Repository name"); } } @debounce(300) private onDidChangeValue(): void { this.query(); } @throttle private async query(): Promise { this.quickpick.busy = true; try { const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || []; if (remoteSources.length === 0) { this.quickpick.items = [{ label: localize('none found', "No remote repositories found."), alwaysShow: true }]; } else { this.quickpick.items = remoteSources.map(remoteSource => ({ label: remoteSource.name, description: remoteSource.description || (typeof remoteSource.url === 'string' ? remoteSource.url : remoteSource.url[0]), remoteSource })); } } catch (err) { this.quickpick.items = [{ label: localize('error', "$(error) Error: {0}", err.message), alwaysShow: true }]; console.error(err); } finally { this.quickpick.busy = false; } } async pick(): Promise { this.query(); const result = await getQuickPickResult(this.quickpick); return result?.remoteSource; } } export interface PickRemoteSourceOptions { readonly providerLabel?: (provider: RemoteSourceProvider) => string; readonly urlLabel?: string; } export async function pickRemoteSource(model: Model, options: PickRemoteSourceOptions = {}): Promise { const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteSourceProvider, url?: string })>(); quickpick.ignoreFocusOut = true; const providers = model.getRemoteProviders() .map(provider => ({ label: (provider.icon ? `$(${provider.icon}) ` : '') + (options.providerLabel ? options.providerLabel(provider) : provider.name), alwaysShow: true, provider })); quickpick.placeholder = providers.length === 0 ? localize('provide url', "Provide repository URL") : localize('provide url or pick', "Provide repository URL or pick a repository source."); const updatePicks = (value?: string) => { if (value) { quickpick.items = [{ label: options.urlLabel ?? localize('url', "URL"), description: value, alwaysShow: true, url: value }, ...providers]; } else { quickpick.items = providers; } }; quickpick.onDidChangeValue(updatePicks); updatePicks(); const result = await getQuickPickResult(quickpick); if (result) { if (result.url) { return result.url; } else if (result.provider) { const quickpick = new RemoteSourceProviderQuickPick(result.provider); const remote = await quickpick.pick(); if (remote) { if (typeof remote.url === 'string') { return remote.url; } else if (remote.url.length > 0) { return await window.showQuickPick(remote.url, { ignoreFocusOut: true, placeHolder: localize('pick url', "Choose a URL to clone from.") }); } } } } return undefined; }