mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Introduce simpler interface for IEditSessionContribution
(#179921)
* Simpler interface for IEditSessionContribution * Add doc * More doc
This commit is contained in:
parent
739b93cce8
commit
8e85312936
|
@ -4,12 +4,30 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
|
||||
export interface IEditSessionContribution {
|
||||
getStateToStore(workspaceFolder: IWorkspaceFolder): unknown;
|
||||
resumeState(workspaceFolder: IWorkspaceFolder, state: unknown): void;
|
||||
/**
|
||||
* Called as part of storing an edit session.
|
||||
* @returns An opaque object representing state that this contribution
|
||||
* knows how to restore. Stored state will be passed back to this
|
||||
* contribution when an edit session is resumed via {@link resumeState}.
|
||||
*/
|
||||
getStateToStore(): unknown;
|
||||
|
||||
/**
|
||||
*
|
||||
* Called as part of resuming an edit session.
|
||||
* @param state State that this contribution has previously provided in
|
||||
* {@link getStateToStore}.
|
||||
* @param uriResolver A handler capable of converting URIs which may have
|
||||
* originated on another filesystem to URIs which exist in the current
|
||||
* workspace. If no conversion is possible, e.g. because the specified
|
||||
* URI bears no relation to the current workspace, this returns the original
|
||||
* URI that was passed in.
|
||||
*/
|
||||
resumeState(state: unknown, uriResolver: (uri: URI) => URI): void;
|
||||
}
|
||||
|
||||
class EditSessionStateRegistryImpl {
|
||||
|
@ -17,7 +35,8 @@ class EditSessionStateRegistryImpl {
|
|||
|
||||
public registerEditSessionsContribution(contributionPoint: string, editSessionsContribution: IEditSessionContribution): IDisposable {
|
||||
if (this._registeredEditSessionContributions.has(contributionPoint)) {
|
||||
throw new Error(`Edit session contribution point with identifier ${contributionPoint} already exists`);
|
||||
console.warn(`Edit session contribution point with identifier ${contributionPoint} already exists`);
|
||||
return { dispose: () => { } };
|
||||
}
|
||||
|
||||
this._registeredEditSessionContributions.set(contributionPoint, editSessionsContribution);
|
||||
|
|
|
@ -15,7 +15,7 @@ import { ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { basename, joinPath, relativePath } from 'vs/base/common/resources';
|
||||
import { basename, isEqualOrParent, joinPath, relativePath } from 'vs/base/common/resources';
|
||||
import { encodeBase64 } from 'vs/base/common/buffer';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IProgress, IProgressService, IProgressStep, ProgressLocation } from 'vs/platform/progress/common/progress';
|
||||
|
@ -405,6 +405,21 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
|||
await that.progressService.withProgress({ ...resumeProgressOptions, title: resumeProgressOptionsTitle }, async () => await that.resumeEditSession(editSessionId, undefined, forceApplyUnrelatedChange));
|
||||
}
|
||||
}));
|
||||
this._register(registerAction2(class ResumeLatestEditSessionAction extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'workbench.editSessions.actions.resumeFromSerializedPayload',
|
||||
title: { value: localize('resume cloud changes', "Resume Changes from Serialized Data"), original: 'Resume Changes from Serialized Data' },
|
||||
category: 'Developer',
|
||||
f1: true,
|
||||
});
|
||||
}
|
||||
|
||||
async run(accessor: ServicesAccessor, editSessionId?: string): Promise<void> {
|
||||
const data = await that.quickInputService.input({ prompt: 'Enter serialized data' });
|
||||
await that.progressService.withProgress({ ...resumeProgressOptions, title: resumeProgressOptionsTitle }, async () => await that.resumeEditSession(editSessionId, undefined, undefined, undefined, undefined, data));
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private registerStoreLatestEditSessionAction(): void {
|
||||
|
@ -440,7 +455,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
|||
}));
|
||||
}
|
||||
|
||||
async resumeEditSession(ref?: string, silent?: boolean, forceApplyUnrelatedChange?: boolean, applyPartialMatch?: boolean, progress?: IProgress<IProgressStep>): Promise<void> {
|
||||
async resumeEditSession(ref?: string, silent?: boolean, forceApplyUnrelatedChange?: boolean, applyPartialMatch?: boolean, progress?: IProgress<IProgressStep>, serializedData?: string): Promise<void> {
|
||||
// Wait for the remote environment to become available, if any
|
||||
await this.remoteAgentService.getEnvironment();
|
||||
|
||||
|
@ -467,7 +482,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
|||
performance.mark('code/willResumeEditSessionFromIdentifier');
|
||||
|
||||
progress?.report({ message: localize('checkingForWorkingChanges', 'Checking for pending cloud changes...') });
|
||||
const data = await this.editSessionsStorageService.read(ref);
|
||||
const data = serializedData ? { editSession: JSON.parse(serializedData), ref: '' } : await this.editSessionsStorageService.read(ref);
|
||||
if (!data) {
|
||||
if (ref === undefined && !silent) {
|
||||
this.notificationService.info(localize('no cloud changes', 'There are no changes to resume from the cloud.'));
|
||||
|
@ -541,9 +556,9 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
|||
const contributedStateHandlers: (() => void)[] = [];
|
||||
const conflictingChanges = [];
|
||||
const workspaceFolders = this.contextService.getWorkspace().folders;
|
||||
const cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
for (const folder of editSession.folders) {
|
||||
const cancellationTokenSource = new CancellationTokenSource();
|
||||
let folderRoot: IWorkspaceFolder | undefined;
|
||||
|
||||
if (folder.canonicalIdentity) {
|
||||
|
@ -606,19 +621,45 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
|||
conflictingChanges.push({ uri, type: change.type, contents: change.contents });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const workspaceFolder = folderRoot;
|
||||
if (workspaceFolder) {
|
||||
// look through all registered contributions to gather additional state
|
||||
EditSessionRegistry.getEditSessionContributions().forEach(([key, contrib]) => {
|
||||
const state = folder[key];
|
||||
if (state) {
|
||||
contributedStateHandlers.push(() => contrib.resumeState(workspaceFolder, state));
|
||||
}
|
||||
});
|
||||
const incomingFolderUrisToIdentifiers = new Map<string, [string, EditSessionIdentityMatch]>();
|
||||
for (const folder of editSession.folders) {
|
||||
const { canonicalIdentity } = folder;
|
||||
for (const workspaceFolder of workspaceFolders) {
|
||||
const identity = await this.editSessionIdentityService.getEditSessionIdentifier(workspaceFolder, cancellationTokenSource.token);
|
||||
if (!identity || !canonicalIdentity || !folder.absoluteUri) {
|
||||
continue;
|
||||
}
|
||||
const match = identity === canonicalIdentity
|
||||
? EditSessionIdentityMatch.Complete
|
||||
: await this.editSessionIdentityService.provideEditSessionIdentityMatch(workspaceFolder, identity, canonicalIdentity, cancellationTokenSource.token);
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
incomingFolderUrisToIdentifiers.set(folder.absoluteUri.toString(), [workspaceFolder.uri.toString(), match]);
|
||||
}
|
||||
}
|
||||
|
||||
EditSessionRegistry.getEditSessionContributions().forEach(([key, contrib]) => {
|
||||
const state = editSession.state[key];
|
||||
if (state) {
|
||||
contributedStateHandlers.push(() => contrib.resumeState(state, (incomingUri: URI) => {
|
||||
for (const absoluteUri of incomingFolderUrisToIdentifiers.keys()) {
|
||||
if (isEqualOrParent(incomingUri, URI.parse(absoluteUri))) {
|
||||
const [workspaceFolderUri, match] = incomingFolderUrisToIdentifiers.get(absoluteUri)!;
|
||||
if (match === EditSessionIdentityMatch.Complete) {
|
||||
const relativeFilePath = relativePath(URI.parse(absoluteUri), incomingUri);
|
||||
return relativeFilePath ? joinPath(URI.parse(workspaceFolderUri), relativeFilePath) : incomingUri;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return incomingUri;
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return { changes, conflictingChanges, contributedStateHandlers };
|
||||
}
|
||||
|
||||
|
@ -698,19 +739,20 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
|||
}
|
||||
|
||||
let canonicalIdentity = undefined;
|
||||
const contributedData: { [key: string]: unknown } = {};
|
||||
if (workspaceFolder !== null && workspaceFolder !== undefined) {
|
||||
canonicalIdentity = await this.editSessionIdentityService.getEditSessionIdentifier(workspaceFolder, cancellationToken);
|
||||
|
||||
// look through all registered contributions to gather additional state
|
||||
EditSessionRegistry.getEditSessionContributions().forEach(([key, contrib]) => {
|
||||
contributedData[key] = contrib.getStateToStore(workspaceFolder);
|
||||
});
|
||||
}
|
||||
|
||||
folders.push({ ...contributedData, workingChanges, name: name ?? '', canonicalIdentity: canonicalIdentity ?? undefined });
|
||||
// TODO@joyceerhl debt: don't store working changes as a child of the folder
|
||||
folders.push({ workingChanges, name: name ?? '', canonicalIdentity: canonicalIdentity ?? undefined, absoluteUri: workspaceFolder?.toString() });
|
||||
}
|
||||
|
||||
// Look through all registered contributions to gather additional state
|
||||
const contributedData: { [key: string]: unknown } = {};
|
||||
EditSessionRegistry.getEditSessionContributions().forEach(([key, contrib]) => {
|
||||
contributedData[key] = contrib.getStateToStore();
|
||||
});
|
||||
|
||||
if (!hasEdits) {
|
||||
this.logService.info('Skipped storing working changes in the cloud as there are no edits to store.');
|
||||
if (fromStoreCommand) {
|
||||
|
@ -719,7 +761,7 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo
|
|||
return undefined;
|
||||
}
|
||||
|
||||
const data: EditSession = { folders, version: 2 };
|
||||
const data: EditSession = { folders, version: 2, state: contributedData };
|
||||
|
||||
try {
|
||||
this.logService.info(`Storing edit session...`);
|
||||
|
|
|
@ -70,16 +70,16 @@ export interface Folder {
|
|||
name: string;
|
||||
canonicalIdentity: string | undefined;
|
||||
workingChanges: Change[];
|
||||
// additional data collected from workbench contributions
|
||||
[key: string]: unknown;
|
||||
absoluteUri: string | undefined;
|
||||
}
|
||||
|
||||
export const EditSessionSchemaVersion = 2;
|
||||
export const EditSessionSchemaVersion = 3;
|
||||
|
||||
export interface EditSession {
|
||||
version: number;
|
||||
machine?: string;
|
||||
folders: Folder[];
|
||||
state: { [key: string]: unknown };
|
||||
}
|
||||
|
||||
export const EDIT_SESSIONS_SIGNED_IN_KEY = 'editSessionsSignedIn';
|
||||
|
|
|
@ -42,6 +42,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IEditSessionIdentityService } from 'vs/platform/workspace/common/editSessions';
|
||||
|
||||
const folderName = 'test-folder';
|
||||
const folderUri = URI.file(`/${folderName}`);
|
||||
|
@ -131,6 +132,11 @@ suite('Edit session sync', () => {
|
|||
instantiationService.stub(IEditorService, new class extends mock<IEditorService>() {
|
||||
override saveAll = async (_options: ISaveAllEditorsOptions) => true;
|
||||
});
|
||||
instantiationService.stub(IEditSessionIdentityService, new class extends mock<IEditSessionIdentityService>() {
|
||||
override async getEditSessionIdentifier() {
|
||||
return 'test-identity';
|
||||
}
|
||||
});
|
||||
|
||||
editSessionsContribution = instantiationService.createInstance(EditSessionsContribution);
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue