mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-06 01:25:38 -05:00
Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127 (#9385)
* Merge from vscode e5834d3280fcd04898efeac32b9cf1b893f9b127 * distro
This commit is contained in:
@@ -112,7 +112,7 @@ export class EnvironmentService implements IEnvironmentService {
|
||||
get settingsResource(): URI { return resources.joinPath(this.userRoamingDataHome, 'settings.json'); }
|
||||
|
||||
@memoize
|
||||
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, '.sync'); }
|
||||
get userDataSyncHome(): URI { return resources.joinPath(this.userRoamingDataHome, 'sync'); }
|
||||
|
||||
@memoize
|
||||
get settingsSyncPreviewResource(): URI { return resources.joinPath(this.userDataSyncHome, 'settings.json'); }
|
||||
|
||||
@@ -101,6 +101,12 @@ export interface INotification extends INotificationProperties {
|
||||
* this usecase and much easier to use!
|
||||
*/
|
||||
actions?: INotificationActions;
|
||||
|
||||
/**
|
||||
* The initial set of progress properties for the notification. To update progress
|
||||
* later on, access the `INotificationHandle.progress` property.
|
||||
*/
|
||||
progress?: INotificationProgressProperties;
|
||||
}
|
||||
|
||||
export interface INotificationActions {
|
||||
@@ -119,6 +125,24 @@ export interface INotificationActions {
|
||||
secondary?: ReadonlyArray<IAction>;
|
||||
}
|
||||
|
||||
export interface INotificationProgressProperties {
|
||||
|
||||
/**
|
||||
* Causes the progress bar to spin infinitley.
|
||||
*/
|
||||
infinite?: boolean;
|
||||
|
||||
/**
|
||||
* Indicate the total amount of work.
|
||||
*/
|
||||
total?: number;
|
||||
|
||||
/**
|
||||
* Indicate that a specific chunk of work is done.
|
||||
*/
|
||||
worked?: number;
|
||||
}
|
||||
|
||||
export interface INotificationProgress {
|
||||
|
||||
/**
|
||||
@@ -149,6 +173,13 @@ export interface INotificationHandle {
|
||||
*/
|
||||
readonly onDidClose: Event<void>;
|
||||
|
||||
/**
|
||||
* Will be fired whenever the visibility of the notification changes.
|
||||
* A notification can either be visible as toast or inside the notification
|
||||
* center if it is visible.
|
||||
*/
|
||||
readonly onDidChangeVisibility: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Allows to indicate progress on the notification even after the
|
||||
* notification is already visible.
|
||||
|
||||
@@ -240,9 +240,36 @@ export class FileStorageDatabase extends Disposable implements IStorageDatabase
|
||||
private async onDidStorageChangeExternal(): Promise<void> {
|
||||
const items = await this.doGetItemsFromFile();
|
||||
|
||||
// pervious cache, diff for changes
|
||||
let changed = new Map<string, string>();
|
||||
let deleted = new Set<string>();
|
||||
if (this.cache) {
|
||||
items.forEach((value, key) => {
|
||||
const existingValue = this.cache?.get(key);
|
||||
if (existingValue !== value) {
|
||||
changed.set(key, value);
|
||||
}
|
||||
});
|
||||
|
||||
this.cache.forEach((_, key) => {
|
||||
if (!items.has(key)) {
|
||||
deleted.add(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// no previous cache, consider all as changed
|
||||
else {
|
||||
changed = items;
|
||||
}
|
||||
|
||||
// Update cache
|
||||
this.cache = items;
|
||||
|
||||
this._onDidChangeItemsExternal.fire({ items });
|
||||
// Emit as event as needed
|
||||
if (changed.size > 0 || deleted.size > 0) {
|
||||
this._onDidChangeItemsExternal.fire({ changed, deleted });
|
||||
}
|
||||
}
|
||||
|
||||
async getItems(): Promise<Map<string, string>> {
|
||||
|
||||
@@ -24,15 +24,16 @@ interface ISerializableUpdateRequest {
|
||||
}
|
||||
|
||||
interface ISerializableItemsChangeEvent {
|
||||
items: Item[];
|
||||
changed?: Item[];
|
||||
deleted?: Key[];
|
||||
}
|
||||
|
||||
export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel {
|
||||
|
||||
private static readonly STORAGE_CHANGE_DEBOUNCE_TIME = 100;
|
||||
|
||||
private readonly _onDidChangeItems: Emitter<ISerializableItemsChangeEvent> = this._register(new Emitter<ISerializableItemsChangeEvent>());
|
||||
readonly onDidChangeItems: Event<ISerializableItemsChangeEvent> = this._onDidChangeItems.event;
|
||||
private readonly _onDidChangeItems = this._register(new Emitter<ISerializableItemsChangeEvent>());
|
||||
readonly onDidChangeItems = this._onDidChangeItems.event;
|
||||
|
||||
private whenReady: Promise<void>;
|
||||
|
||||
@@ -99,15 +100,18 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
|
||||
}
|
||||
|
||||
private serializeEvents(events: IStorageChangeEvent[]): ISerializableItemsChangeEvent {
|
||||
const items = new Map<Key, Value>();
|
||||
const changed = new Map<Key, Value>();
|
||||
const deleted = new Set<Key>();
|
||||
events.forEach(event => {
|
||||
const existing = this.storageMainService.get(event.key);
|
||||
if (typeof existing === 'string') {
|
||||
items.set(event.key, existing);
|
||||
changed.set(event.key, existing);
|
||||
} else {
|
||||
deleted.add(event.key);
|
||||
}
|
||||
});
|
||||
|
||||
return { items: mapToSerializable(items) };
|
||||
return { changed: mapToSerializable(changed), deleted: values(deleted) };
|
||||
}
|
||||
|
||||
listen(_: unknown, event: string): Event<any> {
|
||||
@@ -170,8 +174,11 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
|
||||
}
|
||||
|
||||
private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
|
||||
if (Array.isArray(e.items)) {
|
||||
this._onDidChangeItemsExternal.fire({ items: serializableToMap(e.items) });
|
||||
if (Array.isArray(e.changed) || Array.isArray(e.deleted)) {
|
||||
this._onDidChangeItemsExternal.fire({
|
||||
changed: e.changed ? serializableToMap(e.changed) : undefined,
|
||||
deleted: e.deleted ? new Set<string>(e.deleted) : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage';
|
||||
import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage';
|
||||
@@ -25,11 +25,11 @@ export class NativeStorageService extends Disposable implements IStorageService
|
||||
private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb';
|
||||
private static readonly WORKSPACE_META_NAME = 'workspace.json';
|
||||
|
||||
private readonly _onDidChangeStorage: Emitter<IWorkspaceStorageChangeEvent> = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
readonly onDidChangeStorage: Event<IWorkspaceStorageChangeEvent> = this._onDidChangeStorage.event;
|
||||
private readonly _onDidChangeStorage = this._register(new Emitter<IWorkspaceStorageChangeEvent>());
|
||||
readonly onDidChangeStorage = this._onDidChangeStorage.event;
|
||||
|
||||
private readonly _onWillSaveState: Emitter<IWillSaveStateEvent> = this._register(new Emitter<IWillSaveStateEvent>());
|
||||
readonly onWillSaveState: Event<IWillSaveStateEvent> = this._onWillSaveState.event;
|
||||
private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
|
||||
readonly onWillSaveState = this._onWillSaveState.event;
|
||||
|
||||
private globalStorage: IStorage;
|
||||
|
||||
|
||||
@@ -260,7 +260,7 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
this._splitPastWorkspaceElement(element, element.removedResources.set);
|
||||
const message = nls.localize('cannotWorkspaceUndo', "Could not undo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.undo(resource);
|
||||
}
|
||||
|
||||
// this must be the last past element in all the impacted resources!
|
||||
@@ -281,18 +281,25 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
const paths = cannotUndoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
|
||||
const message = nls.localize('cannotWorkspaceUndoDueToChanges', "Could not undo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.undo(resource);
|
||||
}
|
||||
|
||||
return this._dialogService.show(
|
||||
Severity.Info,
|
||||
nls.localize('confirmWorkspace', "Would you like to undo '{0}' across all files?", element.label),
|
||||
[
|
||||
nls.localize('ok', "Yes, change {0} files.", affectedEditStacks.length),
|
||||
nls.localize('nok', "No, change only this file.")
|
||||
]
|
||||
nls.localize('ok', "Undo In {0} Files", affectedEditStacks.length),
|
||||
nls.localize('nok', "Undo This File"),
|
||||
nls.localize('cancel', "Cancel"),
|
||||
],
|
||||
{
|
||||
cancelId: 2
|
||||
}
|
||||
).then((result) => {
|
||||
if (result.choice === 0) {
|
||||
if (result.choice === 2) {
|
||||
// cancel
|
||||
return;
|
||||
} else if (result.choice === 0) {
|
||||
for (const editStack of affectedEditStacks) {
|
||||
editStack.past.pop();
|
||||
editStack.future.push(element);
|
||||
@@ -344,7 +351,7 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
this._splitFutureWorkspaceElement(element, element.removedResources.set);
|
||||
const message = nls.localize('cannotWorkspaceRedo', "Could not redo '{0}' across all files. {1}", element.label, element.removedResources.createMessage());
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.redo(resource);
|
||||
}
|
||||
|
||||
// this must be the last future element in all the impacted resources!
|
||||
@@ -365,7 +372,7 @@ export class UndoRedoService implements IUndoRedoService {
|
||||
const paths = cannotRedoDueToResources.map(r => r.scheme === Schemas.file ? r.fsPath : r.path);
|
||||
const message = nls.localize('cannotWorkspaceRedoDueToChanges', "Could not redo '{0}' across all files because changes were made to {1}", element.label, paths.join(', '));
|
||||
this._notificationService.info(message);
|
||||
return;
|
||||
return this.redo(resource);
|
||||
}
|
||||
|
||||
for (const editStack of affectedEditStacks) {
|
||||
|
||||
@@ -68,7 +68,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
) {
|
||||
super();
|
||||
this.syncFolder = joinPath(environmentService.userDataSyncHome, source);
|
||||
this.lastSyncResource = joinPath(this.syncFolder, `.lastSync${source}.json`);
|
||||
this.lastSyncResource = joinPath(this.syncFolder, `lastSync${source}.json`);
|
||||
this.cleanUpDelayer = new ThrottledDelayer(50);
|
||||
this.cleanUpBackup();
|
||||
}
|
||||
@@ -202,7 +202,7 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
}
|
||||
|
||||
protected async backupLocal(content: VSBuffer): Promise<void> {
|
||||
const resource = joinPath(this.syncFolder, toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''));
|
||||
const resource = joinPath(this.syncFolder, `${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}.json`);
|
||||
try {
|
||||
await this.fileService.writeFile(resource, content);
|
||||
} catch (e) {
|
||||
@@ -213,9 +213,13 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
|
||||
private async cleanUpBackup(): Promise<void> {
|
||||
try {
|
||||
if (!(await this.fileService.exists(this.syncFolder))) {
|
||||
return;
|
||||
}
|
||||
const stat = await this.fileService.resolve(this.syncFolder);
|
||||
if (stat.children) {
|
||||
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}$/.test(stat.name)).sort();
|
||||
const all = stat.children.filter(stat => stat.isFile && /^\d{8}T\d{6}(\.json)?$/.test(stat.name)).sort();
|
||||
console.log(all.map(a => a.name));
|
||||
const backUpMaxAge = 1000 * 60 * 60 * 24 * (this.configurationService.getValue<number>('sync.localBackupDuration') || 30 /* Default 30 days */);
|
||||
let toDelete = all.filter(stat => {
|
||||
const ctime = stat.ctime || new Date(
|
||||
|
||||
@@ -194,7 +194,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
|
||||
if (added.length || removed.length || updated.length) {
|
||||
// back up all disabled or market place extensions
|
||||
const backUpExtensions = localExtensions.filter(e => e.disabled || !!e.identifier.uuid);
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions)));
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(backUpExtensions, null, '\t')));
|
||||
skippedExtensions = await this.updateLocalExtensions(added, removed, updated, skippedExtensions);
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
|
||||
if (local) {
|
||||
// update local
|
||||
this.logService.trace('UI State: Updating local ui state...');
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData)));
|
||||
await this.backupLocal(VSBuffer.fromString(JSON.stringify(localUserData, null, '\t')));
|
||||
await this.writeLocalGlobalState(local);
|
||||
this.logService.info('UI State: Updated local ui state');
|
||||
}
|
||||
|
||||
@@ -8,6 +8,11 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IUserDataSyncLogService, IUserDataSyncService, SyncStatus, IUserDataAutoSyncService, UserDataSyncError, UserDataSyncErrorCode, IUserDataSyncEnablementService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
type AutoSyncTriggerClassification = {
|
||||
source: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
};
|
||||
|
||||
export class UserDataAutoSyncService extends Disposable implements IUserDataAutoSyncService {
|
||||
|
||||
@@ -25,6 +30,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
||||
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
|
||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||
@IAuthenticationTokenService private readonly authTokenService: IAuthenticationTokenService,
|
||||
@ITelemetryService private readonly telemetryService: ITelemetryService,
|
||||
) {
|
||||
super();
|
||||
this.updateEnablement(false, true);
|
||||
@@ -32,7 +38,7 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
||||
this._register(Event.any<any>(authTokenService.onDidChangeToken)(() => this.updateEnablement(true, true)));
|
||||
this._register(Event.any<any>(userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true, true)));
|
||||
this._register(this.userDataSyncEnablementService.onDidChangeEnablement(() => this.updateEnablement(true, false)));
|
||||
this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync()));
|
||||
this._register(this.userDataSyncEnablementService.onDidChangeResourceEnablement(() => this.triggerAutoSync(['resourceEnablement'])));
|
||||
}
|
||||
|
||||
private async updateEnablement(stopIfDisabled: boolean, auto: boolean): Promise<void> {
|
||||
@@ -99,7 +105,8 @@ export class UserDataAutoSyncService extends Disposable implements IUserDataAuto
|
||||
this.successiveFailures = 0;
|
||||
}
|
||||
|
||||
async triggerAutoSync(): Promise<void> {
|
||||
async triggerAutoSync(sources: string[]): Promise<void> {
|
||||
sources.forEach(source => this.telemetryService.publicLog2<{ source: string }, AutoSyncTriggerClassification>('sync/triggerAutoSync', { source }));
|
||||
if (this.enabled) {
|
||||
return this.syncDelayer.trigger(() => {
|
||||
this.logService.info('Auto Sync: Triggered.');
|
||||
|
||||
@@ -278,7 +278,7 @@ export interface IUserDataSyncService {
|
||||
readonly conflictsSources: SyncSource[];
|
||||
readonly onDidChangeConflicts: Event<SyncSource[]>;
|
||||
|
||||
readonly onDidChangeLocal: Event<void>;
|
||||
readonly onDidChangeLocal: Event<SyncSource>;
|
||||
readonly onSyncErrors: Event<[SyncSource, UserDataSyncError][]>;
|
||||
|
||||
readonly lastSyncTime: number | undefined;
|
||||
@@ -299,7 +299,7 @@ export const IUserDataAutoSyncService = createDecorator<IUserDataAutoSyncService
|
||||
export interface IUserDataAutoSyncService {
|
||||
_serviceBrand: any;
|
||||
readonly onError: Event<UserDataSyncError>;
|
||||
triggerAutoSync(): Promise<void>;
|
||||
triggerAutoSync(sources: string[]): Promise<void>;
|
||||
}
|
||||
|
||||
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');
|
||||
|
||||
@@ -86,7 +86,7 @@ export class UserDataAutoSyncChannel implements IServerChannel {
|
||||
|
||||
call(context: any, command: string, args?: any): Promise<any> {
|
||||
switch (command) {
|
||||
case 'triggerAutoSync': return this.service.triggerAutoSync();
|
||||
case 'triggerAutoSync': return this.service.triggerAutoSync(args[0]);
|
||||
}
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
private _onDidChangeStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
|
||||
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
readonly onDidChangeLocal: Event<void>;
|
||||
readonly onDidChangeLocal: Event<SyncSource>;
|
||||
|
||||
private _conflictsSources: SyncSource[] = [];
|
||||
get conflictsSources(): SyncSource[] { return this._conflictsSources; }
|
||||
@@ -74,7 +74,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
this._lastSyncTime = this.storageService.getNumber(LAST_SYNC_TIME_KEY, StorageScope.GLOBAL, undefined);
|
||||
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal));
|
||||
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeLocal, () => s.source)));
|
||||
}
|
||||
|
||||
async pull(): Promise<void> {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Event } from 'vs/base/common/event';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { UserDataAutoSyncService as BaseUserDataAutoSyncService } from 'vs/platform/userDataSync/common/userDataAutoSyncService';
|
||||
import { IAuthenticationTokenService } from 'vs/platform/authentication/common/authentication';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
||||
export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
|
||||
|
||||
@@ -17,15 +18,15 @@ export class UserDataAutoSyncService extends BaseUserDataAutoSyncService {
|
||||
@IElectronService electronService: IElectronService,
|
||||
@IUserDataSyncLogService logService: IUserDataSyncLogService,
|
||||
@IAuthenticationTokenService authTokenService: IAuthenticationTokenService,
|
||||
@ITelemetryService telemetryService: ITelemetryService,
|
||||
) {
|
||||
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService);
|
||||
super(userDataSyncEnablementService, userDataSyncService, logService, authTokenService, telemetryService);
|
||||
|
||||
// Sync immediately if there is a local change.
|
||||
this._register(Event.debounce(Event.any<any>(
|
||||
electronService.onWindowFocus,
|
||||
electronService.onWindowOpen,
|
||||
this._register(Event.debounce<string, string[]>(Event.any<string>(
|
||||
Event.map(electronService.onWindowFocus, () => 'windowFocus'),
|
||||
Event.map(electronService.onWindowOpen, () => 'windowOpen'),
|
||||
userDataSyncService.onDidChangeLocal,
|
||||
), () => undefined, 500)(() => this.triggerAutoSync()));
|
||||
), (last, source) => last ? [...last, source] : [source], 1000)(sources => this.triggerAutoSync(sources)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user