提交 27afc6b4 编写于 作者: J João Moreno

wip: git remote providers

上级 4f6c57a9
......@@ -6,18 +6,19 @@
import { lstat, Stats } from 'fs';
import * as os from 'os';
import * as path from 'path';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env } from 'vscode';
import { commands, Disposable, LineChange, MessageOptions, OutputChannel, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, QuickPick } from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import * as nls from 'vscode-nls';
import { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions } from './api/git';
import { ForcePushMode, Git, Stash } from './git';
import { Model } from './model';
import { Model, RemoteProvider, Remote } from './model';
import { Repository, Resource, ResourceGroupType } from './repository';
import { applyLineChanges, getModifiedRange, intersectDiffWithRange, invertLineChange, toLineRanges } from './staging';
import { fromGitUri, toGitUri, isGitUri } from './uri';
import { grep, isDescendant, pathEquals } from './util';
import { Log, LogLevel } from './log';
import { GitTimelineItem } from './timelineProvider';
import { throttle, debounce } from './decorators';
const localize = nls.loadMessageBundle();
......@@ -233,6 +234,59 @@ interface PushOptions {
silent?: boolean;
}
async function getQuickPickResult<T extends QuickPickItem>(quickpick: QuickPick<T>): Promise<T | undefined> {
const result = await new Promise<T | undefined>(c => {
quickpick.onDidAccept(() => c(quickpick.selectedItems[0]));
quickpick.onDidHide(() => c(undefined));
quickpick.show();
});
quickpick.hide();
return result;
}
class RemoteProviderQuickPick {
private quickpick: QuickPick<QuickPickItem & { remote: Remote }>;
constructor(private provider: RemoteProvider) {
this.quickpick = window.createQuickPick();
this.quickpick.ignoreFocusOut = true;
if (provider.searchSupport) {
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)
onDidChangeValue(): void {
this.query();
}
@throttle
async query(): Promise<void> {
this.quickpick.busy = true;
const remotes = await this.provider.getRemotes(this.quickpick.value);
this.quickpick.busy = false;
this.quickpick.items = remotes.map(remote => ({
label: remote.name,
description: remote.url,
remote
}));
}
async pick(): Promise<Remote | undefined> {
this.query();
const result = await getQuickPickResult(this.quickpick);
return result?.remote;
}
}
export class CommandCenter {
private disposables: Disposable[];
......@@ -454,10 +508,36 @@ export class CommandCenter {
@command('git.clone')
async clone(url?: string, parentPath?: string): Promise<void> {
if (!url) {
url = await window.showInputBox({
prompt: localize('repourl', "Repository URL"),
ignoreFocusOut: true
const quickpick = window.createQuickPick<(QuickPickItem & { provider?: RemoteProvider })>();
quickpick.ignoreFocusOut = true;
const providers = this.model.getRemoteProviders()
.map(provider => ({ label: localize('clonefrom', "Clone from {0}", provider.name), alwaysShow: true, provider }));
quickpick.items = providers;
quickpick.placeholder = providers.length === 0
? localize('provide url', "Provide repository URL to clone from")
: localize('provide url or pick', "Provide repository URL or pick a repository source");
quickpick.onDidChangeValue(value => {
if (value) {
quickpick.items = [{ label: value, description: localize('repourl', "Clone from URL"), alwaysShow: true }, ...providers];
} else {
quickpick.items = providers;
}
});
const result = await getQuickPickResult(quickpick);
if (result) {
if (result.provider) {
const quickpick = new RemoteProviderQuickPick(result.provider);
const remote = await quickpick.pick();
url = remote?.url;
} else {
url = result.label;
}
}
}
if (!url) {
......
......@@ -6,7 +6,7 @@
import { workspace, WorkspaceFoldersChangeEvent, Uri, window, Event, EventEmitter, QuickPickItem, Disposable, SourceControl, SourceControlResourceGroup, TextEditor, Memento, OutputChannel } from 'vscode';
import { Repository, RepositoryState } from './repository';
import { memoize, sequentialize, debounce } from './decorators';
import { dispose, anyEvent, filterEvent, isDescendant, firstIndex, pathEquals } from './util';
import { dispose, anyEvent, filterEvent, isDescendant, firstIndex, pathEquals, toDisposable } from './util';
import { Git } from './git';
import * as path from 'path';
import * as fs from 'fs';
......@@ -44,6 +44,17 @@ interface OpenRepository extends Disposable {
repository: Repository;
}
export interface Remote {
readonly name: string;
readonly url: string;
}
export interface RemoteProvider {
readonly name: string;
readonly searchSupport?: boolean;
getRemotes(query?: string): Remote[] | Promise<Remote[]>;
}
export class Model {
private _onDidOpenRepository = new EventEmitter<Repository>();
......@@ -74,6 +85,8 @@ export class Model {
this._onDidChangeState.fire(state);
}
private remoteProviders = new Set<RemoteProvider>();
private disposables: Disposable[] = [];
constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) {
......@@ -447,6 +460,15 @@ export class Model {
return undefined;
}
registerRemoteProvider(provider: RemoteProvider): Disposable {
this.remoteProviders.add(provider);
return toDisposable(() => this.remoteProviders.delete(provider));
}
getRemoteProviders(): RemoteProvider[] {
return [...this.remoteProviders.values()];
}
dispose(): void {
const openRepositories = [...this.openRepositories];
openRepositories.forEach(r => r.dispose());
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册