Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes for workspaceState sync #182234

Merged
merged 6 commits into from May 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -102,7 +102,7 @@ interface IEditableResourcePreview extends IBaseResourcePreview, IResourcePrevie
acceptResult?: IAcceptResult;
}

interface ISyncResourcePreview extends IBaseSyncResourcePreview {
export interface ISyncResourcePreview extends IBaseSyncResourcePreview {
readonly remoteUserData: IRemoteUserData;
readonly lastSyncUserData: IRemoteUserData | null;
readonly resourcePreviews: IEditableResourcePreview[];
Expand Down
1 change: 1 addition & 0 deletions src/vs/platform/userDataSync/common/userDataSync.ts
Expand Up @@ -363,6 +363,7 @@ export interface IGlobalState {
export interface IWorkspaceState {
folders: IWorkspaceStateFolder[];
storage: IStringDictionary<string>;
version: number;
}

export interface IWorkspaceStateFolder {
Expand Down
36 changes: 27 additions & 9 deletions src/vs/workbench/contrib/editSessions/common/workspaceStateSync.ts
Expand Up @@ -15,7 +15,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity';
import { IUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile';
import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { AbstractSynchroniser, IAcceptResult, IMergeResult, IResourcePreview, ISyncResourcePreview } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { IRemoteUserData, IResourceRefHandle, IUserDataSyncBackupStoreService, IUserDataSyncConfiguration, IUserDataSyncEnablementService, IUserDataSyncLogService, IUserDataSyncStoreService, IUserDataSynchroniser, IWorkspaceState, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { IEditSessionsStorageService } from 'vs/workbench/contrib/editSessions/common/editSessions';
import { IWorkspaceIdentityService } from 'vs/workbench/services/workspaces/common/workspaceIdentityService';
Expand Down Expand Up @@ -82,6 +82,9 @@ export class WorkspaceStateSynchroniser extends AbstractSynchroniser implements
return;
}

// Ensure we have latest state by sending out onWillSaveState event
await this.storageService.flush();

const keys = this.storageService.keys(StorageScope.WORKSPACE, StorageTarget.USER);
if (!keys.length) {
return;
Expand All @@ -95,16 +98,28 @@ export class WorkspaceStateSynchroniser extends AbstractSynchroniser implements
}
});

const content: IWorkspaceState = { folders, storage: contributedData };
const content: IWorkspaceState = { folders, storage: contributedData, version: this.version };
this.editSessionsStorageService.write('workspaceState', stringify(content));
}

protected override async applyResult(remoteUserData: IRemoteUserData): Promise<void> {
const cancellationTokenSource = new CancellationTokenSource();
const remoteWorkspaceState: IWorkspaceState = remoteUserData.syncData ? parse(remoteUserData.syncData.content) : null;
override async apply(): Promise<ISyncResourcePreview | null> {
const resource = await this.editSessionsStorageService.read('workspaceState', undefined);
if (!resource) {
return null;
}

const remoteWorkspaceState: IWorkspaceState = parse(resource.content);
if (!remoteWorkspaceState) {
this.logService.info('Skipping initializing workspace state because remote workspace state does not exist.');
return;
return null;
}

// Evaluate whether storage is applicable for current workspace
const cancellationTokenSource = new CancellationTokenSource();
const replaceUris = await this.workspaceIdentityService.matches(remoteWorkspaceState.folders, cancellationTokenSource.token);
if (!replaceUris) {
this.logService.info('Skipping initializing workspace state because remote workspace state does not match current workspace.');
return null;
}

const storage: IStringDictionary<any> = {};
Expand All @@ -113,9 +128,7 @@ export class WorkspaceStateSynchroniser extends AbstractSynchroniser implements
}

if (Object.keys(storage).length) {
// Evaluate whether storage is applicable for current workspace
const replaceUris = await this.workspaceIdentityService.matches(remoteWorkspaceState.folders, cancellationTokenSource.token);
// If so, initialize storage with remote storage
// Initialize storage with remote storage
for (const key of Object.keys(storage)) {
// Deserialize the stored state
try {
Expand All @@ -129,8 +142,13 @@ export class WorkspaceStateSynchroniser extends AbstractSynchroniser implements
}
}
}
return null;
}

// TODO@joyceerhl implement AbstractSynchronizer in full
protected override applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, result: [IResourcePreview, IAcceptResult][], force: boolean): Promise<void> {
throw new Error('Method not implemented.');
}
protected override async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, isRemoteDataFromCurrentMachine: boolean, userDataSyncConfiguration: IUserDataSyncConfiguration, token: CancellationToken): Promise<IResourcePreview[]> {
return [];
}
Expand Down
Expand Up @@ -16,7 +16,7 @@ import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspac
export const IWorkspaceIdentityService = createDecorator<IWorkspaceIdentityService>('IWorkspaceIdentityService');
export interface IWorkspaceIdentityService {
_serviceBrand: undefined;
matches(folders: IWorkspaceStateFolder[], cancellationToken: CancellationToken): Promise<(obj: any) => any>;
matches(folders: IWorkspaceStateFolder[], cancellationToken: CancellationToken): Promise<((obj: any) => any) | false>;
getWorkspaceStateFolders(cancellationToken: CancellationToken): Promise<IWorkspaceStateFolder[]>;
}

Expand All @@ -40,7 +40,7 @@ export class WorkspaceIdentityService implements IWorkspaceIdentityService {
return workspaceStateFolders;
}

async matches(incomingWorkspaceFolders: IWorkspaceStateFolder[], cancellationToken: CancellationToken): Promise<(value: any) => any> {
async matches(incomingWorkspaceFolders: IWorkspaceStateFolder[], cancellationToken: CancellationToken): Promise<((value: any) => any) | false> {
const incomingToCurrentWorkspaceFolderUris: { [key: string]: string } = {};

const incomingIdentitiesToIncomingWorkspaceFolders: { [key: string]: string } = {};
Expand Down Expand Up @@ -81,7 +81,7 @@ export class WorkspaceIdentityService implements IWorkspaceIdentityService {
continue;
}

return () => { };
return false;
}

const convertUri = (uriToConvert: URI) => {
Expand Down