提交 b089ff14 编写于 作者: S Sandeep Somavarapu

#86678 Enhance back up views

- Show individual resources
上级 47a52744
......@@ -52,7 +52,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, UserDataAutoSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { IElectronService } from 'vs/platform/electron/node/electron';
import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
......@@ -219,14 +219,6 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const authTokenChannel = new AuthenticationTokenServiceChannel(authTokenService);
server.registerChannel('authToken', authTokenChannel);
const userDataSyncStoreService = accessor.get(IUserDataSyncStoreService);
const userDataSyncStoreServiceChannel = new UserDataSyncStoreServiceChannel(userDataSyncStoreService);
server.registerChannel('userDataSyncStoreService', userDataSyncStoreServiceChannel);
const userDataSyncBackupStoreService = accessor.get(IUserDataSyncBackupStoreService);
const userDataSyncBackupStoreServiceChannel = new UserDataSyncBackupStoreServiceChannel(userDataSyncBackupStoreService);
server.registerChannel('userDataSyncBackupStoreService', userDataSyncBackupStoreServiceChannel);
const userDataSyncService = accessor.get(IUserDataSyncService);
const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService);
server.registerChannel('userDataSync', userDataSyncChannel);
......
......@@ -7,9 +7,9 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService, IFileContent, FileChangesEvent, FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
import { URI } from 'vs/base/common/uri';
import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncResource, SyncStatus, IUserData, IUserDataSyncStoreService, UserDataSyncErrorCode, UserDataSyncError, IUserDataSyncLogService, IUserDataSyncUtilService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, ISyncResourceHandle, USER_DATA_SYNC_SCHEME } from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { joinPath, dirname, isEqual } from 'vs/base/common/resources';
import { joinPath, dirname, isEqual, basename } from 'vs/base/common/resources';
import { CancelablePromise } from 'vs/base/common/async';
import { Emitter, Event } from 'vs/base/common/event';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
......@@ -173,19 +173,35 @@ export abstract class AbstractSynchroniser extends Disposable {
return !!lastSyncData;
}
async getConflictContent(conflictResource: URI): Promise<string | null> {
return null;
async getRemoteSyncResourceHandles(): Promise<ISyncResourceHandle[]> {
const handles = await this.userDataSyncStoreService.getAllRefs(this.resource);
return handles.map(({ created, ref }) => ({ created, uri: this.toRemoteBackupResource(ref) }));
}
async getRemoteContent(ref?: string): Promise<string | null> {
const refOrLastSyncUserData: string | IRemoteUserData | null = ref || await this.getLastSyncUserData();
const { content } = await this.getUserData(refOrLastSyncUserData);
return content;
async getLocalSyncResourceHandles(): Promise<ISyncResourceHandle[]> {
const handles = await this.userDataSyncBackupStoreService.getAllRefs(this.resource);
return handles.map(({ created, ref }) => ({ created, uri: this.toLocalBackupResource(ref) }));
}
private toRemoteBackupResource(ref: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote-backup', path: `/${this.resource}/${ref}` });
}
async getLocalBackupContent(ref?: string): Promise<string | null> {
private toLocalBackupResource(ref: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${this.resource}/${ref}` });
}
async resolveContent(uri: URI): Promise<string | null> {
const ref = basename(uri);
if (isEqual(uri, this.toRemoteBackupResource(ref))) {
const { content } = await this.getUserData(ref);
return content;
}
if (isEqual(uri, this.toLocalBackupResource(ref))) {
return this.userDataSyncBackupStoreService.resolveContent(this.resource, ref);
}
return null;
}
async resetLocal(): Promise<void> {
try {
......@@ -265,9 +281,10 @@ export abstract class AbstractSynchroniser extends Disposable {
return this.userDataSyncBackupStoreService.backup(this.resource, JSON.stringify(syncData));
}
abstract stop(): Promise<void>;
protected abstract readonly version: number;
protected abstract performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus>;
abstract stop(): Promise<void>;
}
export interface IFileSyncPreviewResult {
......@@ -310,7 +327,7 @@ export abstract class AbstractFileSynchroniser extends AbstractSynchroniser {
this.setStatus(SyncStatus.Idle);
}
async getConflictContent(conflictResource: URI): Promise<string | null> {
protected async getConflictContent(conflictResource: URI): Promise<string | null> {
if (isEqual(this.remotePreviewResource, conflictResource) || isEqual(this.localPreviewResource, conflictResource)) {
if (this.syncPreviewResultPromise) {
const result = await this.syncPreviewResultPromise;
......
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, ISyncExtension, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement';
......@@ -16,6 +16,9 @@ import { isNonEmptyArray } from 'vs/base/common/arrays';
import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { URI } from 'vs/base/common/uri';
import { joinPath, dirname, basename } from 'vs/base/common/resources';
import { format } from 'vs/base/common/jsonFormatter';
import { applyEdits } from 'vs/base/common/jsonEdit';
interface ISyncPreviewResult {
readonly localExtensions: ISyncExtension[];
......@@ -120,28 +123,24 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
async stop(): Promise<void> { }
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
const content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
return [{ resource: joinPath(uri, 'extensions.json') }];
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
async resolveContent(uri: URI): Promise<string | null> {
let content = await super.resolveContent(uri);
if (content) {
return content;
}
private getFragment(content: string, fragment: string): string | null {
content = await super.resolveContent(dirname(uri));
if (content) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'extensions':
return syncData.content;
switch (basename(uri)) {
case 'extensions.json':
const edits = format(syncData.content, undefined, {});
return applyEdits(syncData.content, edits);
}
}
}
return null;
......
......@@ -3,11 +3,11 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IGlobalState, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { Event } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { dirname } from 'vs/base/common/resources';
import { dirname, joinPath, basename } from 'vs/base/common/resources';
import { IFileService } from 'vs/platform/files/common/files';
import { IStringDictionary } from 'vs/base/common/collections';
import { edit } from 'vs/platform/userDataSync/common/content';
......@@ -17,6 +17,8 @@ import { AbstractSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { URI } from 'vs/base/common/uri';
import { format } from 'vs/base/common/jsonFormatter';
import { applyEdits } from 'vs/base/common/jsonEdit';
const argvProperties: string[] = ['locale'];
......@@ -105,28 +107,24 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
async stop(): Promise<void> { }
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
return [{ resource: joinPath(uri, 'globalState.json') }];
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
async resolveContent(uri: URI): Promise<string | null> {
let content = await super.resolveContent(uri);
if (content) {
return content;
}
private getFragment(content: string, fragment: string): string | null {
content = await super.resolveContent(dirname(uri));
if (content) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'globalState':
return syncData.content;
switch (basename(uri)) {
case 'globalState.json':
const edits = format(syncData.content, undefined, {});
return applyEdits(syncData.content, edits);
}
}
}
return null;
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, SyncResource, IUserDataSynchroniser, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
......@@ -19,7 +19,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays';
import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData } from 'vs/platform/userDataSync/common/abstractSynchronizer';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { URI } from 'vs/base/common/uri';
import { joinPath, isEqual } from 'vs/base/common/resources';
import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources';
interface ISyncContent {
mac?: string;
......@@ -160,38 +160,36 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
return false;
}
async getConflictContent(conflictResource: URI): Promise<string | null> {
const content = await super.getConflictContent(conflictResource);
return content !== null ? this.getKeybindingsContentFromSyncContent(content) : null;
}
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
const content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
return [{ resource: joinPath(uri, 'keybindings.json'), comparableResource: this.file }];
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
async resolveContent(uri: URI): Promise<string | null> {
if (isEqual(this.remotePreviewResource, uri)) {
return this.getConflictContent(uri);
}
let content = await super.resolveContent(uri);
if (content) {
return content;
}
private getFragment(content: string, fragment: string): string | null {
content = await super.resolveContent(dirname(uri));
if (content) {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'keybindings':
switch (basename(uri)) {
case 'keybindings.json':
return this.getKeybindingsContentFromSyncContent(syncData.content);
}
}
}
return null;
}
protected async getConflictContent(conflictResource: URI): Promise<string | null> {
const content = await super.getConflictContent(conflictResource);
return content !== null ? this.getKeybindingsContentFromSyncContent(content) : null;
}
protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus> {
try {
const result = await this.getPreview(remoteUserData, lastSyncUserData);
......
......@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IFileService, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, CONFIGURATION_SYNC_STORE_KEY, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { parse } from 'vs/base/common/json';
import { localize } from 'vs/nls';
......@@ -20,7 +20,7 @@ import { IFileSyncPreviewResult, AbstractJsonFileSynchroniser, IRemoteUserData }
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { URI } from 'vs/base/common/uri';
import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { joinPath, isEqual } from 'vs/base/common/resources';
import { joinPath, isEqual, dirname, basename } from 'vs/base/common/resources';
export interface ISettingsSyncContent {
settings: string;
......@@ -173,51 +173,49 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser {
return false;
}
async getConflictContent(conflictResource: URI): Promise<string | null> {
let content = await super.getConflictContent(conflictResource);
if (content !== null) {
const settingsSyncContent = this.parseSettingsSyncContent(content);
content = settingsSyncContent ? settingsSyncContent.settings : null;
}
if (content !== null) {
const formatUtils = await this.getFormattingOptions();
// remove ignored settings from the remote content for preview
const ignoredSettings = await this.getIgnoredSettings();
content = updateIgnoredSettings(content, '{}', ignoredSettings, formatUtils);
}
return content;
}
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
return [{ resource: joinPath(uri, 'settings.json'), comparableResource: this.file }];
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
async resolveContent(uri: URI): Promise<string | null> {
if (isEqual(this.remotePreviewResource, uri)) {
return this.getConflictContent(uri);
}
let content = await super.resolveContent(uri);
if (content) {
return content;
}
private getFragment(content: string, fragment: string): string | null {
content = await super.resolveContent(dirname(uri));
if (content) {
const syncData = this.parseSyncData(content);
if (syncData) {
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content);
if (settingsSyncContent) {
switch (fragment) {
case 'settings':
switch (basename(uri)) {
case 'settings.json':
return settingsSyncContent.settings;
}
}
}
}
return null;
}
protected async getConflictContent(conflictResource: URI): Promise<string | null> {
let content = await super.getConflictContent(conflictResource);
if (content !== null) {
const settingsSyncContent = this.parseSettingsSyncContent(content);
content = settingsSyncContent ? settingsSyncContent.settings : null;
}
if (content !== null) {
const formatUtils = await this.getFormattingOptions();
// remove ignored settings from the remote content for preview
const ignoredSettings = await this.getIgnoredSettings();
content = updateIgnoredSettings(content, '{}', ignoredSettings, formatUtils);
}
return content;
}
async acceptConflict(conflict: URI, content: string): Promise<void> {
if (this.status === SyncStatus.HasConflicts
&& (isEqual(this.localPreviewResource, conflict) || isEqual(this.remotePreviewResource, conflict))
......
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, UserDataSyncError, UserDataSyncErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSynchroniser, SyncResource, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService, Conflict, USER_DATA_SYNC_SCHEME, PREVIEW_DIR_NAME, UserDataSyncError, UserDataSyncErrorCode, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IFileService, FileChangesEvent, IFileStat, IFileContent, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
......@@ -11,7 +11,7 @@ import { AbstractSynchroniser, IRemoteUserData, ISyncData } from 'vs/platform/us
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStringDictionary } from 'vs/base/common/collections';
import { URI } from 'vs/base/common/uri';
import { joinPath, extname, relativePath, isEqualOrParent, isEqual, basename } from 'vs/base/common/resources';
import { joinPath, extname, relativePath, isEqualOrParent, isEqual, basename, dirname } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { merge } from 'vs/platform/userDataSync/common/snippetsMerge';
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
......@@ -148,49 +148,56 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD
this.setStatus(SyncStatus.Idle);
}
async getConflictContent(conflictResource: URI): Promise<string | null> {
if (isEqualOrParent(conflictResource.with({ scheme: this.syncFolder.scheme }), this.snippetsPreviewFolder) && this.syncPreviewResultPromise) {
const result = await this.syncPreviewResultPromise;
const key = relativePath(this.snippetsPreviewFolder, conflictResource.with({ scheme: this.snippetsPreviewFolder.scheme }))!;
if (conflictResource.scheme === this.snippetsPreviewFolder.scheme) {
return result.local[key] ? result.local[key].value.toString() : null;
} else if (result.remoteUserData && result.remoteUserData.syncData) {
const snippets = this.parseSnippets(result.remoteUserData.syncData);
return snippets[key] || null;
}
async getAssociatedResources({ uri }: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
let content = await super.resolveContent(uri);
if (content) {
const syncData = this.parseSyncData(content);
if (syncData) {
const snippets = this.parseSnippets(syncData);
const result = [];
for (const snippet of Object.keys(snippets)) {
const resource = joinPath(uri, snippet);
const comparableResource = joinPath(this.snippetsFolder, snippet);
const exists = await this.fileService.exists(comparableResource);
result.push({ resource, comparableResource: exists ? comparableResource : undefined });
}
return null;
return result;
}
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
const content = await super.getRemoteContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
return [];
}
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
async resolveContent(uri: URI): Promise<string | null> {
if (isEqualOrParent(uri.with({ scheme: this.syncFolder.scheme }), this.snippetsPreviewFolder)) {
return this.getConflictContent(uri);
}
let content = await super.resolveContent(uri);
if (content) {
return content;
}
private getFragment(content: string, fragment: string): string | null {
content = await super.resolveContent(dirname(uri));
if (content) {
const syncData = this.parseSyncData(content);
return syncData ? this.getFragmentFromSyncData(syncData, fragment) : null;
if (syncData) {
const snippets = this.parseSnippets(syncData);
return snippets[basename(uri)] || null;
}
}
return null;
}
private getFragmentFromSyncData(syncData: ISyncData, fragment: string): string | null {
switch (fragment) {
case 'snippets':
return syncData.content;
default:
const remoteSnippets = this.parseSnippets(syncData);
return remoteSnippets[fragment] || null;
protected async getConflictContent(conflictResource: URI): Promise<string | null> {
if (this.syncPreviewResultPromise) {
const result = await this.syncPreviewResultPromise;
const key = relativePath(this.snippetsPreviewFolder, conflictResource.with({ scheme: this.snippetsPreviewFolder.scheme }))!;
if (conflictResource.scheme === this.snippetsPreviewFolder.scheme) {
return result.local[key] ? result.local[key].value.toString() : null;
} else if (result.remoteUserData && result.remoteUserData.syncData) {
const snippets = this.parseSnippets(result.remoteUserData.syncData);
return snippets[key] || null;
}
}
return null;
}
async acceptConflict(conflictResource: URI, content: string): Promise<void> {
......
......@@ -18,7 +18,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { URI } from 'vs/base/common/uri';
import { joinPath, dirname, basename, isEqualOrParent } from 'vs/base/common/resources';
import { joinPath, isEqualOrParent } from 'vs/base/common/resources';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IProductService } from 'vs/platform/product/common/productService';
import { distinct } from 'vs/base/common/arrays';
......@@ -243,6 +243,11 @@ export const enum SyncStatus {
HasConflicts = 'hasConflicts',
}
export interface ISyncResourceHandle {
created: number;
uri: URI;
}
export type Conflict = { remote: URI, local: URI };
export interface IUserDataSynchroniser {
......@@ -263,11 +268,12 @@ export interface IUserDataSynchroniser {
hasLocalData(): Promise<boolean>;
resetLocal(): Promise<void>;
getConflictContent(conflictResource: URI): Promise<string | null>;
resolveContent(resource: URI): Promise<string | null>;
acceptConflict(conflictResource: URI, content: string): Promise<void>;
getRemoteContent(ref?: string, fragment?: string): Promise<string | null>;
getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null>;
getRemoteSyncResourceHandles(): Promise<ISyncResourceHandle[]>;
getLocalSyncResourceHandles(): Promise<ISyncResourceHandle[]>;
getAssociatedResources(syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]>;
}
//#endregion
......@@ -315,6 +321,10 @@ export interface IUserDataSyncService {
isFirstTimeSyncWithMerge(): Promise<boolean>;
resolveContent(resource: URI): Promise<string | null>;
acceptConflict(conflictResource: URI, content: string): Promise<void>;
getLocalSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]>;
getRemoteSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]>;
getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]>;
}
export const IUserDataAutoSyncService = createDecorator<IUserDataAutoSyncService>('IUserDataAutoSyncService');
......@@ -347,25 +357,6 @@ export const USER_DATA_SYNC_SCHEME = 'vscode-userdata-sync';
export const CONTEXT_SYNC_STATE = new RawContextKey<string>('syncStatus', SyncStatus.Uninitialized);
export const CONTEXT_SYNC_ENABLEMENT = new RawContextKey<boolean>('syncEnabled', false);
export function toRemoteBackupSyncResource(resource: SyncResource, ref?: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote-backup', path: `/${resource}/${ref ? ref : 'latest'}` });
}
export function toLocalBackupSyncResource(resource: SyncResource, ref?: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${resource}/${ref ? ref : 'latest'}` });
}
export function resolveBackupSyncResource(resource: URI): { remote: boolean, resource: SyncResource, path: string } | null {
if (resource.scheme === USER_DATA_SYNC_SCHEME
&& resource.authority === 'remote-backup' || resource.authority === 'local-backup') {
const resourceKey: SyncResource = basename(dirname(resource)) as SyncResource;
const path = resource.path.substring(resourceKey.length + 1);
if (resourceKey && path) {
const remote = resource.authority === 'remote-backup';
return { remote, resource: resourceKey, path };
}
}
return null;
}
export const PREVIEW_DIR_NAME = 'preview';
export function getSyncResourceFromLocalPreview(localPreview: URI, environmentService: IEnvironmentService): SyncResource | undefined {
if (localPreview.scheme === USER_DATA_SYNC_SCHEME) {
......
......@@ -5,7 +5,7 @@
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event';
import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataSync';
import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
......@@ -28,14 +28,17 @@ export class UserDataSyncChannel implements IServerChannel {
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case '_getInitialData': return Promise.resolve([this.service.status, this.service.conflicts, this.service.lastSyncTime]);
case 'sync': return this.service.sync();
case 'acceptConflict': return this.service.acceptConflict(URI.revive(args[0]), args[1]);
case 'pull': return this.service.pull();
case 'sync': return this.service.sync();
case 'stop': this.service.stop(); return Promise.resolve();
case 'reset': return this.service.reset();
case 'resetLocal': return this.service.resetLocal();
case 'resolveContent': return this.service.resolveContent(URI.revive(args[0]));
case 'isFirstTimeSyncWithMerge': return this.service.isFirstTimeSyncWithMerge();
case 'acceptConflict': return this.service.acceptConflict(URI.revive(args[0]), args[1]);
case 'resolveContent': return this.service.resolveContent(URI.revive(args[0]));
case 'getLocalSyncResourceHandles': return this.service.getLocalSyncResourceHandles(args[0]);
case 'getRemoteSyncResourceHandles': return this.service.getRemoteSyncResourceHandles(args[0]);
case 'getAssociatedResources': return this.service.getAssociatedResources(args[0], { created: args[1].created, uri: URI.revive(args[1].uri) });
}
throw new Error('Invalid call');
}
......@@ -98,38 +101,3 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService {
}
}
export class UserDataSyncStoreServiceChannel implements IServerChannel {
constructor(private readonly service: IUserDataSyncStoreService) { }
listen(_: unknown, event: string): Event<any> {
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'getAllRefs': return this.service.getAllRefs(args[0]);
case 'resolveContent': return this.service.resolveContent(args[0], args[1]);
case 'delete': return this.service.delete(args[0]);
}
throw new Error('Invalid call');
}
}
export class UserDataSyncBackupStoreServiceChannel implements IServerChannel {
constructor(private readonly service: IUserDataSyncBackupStoreService) { }
listen(_: unknown, event: string): Event<any> {
throw new Error(`Event not found: ${event}`);
}
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case 'getAllRefs': return this.service.getAllRefs(args[0]);
case 'resolveContent': return this.service.resolveContent(args[0], args[1]);
}
throw new Error('Invalid call');
}
}
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, resolveBackupSyncResource, SyncResourceConflicts } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, SyncStatus, IUserDataSyncStoreService, SyncResource, IUserDataSyncLogService, IUserDataSynchroniser, UserDataSyncStoreError, UserDataSyncErrorCode, UserDataSyncError, SyncResourceConflicts, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Emitter, Event } from 'vs/base/common/event';
......@@ -188,23 +188,25 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
}
async resolveContent(resource: URI): Promise<string | null> {
const result = resolveBackupSyncResource(resource);
if (result) {
const synchronizer = this.synchronisers.filter(s => s.resource === result.resource)[0];
if (synchronizer) {
const ref = result.path !== 'latest' ? result.path : undefined;
return result.remote ? synchronizer.getRemoteContent(ref, resource.fragment) : synchronizer.getLocalBackupContent(ref, resource.fragment);
for (const synchroniser of this.synchronisers) {
const content = await synchroniser.resolveContent(resource);
if (content) {
return content;
}
}
return null;
}
for (const synchronizer of this.synchronisers) {
const content = await synchronizer.getConflictContent(resource);
if (content !== null) {
return content;
getRemoteSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> {
return this.getSynchroniser(resource).getRemoteSyncResourceHandles();
}
getLocalSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> {
return this.getSynchroniser(resource).getLocalSyncResourceHandles();
}
return null;
getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
return this.getSynchroniser(resource).getAssociatedResources(syncResourceHandle);
}
async isFirstTimeSyncWithMerge(): Promise<boolean> {
......
......@@ -750,14 +750,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
private getConflictsEditorInputs(syncResource: SyncResource): DiffEditorInput[] {
return this.editorService.editors.filter(input => {
const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource;
return getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) === syncResource;
return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) === syncResource;
}) as DiffEditorInput[];
}
private getAllConflictsEditorInputs(): IEditorInput[] {
return this.editorService.editors.filter(input => {
const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource;
return getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) !== undefined;
return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) !== undefined;
});
}
......
......@@ -10,13 +10,12 @@ import { localize } from 'vs/nls';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { TreeViewPane, TreeView } from 'vs/workbench/browser/parts/views/treeView';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ALL_SYNC_RESOURCES, CONTEXT_SYNC_ENABLEMENT, IUserDataSyncStoreService, toRemoteBackupSyncResource, resolveBackupSyncResource, IUserDataSyncBackupStoreService, IResourceRefHandle, toLocalBackupSyncResource, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { ALL_SYNC_RESOURCES, CONTEXT_SYNC_ENABLEMENT, SyncResource, IUserDataSyncService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions';
import { IContextKeyService, RawContextKey, ContextKeyExpr, ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
import { URI } from 'vs/base/common/uri';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { FolderThemeIcon, FileThemeIcon } from 'vs/platform/theme/common/themeService';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { FolderThemeIcon } from 'vs/platform/theme/common/themeService';
import { fromNow } from 'vs/base/common/date';
import { pad } from 'vs/base/common/strings';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
......@@ -26,8 +25,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
@IUserDataSyncBackupStoreService private readonly userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
) {
const container = this.registerSyncViewContainer();
this.registerBackupView(container, true);
......@@ -59,9 +57,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
const disposable = treeView.onDidChangeVisibility(visible => {
if (visible && !treeView.dataProvider) {
disposable.dispose();
treeView.dataProvider = this.instantiationService.createInstance(UserDataSyncHistoryViewDataProvider, id,
(resource: SyncResource) => remote ? this.userDataSyncStoreService.getAllRefs(resource) : this.userDataSyncBackupStoreService.getAllRefs(resource),
(resource: SyncResource, ref: string) => remote ? toRemoteBackupSyncResource(resource, ref) : toLocalBackupSyncResource(resource, ref));
treeView.dataProvider = new UserDataSyncHistoryViewDataProvider(remote, this.userDataSyncService);
}
});
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
......@@ -104,28 +100,11 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
registerAction2(class extends Action2 {
constructor() {
super({
id: `workbench.actions.sync.${viewId}.resolveResourceRef`,
title: localize('workbench.actions.sync.resolveResourceRef', "Resolve Resource Ref"),
});
}
async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise<void> {
const editorService = accessor.get(IEditorService);
let resource = URI.parse(handle.$treeItemHandle);
const result = resolveBackupSyncResource(resource);
if (result) {
resource = resource.with({ fragment: result.resource });
await editorService.openEditor({ resource });
}
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: `workbench.actions.sync.${viewId}.resolveResourceRefCompletely`,
title: localize('workbench.actions.sync.resolveResourceRefCompletely', "Show full content"),
id: `workbench.actions.sync.resolveResource`,
title: localize('workbench.actions.sync.resolveResourceRef', "Show full content"),
menu: {
id: MenuId.ViewItemContext,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /syncref-.*/i))
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i))
},
});
}
......@@ -134,34 +113,28 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
await editorService.openEditor({ resource: URI.parse(handle.$treeItemHandle) });
}
});
registerAction2(class extends Action2 {
constructor() {
super({
id: `workbench.actions.${viewId}.commpareWithLocal`,
title: localize('workbench.action.deleteRef', "Open Changes"),
menu: {
id: MenuId.ViewItemContext,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /syncref-(settings|keybindings).*/i))
},
id: `workbench.actions.sync.commpareWithLocal`,
title: localize('workbench.actions.sync.commpareWithLocal', "Open Changes"),
});
}
async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise<void> {
const editorService = accessor.get(IEditorService);
const environmentService = accessor.get(IEnvironmentService);
const resource = URI.parse(handle.$treeItemHandle);
const result = resolveBackupSyncResource(resource);
if (result) {
const leftResource: URI = resource.with({ fragment: result.resource });
const rightResource: URI = result.resource === 'settings' ? environmentService.settingsResource : environmentService.keybindingsResource;
const { resource, comparableResource } = <{ resource: string, comparableResource?: string }>JSON.parse(handle.$treeItemHandle);
if (comparableResource) {
await editorService.openEditor({
leftResource,
rightResource,
leftResource: URI.parse(resource),
rightResource: URI.parse(comparableResource),
options: {
preserveFocus: false,
pinned: true,
preserveFocus: true,
revealIfVisible: true,
},
});
} else {
await editorService.openEditor({ resource: URI.parse(resource) });
}
}
});
......@@ -169,48 +142,55 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
}
interface SyncResourceTreeItem extends ITreeItem {
resource: SyncResource;
resourceHandle: ISyncResourceHandle;
}
class UserDataSyncHistoryViewDataProvider implements ITreeViewDataProvider {
constructor(
private readonly viewId: string,
private getAllRefs: (resource: SyncResource) => Promise<IResourceRefHandle[]>,
private toResource: (resource: SyncResource, ref: string) => URI
) {
}
constructor(private readonly remote: boolean, private userDataSyncService: IUserDataSyncService) { }
async getChildren(element?: ITreeItem): Promise<ITreeItem[]> {
if (element) {
return this.getResources(element.handle);
}
if (!element) {
return ALL_SYNC_RESOURCES.map(resourceKey => ({
handle: resourceKey,
collapsibleState: TreeItemCollapsibleState.Collapsed,
label: { label: resourceKey },
themeIcon: FolderThemeIcon,
contextValue: `sync-${resourceKey}`
}));
}
private async getResources(handle: string): Promise<ITreeItem[]> {
const resourceKey = ALL_SYNC_RESOURCES.filter(key => key === handle)[0];
const resourceKey = ALL_SYNC_RESOURCES.filter(key => key === element.handle)[0] as SyncResource;
if (resourceKey) {
const refHandles = await this.getAllRefs(resourceKey);
return refHandles.map(({ ref, created }) => {
const handle = this.toResource(resourceKey, ref).toString();
const refHandles = this.remote ? await this.userDataSyncService.getRemoteSyncResourceHandles(resourceKey) : await this.userDataSyncService.getLocalSyncResourceHandles(resourceKey);
return refHandles.map(({ uri, created }) => {
return <SyncResourceTreeItem>{
handle: uri.toString(),
collapsibleState: TreeItemCollapsibleState.Collapsed,
label: { label: label(new Date(created)) },
description: fromNow(created, true),
resourceUri: uri,
resource: resourceKey,
resourceHandle: { uri, created },
contextValue: `sync-resource-${resourceKey}`
};
});
}
if ((<SyncResourceTreeItem>element).resourceHandle) {
const associatedResources = await this.userDataSyncService.getAssociatedResources((<SyncResourceTreeItem>element).resource, (<SyncResourceTreeItem>element).resourceHandle);
return associatedResources.map(({ resource, comparableResource }) => {
const handle = JSON.stringify({ resource: resource.toString(), comparableResource: comparableResource?.toString() });
return {
handle,
collapsibleState: TreeItemCollapsibleState.None,
label: { label: label(new Date(created)) },
description: fromNow(created, true),
command: { id: `workbench.actions.sync.${this.viewId}.resolveResourceRef`, title: '', arguments: [<TreeViewItemHandleArg>{ $treeItemHandle: handle, $treeViewId: '' }] },
themeIcon: FileThemeIcon,
contextValue: `syncref-${resourceKey}`
resourceUri: resource,
command: { id: `workbench.actions.sync.commpareWithLocal`, title: '', arguments: [<TreeViewItemHandleArg>{ $treeViewId: '', $treeItemHandle: handle }] },
contextValue: `sync-associatedResource-${(<SyncResourceTreeItem>element).resource}`
};
});
}
return [];
}
}
function label(date: Date): string {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IResourceRefHandle, IUserDataSyncBackupStoreService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
export class UserDataSyncBackupStoreService implements IUserDataSyncBackupStoreService {
_serviceBrand: undefined;
private readonly channel: IChannel;
constructor(
@ISharedProcessService sharedProcessService: ISharedProcessService,
) {
this.channel = sharedProcessService.getChannel('userDataSyncBackupStoreService');
}
backup(key: SyncResource, content: string): Promise<void> {
return this.channel.call('backup', [key, content]);
}
getAllRefs(key: SyncResource): Promise<IResourceRefHandle[]> {
return this.channel.call('getAllRefs', [key]);
}
resolveContent(key: SyncResource, ref: string): Promise<string | null> {
return this.channel.call('resolveContent', [key, ref]);
}
}
registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService);
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncStatus, SyncResource, IUserDataSyncService, UserDataSyncError, SyncResourceConflicts } from 'vs/platform/userDataSync/common/userDataSync';
import { SyncStatus, SyncResource, IUserDataSyncService, UserDataSyncError, SyncResourceConflicts, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
......@@ -73,8 +73,8 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return this.channel.call('sync');
}
acceptConflict(conflict: URI, content: string): Promise<void> {
return this.channel.call('acceptConflict', [conflict, content]);
stop(): Promise<void> {
return this.channel.call('stop');
}
reset(): Promise<void> {
......@@ -85,16 +85,31 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
return this.channel.call('resetLocal');
}
stop(): Promise<void> {
return this.channel.call('stop');
isFirstTimeSyncWithMerge(): Promise<boolean> {
return this.channel.call('isFirstTimeSyncWithMerge');
}
acceptConflict(conflict: URI, content: string): Promise<void> {
return this.channel.call('acceptConflict', [conflict, content]);
}
resolveContent(resource: URI): Promise<string | null> {
return this.channel.call('resolveContent', [resource]);
}
isFirstTimeSyncWithMerge(): Promise<boolean> {
return this.channel.call('isFirstTimeSyncWithMerge');
async getLocalSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> {
const handles = await this.channel.call<ISyncResourceHandle[]>('getLocalSyncResourceHandles', [resource]);
return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) }));
}
async getRemoteSyncResourceHandles(resource: SyncResource): Promise<ISyncResourceHandle[]> {
const handles = await this.channel.call<ISyncResourceHandle[]>('getRemoteSyncResourceHandles', [resource]);
return handles.map(({ created, uri }) => ({ created, uri: URI.revive(uri) }));
}
async getAssociatedResources(resource: SyncResource, syncResourceHandle: ISyncResourceHandle): Promise<{ resource: URI, comparableResource?: URI }[]> {
const result = await this.channel.call<{ resource: URI, comparableResource?: URI }[]>('getAssociatedResources', [resource, syncResourceHandle]);
return result.map(({ resource, comparableResource }) => ({ resource: URI.revive(resource), comparableResource: URI.revive(comparableResource) }));
}
private async updateStatus(status: SyncStatus): Promise<void> {
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { SyncResource, IUserDataSyncStoreService, IUserDataSyncStore, getUserDataSyncStore, IUserData, IUserDataManifest, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProductService } from 'vs/platform/product/common/productService';
export class UserDataSyncStoreService implements IUserDataSyncStoreService {
_serviceBrand: undefined;
private readonly channel: IChannel;
readonly userDataSyncStore: IUserDataSyncStore | undefined;
constructor(
@ISharedProcessService sharedProcessService: ISharedProcessService,
@IProductService productService: IProductService,
@IConfigurationService configurationService: IConfigurationService
) {
this.channel = sharedProcessService.getChannel('userDataSyncStoreService');
this.userDataSyncStore = getUserDataSyncStore(productService, configurationService);
}
read(key: SyncResource, oldValue: IUserData | null, source?: SyncResource): Promise<IUserData> {
throw new Error('Not Supported');
}
write(key: SyncResource, content: string, ref: string | null, source?: SyncResource): Promise<string> {
throw new Error('Not Supported');
}
manifest(): Promise<IUserDataManifest | null> {
throw new Error('Not Supported');
}
clear(): Promise<void> {
throw new Error('Not Supported');
}
getAllRefs(key: SyncResource): Promise<IResourceRefHandle[]> {
return this.channel.call('getAllRefs', [key]);
}
resolveContent(key: SyncResource, ref: string): Promise<string | null> {
return this.channel.call('resolveContent', [key, ref]);
}
delete(key: SyncResource): Promise<void> {
return this.channel.call('delete', [key]);
}
}
registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService);
......@@ -51,8 +51,6 @@ import 'vs/workbench/services/workspaces/electron-browser/workspacesService';
import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncBackupStoreService';
import 'vs/workbench/services/authentication/electron-browser/authenticationTokenService';
import 'vs/workbench/services/authentication/browser/authenticationService';
import 'vs/workbench/services/host/electron-browser/desktopHostService';
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册