mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
Merge from vscode 718331d6f3ebd1b571530ab499edb266ddd493d5
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent, FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
|
||||
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, DEFAULT_IGNORED_SETTINGS, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserData, UserDataSyncError, UserDataSyncErrorCode, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService, IConflictSetting, ISettingsSyncService, CONFIGURATION_SYNC_STORE_KEY, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { parse, ParseError } from 'vs/base/common/json';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -12,22 +12,22 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { updateIgnoredSettings, merge } from 'vs/platform/userDataSync/common/settingsMerge';
|
||||
import { updateIgnoredSettings, merge, getIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { isEmptyObject, isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { edit } from 'vs/platform/userDataSync/common/content';
|
||||
import { AbstractFileSynchroniser } from 'vs/platform/userDataSync/common/abstractSynchronizer';
|
||||
|
||||
interface ISyncPreviewResult {
|
||||
readonly fileContent: IFileContent | null;
|
||||
readonly remoteUserData: IUserData;
|
||||
readonly lastSyncUserData: IUserData | null;
|
||||
readonly content: string | null;
|
||||
readonly hasLocalChanged: boolean;
|
||||
readonly hasRemoteChanged: boolean;
|
||||
readonly remoteContent: string | null;
|
||||
readonly hasConflicts: boolean;
|
||||
readonly conflictSettings: IConflictSetting[];
|
||||
}
|
||||
@@ -84,23 +84,23 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
this.logService.info('Settings: Started pulling settings...');
|
||||
this.setStatus(SyncStatus.Syncing);
|
||||
|
||||
const remoteUserData = await this.getRemoteUserData();
|
||||
const lastSyncUserData = await this.getLastSyncUserData();
|
||||
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
|
||||
|
||||
if (remoteUserData.content !== null) {
|
||||
const fileContent = await this.getLocalFileContent();
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
// Update ignored settings
|
||||
// Update ignored settings from local file content
|
||||
const content = updateIgnoredSettings(remoteUserData.content, fileContent ? fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils);
|
||||
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(content));
|
||||
|
||||
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<ISyncPreviewResult>({
|
||||
hasConflicts: false,
|
||||
conflictSettings: [],
|
||||
fileContent,
|
||||
remoteUserData,
|
||||
lastSyncUserData,
|
||||
content,
|
||||
hasLocalChanged: true,
|
||||
hasRemoteChanged: false,
|
||||
remoteContent: content,
|
||||
remoteUserData,
|
||||
hasConflicts: false,
|
||||
conflictSettings: [],
|
||||
}));
|
||||
|
||||
await this.apply();
|
||||
@@ -135,20 +135,21 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
// Remove ignored settings
|
||||
const content = updateIgnoredSettings(fileContent.value.toString(), '{}', getIgnoredSettings(this.configurationService), formatUtils);
|
||||
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(content));
|
||||
const remoteUserData = await this.getRemoteUserData();
|
||||
const lastSyncUserData = await this.getLastSyncUserData();
|
||||
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
|
||||
|
||||
this.syncPreviewResultPromise = createCancelablePromise(() => Promise.resolve<ISyncPreviewResult>({
|
||||
conflictSettings: [],
|
||||
hasConflicts: false,
|
||||
fileContent,
|
||||
hasLocalChanged: false,
|
||||
hasRemoteChanged: true,
|
||||
remoteContent: content,
|
||||
remoteUserData,
|
||||
lastSyncUserData,
|
||||
content,
|
||||
hasRemoteChanged: true,
|
||||
hasLocalChanged: false,
|
||||
hasConflicts: false,
|
||||
conflictSettings: [],
|
||||
}));
|
||||
|
||||
await this.apply(undefined, true);
|
||||
await this.apply(true);
|
||||
}
|
||||
|
||||
// No local exists to push
|
||||
@@ -182,7 +183,7 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
if (this.syncPreviewResultPromise) {
|
||||
this.syncPreviewResultPromise.cancel();
|
||||
this.syncPreviewResultPromise = null;
|
||||
this.logService.info('Settings: Stopped synchronizing settings.');
|
||||
this.logService.trace('Settings: Stopped synchronizing settings.');
|
||||
}
|
||||
await this.fileService.del(this.environmentService.settingsSyncPreviewResource);
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
@@ -207,15 +208,21 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
return false;
|
||||
}
|
||||
|
||||
async getRemoteContent(): Promise<string | null> {
|
||||
async getRemoteContent(preview?: boolean): Promise<string | null> {
|
||||
let content: string | null | undefined = null;
|
||||
if (this.syncPreviewResultPromise) {
|
||||
const preview = await this.syncPreviewResultPromise;
|
||||
content = preview.remoteUserData?.content;
|
||||
} else {
|
||||
const remoteUserData = await this.getRemoteUserData();
|
||||
const lastSyncData = await this.getLastSyncUserData();
|
||||
const remoteUserData = await this.getRemoteUserData(lastSyncData);
|
||||
content = remoteUserData.content;
|
||||
}
|
||||
if (preview && !isUndefinedOrNull(content)) {
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
// remove ignored settings from the remote content for preview
|
||||
content = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formatUtils);
|
||||
}
|
||||
return content !== undefined ? content : null;
|
||||
}
|
||||
|
||||
@@ -227,16 +234,20 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
}
|
||||
}
|
||||
|
||||
async resolveConflicts(content: string): Promise<void> {
|
||||
async accept(content: string): Promise<void> {
|
||||
if (this.status === SyncStatus.HasConflicts) {
|
||||
try {
|
||||
await this.apply(content, true);
|
||||
const preview = await this.syncPreviewResultPromise!;
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
// Add ignored settings from local file content
|
||||
content = updateIgnoredSettings(content, preview.fileContent ? preview.fileContent.value.toString() : '{}', getIgnoredSettings(this.configurationService), formatUtils);
|
||||
this.syncPreviewResultPromise = createCancelablePromise(async () => ({ ...preview, content }));
|
||||
await this.apply(true);
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
|
||||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
|
||||
throw new Error('New local version available.');
|
||||
throw new UserDataSyncError('Failed to resolve conflicts as there is a new local version available.', UserDataSyncErrorCode.NewLocal);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
@@ -270,32 +281,27 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.Rejected) {
|
||||
// Rejected as there is a new remote version. Syncing again,
|
||||
this.logService.info('Settings: Failed to synchronise settings as there is a new remote version available. Synchronizing again...');
|
||||
this.logService.info('Settings: Failed to synchronize settings as there is a new remote version available. Synchronizing again...');
|
||||
return this.sync();
|
||||
}
|
||||
if ((e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) ||
|
||||
(e instanceof FileOperationError && e.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE)) {
|
||||
// Rejected as there is a new local version. Syncing again.
|
||||
this.logService.info('Settings: Failed to synchronise settings as there is a new local version available. Synchronizing again...');
|
||||
this.logService.info('Settings: Failed to synchronize settings as there is a new local version available. Synchronizing again...');
|
||||
return this.sync();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private async apply(content?: string, forcePush?: boolean): Promise<void> {
|
||||
private async apply(forcePush?: boolean): Promise<void> {
|
||||
if (!this.syncPreviewResultPromise) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (content === undefined) {
|
||||
if (await this.fileService.exists(this.environmentService.settingsSyncPreviewResource)) {
|
||||
const settingsPreivew = await this.fileService.readFile(this.environmentService.settingsSyncPreviewResource);
|
||||
content = settingsPreivew.value.toString();
|
||||
}
|
||||
}
|
||||
let { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
|
||||
|
||||
if (content !== undefined) {
|
||||
if (content !== null) {
|
||||
|
||||
if (this.hasErrors(content)) {
|
||||
const error = new Error(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again."));
|
||||
@@ -303,25 +309,20 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
throw error;
|
||||
}
|
||||
|
||||
let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
|
||||
if (!hasLocalChanged && !hasRemoteChanged) {
|
||||
this.logService.trace('Settings: No changes found during synchronizing settings.');
|
||||
}
|
||||
if (hasLocalChanged) {
|
||||
this.logService.info('Settings: Updating local settings');
|
||||
this.logService.trace('Settings: Updating local settings...');
|
||||
await this.updateLocalFileContent(content, fileContent);
|
||||
this.logService.info('Settings: Updated local settings');
|
||||
}
|
||||
if (hasRemoteChanged) {
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
const remoteContent = updateIgnoredSettings(content, remoteUserData.content || '{}', getIgnoredSettings(this.configurationService, content), formatUtils);
|
||||
this.logService.info('Settings: Updating remote settings');
|
||||
const ref = await this.updateRemoteUserData(remoteContent, forcePush ? null : remoteUserData.ref);
|
||||
// Update ignored settings from remote
|
||||
content = updateIgnoredSettings(content, remoteUserData.content || '{}', getIgnoredSettings(this.configurationService, content), formatUtils);
|
||||
this.logService.trace('Settings: Updating remote settings...');
|
||||
const ref = await this.updateRemoteUserData(content, forcePush ? null : remoteUserData.ref);
|
||||
this.logService.info('Settings: Updated remote settings');
|
||||
remoteUserData = { ref, content };
|
||||
}
|
||||
if (remoteUserData.content) {
|
||||
this.logService.info('Settings: Updating last synchronised sttings');
|
||||
await this.updateLastSyncUserData(remoteUserData);
|
||||
}
|
||||
|
||||
// Delete the preview
|
||||
await this.fileService.del(this.environmentService.settingsSyncPreviewResource);
|
||||
@@ -329,6 +330,12 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
this.logService.trace('Settings: No changes found during synchronizing settings.');
|
||||
}
|
||||
|
||||
if (lastSyncUserData?.ref !== remoteUserData.ref) {
|
||||
this.logService.trace('Settings: Updating last synchronized settings...');
|
||||
await this.updateLastSyncUserData(remoteUserData);
|
||||
this.logService.info('Settings: Updated last synchronized settings');
|
||||
}
|
||||
|
||||
this.syncPreviewResultPromise = null;
|
||||
}
|
||||
|
||||
@@ -346,16 +353,17 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
}
|
||||
|
||||
private async generatePreview(resolvedConflicts: { key: string, value: any }[], token: CancellationToken): Promise<ISyncPreviewResult> {
|
||||
const lastSyncData = await this.getLastSyncUserData();
|
||||
const remoteUserData = await this.getRemoteUserData(lastSyncData);
|
||||
const lastSyncUserData = await this.getLastSyncUserData();
|
||||
const remoteUserData = await this.getRemoteUserData(lastSyncUserData);
|
||||
// Get file content last to get the latest
|
||||
const fileContent = await this.getLocalFileContent();
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
|
||||
let content: string | null = null;
|
||||
let hasLocalChanged: boolean = false;
|
||||
let hasRemoteChanged: boolean = false;
|
||||
let hasConflicts: boolean = false;
|
||||
let conflictSettings: IConflictSetting[] = [];
|
||||
let previewContent: string | null = null;
|
||||
let remoteContent: string | null = null;
|
||||
|
||||
if (remoteUserData.content) {
|
||||
const localContent: string = fileContent ? fileContent.value.toString() : '{}';
|
||||
@@ -367,31 +375,30 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
|
||||
else {
|
||||
this.logService.trace('Settings: Merging remote settings with local settings...');
|
||||
const formatUtils = await this.getFormattingOptions();
|
||||
const result = merge(localContent, remoteUserData.content, lastSyncData ? lastSyncData.content : null, getIgnoredSettings(this.configurationService), resolvedConflicts, formatUtils);
|
||||
hasConflicts = result.hasConflicts;
|
||||
const result = merge(localContent, remoteUserData.content, lastSyncUserData ? lastSyncUserData.content : null, getIgnoredSettings(this.configurationService), resolvedConflicts, formatUtils);
|
||||
content = result.localContent || result.remoteContent;
|
||||
hasLocalChanged = result.localContent !== null;
|
||||
hasRemoteChanged = result.remoteContent !== null;
|
||||
hasConflicts = result.hasConflicts;
|
||||
conflictSettings = result.conflictsSettings;
|
||||
remoteContent = result.remoteContent;
|
||||
previewContent = result.localContent || result.remoteContent;
|
||||
}
|
||||
}
|
||||
|
||||
// First time syncing to remote
|
||||
else if (fileContent) {
|
||||
this.logService.info('Settings: Remote settings does not exist. Synchronizing settings for the first time.');
|
||||
this.logService.trace('Settings: Remote settings does not exist. Synchronizing settings for the first time.');
|
||||
content = fileContent.value.toString();
|
||||
hasRemoteChanged = true;
|
||||
previewContent = fileContent.value.toString();
|
||||
remoteContent = fileContent.value.toString();
|
||||
}
|
||||
|
||||
if (previewContent && !token.isCancellationRequested) {
|
||||
if (content && !token.isCancellationRequested) {
|
||||
// Remove the ignored settings from the preview.
|
||||
const previewContent = updateIgnoredSettings(content, '{}', getIgnoredSettings(this.configurationService), formatUtils);
|
||||
await this.fileService.writeFile(this.environmentService.settingsSyncPreviewResource, VSBuffer.fromString(previewContent));
|
||||
}
|
||||
|
||||
this.setConflicts(conflictSettings);
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, remoteContent, conflictSettings, hasConflicts };
|
||||
return { fileContent, remoteUserData, lastSyncUserData, content, hasLocalChanged, hasRemoteChanged, conflictSettings, hasConflicts };
|
||||
}
|
||||
|
||||
private _formattingOptions: Promise<FormattingOptions> | undefined = undefined;
|
||||
@@ -403,26 +410,3 @@ export class SettingsSynchroniser extends AbstractFileSynchroniser implements IS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function getIgnoredSettings(configurationService: IConfigurationService, settingsContent?: string): string[] {
|
||||
let value: string[] = [];
|
||||
if (settingsContent) {
|
||||
const setting = parse(settingsContent);
|
||||
if (setting) {
|
||||
value = setting['sync.ignoredSettings'];
|
||||
}
|
||||
} else {
|
||||
value = configurationService.getValue<string[]>('sync.ignoredSettings');
|
||||
}
|
||||
const added: string[] = [], removed: string[] = [];
|
||||
if (Array.isArray(value)) {
|
||||
for (const key of value) {
|
||||
if (startsWith(key, '-')) {
|
||||
removed.push(key.substring(1));
|
||||
} else {
|
||||
added.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...DEFAULT_IGNORED_SETTINGS, ...added].filter(setting => removed.indexOf(setting) === -1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user