mirror of
https://github.com/microsoft/vscode.git
synced 2026-05-08 09:08:48 +01:00
wip: git remote providers
This commit is contained in:
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user