提交 479b3daf 编写于 作者: J João Moreno

Merge branch 'joao/git-remote-providers'

......@@ -5,7 +5,7 @@
import { Model } from '../model';
import { Repository as BaseRepository, Resource } from '../repository';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, GitExtension, RefType } from './git';
import { InputBox, Git, API, Repository, Remote, RepositoryState, Branch, Ref, Submodule, Commit, Change, RepositoryUIState, Status, LogOptions, APIState, CommitOptions, GitExtension, RefType, RemoteSourceProvider } from './git';
import { Event, SourceControlInputBox, Uri, SourceControl, Disposable, commands } from 'vscode';
import { mapEvent } from '../util';
import { toGitUri } from '../uri';
......@@ -248,6 +248,10 @@ export class ApiImpl implements API {
return result ? new ApiRepository(result) : null;
}
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
return this._model.registerRemoteSourceProvider(provider);
}
constructor(private _model: Model) { }
}
......
......@@ -3,7 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Uri, SourceControlInputBox, Event, CancellationToken } from 'vscode';
import { Uri, Event, Disposable, ProviderResult } from 'vscode';
export { ProviderResult } from 'vscode';
export interface Git {
readonly path: string;
......@@ -189,6 +190,17 @@ export interface Repository {
commit(message: string, opts?: CommitOptions): Promise<void>;
}
export interface RemoteSource {
readonly name: string;
readonly url: string;
}
export interface RemoteSourceProvider {
readonly name: string;
readonly supportsQuery?: boolean;
getRemoteSources(query?: string): ProviderResult<RemoteSource[]>;
}
export type APIState = 'uninitialized' | 'initialized';
export interface API {
......@@ -201,6 +213,7 @@ export interface API {
toGitUri(uri: Uri, ref: string): Uri;
getRepository(uri: Uri): Repository | null;
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable;
}
export interface GitExtension {
......
......@@ -6,10 +6,10 @@
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 { Branch, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourceProvider, RemoteSource } from './api/git';
import { ForcePushMode, Git, Stash } from './git';
import { Model } from './model';
import { Repository, Resource, ResourceGroupType } from './repository';
......@@ -18,6 +18,7 @@ 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,65 @@ 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 RemoteSourceProviderQuickPick {
private quickpick: QuickPick<QuickPickItem & { remoteSource?: RemoteSource }>;
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)
onDidChangeValue(): void {
this.query();
}
@throttle
async query(): Promise<void> {
this.quickpick.busy = true;
const remoteSources = await this.provider.getRemoteSources(this.quickpick.value) || [];
this.quickpick.busy = false;
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.url,
remote: remoteSource
}));
}
}
async pick(): Promise<RemoteSource | undefined> {
this.query();
const result = await getQuickPickResult(this.quickpick);
return result?.remoteSource;
}
}
export class CommandCenter {
private disposables: Disposable[];
......@@ -454,10 +514,43 @@ 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?: RemoteSourceProvider })>();
quickpick.ignoreFocusOut = true;
const providers = this.model.getRemoteProviders()
.map(provider => ({ label: localize('clonefrom', "Clone from {0}", 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: localize('repourl', "Clone from URL"),
description: value,
alwaysShow: true
},
...providers];
} else {
quickpick.items = providers;
}
};
quickpick.onDidChangeValue(updatePicks);
updatePicks();
const result = await getQuickPickResult(quickpick);
if (result) {
if (result.provider) {
const quickpick = new RemoteSourceProviderQuickPick(result.provider);
const remote = await quickpick.pick();
url = remote?.url;
} else {
url = result.label;
}
}
}
if (!url) {
......
......@@ -6,13 +6,13 @@
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';
import * as nls from 'vscode-nls';
import { fromGitUri } from './uri';
import { GitErrorCodes, APIState as State } from './api/git';
import { GitErrorCodes, APIState as State, RemoteSourceProvider } from './api/git';
const localize = nls.loadMessageBundle();
......@@ -74,6 +74,8 @@ export class Model {
this._onDidChangeState.fire(state);
}
private remoteProviders = new Set<RemoteSourceProvider>();
private disposables: Disposable[] = [];
constructor(readonly git: Git, private globalState: Memento, private outputChannel: OutputChannel) {
......@@ -447,6 +449,15 @@ export class Model {
return undefined;
}
registerRemoteSourceProvider(provider: RemoteSourceProvider): Disposable {
this.remoteProviders.add(provider);
return toDisposable(() => this.remoteProviders.delete(provider));
}
getRemoteProviders(): RemoteSourceProvider[] {
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.
先完成此消息的编辑!
想要评论请 注册