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

Implement getAllRefs and resolveContent for backup store

上级 7d17f6f9
...@@ -52,7 +52,7 @@ import { IProductService } from 'vs/platform/product/common/productService'; ...@@ -52,7 +52,7 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncService, IUserDataSyncStoreService, registerConfiguration, IUserDataSyncLogService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataSyncEnablementService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService';
import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { UserDataSyncChannel, UserDataSyncUtilServiceClient, SettingsSyncChannel, UserDataAutoSyncChannel, UserDataSyncStoreServiceChannel, UserDataSyncBackupStoreServiceChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc';
import { IElectronService } from 'vs/platform/electron/node/electron'; import { IElectronService } from 'vs/platform/electron/node/electron';
import { LoggerService } from 'vs/platform/log/node/loggerService'; import { LoggerService } from 'vs/platform/log/node/loggerService';
import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog';
...@@ -225,6 +225,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat ...@@ -225,6 +225,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat
const userDataSyncStoreServiceChannel = new UserDataSyncStoreServiceChannel(userDataSyncStoreService); const userDataSyncStoreServiceChannel = new UserDataSyncStoreServiceChannel(userDataSyncStoreService);
server.registerChannel('userDataSyncStoreService', userDataSyncStoreServiceChannel); server.registerChannel('userDataSyncStoreService', userDataSyncStoreServiceChannel);
const userDataSyncBackupStoreService = accessor.get(IUserDataSyncBackupStoreService);
const userDataSyncBackupStoreServiceChannel = new UserDataSyncBackupStoreServiceChannel(userDataSyncBackupStoreService);
server.registerChannel('userDataSyncBackupStoreService', userDataSyncBackupStoreServiceChannel);
const settingsSyncService = accessor.get(ISettingsSyncService); const settingsSyncService = accessor.get(ISettingsSyncService);
const settingsSyncChannel = new SettingsSyncChannel(settingsSyncService); const settingsSyncChannel = new SettingsSyncChannel(settingsSyncService);
server.registerChannel('settingsSync', settingsSyncChannel); server.registerChannel('settingsSync', settingsSyncChannel);
......
...@@ -162,6 +162,10 @@ export abstract class AbstractSynchroniser extends Disposable { ...@@ -162,6 +162,10 @@ export abstract class AbstractSynchroniser extends Disposable {
return content; return content;
} }
async getLocalBackupContent(ref?: string): Promise<string | null> {
return this.userDataSyncBackupStoreService.resolveContent(this.resourceKey, ref);
}
async resetLocal(): Promise<void> { async resetLocal(): Promise<void> {
try { try {
await this.fileService.del(this.lastSyncResource); await this.fileService.del(this.lastSyncResource);
......
...@@ -123,18 +123,30 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse ...@@ -123,18 +123,30 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> { async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
const content = await super.getRemoteContent(ref); const content = await super.getRemoteContent(ref);
if (content !== null && fragment) { if (content !== null && fragment) {
const syncData = this.parseSyncData(content); return this.getFragment(content, fragment);
if (syncData) {
switch (fragment) {
case 'extensions':
return syncData.content;
}
}
return null;
} }
return content; return content;
} }
async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getLocalBackupContent(ref);
if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
}
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'extensions':
return syncData.content;
}
}
return null;
}
accept(content: string): Promise<void> { accept(content: string): Promise<void> {
throw new Error('Extensions: Conflicts should not occur'); throw new Error('Extensions: Conflicts should not occur');
} }
......
...@@ -107,18 +107,30 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs ...@@ -107,18 +107,30 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> { async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getRemoteContent(ref); let content = await super.getRemoteContent(ref);
if (content !== null && fragment) { if (content !== null && fragment) {
const syncData = this.parseSyncData(content); return this.getFragment(content, fragment);
if (syncData) { }
switch (fragment) { return content;
case 'globalState': }
return syncData.content;
} async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
} let content = await super.getLocalBackupContent(ref);
return null; if (content !== null && fragment) {
return this.getFragment(content, fragment);
} }
return content; return content;
} }
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'globalState':
return syncData.content;
}
}
return null;
}
accept(content: string): Promise<void> { accept(content: string): Promise<void> {
throw new Error('UI State: Conflicts should not occur'); throw new Error('UI State: Conflicts should not occur');
} }
......
...@@ -164,18 +164,30 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem ...@@ -164,18 +164,30 @@ export class KeybindingsSynchroniser extends AbstractJsonFileSynchroniser implem
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> { async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
const content = await super.getRemoteContent(ref); const content = await super.getRemoteContent(ref);
if (content !== null && fragment) { if (content !== null && fragment) {
const syncData = this.parseSyncData(content); return this.getFragment(content, fragment);
if (syncData) { }
switch (fragment) { return content;
case 'keybindings': }
return this.getKeybindingsContentFromSyncContent(syncData.content);
} async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
} let content = await super.getLocalBackupContent(ref);
return null; if (content !== null && fragment) {
return this.getFragment(content, fragment);
} }
return content; return content;
} }
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
switch (fragment) {
case 'keybindings':
return this.getKeybindingsContentFromSyncContent(syncData.content);
}
}
return null;
}
protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus> { protected async performSync(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null): Promise<SyncStatus> {
try { try {
const result = await this.getPreview(remoteUserData, lastSyncUserData); const result = await this.getPreview(remoteUserData, lastSyncUserData);
......
...@@ -205,19 +205,31 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement ...@@ -205,19 +205,31 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement
async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> { async getRemoteContent(ref?: string, fragment?: string): Promise<string | null> {
let content = await super.getRemoteContent(ref); let content = await super.getRemoteContent(ref);
if (content !== null && fragment) { if (content !== null && fragment) {
const syncData = this.parseSyncData(content); return this.getFragment(content, fragment);
if (syncData) { }
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content); return content;
if (settingsSyncContent) { }
switch (fragment) {
case 'settings': async getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
return settingsSyncContent.settings; let content = await super.getLocalBackupContent(ref);
} if (content !== null && fragment) {
return this.getFragment(content, fragment);
}
return content;
}
private getFragment(content: string, fragment: string): string | null {
const syncData = this.parseSyncData(content);
if (syncData) {
const settingsSyncContent = this.parseSettingsSyncContent(syncData.content);
if (settingsSyncContent) {
switch (fragment) {
case 'settings':
return settingsSyncContent.settings;
} }
} }
return null;
} }
return content; return null;
} }
async accept(content: string): Promise<void> { async accept(content: string): Promise<void> {
......
...@@ -165,6 +165,8 @@ export const IUserDataSyncBackupStoreService = createDecorator<IUserDataSyncBack ...@@ -165,6 +165,8 @@ export const IUserDataSyncBackupStoreService = createDecorator<IUserDataSyncBack
export interface IUserDataSyncBackupStoreService { export interface IUserDataSyncBackupStoreService {
_serviceBrand: undefined; _serviceBrand: undefined;
backup(resourceKey: ResourceKey, content: string): Promise<void>; backup(resourceKey: ResourceKey, content: string): Promise<void>;
getAllRefs(key: ResourceKey): Promise<IResourceRefHandle[]>;
resolveContent(key: ResourceKey, ref?: string): Promise<string | null>;
} }
//#endregion //#endregion
...@@ -261,6 +263,7 @@ export interface IUserDataSynchroniser { ...@@ -261,6 +263,7 @@ export interface IUserDataSynchroniser {
getRemoteContentFromPreview(): Promise<string | null>; getRemoteContentFromPreview(): Promise<string | null>;
getRemoteContent(ref?: string, fragment?: string): Promise<string | null>; getRemoteContent(ref?: string, fragment?: string): Promise<string | null>;
getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null>;
accept(content: string): Promise<void>; accept(content: string): Promise<void>;
} }
...@@ -354,6 +357,9 @@ export function toRemoteSyncResourceFromSource(source: SyncSource, ref?: string) ...@@ -354,6 +357,9 @@ export function toRemoteSyncResourceFromSource(source: SyncSource, ref?: string)
export function toRemoteSyncResource(resourceKey: ResourceKey, ref?: string): URI { export function toRemoteSyncResource(resourceKey: ResourceKey, ref?: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote', path: `/${resourceKey}/${ref ? ref : 'latest'}` }); return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'remote', path: `/${resourceKey}/${ref ? ref : 'latest'}` });
} }
export function toLocalBackupSyncResource(resourceKey: ResourceKey, ref?: string): URI {
return URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'local-backup', path: `/${resourceKey}/${ref ? ref : 'latest'}` });
}
export function resolveSyncResource(resource: URI): { remote: boolean, resourceKey: ResourceKey, ref?: string } | null { export function resolveSyncResource(resource: URI): { remote: boolean, resourceKey: ResourceKey, ref?: string } | null {
const remote = resource.authority === 'remote'; const remote = resource.authority === 'remote';
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Disposable, } from 'vs/base/common/lifecycle'; import { Disposable, } from 'vs/base/common/lifecycle';
import { IUserDataSyncLogService, ResourceKey, ALL_RESOURCE_KEYS, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncLogService, ResourceKey, ALL_RESOURCE_KEYS, IUserDataSyncBackupStoreService, IResourceRefHandle } from 'vs/platform/userDataSync/common/userDataSync';
import { joinPath } from 'vs/base/common/resources'; import { joinPath } from 'vs/base/common/resources';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService, IFileStat } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { toLocalISOString } from 'vs/base/common/date'; import { toLocalISOString } from 'vs/base/common/date';
import { VSBuffer } from 'vs/base/common/buffer'; import { VSBuffer } from 'vs/base/common/buffer';
...@@ -26,6 +26,34 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD ...@@ -26,6 +26,34 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
ALL_RESOURCE_KEYS.forEach(resourceKey => this.cleanUpBackup(resourceKey)); ALL_RESOURCE_KEYS.forEach(resourceKey => this.cleanUpBackup(resourceKey));
} }
async getAllRefs(resourceKey: ResourceKey): Promise<IResourceRefHandle[]> {
const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
const stat = await this.fileService.resolve(folder);
if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort().reverse();
return all.map(stat => ({
ref: stat.name,
created: this.getCreationTime(stat)
}));
}
return [];
}
async resolveContent(resourceKey: ResourceKey, ref?: string): Promise<string | null> {
if (!ref) {
const refs = await this.getAllRefs(resourceKey);
if (refs.length) {
ref = refs[refs.length - 1].ref;
}
}
if (ref) {
const file = joinPath(this.environmentService.userDataSyncHome, resourceKey, ref);
const content = await this.fileService.readFile(file);
return content.value.toString();
}
return null;
}
async backup(resourceKey: ResourceKey, content: string): Promise<void> { async backup(resourceKey: ResourceKey, content: string): Promise<void> {
const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey); const folder = joinPath(this.environmentService.userDataSyncHome, resourceKey);
const resource = joinPath(folder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`); const resource = joinPath(folder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
...@@ -53,17 +81,7 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD ...@@ -53,17 +81,7 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
if (stat.children) { if (stat.children) {
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort(); const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort();
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */); const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
let toDelete = all.filter(stat => { let toDelete = all.filter(stat => Date.now() - this.getCreationTime(stat) > backUpMaxAge);
const ctime = stat.ctime || new Date(
parseInt(stat.name.substring(0, 4)),
parseInt(stat.name.substring(4, 6)) - 1,
parseInt(stat.name.substring(6, 8)),
parseInt(stat.name.substring(9, 11)),
parseInt(stat.name.substring(11, 13)),
parseInt(stat.name.substring(13, 15))
).getTime();
return Date.now() - ctime > backUpMaxAge;
});
const remaining = all.length - toDelete.length; const remaining = all.length - toDelete.length;
if (remaining < 10) { if (remaining < 10) {
toDelete = toDelete.slice(10 - remaining); toDelete = toDelete.slice(10 - remaining);
...@@ -77,4 +95,15 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD ...@@ -77,4 +95,15 @@ export class UserDataSyncBackupStoreService extends Disposable implements IUserD
this.logService.error(e); this.logService.error(e);
} }
} }
private getCreationTime(stat: IFileStat) {
return stat.ctime || new Date(
parseInt(stat.name.substring(0, 4)),
parseInt(stat.name.substring(4, 6)) - 1,
parseInt(stat.name.substring(6, 8)),
parseInt(stat.name.substring(9, 11)),
parseInt(stat.name.substring(11, 13)),
parseInt(stat.name.substring(13, 15))
).getTime();
}
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc';
import { Event } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync'; import { IUserDataSyncService, IUserDataSyncUtilService, ISettingsSyncService, IUserDataAutoSyncService, IUserDataSyncStoreService, IUserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSync';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections'; import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { FormattingOptions } from 'vs/base/common/jsonFormatter';
...@@ -69,6 +69,7 @@ export class SettingsSyncChannel implements IServerChannel { ...@@ -69,6 +69,7 @@ export class SettingsSyncChannel implements IServerChannel {
case 'resolveSettingsConflicts': return this.service.resolveSettingsConflicts(args[0]); case 'resolveSettingsConflicts': return this.service.resolveSettingsConflicts(args[0]);
case 'getRemoteContentFromPreview': return this.service.getRemoteContentFromPreview(); case 'getRemoteContentFromPreview': return this.service.getRemoteContentFromPreview();
case 'getRemoteContent': return this.service.getRemoteContent(args[0], args[1]); case 'getRemoteContent': return this.service.getRemoteContent(args[0], args[1]);
case 'getLocalBackupContent': return this.service.getLocalBackupContent(args[0], args[1]);
} }
throw new Error('Invalid call'); throw new Error('Invalid call');
} }
...@@ -149,3 +150,20 @@ export class UserDataSyncStoreServiceChannel implements IServerChannel { ...@@ -149,3 +150,20 @@ export class UserDataSyncStoreServiceChannel implements IServerChannel {
throw new Error('Invalid call'); 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');
}
}
...@@ -185,7 +185,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ ...@@ -185,7 +185,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
if (PREVIEW_QUERY === resource.query) { if (PREVIEW_QUERY === resource.query) {
return result.remote ? synchronizer.getRemoteContentFromPreview() : null; return result.remote ? synchronizer.getRemoteContentFromPreview() : null;
} }
return result.remote ? synchronizer.getRemoteContent(result.ref, resource.fragment) : null; return result.remote ? synchronizer.getRemoteContent(result.ref, resource.fragment) : synchronizer.getLocalBackupContent(result.ref, resource.fragment);
} }
} }
return null; return null;
......
...@@ -88,6 +88,10 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ ...@@ -88,6 +88,10 @@ export class SettingsSyncService extends Disposable implements ISettingsSyncServ
return this.channel.call('getRemoteContent', [ref, fragment]); return this.channel.call('getRemoteContent', [ref, fragment]);
} }
getLocalBackupContent(ref?: string, fragment?: string): Promise<string | null> {
return this.channel.call('getLocalBackupContent', [ref, fragment]);
}
getRemoteContentFromPreview(): Promise<string | null> { getRemoteContentFromPreview(): Promise<string | null> {
return this.channel.call('getRemoteContentFromPreview', []); return this.channel.call('getRemoteContentFromPreview', []);
} }
......
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ResourceKey, IResourceRefHandle, IUserDataSyncBackupStoreService } 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: ResourceKey, content: string): Promise<void> {
return this.channel.call('backup', [key, content]);
}
getAllRefs(key: ResourceKey): Promise<IResourceRefHandle[]> {
return this.channel.call('getAllRefs', [key]);
}
resolveContent(key: ResourceKey, ref: string): Promise<string | null> {
return this.channel.call('resolveContent', [key, ref]);
}
}
registerSingleton(IUserDataSyncBackupStoreService, UserDataSyncBackupStoreService);
...@@ -53,6 +53,7 @@ import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService' ...@@ -53,6 +53,7 @@ import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'
import 'vs/workbench/services/userDataSync/electron-browser/settingsSyncService'; import 'vs/workbench/services/userDataSync/electron-browser/settingsSyncService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataAutoSyncService';
import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncStoreService'; 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/electron-browser/authenticationTokenService';
import 'vs/workbench/services/authentication/browser/authenticationService'; import 'vs/workbench/services/authentication/browser/authenticationService';
import 'vs/workbench/services/host/electron-browser/desktopHostService'; 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.
先完成此消息的编辑!
想要评论请 注册