瀏覽代碼

Reworks getting of repository descriptors

Removes GK prefix from workspace types
main
Eric Amodio 1 年之前
父節點
當前提交
5480aa8025
共有 4 個文件被更改,包括 122 次插入198 次删除
  1. +37
    -81
      src/plus/workspaces/models.ts
  2. +39
    -46
      src/plus/workspaces/workspacesService.ts
  3. +7
    -5
      src/views/nodes/repositoryNode.ts
  4. +39
    -66
      src/views/nodes/workspaceNode.ts

+ 37
- 81
src/plus/workspaces/models.ts 查看文件

@ -1,3 +1,4 @@
import type { Container } from '../../container';
import type { Repository } from '../../git/models/repository';
export enum WorkspaceType {
@ -24,19 +25,19 @@ export interface RemoteDescriptor {
}
export interface GetWorkspacesResponse {
cloudWorkspaces: GKCloudWorkspace[];
localWorkspaces: GKLocalWorkspace[];
cloudWorkspaces: CloudWorkspace[];
localWorkspaces: LocalWorkspace[];
cloudWorkspaceInfo: string | undefined;
localWorkspaceInfo: string | undefined;
}
export interface LoadCloudWorkspacesResponse {
cloudWorkspaces: GKCloudWorkspace[] | undefined;
cloudWorkspaces: CloudWorkspace[] | undefined;
cloudWorkspaceInfo: string | undefined;
}
export interface LoadLocalWorkspacesResponse {
localWorkspaces: GKLocalWorkspace[] | undefined;
localWorkspaces: LocalWorkspace[] | undefined;
localWorkspaceInfo: string | undefined;
}
@ -46,60 +47,39 @@ export interface GetCloudWorkspaceRepositoriesResponse {
}
// Cloud Workspace types
export class GKCloudWorkspace {
private readonly _type: WorkspaceType = WorkspaceType.Cloud;
private readonly _id: string;
private readonly _organizationId: string | undefined;
private readonly _name: string;
private readonly _provider: CloudWorkspaceProviderType;
export class CloudWorkspace {
readonly type = WorkspaceType.Cloud;
private _repositories: CloudWorkspaceRepositoryDescriptor[] | undefined;
constructor(
id: string,
name: string,
organizationId: string | undefined,
provider: CloudWorkspaceProviderType,
private readonly getReposFn: (workspaceId: string) => Promise<GetCloudWorkspaceRepositoriesResponse>,
private readonly container: Container,
public readonly id: string,
public readonly name: string,
public readonly organizationId: string | undefined,
public readonly provider: CloudWorkspaceProviderType,
repositories?: CloudWorkspaceRepositoryDescriptor[],
) {
this._id = id;
this._name = name;
this._organizationId = organizationId;
this._provider = provider;
this._repositories = repositories;
}
get type(): WorkspaceType {
return this._type;
}
get id(): string {
return this._id;
}
get name(): string {
return this._name;
}
get organization_id(): string | undefined {
return this._organizationId;
get shared(): boolean {
return this.organizationId != null;
}
get provider(): CloudWorkspaceProviderType {
return this._provider;
}
async getRepositoryDescriptors(): Promise<CloudWorkspaceRepositoryDescriptor[]> {
if (this._repositories == null) {
this._repositories = await this.container.workspaces.getCloudWorkspaceRepositories(this.id);
}
get repositories(): CloudWorkspaceRepositoryDescriptor[] | undefined {
return this._repositories;
}
isShared(): boolean {
return this._organizationId != null;
}
getRepository(name: string): CloudWorkspaceRepositoryDescriptor | undefined {
return this._repositories?.find(r => r.name === name);
async getRepositoryDescriptor(name: string): Promise<CloudWorkspaceRepositoryDescriptor | undefined> {
return (await this.getRepositoryDescriptors()).find(r => r.name === name);
}
// TODO@axosoft-ramint this should be the entry point, not a backdoor to update the cache
addRepositories(repositories: CloudWorkspaceRepositoryDescriptor[]): void {
if (this._repositories == null) {
this._repositories = repositories;
@ -108,22 +88,12 @@ export class GKCloudWorkspace {
}
}
// TODO@axosoft-ramint this should be the entry point, not a backdoor to update the cache
removeRepositories(repoNames: string[]): void {
if (this._repositories == null) return;
this._repositories = this._repositories.filter(r => !repoNames.includes(r.name));
}
async getOrLoadRepositories(): Promise<GetCloudWorkspaceRepositoriesResponse> {
if (this._repositories != null) return { repositories: this._repositories, repositoriesInfo: undefined };
const getResponse = await this.getReposFn(this._id);
if (getResponse.repositories != null) {
this._repositories = getResponse.repositories;
}
return getResponse;
}
}
export interface CloudWorkspaceRepositoryDescriptor {
@ -495,39 +465,25 @@ export interface RemoveWorkspaceRepoDescriptor {
}
// Local Workspace Types
export class GKLocalWorkspace {
private readonly _type: WorkspaceType = WorkspaceType.Local;
private readonly _id: string;
private readonly _name: string;
private readonly _repositories: LocalWorkspaceRepositoryDescriptor[] | undefined;
constructor(id: string, name: string, repositories?: LocalWorkspaceRepositoryDescriptor[]) {
this._id = id;
this._name = name;
this._repositories = repositories;
}
get type(): WorkspaceType {
return this._type;
}
export class LocalWorkspace {
readonly type = WorkspaceType.Local;
get id(): string {
return this._id;
}
get name(): string {
return this._name;
}
constructor(
public readonly id: string,
public readonly name: string,
private readonly repositories: LocalWorkspaceRepositoryDescriptor[],
) {}
get repositories(): LocalWorkspaceRepositoryDescriptor[] | undefined {
return this._repositories;
get shared(): boolean {
return false;
}
isShared(): boolean {
return false;
getRepositoryDescriptors(): Promise<LocalWorkspaceRepositoryDescriptor[]> {
return Promise.resolve(this.repositories);
}
getRepository(name: string): LocalWorkspaceRepositoryDescriptor | undefined {
return this._repositories?.find(r => r.name === name);
getRepositoryDescriptor(name: string): Promise<LocalWorkspaceRepositoryDescriptor | undefined> {
return Promise.resolve(this.repositories.find(r => r.name === name));
}
}

+ 39
- 46
src/plus/workspaces/workspacesService.ts 查看文件

@ -16,7 +16,6 @@ import type {
CloudWorkspaceData,
CloudWorkspaceProviderType,
CloudWorkspaceRepositoryDescriptor,
GetCloudWorkspaceRepositoriesResponse,
GetWorkspacesResponse,
LoadCloudWorkspacesResponse,
LoadLocalWorkspacesResponse,
@ -28,10 +27,10 @@ import type {
WorkspacesResponse,
} from './models';
import {
CloudWorkspace,
CloudWorkspaceProviderInputType,
cloudWorkspaceProviderTypeToRemoteProviderId,
GKCloudWorkspace,
GKLocalWorkspace,
LocalWorkspace,
WorkspaceAddRepositoriesChoice,
WorkspaceType,
} from './models';
@ -39,36 +38,16 @@ import { WorkspacesApi } from './workspacesApi';
import type { WorkspacesPathMappingProvider } from './workspacesPathMappingProvider';
export class WorkspacesService implements Disposable {
private _cloudWorkspaces: GKCloudWorkspace[] | undefined = undefined;
private _localWorkspaces: GKLocalWorkspace[] | undefined = undefined;
private _workspacesApi: WorkspacesApi;
private _workspacesPathProvider: WorkspacesPathMappingProvider;
private _onDidChangeWorkspaces: EventEmitter<void> = new EventEmitter<void>();
get onDidChangeWorkspaces(): Event<void> {
return this._onDidChangeWorkspaces.event;
}
private _disposable: Disposable;
// TODO@ramint Add error handling/logging when this is used.
private readonly _getCloudWorkspaceRepos: (workspaceId: string) => Promise<GetCloudWorkspaceRepositoriesResponse> =
async (workspaceId: string) => {
try {
const workspaceRepos = await this._workspacesApi.getWorkspaceRepositories(workspaceId);
const repoDescriptors = workspaceRepos?.data?.project?.provider_data?.repositories?.nodes;
return {
repositories:
repoDescriptors != null
? repoDescriptors.map(descriptor => ({ ...descriptor, workspaceId: workspaceId }))
: [],
repositoriesInfo: undefined,
};
} catch {
return {
repositories: undefined,
repositoriesInfo: 'Failed to load repositories for this workspace.',
};
}
};
private _cloudWorkspaces: CloudWorkspace[] | undefined;
private _disposable: Disposable;
private _localWorkspaces: LocalWorkspace[] | undefined;
private _workspacesApi: WorkspacesApi;
private _workspacesPathProvider: WorkspacesPathMappingProvider;
constructor(private readonly container: Container, private readonly server: ServerConnection) {
this._workspacesApi = new WorkspacesApi(this.container, this.server);
@ -100,7 +79,7 @@ export class WorkspacesService implements Disposable {
};
}
const cloudWorkspaces: GKCloudWorkspace[] = [];
const cloudWorkspaces: CloudWorkspace[] = [];
let workspaces: CloudWorkspaceData[] | undefined;
try {
const workspaceResponse: WorkspacesResponse | undefined = excludeRepositories
@ -137,12 +116,12 @@ export class WorkspacesService implements Disposable {
}
cloudWorkspaces.push(
new GKCloudWorkspace(
new CloudWorkspace(
this.container,
workspace.id,
workspace.name,
workspace.organization?.id,
workspace.provider as CloudWorkspaceProviderType,
this._getCloudWorkspaceRepos,
repositories,
),
);
@ -160,12 +139,12 @@ export class WorkspacesService implements Disposable {
// TODO@ramint: When we interact more with local workspaces, this should return more info about failures.
private async loadLocalWorkspaces(): Promise<LoadLocalWorkspacesResponse> {
const localWorkspaces: GKLocalWorkspace[] = [];
const localWorkspaces: LocalWorkspace[] = [];
const workspaceFileData: LocalWorkspaceData =
(await this._workspacesPathProvider.getLocalWorkspaceData())?.workspaces || {};
for (const workspace of Object.values(workspaceFileData)) {
localWorkspaces.push(
new GKLocalWorkspace(
new LocalWorkspace(
workspace.localId,
workspace.name,
workspace.repositories.map(repositoryPath => ({
@ -183,11 +162,11 @@ export class WorkspacesService implements Disposable {
};
}
private getCloudWorkspace(workspaceId: string): GKCloudWorkspace | undefined {
private getCloudWorkspace(workspaceId: string): CloudWorkspace | undefined {
return this._cloudWorkspaces?.find(workspace => workspace.id === workspaceId);
}
private getLocalWorkspace(workspaceId: string): GKLocalWorkspace | undefined {
private getLocalWorkspace(workspaceId: string): LocalWorkspace | undefined {
return this._localWorkspaces?.find(workspace => workspace.id === workspaceId);
}
@ -217,6 +196,13 @@ export class WorkspacesService implements Disposable {
return getWorkspacesResponse;
}
async getCloudWorkspaceRepositories(workspaceId: string): Promise<CloudWorkspaceRepositoryDescriptor[]> {
// TODO@ramint Add error handling/logging when this is used.
const workspaceRepos = await this._workspacesApi.getWorkspaceRepositories(workspaceId);
const descriptors = workspaceRepos?.data?.project?.provider_data?.repositories?.nodes;
return descriptors?.map(d => ({ ...d, workspaceId: workspaceId })) ?? [];
}
resetWorkspaces(options?: { cloud?: boolean; local?: boolean }) {
if (options?.cloud ?? true) {
this._cloudWorkspaces = undefined;
@ -260,7 +246,9 @@ export class WorkspacesService implements Disposable {
async locateAllCloudWorkspaceRepos(workspaceId: string, cancellation?: CancellationToken): Promise<void> {
const workspace = this.getCloudWorkspace(workspaceId);
if (workspace == null) return;
if (workspace.repositories == null || workspace.repositories.length === 0) return;
const repoDescriptors = await workspace.getRepositoryDescriptors();
if (repoDescriptors == null || repoDescriptors.length === 0) return;
const foundRepos = await this.getRepositoriesInParentFolder(cancellation);
if (foundRepos == null || foundRepos.length === 0 || cancellation?.isCancellationRequested) return;
@ -564,12 +552,12 @@ export class WorkspacesService implements Disposable {
}
this._cloudWorkspaces?.push(
new GKCloudWorkspace(
new CloudWorkspace(
this.container,
createdProjectData.id,
createdProjectData.name,
createdProjectData.organization?.id,
createdProjectData.provider as CloudWorkspaceProviderType,
this._getCloudWorkspaceRepos,
[],
),
);
@ -780,10 +768,13 @@ export class WorkspacesService implements Disposable {
},
): Promise<WorkspaceRepositoriesByName> {
const workspaceRepositoriesByName: WorkspaceRepositoriesByName = new Map<string, RepositoryMatch>();
const workspace: GKCloudWorkspace | GKLocalWorkspace | undefined =
this.getCloudWorkspace(workspaceId) ?? this.getLocalWorkspace(workspaceId);
if (workspace?.repositories == null || workspace.repositories.length === 0) return workspaceRepositoriesByName;
const workspace = this.getLocalWorkspace(workspaceId) ?? this.getCloudWorkspace(workspaceId);
if (workspace == null) return workspaceRepositoriesByName;
const repoDescriptors = await workspace.getRepositoryDescriptors();
if (repoDescriptors == null || repoDescriptors.length === 0) return workspaceRepositoriesByName;
const currentRepositories = options?.repositories ?? this.container.git.repositories;
const reposProviderMap = new Map<string, Repository>();
@ -792,7 +783,7 @@ export class WorkspacesService implements Disposable {
if (options?.cancellation?.isCancellationRequested) break;
reposPathMap.set(normalizePath(repo.uri.fsPath.toLowerCase()), repo);
if (workspace instanceof GKCloudWorkspace) {
if (workspace instanceof CloudWorkspace) {
const remotes = await repo.getRemotes();
for (const remote of remotes) {
const remoteDescriptor = getRemoteDescriptor(remote);
@ -805,7 +796,7 @@ export class WorkspacesService implements Disposable {
}
}
for (const descriptor of workspace.repositories) {
for (const descriptor of repoDescriptors) {
let repoLocalPath = null;
let foundRepo = null;
@ -864,12 +855,14 @@ export class WorkspacesService implements Disposable {
workspaceType: WorkspaceType,
options?: { open?: boolean },
): Promise<void> {
const workspace: GKCloudWorkspace | GKLocalWorkspace | undefined =
const workspace =
workspaceType === WorkspaceType.Cloud
? this.getCloudWorkspace(workspaceId)
: this.getLocalWorkspace(workspaceId);
if (workspace == null) return;
if (workspace?.repositories == null) return;
const repoDescriptors = await workspace.getRepositoryDescriptors();
if (repoDescriptors == null) return;
const workspaceRepositoriesByName = await this.resolveWorkspaceRepositoriesByName(workspaceId, {
resolveFromPath: true,
@ -889,7 +882,7 @@ export class WorkspacesService implements Disposable {
}
}
if (workspaceFolderPaths.length < workspace.repositories.length) {
if (workspaceFolderPaths.length < repoDescriptors.length) {
const confirmation = await window.showWarningMessage(
`Some repositories in this workspace could not be located locally. Do you want to continue?`,
{ modal: true },

+ 7
- 5
src/views/nodes/repositoryNode.ts 查看文件

@ -8,10 +8,12 @@ import type { RepositoryChangeEvent, RepositoryFileSystemChangeEvent } from '../
import { Repository, RepositoryChange, RepositoryChangeComparisonMode } from '../../git/models/repository';
import type { GitStatus } from '../../git/models/status';
import type {
CloudWorkspace,
CloudWorkspaceRepositoryDescriptor,
LocalWorkspace,
LocalWorkspaceRepositoryDescriptor,
} from '../../plus/workspaces/models';
import { GKCloudWorkspace, GKLocalWorkspace } from '../../plus/workspaces/models';
import { WorkspaceType } from '../../plus/workspaces/models';
import { findLastIndex } from '../../system/array';
import { gate } from '../../system/decorators/gate';
import { debug, log } from '../../system/decorators/log';
@ -50,7 +52,7 @@ export class RepositoryNode extends SubscribeableViewNode
parent: ViewNode,
public readonly repo: Repository,
private readonly options?: {
workspace?: GKCloudWorkspace | GKLocalWorkspace;
workspace?: CloudWorkspace | LocalWorkspace;
workspaceRepoDescriptor: CloudWorkspaceRepositoryDescriptor | LocalWorkspaceRepositoryDescriptor;
},
) {
@ -264,9 +266,9 @@ export class RepositoryNode extends SubscribeableViewNode
}
if (this.options?.workspace) {
contextValue += '+workspace';
if (this.options.workspace instanceof GKCloudWorkspace) {
if (this.options.workspace.type === WorkspaceType.Cloud) {
contextValue += '+cloud';
} else if (this.options.workspace instanceof GKLocalWorkspace) {
} else if (this.options.workspace.type === WorkspaceType.Local) {
contextValue += '+local';
}
}
@ -347,7 +349,7 @@ export class RepositoryNode extends SubscribeableViewNode
light: this.view.container.context.asAbsolutePath(`images/light/icon-repo${iconSuffix}.svg`),
};
if (this.options?.workspace && !this.repo.closed) {
if (this.options?.workspace != null && !this.repo.closed) {
item.resourceUri = Uri.parse(`gitlens-view://workspaces/repository/open`);
}

+ 39
- 66
src/views/nodes/workspaceNode.ts 查看文件

@ -1,13 +1,10 @@
import { ThemeIcon, TreeItem, TreeItemCollapsibleState } from 'vscode';
import { GitUri } from '../../git/gitUri';
import type {
CloudWorkspaceRepositoryDescriptor,
LocalWorkspaceRepositoryDescriptor,
WorkspaceRepositoriesByName,
} from '../../plus/workspaces/models';
import { GKCloudWorkspace, GKLocalWorkspace, WorkspaceType } from '../../plus/workspaces/models';
import type { CloudWorkspace, LocalWorkspace, WorkspaceRepositoriesByName } from '../../plus/workspaces/models';
import { WorkspaceType } from '../../plus/workspaces/models';
import { createCommand } from '../../system/command';
import type { WorkspacesView } from '../workspacesView';
import { MessageNode } from './common';
import { CommandMessageNode, MessageNode } from './common';
import { RepositoryNode } from './repositoryNode';
import { ContextValues, ViewNode } from './viewNode';
import { WorkspaceMissingRepositoryNode } from './workspaceMissingRepositoryNode';
@ -18,40 +15,17 @@ export class WorkspaceNode extends ViewNode {
return `gitlens${this.key}(${workspaceId})`;
}
private _workspace: GKCloudWorkspace | GKLocalWorkspace;
private _type: WorkspaceType;
constructor(
uri: GitUri,
view: WorkspacesView,
parent: ViewNode,
public readonly workspace: GKCloudWorkspace | GKLocalWorkspace,
public readonly workspace: CloudWorkspace | LocalWorkspace,
) {
super(uri, view, parent);
this._workspace = workspace;
this._type = workspace.type;
}
override get id(): string {
return WorkspaceNode.getId(this._workspace.id ?? '');
}
get name(): string {
return this._workspace?.name ?? '';
}
get workspaceId(): string {
return this._workspace.id ?? '';
}
get type(): WorkspaceType {
return this._type;
}
private async getRepositories(): Promise<
CloudWorkspaceRepositoryDescriptor[] | LocalWorkspaceRepositoryDescriptor[] | undefined
> {
return Promise.resolve(this._workspace?.repositories);
return WorkspaceNode.getId(this.workspace.id);
}
private _children: ViewNode[] | undefined;
@ -59,23 +33,29 @@ export class WorkspaceNode extends ViewNode {
async getChildren(): Promise<ViewNode[]> {
if (this._children == null) {
this._children = [];
let descriptors: CloudWorkspaceRepositoryDescriptor[] | LocalWorkspaceRepositoryDescriptor[] | undefined;
let repositoryInfo: string | undefined;
if (this.workspace instanceof GKLocalWorkspace) {
descriptors = (await this.getRepositories()) ?? [];
} else {
const { repositories: repos, repositoriesInfo: repoInfo } =
await this.workspace.getOrLoadRepositories();
descriptors = repos;
repositoryInfo = repoInfo;
}
if (descriptors?.length === 0) {
this._children.push(new MessageNode(this.view, this, 'No repositories in this workspace.'));
return this._children;
} else if (descriptors?.length) {
try {
const descriptors = await this.workspace.getRepositoryDescriptors();
if (descriptors == null || descriptors.length === 0) {
this._children.push(
new CommandMessageNode(
this.view,
this,
createCommand<[WorkspaceNode]>(
'gitlens.views.workspaces.addRepos',
'Add Repositories...',
this,
),
'No repositories',
),
);
return this._children;
}
// TODO@eamodio this should not be done here -- it should be done in the workspaces model (when loading the repos)
const reposByName: WorkspaceRepositoriesByName =
await this.view.container.workspaces.resolveWorkspaceRepositoriesByName(this.workspaceId, {
await this.view.container.workspaces.resolveWorkspaceRepositoriesByName(this.workspace.id, {
resolveFromPath: true,
usePathMapping: true,
});
@ -84,22 +64,20 @@ export class WorkspaceNode extends ViewNode {
const repo = reposByName.get(descriptor.name)?.repository;
if (!repo) {
this._children.push(
new WorkspaceMissingRepositoryNode(this.view, this, this.workspaceId, descriptor),
new WorkspaceMissingRepositoryNode(this.view, this, this.workspace.id, descriptor),
);
continue;
}
this._children.push(
new RepositoryNode(GitUri.fromRepoPath(repo.path), this.view, this, repo, {
workspace: this._workspace,
workspace: this.workspace,
workspaceRepoDescriptor: descriptor,
}),
);
}
}
if (repositoryInfo != null) {
this._children.push(new MessageNode(this.view, this, repositoryInfo));
} catch (ex) {
return [new MessageNode(this.view, this, 'Failed to load repositories')];
}
}
@ -107,29 +85,24 @@ export class WorkspaceNode extends ViewNode {
}
getTreeItem(): TreeItem {
const description = '';
// TODO@ramint Icon needs to change based on workspace type, and need a tooltip.
const icon: ThemeIcon = new ThemeIcon(this._type == WorkspaceType.Cloud ? 'cloud' : 'folder');
const item = new TreeItem(this.workspace.name, TreeItemCollapsibleState.Collapsed);
const item = new TreeItem(this.name, TreeItemCollapsibleState.Collapsed);
let contextValue = `${ContextValues.Workspace}`;
if (this._type === WorkspaceType.Cloud) {
if (this.workspace.type === WorkspaceType.Cloud) {
contextValue += '+cloud';
} else {
contextValue += '+local';
}
item.id = this.id;
item.description = description;
item.contextValue = contextValue;
item.iconPath = icon;
item.tooltip = `${this.name}\n${
this._type === WorkspaceType.Cloud
? `Cloud Workspace ${this._workspace.isShared() ? '(Shared)' : ''}`
item.iconPath = new ThemeIcon(thisclass="p">.workspacen>.type == WorkspaceType.Cloud ? 'cloud' : 'folder');
item.tooltip = `${this.workspace.name}\n${
this.workspace.type === WorkspaceType.Cloud
? `Cloud Workspace ${this.workspace.shared ? '(Shared)' : ''}`
: 'Local Workspace'
}${
this._workspace instanceof GKCloudWorkspace && this._workspace.provider != null
? `\nProvider: ${this._workspace.provider}`
this.workspace.type === WorkspaceType.Cloud && this.workspace.provider != null
? `\nProvider: ${this.workspace.provider}`
: ''
}`;
item.resourceUri = undefined;

Loading…
取消
儲存