Merge remote-tracking branch 'origin/master'

This commit is contained in:
João Moreno
2020-11-04 15:37:52 +01:00
56 changed files with 863 additions and 707 deletions
+28 -4
View File
@@ -43,9 +43,10 @@ export interface IStorageDatabase {
export interface IStorage extends IDisposable {
readonly onDidChangeStorage: Event<string>;
readonly items: Map<string, string>;
readonly size: number;
readonly onDidChangeStorage: Event<string>;
init(): Promise<void>;
@@ -61,6 +62,8 @@ export interface IStorage extends IDisposable {
set(key: string, value: string | boolean | number | undefined | null): Promise<void>;
delete(key: string): Promise<void>;
whenFlushed(): Promise<void>;
close(): Promise<void>;
}
@@ -86,6 +89,8 @@ export class Storage extends Disposable implements IStorage {
private pendingDeletes = new Set<string>();
private pendingInserts = new Map<string, string>();
private readonly whenFlushedCallbacks: Function[] = [];
constructor(
protected readonly database: IStorageDatabase,
private readonly options: IStorageOptions = Object.create(null)
@@ -273,8 +278,12 @@ export class Storage extends Disposable implements IStorage {
await this.database.close(() => this.cache);
}
private get hasPending() {
return this.pendingInserts.size > 0 || this.pendingDeletes.size > 0;
}
private flushPending(): Promise<void> {
if (this.pendingInserts.size === 0 && this.pendingDeletes.size === 0) {
if (!this.hasPending) {
return Promise.resolve(); // return early if nothing to do
}
@@ -285,8 +294,23 @@ export class Storage extends Disposable implements IStorage {
this.pendingDeletes = new Set<string>();
this.pendingInserts = new Map<string, string>();
// Update in storage
return this.database.updateItems(updateRequest);
// Update in storage and release any
// waiters we have once done
return this.database.updateItems(updateRequest).finally(() => {
if (!this.hasPending) {
while (this.whenFlushedCallbacks.length) {
this.whenFlushedCallbacks.pop()?.();
}
}
});
}
whenFlushed(): Promise<void> {
if (!this.hasPending) {
return Promise.resolve(); // return early if nothing to do
}
return new Promise(resolve => this.whenFlushedCallbacks.push(resolve));
}
}
@@ -45,11 +45,16 @@ suite('Storage Library', function () {
changes.add(key);
});
await storage.whenFlushed(); // returns immediately when no pending updates
// Simple updates
const set1Promise = storage.set('bar', 'foo');
const set2Promise = storage.set('barNumber', 55);
const set3Promise = storage.set('barBoolean', true);
let flushPromiseResolved = false;
storage.whenFlushed().then(() => flushPromiseResolved = true);
equal(storage.get('bar'), 'foo');
equal(storage.getNumber('barNumber'), 55);
equal(storage.getBoolean('barBoolean'), true);
@@ -62,6 +67,7 @@ suite('Storage Library', function () {
let setPromiseResolved = false;
await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true);
equal(setPromiseResolved, true);
equal(flushPromiseResolved, true);
changes = new Set<string>();
@@ -166,6 +172,9 @@ suite('Storage Library', function () {
const set1Promise = storage.set('foo', 'bar');
const set2Promise = storage.set('bar', 'foo');
let flushPromiseResolved = false;
storage.whenFlushed().then(() => flushPromiseResolved = true);
equal(storage.get('foo'), 'bar');
equal(storage.get('bar'), 'foo');
@@ -175,6 +184,7 @@ suite('Storage Library', function () {
await storage.close();
equal(setPromiseResolved, true);
equal(flushPromiseResolved, true);
storage = new Storage(new SQLiteStorageDatabase(join(storageDir, 'storage.db')));
await storage.init();
@@ -226,6 +236,9 @@ suite('Storage Library', function () {
const set2Promise = storage.set('foo', 'bar2');
const set3Promise = storage.set('foo', 'bar3');
let flushPromiseResolved = false;
storage.whenFlushed().then(() => flushPromiseResolved = true);
equal(storage.get('foo'), 'bar3');
equal(changes.size, 1);
ok(changes.has('foo'));
@@ -233,6 +246,7 @@ suite('Storage Library', function () {
let setPromiseResolved = false;
await Promise.all([set1Promise, set2Promise, set3Promise]).then(() => setPromiseResolved = true);
ok(setPromiseResolved);
ok(flushPromiseResolved);
changes = new Set<string>();
+1 -3
View File
@@ -26,7 +26,6 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { EditorOption } from 'vs/editor/common/config/editorOptions';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
const SEARCH_STRING_MAX_LENGTH = 524288;
@@ -411,7 +410,6 @@ export class FindController extends CommonFindController implements IFindControl
@IThemeService private readonly _themeService: IThemeService,
@INotificationService private readonly _notificationService: INotificationService,
@IStorageService _storageService: IStorageService,
@IStorageKeysSyncRegistryService private readonly _storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IClipboardService clipboardService: IClipboardService,
) {
super(editor, _contextKeyService, _storageService, clipboardService);
@@ -468,7 +466,7 @@ export class FindController extends CommonFindController implements IFindControl
}
private _createFindWidget() {
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService, this._storageKeysSyncRegistryService));
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService));
this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService));
}
}
+2 -5
View File
@@ -34,9 +34,8 @@ import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatch
import { IColorTheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
const findSelectionIcon = registerIcon('find-selection', Codicon.selection);
@@ -164,7 +163,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
themeService: IThemeService,
storageService: IStorageService,
notificationService: INotificationService,
storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
this._codeEditor = codeEditor;
@@ -176,7 +174,6 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
this._storageService = storageService;
this._notificationService = notificationService;
storageKeysSyncRegistryService.registerStorageKey({ key: ctrlEnterReplaceAllWarningPromptedKey, version: 1 });
this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL);
this._isVisible = false;
@@ -880,7 +877,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IVerticalSashL
);
this._ctrlEnterReplaceAllWarningPrompted = true;
this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL);
this._storageService.store2(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL, StorageTarget.USER);
}
@@ -61,17 +61,20 @@ suite('Multicursor selection', () => {
let serviceCollection = new ServiceCollection();
serviceCollection.set(IStorageService, {
_serviceBrand: undefined,
onDidChangeStorage: Event.None,
onDidChangeValue: Event.None,
onDidChangeTarget: Event.None,
onWillSaveState: Event.None,
get: (key: string) => queryState[key],
getBoolean: (key: string) => !!queryState[key],
getNumber: (key: string) => undefined!,
store2: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
store: (key: string, value: any) => { queryState[key] = value; return Promise.resolve(); },
remove: (key) => undefined,
logStorage: () => undefined,
migrate: (toWorkspace) => Promise.resolve(undefined),
flush: () => undefined,
isNew: () => true
flush: () => Promise.resolve(undefined),
isNew: () => true,
keys: () => []
} as IStorageService);
test('issue #8817: Cursor position changes when you cancel multicursor', () => {
@@ -584,6 +584,10 @@ export class SuggestController implements IEditorContribution {
toggleSuggestionFocus(): void {
this.widget.value.toggleDetailsFocus();
}
resetWidgetSize(): void {
this.widget.value.resetPersistedSize();
}
}
export class TriggerSuggestAction extends EditorAction {
@@ -875,3 +879,20 @@ registerEditorCommand(new SuggestCommand({
primary: KeyMod.Shift | KeyCode.Tab
}
}));
registerEditorAction(class extends EditorAction {
constructor() {
super({
id: 'editor.action.resetSuggestSize',
label: nls.localize('suggest.reset.label', "Reset Suggest Widget Size"),
alias: 'Reset Suggest Widget Size',
precondition: undefined
});
}
run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
SuggestController.get(editor).resetWidgetSize();
}
});
+17 -5
View File
@@ -84,6 +84,10 @@ class PersistedWidgetSize {
store(size: dom.Dimension) {
this._service.store(this._key, JSON.stringify(size), StorageScope.GLOBAL);
}
reset(): void {
this._service.remove(this._key, StorageScope.GLOBAL);
}
}
export class SuggestWidget implements IDisposable {
@@ -148,11 +152,13 @@ export class SuggestWidget implements IDisposable {
this._persistedSize = new PersistedWidgetSize(_storageService, editor);
let persistedSize: dom.Dimension | undefined;
let currentSize: dom.Dimension | undefined;
let persistHeight = false;
let persistWidth = false;
this._disposables.add(this.element.onDidWillResize(() => {
this._contentWidget.lockPreference();
persistedSize = this._persistedSize.restore();
currentSize = this.element.size;
}));
this._disposables.add(this.element.onDidResize(e => {
@@ -161,14 +167,15 @@ export class SuggestWidget implements IDisposable {
persistHeight = persistHeight || !!e.north || !!e.south;
persistWidth = persistWidth || !!e.east || !!e.west;
if (e.done) {
// only store width or height value that have changed
// only store width or height value that have changed and also
// only store changes that are above a certain threshold
const threshold = Math.floor(this.getLayoutInfo().itemHeight / 3);
let { width, height } = this.element.size;
if (persistedSize) {
if (!persistHeight) {
if (persistedSize && currentSize) {
if (!persistHeight || Math.abs(currentSize.height - height) > threshold) {
height = persistedSize.height;
}
if (!persistWidth) {
if (!persistWidth || Math.abs(currentSize.width - width) > threshold) {
width = persistedSize.width;
}
}
@@ -177,6 +184,7 @@ export class SuggestWidget implements IDisposable {
// reset working state
this._contentWidget.unlockPreference();
persistedSize = undefined;
currentSize = undefined;
persistHeight = false;
persistWidth = false;
}
@@ -677,6 +685,10 @@ export class SuggestWidget implements IDisposable {
}
}
resetPersistedSize(): void {
this._persistedSize.reset();
}
hideWidget(): void {
this._loadingTimeout?.dispose();
this._setState(State.Hidden);
@@ -7,7 +7,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtensionIdentifier, IGlobalExtensionEnablementService, DISABLED_EXTENSIONS_STORAGE_PATH } from 'vs/platform/extensionManagement/common/extensionManagement';
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
import { IStorageService, StorageScope, IStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent } from 'vs/platform/storage/common/storage';
import { isUndefinedOrNull } from 'vs/base/common/types';
export class GlobalExtensionEnablementService extends Disposable implements IGlobalExtensionEnablementService {
@@ -96,7 +96,7 @@ export class StorageManager extends Disposable {
constructor(private storageService: IStorageService) {
super();
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e)));
}
get(key: string, scope: StorageScope): IExtensionIdentifier[] {
@@ -127,7 +127,7 @@ export class StorageManager extends Disposable {
}
}
private onDidStorageChange(storageChangeEvent: IStorageChangeEvent): void {
private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void {
if (storageChangeEvent.scope === StorageScope.GLOBAL) {
if (!isUndefinedOrNull(this.storage[storageChangeEvent.key])) {
const newValue = this._get(storageChangeEvent.key, storageChangeEvent.scope);
@@ -11,7 +11,7 @@ import { DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecyc
import { or, matchesPrefix, matchesWords, matchesContiguousSubString } from 'vs/base/common/filters';
import { withNullAsUndefined } from 'vs/base/common/types';
import { LRUCache } from 'vs/base/common/map';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -21,7 +21,6 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export interface ICommandQuickPick extends IPickerQuickAccessItem {
commandId: string;
@@ -204,14 +203,9 @@ export class CommandsHistory extends Disposable {
constructor(
@IStorageService private readonly storageService: IStorageService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_CACHE, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: CommandsHistory.PREF_KEY_COUNTER, version: 1 });
this.updateConfiguration();
this.load();
@@ -279,8 +273,8 @@ export class CommandsHistory extends Disposable {
const serializedCache: ISerializedCommandHistory = { usesLRU: true, entries: [] };
CommandsHistory.cache.forEach((value, key) => serializedCache.entries.push({ key, value }));
storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL);
storageService.store(CommandsHistory.PREF_KEY_COUNTER, CommandsHistory.counter, StorageScope.GLOBAL);
storageService.store2(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(CommandsHistory.PREF_KEY_COUNTER, CommandsHistory.counter, StorageScope.GLOBAL, StorageTarget.USER);
}
static getConfiguredCommandHistoryLength(configurationService: IConfigurationService): number {
@@ -5,7 +5,7 @@
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { IStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, IS_NEW_KEY } from 'vs/platform/storage/common/storage';
import { StorageScope, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
@@ -16,15 +16,7 @@ import { runWhenIdle, RunOnceScheduler } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { assertIsDefined, assertAllDefined } from 'vs/base/common/types';
export class BrowserStorageService extends Disposable implements IStorageService {
declare readonly _serviceBrand: undefined;
private readonly _onDidChangeStorage = this._register(new Emitter<IStorageChangeEvent>());
readonly onDidChangeStorage = this._onDidChangeStorage.event;
private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState = this._onWillSaveState.event;
export class BrowserStorageService extends AbstractStorageService {
private globalStorage: IStorage | undefined;
private workspaceStorage: IStorage | undefined;
@@ -40,6 +32,10 @@ export class BrowserStorageService extends Disposable implements IStorageService
private readonly periodicFlushScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), 5000 /* every 5s */));
private runWhenIdleDisposable: IDisposable | undefined = undefined;
get hasPendingUpdate(): boolean {
return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate);
}
constructor(
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IFileService private readonly fileService: IFileService
@@ -66,13 +62,13 @@ export class BrowserStorageService extends Disposable implements IStorageService
this.workspaceStorageDatabase = this._register(new FileStorageDatabase(this.workspaceStorageFile, false /* do not watch for external changes */, this.fileService));
this.workspaceStorage = this._register(new Storage(this.workspaceStorageDatabase));
this._register(this.workspaceStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.WORKSPACE })));
this._register(this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key)));
// Global Storage
this.globalStorageFile = joinPath(stateRoot, 'global.json');
this.globalStorageDatabase = this._register(new FileStorageDatabase(this.globalStorageFile, true /* watch for external changes */, this.fileService));
this.globalStorage = this._register(new Storage(this.globalStorageDatabase));
this._register(this.globalStorage.onDidChangeStorage(key => this._onDidChangeStorage.fire({ key, scope: StorageScope.GLOBAL })));
this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key)));
// Init both
await Promise.all([
@@ -122,11 +118,11 @@ export class BrowserStorageService extends Disposable implements IStorageService
return this.getStorage(scope).getNumber(key, fallbackValue);
}
store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
this.getStorage(scope).set(key, value);
}
remove(key: string, scope: StorageScope): void {
protected doRemove(key: string, scope: StorageScope): void {
this.getStorage(scope).delete(key);
}
@@ -149,6 +145,13 @@ export class BrowserStorageService extends Disposable implements IStorageService
throw new Error('Migrating storage is currently unsupported in Web');
}
protected async doFlush(): Promise<void> {
await Promise.all([
this.getStorage(StorageScope.GLOBAL).whenFlushed(),
this.getStorage(StorageScope.WORKSPACE).whenFlushed()
]);
}
private doFlushWhenIdle(): void {
// Dispose any previous idle runner
@@ -175,14 +178,6 @@ export class BrowserStorageService extends Disposable implements IStorageService
});
}
get hasPendingUpdate(): boolean {
return (!!this.globalStorageDatabase && this.globalStorageDatabase.hasPendingUpdate) || (!!this.workspaceStorageDatabase && this.workspaceStorageDatabase.hasPendingUpdate);
}
flush(): void {
this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE });
}
close(): void {
// We explicitly do not close our DBs because writing data onBeforeUnload()
// can result in unexpected results. Namely, it seems that - even though this
@@ -195,10 +190,6 @@ export class BrowserStorageService extends Disposable implements IStorageService
this.dispose();
}
isNew(scope: StorageScope): boolean {
return this.getBoolean(IS_NEW_KEY, scope) === true;
}
dispose(): void {
dispose(this.runWhenIdleDisposable);
this.runWhenIdleDisposable = undefined;
+288 -48
View File
@@ -4,12 +4,13 @@
*--------------------------------------------------------------------------------------------*/
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Event, Emitter } from 'vs/base/common/event';
import { Event, Emitter, PauseableEmitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { isUndefinedOrNull } from 'vs/base/common/types';
import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
export const IS_NEW_KEY = '__$__isNewStorageMarker';
const TARGET_KEY = '__$__targetStorageMarker';
export const IStorageService = createDecorator<IStorageService>('storageService');
@@ -29,7 +30,12 @@ export interface IStorageService {
/**
* Emitted whenever data is updated or deleted.
*/
readonly onDidChangeStorage: Event<IStorageChangeEvent>;
readonly onDidChangeValue: Event<IStorageValueChangeEvent>;
/**
* Emitted whenever target of a storage entry changes.
*/
readonly onDidChangeTarget: Event<IStorageTargetChangeEvent>;
/**
* Emitted when the storage is about to persist. This is the right time
@@ -48,45 +54,55 @@ export interface IStorageService {
/**
* Retrieve an element stored with the given key from storage. Use
* the provided defaultValue if the element is null or undefined.
* the provided `defaultValue` if the element is `null` or `undefined`.
*
* The scope argument allows to define the scope of the storage
* operation to either the current workspace only or all workspaces.
* @param scope allows to define the scope of the storage operation
* to either the current workspace only or all workspaces.
*/
get(key: string, scope: StorageScope, fallbackValue: string): string;
get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined;
/**
* Retrieve an element stored with the given key from storage. Use
* the provided defaultValue if the element is null or undefined. The element
* will be converted to a boolean.
* the provided `defaultValue` if the element is `null` or `undefined`.
* The element will be converted to a `boolean`.
*
* The scope argument allows to define the scope of the storage
* operation to either the current workspace only or all workspaces.
* @param scope allows to define the scope of the storage operation
* to either the current workspace only or all workspaces.
*/
getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined;
/**
* Retrieve an element stored with the given key from storage. Use
* the provided defaultValue if the element is null or undefined. The element
* will be converted to a number using parseInt with a base of 10.
* the provided `defaultValue` if the element is `null` or `undefined`.
* The element will be converted to a `number` using `parseInt` with a
* base of `10`.
*
* The scope argument allows to define the scope of the storage
* operation to either the current workspace only or all workspaces.
* @param scope allows to define the scope of the storage operation
* to either the current workspace only or all workspaces.
*/
getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined;
/**
* Store a value under the given key to storage. The value will be converted to a string.
* Storing either undefined or null will remove the entry under the key.
*
* The scope argument allows to define the scope of the storage
* operation to either the current workspace only or all workspaces.
* @deprecated use store2 instead
*/
store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void;
/**
* Store a value under the given key to storage. The value will be
* converted to a `string`. Storing either `undefined` or `null` will
* remove the entry under the key.
*
* @param scope allows to define the scope of the storage operation
* to either the current workspace only or all workspaces.
*
* @param target allows to define the target of the storage operation
* to either the current machine or user.
*/
store2(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void;
/**
* Delete an element stored under the provided key from storage.
*
@@ -95,6 +111,22 @@ export interface IStorageService {
*/
remove(key: string, scope: StorageScope): void;
/**
* Returns all the keys used in the storage for the provided `scope`
* and `target`.
*
* Note: this will NOT return all keys stored in the storage layer.
* Some keys may not have an associated `StorageTarget` and thus
* will be excluded from the results.
*
* @param scope allows to define the scope for the keys
* to either the current workspace only or all workspaces.
*
* @param target allows to define the target for the keys
* to either the current machine or user.
*/
keys(scope: StorageScope, target: StorageTarget): string[];
/**
* Log the contents of the storage to the console.
*/
@@ -108,16 +140,18 @@ export interface IStorageService {
/**
* Whether the storage for the given scope was created during this session or
* existed before.
*
*/
isNew(scope: StorageScope): boolean;
/**
* Allows to flush state, e.g. in cases where a shutdown is
* imminent. This will send out the onWillSaveState to ask
* imminent. This will send out the `onWillSaveState` to ask
* everyone for latest state.
*
* @returns a `Promise` that can be awaited on when all updates
* to the underlying storage have been flushed.
*/
flush(): void;
flush(): Promise<void>;
}
export const enum StorageScope {
@@ -133,21 +167,242 @@ export const enum StorageScope {
WORKSPACE
}
export interface IStorageChangeEvent {
export const enum StorageTarget {
/**
* The stored data is user specific and applies across machines.
*/
USER,
/**
* The stored data is machine specific.
*/
MACHINE
}
export interface IStorageValueChangeEvent {
/**
* The scope for the storage entry that changed
* or was removed.
*/
readonly scope: StorageScope;
/**
* The `key` of the storage entry that was changed
* or was removed.
*/
readonly key: string;
/**
* The `target` can be `undefined` if a key is being
* removed.
*/
readonly target: StorageTarget | undefined;
}
export interface IStorageTargetChangeEvent {
readonly scope: StorageScope;
}
export class InMemoryStorageService extends Disposable implements IStorageService {
interface IKeyTargets {
[key: string]: StorageTarget
}
export abstract class AbstractStorageService extends Disposable implements IStorageService {
declare readonly _serviceBrand: undefined;
private readonly _onDidChangeStorage = this._register(new Emitter<IStorageChangeEvent>());
readonly onDidChangeStorage = this._onDidChangeStorage.event;
private readonly _onDidChangeValue = this._register(new PauseableEmitter<IStorageValueChangeEvent>());
readonly onDidChangeValue = this._onDidChangeValue.event;
protected readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
private readonly _onDidChangeTarget = this._register(new PauseableEmitter<IStorageTargetChangeEvent>());
readonly onDidChangeTarget = this._onDidChangeTarget.event;
private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState = this._onWillSaveState.event;
protected emitDidChangeValue(scope: StorageScope, key: string): void {
// Specially handle `TARGET_KEY`
if (key === TARGET_KEY) {
// Clear our cached version which is now out of date
if (scope === StorageScope.GLOBAL) {
this._globalKeyTargets = undefined;
} else if (scope === StorageScope.WORKSPACE) {
this._workspaceKeyTargets = undefined;
}
// Emit as `didChangeTarget` event
this._onDidChangeTarget.fire({ scope });
}
// Emit any other key to outside
else {
this._onDidChangeValue.fire({ scope, key, target: this.getKeyTargets(scope)[key] });
}
}
protected emitWillSaveState(reason: WillSaveStateReason): void {
this._onWillSaveState.fire({ reason });
}
store2(key: string, value: string | boolean | number | undefined | null, scope: StorageScope, target: StorageTarget): void {
// We remove the key for undefined/null values
if (isUndefinedOrNull(value)) {
this.remove(key, scope);
return;
}
// Update our datastructures but send events only after
this.withPausedEmitters(() => {
// Update key-target map
this.updateKeyTarget(key, scope, target);
// Store actual value
this.doStore(key, value, scope);
});
}
store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
this.store2(key, value, scope, StorageTarget.MACHINE);
}
remove(key: string, scope: StorageScope): void {
// Update our datastructures but send events only after
this.withPausedEmitters(() => {
// Update key-target map
this.updateKeyTarget(key, scope, undefined);
// Remove actual key
this.doRemove(key, scope);
});
}
private withPausedEmitters(fn: Function): void {
// Pause emitters
this._onDidChangeValue.pause();
this._onDidChangeTarget.pause();
try {
fn();
} finally {
// Resume emitters
this._onDidChangeValue.resume();
this._onDidChangeTarget.resume();
}
}
keys(scope: StorageScope, target: StorageTarget): string[] {
const keys: string[] = [];
for (const [key, keyTarget] of Object.entries(this.getKeyTargets(scope))) {
if (keyTarget === target) {
keys.push(key);
}
}
return keys;
}
private updateKeyTarget(key: string, scope: StorageScope, target: StorageTarget | undefined): void {
// Add
const keyTargets = this.getKeyTargets(scope);
if (typeof target === 'number') {
if (keyTargets[key] !== target) {
keyTargets[key] = target;
this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope);
}
}
// Remove
else {
if (typeof keyTargets[key] === 'number') {
delete keyTargets[key];
this.doStore(TARGET_KEY, JSON.stringify(keyTargets), scope);
}
}
}
private _workspaceKeyTargets: IKeyTargets | undefined = undefined;
private get workspaceKeyTargets(): IKeyTargets {
if (!this._workspaceKeyTargets) {
this._workspaceKeyTargets = this.loadKeyTargets(StorageScope.WORKSPACE);
}
return this._workspaceKeyTargets;
}
private _globalKeyTargets: IKeyTargets | undefined = undefined;
private get globalKeyTargets(): IKeyTargets {
if (!this._globalKeyTargets) {
this._globalKeyTargets = this.loadKeyTargets(StorageScope.GLOBAL);
}
return this._globalKeyTargets;
}
private getKeyTargets(scope: StorageScope): IKeyTargets {
return scope === StorageScope.GLOBAL ? this.globalKeyTargets : this.workspaceKeyTargets;
}
private loadKeyTargets(scope: StorageScope): { [key: string]: StorageTarget } {
const keysRaw = this.get(TARGET_KEY, scope);
if (keysRaw) {
try {
return JSON.parse(keysRaw);
} catch (error) {
// Fail gracefully
}
}
return Object.create(null);
}
isNew(scope: StorageScope): boolean {
return this.getBoolean(IS_NEW_KEY, scope) === true;
}
flush(): Promise<void> {
// Signal event to collect changes
this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE });
// Await flush
return this.doFlush();
}
// --- abstract
abstract get(key: string, scope: StorageScope, fallbackValue: string): string;
abstract get(key: string, scope: StorageScope, fallbackValue?: string): string | undefined;
abstract getBoolean(key: string, scope: StorageScope, fallbackValue: boolean): boolean;
abstract getBoolean(key: string, scope: StorageScope, fallbackValue?: boolean): boolean | undefined;
abstract getNumber(key: string, scope: StorageScope, fallbackValue: number): number;
abstract getNumber(key: string, scope: StorageScope, fallbackValue?: number): number | undefined;
protected abstract doStore(key: string, value: string | boolean | number, scope: StorageScope): void;
protected abstract doRemove(key: string, scope: StorageScope): void;
protected abstract doFlush(): Promise<void>;
abstract migrate(toWorkspace: IWorkspaceInitializationPayload): Promise<void>;
abstract logStorage(): void;
}
export class InMemoryStorageService extends AbstractStorageService {
private readonly globalCache = new Map<string, string>();
private readonly workspaceCache = new Map<string, string>();
@@ -188,12 +443,7 @@ export class InMemoryStorageService extends Disposable implements IStorageServic
return parseInt(value, 10);
}
store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): Promise<void> {
// We remove the key for undefined/null values
if (isUndefinedOrNull(value)) {
return this.remove(key, scope);
}
protected doStore(key: string, value: string | boolean | number, scope: StorageScope): void {
// Otherwise, convert to String and store
const valueStr = String(value);
@@ -201,28 +451,24 @@ export class InMemoryStorageService extends Disposable implements IStorageServic
// Return early if value already set
const currentValue = this.getCache(scope).get(key);
if (currentValue === valueStr) {
return Promise.resolve();
return;
}
// Update in cache
this.getCache(scope).set(key, valueStr);
// Events
this._onDidChangeStorage.fire({ scope, key });
return Promise.resolve();
this.emitDidChangeValue(scope, key);
}
remove(key: string, scope: StorageScope): Promise<void> {
protected doRemove(key: string, scope: StorageScope): void {
const wasDeleted = this.getCache(scope).delete(key);
if (!wasDeleted) {
return Promise.resolve(); // Return early if value already deleted
return; // Return early if value already deleted
}
// Events
this._onDidChangeStorage.fire({ scope, key });
return Promise.resolve();
this.emitDidChangeValue(scope, key);
}
logStorage(): void {
@@ -233,13 +479,7 @@ export class InMemoryStorageService extends Disposable implements IStorageServic
// not supported
}
flush(): void {
this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE });
}
isNew(): boolean {
return true; // always new when in-memory
}
async doFlush(): Promise<void> { }
async close(): Promise<void> { }
}
+15 -29
View File
@@ -3,10 +3,9 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Emitter } from 'vs/base/common/event';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage, IS_NEW_KEY } from 'vs/platform/storage/common/storage';
import { StorageScope, WillSaveStateReason, logStorage, IS_NEW_KEY, AbstractStorageService } from 'vs/platform/storage/common/storage';
import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage';
import { Storage, IStorageDatabase, IStorage, StorageHint } from 'vs/base/parts/storage/common/storage';
import { mark } from 'vs/base/common/performance';
@@ -17,19 +16,11 @@ import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderW
import { assertIsDefined } from 'vs/base/common/types';
import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async';
export class NativeStorageService extends Disposable implements IStorageService {
declare readonly _serviceBrand: undefined;
export class NativeStorageService extends AbstractStorageService {
private static readonly WORKSPACE_STORAGE_NAME = 'state.vscdb';
private static readonly WORKSPACE_META_NAME = 'workspace.json';
private readonly _onDidChangeStorage = this._register(new Emitter<IStorageChangeEvent>());
readonly onDidChangeStorage = this._onDidChangeStorage.event;
private readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState = this._onWillSaveState.event;
private readonly globalStorage = new Storage(this.globalStorageDatabase);
private workspaceStoragePath: string | undefined;
@@ -54,11 +45,7 @@ export class NativeStorageService extends Disposable implements IStorageService
private registerListeners(): void {
// Global Storage change events
this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL)));
}
private handleDidChangeStorage(key: string, scope: StorageScope): void {
this._onDidChangeStorage.fire({ key, scope });
this._register(this.globalStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.GLOBAL, key)));
}
initialize(payload?: IWorkspaceInitializationPayload): Promise<void> {
@@ -134,7 +121,7 @@ export class NativeStorageService extends Disposable implements IStorageService
// Create new
this.workspaceStoragePath = workspaceStoragePath;
this.workspaceStorage = new Storage(new SQLiteStorageDatabase(workspaceStoragePath, { logging: workspaceLoggingOptions }), { hint });
this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.WORKSPACE));
this.workspaceStorageListener = this.workspaceStorage.onDidChangeStorage(key => this.emitDidChangeValue(StorageScope.WORKSPACE, key));
return this.workspaceStorage;
}
@@ -201,11 +188,11 @@ export class NativeStorageService extends Disposable implements IStorageService
return this.getStorage(scope).getNumber(key, fallbackValue);
}
store(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
protected doStore(key: string, value: string | boolean | number | undefined | null, scope: StorageScope): void {
this.getStorage(scope).set(key, value);
}
remove(key: string, scope: StorageScope): void {
protected doRemove(key: string, scope: StorageScope): void {
this.getStorage(scope).delete(key);
}
@@ -213,6 +200,13 @@ export class NativeStorageService extends Disposable implements IStorageService
return assertIsDefined(scope === StorageScope.GLOBAL ? this.globalStorage : this.workspaceStorage);
}
protected async doFlush(): Promise<void> {
await Promise.all([
this.getStorage(StorageScope.GLOBAL).whenFlushed(),
this.getStorage(StorageScope.WORKSPACE).whenFlushed()
]);
}
private doFlushWhenIdle(): void {
// Dispose any previous idle runner
@@ -229,10 +223,6 @@ export class NativeStorageService extends Disposable implements IStorageService
});
}
flush(): void {
this._onWillSaveState.fire({ reason: WillSaveStateReason.NONE });
}
async close(): Promise<void> {
// Stop periodic scheduler and idle runner as we now collect state normally
@@ -241,7 +231,7 @@ export class NativeStorageService extends Disposable implements IStorageService
this.runWhenIdleDisposable = undefined;
// Signal as event so that clients can still store data
this._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
this.emitWillSaveState(WillSaveStateReason.SHUTDOWN);
// Do it
await Promise.all([
@@ -277,8 +267,4 @@ export class NativeStorageService extends Disposable implements IStorageService
// Recreate and init workspace storage
return this.createWorkspaceStorage(newWorkspaceStoragePath).init();
}
isNew(scope: StorageScope): boolean {
return this.getBoolean(IS_NEW_KEY, scope) === true;
}
}
@@ -0,0 +1,198 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { strictEqual, ok, equal } from 'assert';
import { StorageScope, InMemoryStorageService, StorageTarget, IStorageValueChangeEvent, IStorageTargetChangeEvent } from 'vs/platform/storage/common/storage';
suite('StorageService', function () {
test('Get Data, Integer, Boolean (global, in-memory)', () => {
storeData(StorageScope.GLOBAL);
});
test('Get Data, Integer, Boolean (workspace, in-memory)', () => {
storeData(StorageScope.WORKSPACE);
});
function storeData(scope: StorageScope): void {
const storage = new InMemoryStorageService();
let storageValueChangeEvents: IStorageValueChangeEvent[] = [];
storage.onDidChangeValue(e => storageValueChangeEvents.push(e));
strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar');
strictEqual(storage.get('test.get', scope, ''), '');
strictEqual(storage.getNumber('test.getNumber', scope, 5), 5);
strictEqual(storage.getNumber('test.getNumber', scope, 0), 0);
strictEqual(storage.getBoolean('test.getBoolean', scope, true), true);
strictEqual(storage.getBoolean('test.getBoolean', scope, false), false);
storage.store('test.get', 'foobar', scope);
strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar');
let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get');
equal(storageValueChangeEvent?.scope, scope);
equal(storageValueChangeEvent?.key, 'test.get');
storageValueChangeEvents = [];
storage.store('test.get', '', scope);
strictEqual(storage.get('test.get', scope, (undefined)!), '');
storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.get');
equal(storageValueChangeEvent!.scope, scope);
equal(storageValueChangeEvent!.key, 'test.get');
storage.store('test.getNumber', 5, scope);
strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5);
storage.store('test.getNumber', 0, scope);
strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0);
storage.store('test.getBoolean', true, scope);
strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true);
storage.store('test.getBoolean', false, scope);
strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false);
strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault');
strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5);
strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true);
}
test('Remove Data (global, in-memory)', () => {
removeData(StorageScope.GLOBAL);
});
test('Remove Data (workspace, in-memory)', () => {
removeData(StorageScope.WORKSPACE);
});
function removeData(scope: StorageScope): void {
const storage = new InMemoryStorageService();
let storageValueChangeEvents: IStorageValueChangeEvent[] = [];
storage.onDidChangeValue(e => storageValueChangeEvents.push(e));
storage.store('test.remove', 'foobar', scope);
strictEqual('foobar', storage.get('test.remove', scope, (undefined)!));
storage.remove('test.remove', scope);
ok(!storage.get('test.remove', scope, (undefined)!));
let storageValueChangeEvent = storageValueChangeEvents.find(e => e.key === 'test.remove');
equal(storageValueChangeEvent?.scope, scope);
equal(storageValueChangeEvent?.key, 'test.remove');
}
test('Keys (in-memory)', () => {
const storage = new InMemoryStorageService();
let storageTargetEvent: IStorageTargetChangeEvent | undefined = undefined;
storage.onDidChangeTarget(e => storageTargetEvent = e);
let storageValueChangeEvent: IStorageValueChangeEvent | undefined = undefined;
storage.onDidChangeValue(e => storageValueChangeEvent = e);
// Empty
for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) {
for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) {
strictEqual(storage.keys(scope, target).length, 0);
}
}
// Add values
for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) {
for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) {
storageTargetEvent = Object.create(null);
storageValueChangeEvent = Object.create(null);
storage.store2('test.target1', 'value1', scope, target);
strictEqual(storage.keys(scope, target).length, 1);
equal(storageTargetEvent?.scope, scope);
equal(storageValueChangeEvent?.key, 'test.target1');
equal(storageValueChangeEvent?.scope, scope);
equal(storageValueChangeEvent?.target, target);
storageTargetEvent = undefined;
storageValueChangeEvent = Object.create(null);
storage.store2('test.target1', 'otherValue1', scope, target);
strictEqual(storage.keys(scope, target).length, 1);
equal(storageTargetEvent, undefined);
equal(storageValueChangeEvent?.key, 'test.target1');
equal(storageValueChangeEvent?.scope, scope);
equal(storageValueChangeEvent?.target, target);
storage.store2('test.target2', 'value2', scope, target);
storage.store2('test.target3', 'value3', scope, target);
strictEqual(storage.keys(scope, target).length, 3);
}
}
// Remove values
for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) {
for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) {
const keysLength = storage.keys(scope, target).length;
storage.store2('test.target4', 'value1', scope, target);
strictEqual(storage.keys(scope, target).length, keysLength + 1);
storageTargetEvent = Object.create(null);
storageValueChangeEvent = Object.create(null);
storage.remove('test.target4', scope);
strictEqual(storage.keys(scope, target).length, keysLength);
equal(storageTargetEvent?.scope, scope);
equal(storageValueChangeEvent?.key, 'test.target4');
equal(storageValueChangeEvent?.scope, scope);
}
}
// Remove all
for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) {
for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) {
const keys = storage.keys(scope, target);
for (const key of keys) {
storage.remove(key, scope);
}
strictEqual(storage.keys(scope, target).length, 0);
}
}
// Adding undefined or null removes value
for (const scope of [StorageScope.WORKSPACE, StorageScope.GLOBAL]) {
for (const target of [StorageTarget.MACHINE, StorageTarget.USER]) {
storage.store2('test.target1', 'value1', scope, target);
strictEqual(storage.keys(scope, target).length, 1);
storageTargetEvent = Object.create(null);
storage.store2('test.target1', undefined, scope, target);
strictEqual(storage.keys(scope, target).length, 0);
equal(storageTargetEvent?.scope, scope);
storage.store2('test.target1', '', scope, target);
strictEqual(storage.keys(scope, target).length, 1);
storage.store2('test.target1', null, scope, target);
strictEqual(storage.keys(scope, target).length, 0);
}
}
// Target change
storageTargetEvent = undefined;
storage.store2('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE);
ok(storageTargetEvent);
storageTargetEvent = undefined;
storage.store2('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.USER);
ok(storageTargetEvent);
storageTargetEvent = undefined;
storage.store2('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE);
ok(storageTargetEvent);
storageTargetEvent = undefined;
storage.store2('test.target5', 'value1', StorageScope.GLOBAL, StorageTarget.MACHINE);
ok(!storageTargetEvent); // no change in target
});
});
@@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { strictEqual, ok, equal } from 'assert';
import { StorageScope, InMemoryStorageService } from 'vs/platform/storage/common/storage';
import { equal } from 'assert';
import { StorageScope } from 'vs/platform/storage/common/storage';
import { NativeStorageService } from 'vs/platform/storage/node/storageService';
import { generateUuid } from 'vs/base/common/uuid';
import { join } from 'vs/base/common/path';
@@ -16,66 +16,7 @@ import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
import { InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage';
import { URI } from 'vs/base/common/uri';
suite('StorageService', function () {
test('Remove Data (global, in-memory)', () => {
removeData(StorageScope.GLOBAL);
});
test('Remove Data (workspace, in-memory)', () => {
removeData(StorageScope.WORKSPACE);
});
function removeData(scope: StorageScope): void {
const storage = new InMemoryStorageService();
storage.store('test.remove', 'foobar', scope);
strictEqual('foobar', storage.get('test.remove', scope, (undefined)!));
storage.remove('test.remove', scope);
ok(!storage.get('test.remove', scope, (undefined)!));
}
test('Get Data, Integer, Boolean (global, in-memory)', () => {
storeData(StorageScope.GLOBAL);
});
test('Get Data, Integer, Boolean (workspace, in-memory)', () => {
storeData(StorageScope.WORKSPACE);
});
function storeData(scope: StorageScope): void {
const storage = new InMemoryStorageService();
strictEqual(storage.get('test.get', scope, 'foobar'), 'foobar');
strictEqual(storage.get('test.get', scope, ''), '');
strictEqual(storage.getNumber('test.getNumber', scope, 5), 5);
strictEqual(storage.getNumber('test.getNumber', scope, 0), 0);
strictEqual(storage.getBoolean('test.getBoolean', scope, true), true);
strictEqual(storage.getBoolean('test.getBoolean', scope, false), false);
storage.store('test.get', 'foobar', scope);
strictEqual(storage.get('test.get', scope, (undefined)!), 'foobar');
storage.store('test.get', '', scope);
strictEqual(storage.get('test.get', scope, (undefined)!), '');
storage.store('test.getNumber', 5, scope);
strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 5);
storage.store('test.getNumber', 0, scope);
strictEqual(storage.getNumber('test.getNumber', scope, (undefined)!), 0);
storage.store('test.getBoolean', true, scope);
strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), true);
storage.store('test.getBoolean', false, scope);
strictEqual(storage.getBoolean('test.getBoolean', scope, (undefined)!), false);
strictEqual(storage.get('test.getDefault', scope, 'getDefault'), 'getDefault');
strictEqual(storage.getNumber('test.getNumberDefault', scope, 5), 5);
strictEqual(storage.getBoolean('test.getBooleanDefault', scope, true), true);
}
suite('NativeStorageService', function () {
function uniqueStorageDir(): string {
const id = generateUuid();
@@ -115,6 +56,10 @@ suite('StorageService', function () {
storage.store('barNumber', 55, StorageScope.WORKSPACE);
storage.store('barBoolean', true, StorageScope.GLOBAL);
equal(storage.get('bar', StorageScope.WORKSPACE), 'foo');
equal(storage.getNumber('barNumber', StorageScope.WORKSPACE), 55);
equal(storage.getBoolean('barBoolean', StorageScope.GLOBAL), true);
await storage.migrate({ id: String(Date.now() + 100) });
equal(storage.get('bar', StorageScope.WORKSPACE), 'foo');
@@ -115,7 +115,7 @@ export class ExtensionsSynchroniser extends AbstractSynchroniser implements IUse
Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)),
this.extensionEnablementService.onDidChangeEnablement,
this.storageKeysSyncRegistryService.onDidChangeExtensionStorageKeys,
Event.filter(this.storageService.onDidChangeStorage, e => e.scope === StorageScope.GLOBAL
Event.filter(this.storageService.onDidChangeValue, e => e.scope === StorageScope.GLOBAL
&& this.storageKeysSyncRegistryService.extensionsStorageKeys.some(([extensionIdentifier]) => areSameExtensions(extensionIdentifier, { id: e.key })))),
() => undefined, 500)(() => this.triggerLocalChange()));
}
@@ -6,24 +6,22 @@
import * as objects from 'vs/base/common/objects';
import { IStorageValue } from 'vs/platform/userDataSync/common/userDataSync';
import { IStringDictionary } from 'vs/base/common/collections';
import { IStorageKey } from 'vs/platform/userDataSync/common/storageKeys';
import { ILogService } from 'vs/platform/log/common/log';
export interface IMergeResult {
local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> };
remote: IStringDictionary<IStorageValue> | null;
skipped: string[];
}
export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStorage: IStringDictionary<IStorageValue> | null, baseStorage: IStringDictionary<IStorageValue> | null, storageKeys: ReadonlyArray<IStorageKey>, previouslySkipped: string[], logService: ILogService): IMergeResult {
export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStorage: IStringDictionary<IStorageValue> | null, baseStorage: IStringDictionary<IStorageValue> | null, machineScopedStorageKeys: ReadonlyArray<string>, logService: ILogService): IMergeResult {
if (!remoteStorage) {
return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} }, skipped: [] };
return { remote: Object.keys(localStorage).length > 0 ? localStorage : null, local: { added: {}, removed: [], updated: {} } };
}
const localToRemote = compare(localStorage, remoteStorage);
if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) {
// No changes found between local and remote.
return { remote: null, local: { added: {}, removed: [], updated: {} }, skipped: [] };
return { remote: null, local: { added: {}, removed: [], updated: {} } };
}
const baseToRemote = baseStorage ? compare(baseStorage, remoteStorage) : { added: Object.keys(remoteStorage).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
@@ -31,26 +29,19 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
const local: { added: IStringDictionary<IStorageValue>, removed: string[], updated: IStringDictionary<IStorageValue> } = { added: {}, removed: [], updated: {} };
const remote: IStringDictionary<IStorageValue> = objects.deepClone(remoteStorage);
const skipped: string[] = [];
// Added in remote
for (const key of baseToRemote.added.values()) {
const remoteValue = remoteStorage[key];
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
if (!storageKey) {
skipped.push(key);
logService.trace(`GlobalState: Skipped adding ${key} in local storage as it is not registered.`);
continue;
}
if (storageKey.version !== remoteValue.version) {
logService.info(`GlobalState: Skipped adding ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
if (machineScopedStorageKeys.includes(key)) {
logService.info(`GlobalState: Skipped adding ${key} in local storage because it is declared as machine scoped.`);
continue;
}
const localValue = localStorage[key];
if (localValue && localValue.value === remoteValue.value) {
continue;
}
if (baseToLocal.added.has(key)) {
if (localValue) {
local.updated[key] = remoteValue;
} else {
local.added[key] = remoteValue;
@@ -60,28 +51,25 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
// Updated in Remote
for (const key of baseToRemote.updated.values()) {
const remoteValue = remoteStorage[key];
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
if (!storageKey) {
skipped.push(key);
logService.trace(`GlobalState: Skipped updating ${key} in local storage as is not registered.`);
continue;
}
if (storageKey.version !== remoteValue.version) {
logService.info(`GlobalState: Skipped updating ${key} in local storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
if (machineScopedStorageKeys.includes(key)) {
logService.info(`GlobalState: Skipped updating ${key} in local storage because it is declared as machine scoped.`);
continue;
}
const localValue = localStorage[key];
if (localValue && localValue.value === remoteValue.value) {
continue;
}
local.updated[key] = remoteValue;
if (localValue) {
local.updated[key] = remoteValue;
} else {
local.added[key] = remoteValue;
}
}
// Removed in remote
for (const key of baseToRemote.removed.values()) {
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
if (!storageKey) {
logService.trace(`GlobalState: Skipped removing ${key} in local storage. It is not registered to sync.`);
if (machineScopedStorageKeys.includes(key)) {
logService.trace(`GlobalState: Skipped removing ${key} in local storage because it is declared as machine scoped.`);
continue;
}
local.removed.push(key);
@@ -89,9 +77,10 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
// Added in local
for (const key of baseToLocal.added.values()) {
if (!baseToRemote.added.has(key)) {
remote[key] = localStorage[key];
if (baseToRemote.added.has(key)) {
continue;
}
remote[key] = localStorage[key];
}
// Updated in local
@@ -99,13 +88,7 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
if (baseToRemote.updated.has(key) || baseToRemote.removed.has(key)) {
continue;
}
const remoteValue = remote[key];
const localValue = localStorage[key];
if (localValue.version < remoteValue.version) {
logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${localValue.version}' and remote version '${remoteValue.version} are not same.`);
continue;
}
remote[key] = localValue;
remote[key] = localStorage[key];
}
// Removed in local
@@ -114,32 +97,10 @@ export function merge(localStorage: IStringDictionary<IStorageValue>, remoteStor
if (baseToRemote.updated.has(key)) {
continue;
}
const storageKey = storageKeys.filter(storageKey => storageKey.key === key)[0];
// do not remove from remote if storage key is not found
if (!storageKey) {
skipped.push(key);
logService.trace(`GlobalState: Skipped removing ${key} in remote storage. It is not registered to sync.`);
continue;
}
const remoteValue = remote[key];
// do not remove from remote if local data version is old
if (storageKey.version < remoteValue.version) {
logService.info(`GlobalState: Skipped updating ${key} in remote storage. Local version '${storageKey.version}' and remote version '${remoteValue.version} are not same.`);
continue;
}
// add to local if it was skipped before
if (previouslySkipped.indexOf(key) !== -1) {
local.added[key] = remote[key];
continue;
}
delete remote[key];
}
return { local, remote: areSame(remote, remoteStorage) ? null : remote, skipped };
return { local, remote: areSame(remote, remoteStorage) ? null : remote };
}
function compare(from: IStringDictionary<any>, to: IStringDictionary<any>): { added: Set<string>, removed: Set<string>, updated: Set<string> } {
@@ -21,9 +21,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { URI } from 'vs/base/common/uri';
import { format } from 'vs/base/common/jsonFormatter';
import { applyEdits } from 'vs/base/common/jsonEdit';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageKeysSyncRegistryService, IStorageKey } from 'vs/platform/userDataSync/common/storageKeys';
import { equals } from 'vs/base/common/arrays';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { CancellationToken } from 'vs/base/common/cancellation';
const argvStoragePrefx = 'globalState.argv.';
@@ -35,15 +33,19 @@ interface IGlobalStateResourceMergeResult extends IAcceptResult {
}
export interface IGlobalStateResourcePreview extends IResourcePreview {
readonly skippedStorageKeys: string[];
readonly localUserData: IGlobalState;
readonly previewResult: IGlobalStateResourceMergeResult;
}
interface ILastSyncUserData extends IRemoteUserData {
skippedStorageKeys: string[] | undefined;
}
/**
* Synchronises global state that includes
* - Global storage with user scope
* - Locale from argv properties
*
* Global storage is synced without checking version just like other resources (settings, keybindings).
* If there is a change in format of the value of a storage key which requires migration then
* Owner of that key should remove that key from user scope and replace that with new user scoped key.
*/
export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUserDataSynchroniser {
private static readonly GLOBAL_STATE_DATA_URI = URI.from({ scheme: USER_DATA_SYNC_SCHEME, authority: 'globalState', path: `/globalState.json` });
@@ -63,23 +65,22 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
@ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService configurationService: IConfigurationService,
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super(SyncResource.GlobalState, fileService, environmentService, storageService, userDataSyncStoreService, userDataSyncBackupStoreService, userDataSyncResourceEnablementService, telemetryService, logService, configurationService);
this._register(this.fileService.watch(this.extUri.dirname(this.environmentService.argvResource)));
this._register(fileService.watch(this.extUri.dirname(this.environmentService.argvResource)));
this._register(
Event.any(
/* Locale change */
Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource)),
/* Storage change */
Event.filter(this.storageService.onDidChangeStorage, e => storageKeysSyncRegistryService.storageKeys.some(({ key }) => e.key === key)),
/* Storage key registered */
this.storageKeysSyncRegistryService.onDidChangeStorageKeys
Event.filter(fileService.onDidFilesChange, e => e.contains(this.environmentService.argvResource)),
/* Global storage with user target has changed */
Event.filter(storageService.onDidChangeValue, e => e.scope === StorageScope.GLOBAL && e.target !== undefined ? e.target === StorageTarget.USER : storageService.keys(StorageScope.GLOBAL, StorageTarget.USER).includes(e.key)),
/* Storage key target has changed */
this.storageService.onDidChangeTarget
)((() => this.triggerLocalChange()))
);
}
protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, token: CancellationToken): Promise<IGlobalStateResourcePreview[]> {
protected async generateSyncPreview(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, token: CancellationToken): Promise<IGlobalStateResourcePreview[]> {
const remoteGlobalState: IGlobalState = remoteUserData.syncData ? JSON.parse(remoteUserData.syncData.content) : null;
const lastSyncGlobalState: IGlobalState | null = lastSyncUserData && lastSyncUserData.syncData ? JSON.parse(lastSyncUserData.syncData.content) : null;
@@ -91,7 +92,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
this.logService.trace(`${this.syncResourceLogLabel}: Remote ui state does not exist. Synchronizing ui state for the first time.`);
}
const { local, remote, skipped } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getSyncStorageKeys(), lastSyncUserData?.skippedStorageKeys || [], this.logService);
const { local, remote } = merge(localGloablState.storage, remoteGlobalState ? remoteGlobalState.storage : null, lastSyncGlobalState ? lastSyncGlobalState.storage : null, this.getIgnoredSyncStorageKeys(), this.logService);
const previewResult: IGlobalStateResourceMergeResult = {
content: null,
local,
@@ -101,7 +102,6 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
};
return [{
skippedStorageKeys: skipped,
localResource: this.localResource,
localContent: this.format(localGloablState),
localUserData: localGloablState,
@@ -152,7 +152,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
private async acceptRemote(resourcePreview: IGlobalStateResourcePreview): Promise<IGlobalStateResourceMergeResult> {
if (resourcePreview.remoteContent !== null) {
const remoteGlobalState: IGlobalState = JSON.parse(resourcePreview.remoteContent);
const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, this.getSyncStorageKeys(), resourcePreview.skippedStorageKeys, this.logService);
const { local, remote } = merge(resourcePreview.localUserData.storage, remoteGlobalState.storage, null, this.getIgnoredSyncStorageKeys(), this.logService);
return {
content: resourcePreview.remoteContent,
local,
@@ -171,8 +171,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
}
}
protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: ILastSyncUserData | null, resourcePreviews: [IGlobalStateResourcePreview, IGlobalStateResourceMergeResult][], force: boolean): Promise<void> {
let { localUserData, skippedStorageKeys } = resourcePreviews[0][0];
protected async applyResult(remoteUserData: IRemoteUserData, lastSyncUserData: IRemoteUserData | null, resourcePreviews: [IGlobalStateResourcePreview, IGlobalStateResourceMergeResult][], force: boolean): Promise<void> {
let { localUserData } = resourcePreviews[0][0];
let { local, remote, localChange, remoteChange } = resourcePreviews[0][1];
if (localChange === Change.None && remoteChange === Change.None) {
@@ -195,10 +195,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
this.logService.info(`${this.syncResourceLogLabel}: Updated remote ui state`);
}
if (lastSyncUserData?.ref !== remoteUserData.ref || !equals(lastSyncUserData.skippedStorageKeys, skippedStorageKeys)) {
if (lastSyncUserData?.ref !== remoteUserData.ref) {
// update last sync
this.logService.trace(`${this.syncResourceLogLabel}: Updating last synchronized ui state...`);
await this.updateLastSyncUserData(remoteUserData, { skippedStorageKeys });
await this.updateLastSyncUserData(remoteUserData);
this.logService.info(`${this.syncResourceLogLabel}: Updated last synchronized ui state`);
}
}
@@ -267,10 +267,10 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
storage[`${argvStoragePrefx}${argvProperty}`] = { version: 1, value: argvValue[argvProperty] };
}
}
for (const { key, version } of this.storageKeysSyncRegistryService.storageKeys) {
for (const key of this.storageService.keys(StorageScope.GLOBAL, StorageTarget.USER)) {
const value = this.storageService.get(key, StorageScope.GLOBAL);
if (value) {
storage[key] = { version, value };
storage[key] = { version: 1, value };
}
}
return { storage };
@@ -317,7 +317,7 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
if (updatedStorageKeys.length) {
this.logService.trace(`${this.syncResourceLogLabel}: Updating global state...`);
for (const key of Object.keys(updatedStorage)) {
this.storageService.store(key, updatedStorage[key], StorageScope.GLOBAL);
this.storageService.store2(key, updatedStorage[key], StorageScope.GLOBAL, StorageTarget.USER);
}
this.logService.info(`${this.syncResourceLogLabel}: Updated global state`, Object.keys(updatedStorage));
}
@@ -336,8 +336,8 @@ export class GlobalStateSynchroniser extends AbstractSynchroniser implements IUs
}
}
private getSyncStorageKeys(): IStorageKey[] {
return [...this.storageKeysSyncRegistryService.storageKeys, ...argvProperties.map(argvProprety => (<IStorageKey>{ key: `${argvStoragePrefx}${argvProprety}`, version: 1 }))];
private getIgnoredSyncStorageKeys(): string[] {
return this.storageService.keys(StorageScope.GLOBAL, StorageTarget.MACHINE);
}
}
@@ -385,7 +385,7 @@ export class GlobalStateInitializer extends AbstractInitializer {
if (Object.keys(storage).length) {
for (const key of Object.keys(storage)) {
this.storageService.store(key, storage[key], StorageScope.GLOBAL);
this.storageService.store2(key, storage[key], StorageScope.GLOBAL, StorageTarget.USER);
}
}
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagement';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
@@ -43,21 +43,6 @@ export interface IStorageKeysSyncRegistryService {
_serviceBrand: any;
/**
* All registered storage keys
*/
readonly storageKeys: ReadonlyArray<IStorageKey>;
/**
* Event that is triggered when storage keys are changed
*/
readonly onDidChangeStorageKeys: Event<ReadonlyArray<IStorageKey>>;
/**
* Register a storage key that has to be synchronized during sync.
*/
registerStorageKey(key: IStorageKey): void;
/**
* All registered extensions storage keys
*/
@@ -83,12 +68,6 @@ export abstract class AbstractStorageKeysSyncRegistryService extends Disposable
declare readonly _serviceBrand: undefined;
protected readonly _storageKeys = new Map<string, IStorageKey>();
get storageKeys(): ReadonlyArray<IStorageKey> { return [...this._storageKeys.values()]; }
protected readonly _onDidChangeStorageKeys: Emitter<ReadonlyArray<IStorageKey>> = this._register(new Emitter<ReadonlyArray<IStorageKey>>());
readonly onDidChangeStorageKeys = this._onDidChangeStorageKeys.event;
protected readonly _extensionsStorageKeys = new Map<string, string[]>();
get extensionsStorageKeys() {
const result: [IExtensionIdWithVersion, ReadonlyArray<string>][] = [];
@@ -98,11 +77,6 @@ export abstract class AbstractStorageKeysSyncRegistryService extends Disposable
protected readonly _onDidChangeExtensionStorageKeys = this._register(new Emitter<[IExtensionIdWithVersion, ReadonlyArray<string>]>());
readonly onDidChangeExtensionStorageKeys = this._onDidChangeExtensionStorageKeys.event;
constructor() {
super();
this._register(toDisposable(() => this._storageKeys.clear()));
}
getExtensioStorageKeys(extension: IExtensionIdWithVersion): ReadonlyArray<string> | undefined {
return this._extensionsStorageKeys.get(ExtensionIdWithVersion.toKey(extension));
}
@@ -112,7 +86,6 @@ export abstract class AbstractStorageKeysSyncRegistryService extends Disposable
this._onDidChangeExtensionStorageKeys.fire([extension, keys]);
}
abstract registerStorageKey(key: IStorageKey): void;
abstract registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void;
}
@@ -120,13 +93,6 @@ export class StorageKeysSyncRegistryService extends AbstractStorageKeysSyncRegis
_serviceBrand: any;
registerStorageKey(storageKey: IStorageKey): void {
if (!this._storageKeys.has(storageKey.key)) {
this._storageKeys.set(storageKey.key, storageKey);
this._onDidChangeStorageKeys.fire(this.storageKeys);
}
}
registerExtensionStorageKeys(extension: IExtensionIdWithVersion, keys: string[]): void {
this.updateExtensionStorageKeys(extension, keys);
}
@@ -11,7 +11,7 @@ import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/use
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { isPromiseCanceledError } from 'vs/base/common/errors';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageService, StorageScope, IStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
import { localize } from 'vs/nls';
@@ -55,7 +55,7 @@ export class UserDataAutoSyncEnablementService extends Disposable implements _IU
@IUserDataSyncStoreManagementService private readonly userDataSyncStoreManagementService: IUserDataSyncStoreManagementService,
) {
super();
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e)));
}
isEnabled(defaultEnablement?: boolean): boolean {
@@ -76,7 +76,7 @@ export class UserDataAutoSyncEnablementService extends Disposable implements _IU
this.storageService.store(enablementKey, enabled, StorageScope.GLOBAL);
}
private onDidStorageChange(storageChangeEvent: IStorageChangeEvent): void {
private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void {
if (storageChangeEvent.scope !== StorageScope.GLOBAL) {
return;
}
@@ -9,7 +9,7 @@ import { IUserDataSyncService, IUserDataSyncUtilService, IUserDataAutoSyncServic
import { URI } from 'vs/base/common/uri';
import { IStringDictionary } from 'vs/base/common/collections';
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
import { IStorageKeysSyncRegistryService, IStorageKey, IExtensionIdWithVersion, AbstractStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IStorageKeysSyncRegistryService, IExtensionIdWithVersion, AbstractStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { ILogService } from 'vs/platform/log/common/log';
import { IUserDataSyncMachinesService } from 'vs/platform/userDataSync/common/userDataSyncMachines';
import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
@@ -175,7 +175,7 @@ export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService {
}
type StorageKeysSyncRegistryServiceInitData = { storageKeys: ReadonlyArray<IStorageKey>, extensionsStorageKeys: ReadonlyArray<[IExtensionIdWithVersion, ReadonlyArray<string>]> };
type StorageKeysSyncRegistryServiceInitData = { extensionsStorageKeys: ReadonlyArray<[IExtensionIdWithVersion, ReadonlyArray<string>]> };
export class StorageKeysSyncRegistryChannel implements IServerChannel {
@@ -183,7 +183,6 @@ export class StorageKeysSyncRegistryChannel implements IServerChannel {
listen(_: unknown, event: string): Event<any> {
switch (event) {
case 'onDidChangeStorageKeys': return this.service.onDidChangeStorageKeys;
case 'onDidChangeExtensionStorageKeys': return this.service.onDidChangeExtensionStorageKeys;
}
throw new Error(`Event not found: ${event}`);
@@ -191,8 +190,7 @@ export class StorageKeysSyncRegistryChannel implements IServerChannel {
call(context: any, command: string, args?: any): Promise<any> {
switch (command) {
case '_getInitialData': return Promise.resolve<StorageKeysSyncRegistryServiceInitData>({ storageKeys: this.service.storageKeys, extensionsStorageKeys: this.service.extensionsStorageKeys });
case 'registerStorageKey': return Promise.resolve(this.service.registerStorageKey(args[0]));
case '_getInitialData': return Promise.resolve<StorageKeysSyncRegistryServiceInitData>({ extensionsStorageKeys: this.service.extensionsStorageKeys });
case 'registerExtensionStorageKeys': return Promise.resolve(this.service.registerExtensionStorageKeys(args[0], args[1]));
}
throw new Error('Invalid call');
@@ -205,32 +203,18 @@ export class StorageKeysSyncRegistryChannelClient extends AbstractStorageKeysSyn
constructor(private readonly channel: IChannel) {
super();
this.channel.call<StorageKeysSyncRegistryServiceInitData>('_getInitialData').then(({ storageKeys, extensionsStorageKeys }) => {
this.updateStorageKeys(storageKeys);
this.channel.call<StorageKeysSyncRegistryServiceInitData>('_getInitialData').then(({ extensionsStorageKeys }) => {
this.updateExtensionsStorageKeys(extensionsStorageKeys);
this._register(this.channel.listen<ReadonlyArray<IStorageKey>>('onDidChangeStorageKeys')(storageKeys => this.updateStorageKeys(storageKeys)));
this._register(this.channel.listen<[IExtensionIdentifierWithVersion, string[]]>('onDidChangeExtensionStorageKeys')(e => this.updateExtensionStorageKeys(e[0], e[1])));
});
}
private async updateStorageKeys(storageKeys: ReadonlyArray<IStorageKey>): Promise<void> {
this._storageKeys.clear();
for (const storageKey of storageKeys) {
this._storageKeys.set(storageKey.key, storageKey);
}
this._onDidChangeStorageKeys.fire(this.storageKeys);
}
private async updateExtensionsStorageKeys(extensionStorageKeys: ReadonlyArray<[IExtensionIdentifierWithVersion, ReadonlyArray<string>]>): Promise<void> {
for (const [extension, keys] of extensionStorageKeys) {
this.updateExtensionStorageKeys(extension, [...keys]);
}
}
registerStorageKey(storageKey: IStorageKey): void {
this.channel.call('registerStorageKey', [storageKey]);
}
registerExtensionStorageKeys(extension: IExtensionIdentifierWithVersion, keys: string[]): void {
this.channel.call('registerExtensionStorageKeys', [extension, keys]);
}
@@ -6,7 +6,7 @@
import { IUserDataSyncResourceEnablementService, ALL_SYNC_RESOURCES, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { Disposable } from 'vs/base/common/lifecycle';
import { Emitter, Event } from 'vs/base/common/event';
import { IStorageService, IStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, IStorageValueChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
type SyncEnablementClassification = {
@@ -28,7 +28,7 @@ export class UserDataSyncResourceEnablementService extends Disposable implements
@ITelemetryService private readonly telemetryService: ITelemetryService,
) {
super();
this._register(storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this._register(storageService.onDidChangeValue(e => this.onDidStorageChange(e)));
}
isResourceEnabled(resource: SyncResource): boolean {
@@ -43,7 +43,7 @@ export class UserDataSyncResourceEnablementService extends Disposable implements
}
}
private onDidStorageChange(storageChangeEvent: IStorageChangeEvent): void {
private onDidStorageChange(storageChangeEvent: IStorageValueChangeEvent): void {
if (storageChangeEvent.scope === StorageScope.GLOBAL) {
const resourceKey = ALL_SYNC_RESOURCES.filter(resourceKey => getEnablementKey(resourceKey) === storageChangeEvent.key)[0];
if (resourceKey) {
@@ -13,7 +13,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -25,7 +25,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -37,7 +37,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -50,7 +50,7 @@ suite('GlobalStateMerge', () => {
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const base = { 'b': { version: 1, value: 'a' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, base, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -62,7 +62,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, [], new NullLogService());
assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' } });
assert.deepEqual(actual.local.updated, {});
@@ -74,7 +74,7 @@ suite('GlobalStateMerge', () => {
const local = {};
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, [], new NullLogService());
assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } });
assert.deepEqual(actual.local.updated, {});
@@ -86,7 +86,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, local, [], new NullLogService());
assert.deepEqual(actual.local.added, { 'b': { version: 1, value: 'b' } });
assert.deepEqual(actual.local.updated, {});
@@ -98,7 +98,7 @@ suite('GlobalStateMerge', () => {
const local = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const remote = { 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, local, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -110,7 +110,7 @@ suite('GlobalStateMerge', () => {
const local = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const remote = {};
const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, local, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -122,7 +122,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'a': { version: 1, value: 'b' } };
const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, local, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } });
@@ -134,7 +134,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const remote = { 'a': { version: 1, value: 'd' }, 'c': { version: 1, value: 'c' } };
const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, local, [], new NullLogService());
assert.deepEqual(actual.local.added, { 'c': { version: 1, value: 'c' } });
assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'd' } });
@@ -146,7 +146,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const remote = { 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -158,7 +158,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' }, 'c': { version: 1, value: 'c' } };
const remote = { 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, remote, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -170,7 +170,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, remote, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -182,7 +182,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'b' } };
const remote = { 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, remote, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -194,7 +194,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'd' }, 'b': { version: 1, value: 'b' } };
const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } };
const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, remote, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -206,7 +206,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'a': { version: 1, value: 'b' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } });
@@ -219,7 +219,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'd' } };
const remote = { 'a': { version: 1, value: 'a' }, 'c': { version: 1, value: 'c' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, base, [], new NullLogService());
assert.deepEqual(actual.local.added, { 'c': { version: 1, value: 'c' } });
assert.deepEqual(actual.local.updated, {});
@@ -232,10 +232,10 @@ suite('GlobalStateMerge', () => {
const local = {};
const remote = { 'a': { version: 1, value: 'b' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, base, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } });
assert.deepEqual(actual.local.added, { 'a': { version: 1, value: 'b' } });
assert.deepEqual(actual.local.updated, {});
assert.deepEqual(actual.local.removed, []);
assert.deepEqual(actual.remote, null);
});
@@ -245,7 +245,7 @@ suite('GlobalStateMerge', () => {
const local = { 'a': { version: 1, value: 'd' } };
const remote = { 'a': { version: 1, value: 'b' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }, { key: 'c', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, base, [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, { 'a': { version: 1, value: 'b' } });
@@ -253,11 +253,11 @@ suite('GlobalStateMerge', () => {
assert.deepEqual(actual.remote, null);
});
test('merge when a new entry is added to remote but not a registered key', async () => {
test('merge when a new entry is added to remote but scoped to machine locally', async () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, null, ['b'], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -265,23 +265,11 @@ suite('GlobalStateMerge', () => {
assert.deepEqual(actual.remote, null);
});
test('merge when a new entry is added to remote but different version', async () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, null, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
assert.deepEqual(actual.local.removed, []);
assert.deepEqual(actual.remote, null);
});
test('merge when an entry is updated to remote but not a registered key', async () => {
test('merge when an entry is updated to remote but scoped to machine locally', async () => {
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'a': { version: 1, value: 'b' } };
const actual = merge(local, remote, local, [], [], new NullLogService());
const actual = merge(local, remote, local, ['a'], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -289,11 +277,12 @@ suite('GlobalStateMerge', () => {
assert.deepEqual(actual.remote, null);
});
test('merge when a new entry is updated to remote but different version', async () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
test('merge when a local value is removed and iscoped to machine locally', async () => {
const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, local, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
const actual = merge(local, remote, base, ['b'], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -301,23 +290,12 @@ suite('GlobalStateMerge', () => {
assert.deepEqual(actual.remote, null);
});
test('merge when a local value is update with lower version', async () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'c' } };
const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } };
test('merge when local moved forwared by changing a key to machine scope', async () => {
const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const remote = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const local = { 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
assert.deepEqual(actual.local.removed, []);
assert.deepEqual(actual.remote, null);
});
test('merge when a local value is update with higher version', async () => {
const local = { 'a': { version: 1, value: 'a' }, 'b': { version: 2, value: 'c' } };
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, remote, [{ key: 'a', version: 1 }, { key: 'b', version: 2 }], [], new NullLogService());
const actual = merge(local, remote, base, ['b'], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
@@ -325,56 +303,4 @@ suite('GlobalStateMerge', () => {
assert.deepEqual(actual.remote, local);
});
test('merge when a local value is removed but not registered', async () => {
const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }], [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
assert.deepEqual(actual.local.removed, []);
assert.deepEqual(actual.remote, null);
});
test('merge when a local value is removed with lower version', async () => {
const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 2, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 1 }], [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
assert.deepEqual(actual.local.removed, []);
assert.deepEqual(actual.remote, null);
});
test('merge when a local value is removed with higher version', async () => {
const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }, { key: 'b', version: 2 }], [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
assert.deepEqual(actual.local.removed, []);
assert.deepEqual(actual.remote, local);
});
test('merge when a local value is not yet registered', async () => {
const base = { 'a': { version: 1, value: 'a' }, 'b': { version: 1, value: 'b' } };
const local = { 'a': { version: 1, value: 'a' } };
const remote = { 'b': { version: 1, value: 'b' }, 'a': { version: 1, value: 'a' } };
const actual = merge(local, remote, base, [{ key: 'a', version: 1 }], [], new NullLogService());
assert.deepEqual(actual.local.added, {});
assert.deepEqual(actual.local.updated, {});
assert.deepEqual(actual.local.removed, []);
assert.deepEqual(actual.remote, null);
});
});
@@ -10,10 +10,9 @@ import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService';
import { IFileService } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { GlobalStateSynchroniser } from 'vs/platform/userDataSync/common/globalStateSync';
import { VSBuffer } from 'vs/base/common/buffer';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
suite('GlobalStateSync', () => {
@@ -28,17 +27,11 @@ suite('GlobalStateSync', () => {
setup(async () => {
testClient = disposableStore.add(new UserDataSyncClient(server));
await testClient.setUp(true);
let storageKeysSyncRegistryService = testClient.instantiationService.get(IStorageKeysSyncRegistryService);
storageKeysSyncRegistryService.registerStorageKey({ key: 'a', version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: 'b', version: 1 });
testObject = (testClient.instantiationService.get(IUserDataSyncService) as UserDataSyncService).getSynchroniser(SyncResource.GlobalState) as GlobalStateSynchroniser;
disposableStore.add(toDisposable(() => testClient.instantiationService.get(IUserDataSyncStoreService).clear()));
client2 = disposableStore.add(new UserDataSyncClient(server));
await client2.setUp(true);
storageKeysSyncRegistryService = client2.instantiationService.get(IStorageKeysSyncRegistryService);
storageKeysSyncRegistryService.registerStorageKey({ key: 'a', version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: 'b', version: 1 });
});
teardown(() => disposableStore.clear());
@@ -72,7 +65,7 @@ suite('GlobalStateSync', () => {
test('when global state is created after first sync', async () => {
await testObject.sync(await testClient.manifest());
updateStorage('a', 'value1', testClient);
updateUserStorage('a', 'value1', testClient);
let lastSyncUserData = await testObject.getLastSyncUserData();
const manifest = await testClient.manifest();
@@ -91,7 +84,8 @@ suite('GlobalStateSync', () => {
});
test('first time sync - outgoing to server (no state)', async () => {
updateStorage('a', 'value1', testClient);
updateUserStorage('a', 'value1', testClient);
updateMachineStorage('b', 'value1', testClient);
await updateLocale(testClient);
await testObject.sync(await testClient.manifest());
@@ -105,7 +99,7 @@ suite('GlobalStateSync', () => {
});
test('first time sync - incoming from server (no state)', async () => {
updateStorage('a', 'value1', client2);
updateUserStorage('a', 'value1', client2);
await updateLocale(client2);
await client2.sync();
@@ -118,10 +112,10 @@ suite('GlobalStateSync', () => {
});
test('first time sync when storage exists', async () => {
updateStorage('a', 'value1', client2);
updateUserStorage('a', 'value1', client2);
await client2.sync();
updateStorage('b', 'value2', testClient);
updateUserStorage('b', 'value2', testClient);
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -136,10 +130,10 @@ suite('GlobalStateSync', () => {
});
test('first time sync when storage exists - has conflicts', async () => {
updateStorage('a', 'value1', client2);
updateUserStorage('a', 'value1', client2);
await client2.sync();
updateStorage('a', 'value2', client2);
updateUserStorage('a', 'value2', client2);
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
@@ -154,10 +148,10 @@ suite('GlobalStateSync', () => {
});
test('sync adding a storage value', async () => {
updateStorage('a', 'value1', testClient);
updateUserStorage('a', 'value1', testClient);
await testObject.sync(await testClient.manifest());
updateStorage('b', 'value2', testClient);
updateUserStorage('b', 'value2', testClient);
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -172,10 +166,10 @@ suite('GlobalStateSync', () => {
});
test('sync updating a storage value', async () => {
updateStorage('a', 'value1', testClient);
updateUserStorage('a', 'value1', testClient);
await testObject.sync(await testClient.manifest());
updateStorage('a', 'value2', testClient);
updateUserStorage('a', 'value2', testClient);
await testObject.sync(await testClient.manifest());
assert.equal(testObject.status, SyncStatus.Idle);
assert.deepEqual(testObject.conflicts, []);
@@ -189,8 +183,8 @@ suite('GlobalStateSync', () => {
});
test('sync removing a storage value', async () => {
updateStorage('a', 'value1', testClient);
updateStorage('b', 'value2', testClient);
updateUserStorage('a', 'value1', testClient);
updateUserStorage('b', 'value2', testClient);
await testObject.sync(await testClient.manifest());
removeStorage('b', testClient);
@@ -218,9 +212,14 @@ suite('GlobalStateSync', () => {
await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'en' })));
}
function updateStorage(key: string, value: string, client: UserDataSyncClient): void {
function updateUserStorage(key: string, value: string, client: UserDataSyncClient): void {
const storageService = client.instantiationService.get(IStorageService);
storageService.store(key, value, StorageScope.GLOBAL);
storageService.store2(key, value, StorageScope.GLOBAL, StorageTarget.USER);
}
function updateMachineStorage(key: string, value: string, client: UserDataSyncClient): void {
const storageService = client.instantiationService.get(IStorageService);
storageService.store2(key, value, StorageScope.GLOBAL, StorageTarget.MACHINE);
}
function removeStorage(key: string, client: UserDataSyncClient): void {
@@ -10,11 +10,10 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { IAuthenticationService, AllowedExtension, readAllowedExtensions, getAuthenticationProviderActivationEvent } from 'vs/workbench/services/authentication/browser/authenticationService';
import { ExtHostAuthenticationShape, ExtHostContext, IExtHostContext, MainContext, MainThreadAuthenticationShape } from '../common/extHost.protocol';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import Severity from 'vs/base/common/severity';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { fromNow } from 'vs/base/common/date';
import { ActivationKind, IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -83,7 +82,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
public readonly label: string,
public readonly supportsMultipleAccounts: boolean,
private readonly notificationService: INotificationService,
private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
private readonly storageService: IStorageService,
private readonly quickInputService: IQuickInputService,
private readonly dialogService: IDialogService
@@ -122,7 +120,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
quickPick.onDidAccept(() => {
const updatedAllowedList = quickPick.selectedItems.map(item => item.extension);
this.storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL);
this.storageService.store2(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL, StorageTarget.USER);
quickPick.dispose();
});
@@ -153,8 +151,6 @@ export class MainThreadAuthenticationProvider extends Disposable {
} else {
this._accounts.set(session.account.label, [session.id]);
}
this.storageKeysSyncRegistryService.registerStorageKey({ key: `${this.id}-${session.account.label}`, version: 1 });
}
async signOut(accountName: string): Promise<void> {
@@ -220,7 +216,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
@IDialogService private readonly dialogService: IDialogService,
@IStorageService private readonly storageService: IStorageService,
@INotificationService private readonly notificationService: INotificationService,
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IExtensionService private readonly extensionService: IExtensionService,
@@ -259,7 +254,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
}
async $registerAuthenticationProvider(id: string, label: string, supportsMultipleAccounts: boolean): Promise<void> {
const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageKeysSyncRegistryService, this.storageService, this.quickInputService, this.dialogService);
const provider = new MainThreadAuthenticationProvider(this._proxy, id, label, supportsMultipleAccounts, this.notificationService, this.storageService, this.quickInputService, this.dialogService);
await provider.initialize();
this.authenticationService.registerAuthenticationProvider(id, provider);
}
@@ -383,7 +378,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
if (!allowList.find(allowed => allowed.id === extensionId)) {
allowList.push({ id: extensionId, name: extensionName });
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
this.storageService.store2(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER);
}
this.storageService.store(`${extensionName}-${providerId}`, session.id, StorageScope.GLOBAL);
@@ -435,7 +430,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
if (allow) {
addAccountUsage(this.storageService, providerId, accountName, extensionId, extensionName);
allowList.push({ id: extensionId, name: extensionName });
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
this.storageService.store2(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER);
}
return allow;
@@ -458,7 +453,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
const allowList = readAllowedExtensions(this.storageService, providerId, accountName);
if (!allowList.find(allowed => allowed.id === extensionId)) {
allowList.push({ id: extensionId, name: extensionName });
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
this.storageService.store2(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL, StorageTarget.USER);
}
this.storageService.store(`${extensionName}-${providerId}`, sessionId, StorageScope.GLOBAL);
@@ -27,7 +27,7 @@ export class MainThreadStorage implements MainThreadStorageShape {
this._storageKeysSyncRegistryService = storageKeysSyncRegistryService;
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostStorage);
this._storageListener = this._storageService.onDidChangeStorage(e => {
this._storageListener = this._storageService.onDidChangeValue(e => {
const shared = e.scope === StorageScope.GLOBAL;
if (shared && this._sharedStorageKeysToWatch.has(e.key)) {
try {
+3 -3
View File
@@ -16,7 +16,7 @@ import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart';
import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel';
import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString } from 'vs/workbench/services/layout/browser/layoutService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IStorageService, StorageScope, WillSaveStateReason } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
@@ -673,7 +673,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
if (sidebarState.length) {
storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, JSON.stringify(sidebarState), StorageScope.GLOBAL);
storageService.store2(ActivitybarPart.PINNED_VIEW_CONTAINERS, JSON.stringify(sidebarState), StorageScope.GLOBAL, StorageTarget.USER);
}
}
}
@@ -743,7 +743,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
}
if (panelState.length) {
storageService.store(PanelPart.PINNED_PANELS, JSON.stringify(panelState), StorageScope.GLOBAL);
storageService.store2(PanelPart.PINNED_PANELS, JSON.stringify(panelState), StorageScope.GLOBAL, StorageTarget.USER);
}
}
}
@@ -32,7 +32,7 @@ import { getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/
import { AuthenticationSession } from 'vs/editor/common/modes';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IProductService } from 'vs/platform/product/common/productService';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
@@ -235,7 +235,7 @@ export class AccountsActionViewItem extends ActivityActionViewItem {
}
menus.push(new Action('hide', nls.localize('hide', "Hide"), undefined, true, _ => {
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL);
this.storageService.store2(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER);
return Promise.resolve();
}));
@@ -19,7 +19,7 @@ import { ACTIVITY_BAR_BACKGROUND, ACTIVITY_BAR_BORDER, ACTIVITY_BAR_FOREGROUND,
import { contrastBorder } from 'vs/platform/theme/common/colorRegistry';
import { CompositeBar, ICompositeBarItem, CompositeDragAndDrop } from 'vs/workbench/browser/parts/compositeBar';
import { Dimension, createCSSRule, asCSSUrl, addDisposableListener, EventType } from 'vs/base/browser/dom';
import { IStorageService, StorageScope, IStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { URI, UriComponents } from 'vs/base/common/uri';
import { ToggleCompositePinnedAction, ICompositeBarColors, ActivityAction, ICompositeActivity } from 'vs/workbench/browser/parts/compositeBarActions';
@@ -34,7 +34,6 @@ import { CustomMenubarControl } from 'vs/workbench/browser/parts/titlebar/menuba
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { getMenuBarVisibility } from 'vs/platform/windows/common/windows';
import { isWeb } from 'vs/base/common/platform';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { Before2D } from 'vs/workbench/browser/dnd';
import { Codicon, iconRegistry } from 'vs/base/common/codicons';
import { Action, Separator } from 'vs/base/common/actions';
@@ -125,14 +124,9 @@ export class ActivitybarPart extends Part implements IActivityBarService {
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.PINNED_VIEW_CONTAINERS, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: ACCOUNTS_VISIBILITY_PREFERENCE_KEY, version: 1 });
this.migrateFromOldCachedViewContainersValue();
for (const cachedViewContainer of this.cachedViewContainers) {
@@ -253,7 +247,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
disposables.clear();
this.onDidRegisterExtensions();
this.compositeBar.onDidChange(() => this.saveCachedViewContainers(), this, disposables);
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables);
}));
// Register for configuration changes
@@ -841,7 +835,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
return this.viewDescriptorService.getViewContainersByLocation(this.location);
}
private onDidStorageChange(e: IStorageChangeEvent): void {
private onDidStorageValueChange(e: IStorageValueChangeEvent): void {
if (e.key === ActivitybarPart.PINNED_VIEW_CONTAINERS && e.scope === StorageScope.GLOBAL
&& this.pinnedViewContainersValue !== this.getStoredPinnedViewContainersValue() /* This checks if current window changed the value or not */) {
this._pinnedViewContainersValue = undefined;
@@ -971,7 +965,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
private setStoredPinnedViewContainersValue(value: string): void {
this.storageService.store(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL);
this.storageService.store2(ActivitybarPart.PINNED_VIEW_CONTAINERS, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private getPlaceholderViewContainers(): IPlaceholderViewContainer[] {
@@ -1011,7 +1005,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
private set homeBarVisibilityPreference(value: boolean) {
this.storageService.store(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL);
this.storageService.store2(ActivitybarPart.HOME_BAR_VISIBILITY_PREFERENCE, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private get accountsVisibilityPreference(): boolean {
@@ -1019,7 +1013,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
}
private set accountsVisibilityPreference(value: boolean) {
this.storageService.store(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL);
this.storageService.store2(ACCOUNTS_VISIBILITY_PREFERENCE_KEY, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private migrateFromOldCachedViewContainersValue(): void {
@@ -13,7 +13,7 @@ import { CompositePart, ICompositeTitleLabel } from 'vs/workbench/browser/parts/
import { Panel, PanelRegistry, Extensions as PanelExtensions, PanelDescriptor } from 'vs/workbench/browser/panel';
import { IPanelService, IPanelIdentifier } from 'vs/workbench/services/panel/common/panelService';
import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService';
import { IStorageService, StorageScope, IStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
@@ -37,7 +37,6 @@ import { ViewContainer, IViewDescriptorService, IViewContainerModel, ViewContain
import { MenuId } from 'vs/platform/actions/common/actions';
import { ViewMenuActions, ViewContainerMenuActions } from 'vs/workbench/browser/parts/views/viewMenuActions';
import { IPaneComposite } from 'vs/workbench/common/panecomposite';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { Before2D, CompositeDragAndDropObserver, ICompositeDragAndDrop, toggleDropEffect } from 'vs/workbench/browser/dnd';
import { IActivity } from 'vs/workbench/common/activity';
@@ -118,7 +117,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
@IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IExtensionService private readonly extensionService: IExtensionService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super(
notificationService,
@@ -140,7 +138,6 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
);
this.panelRegistry = Registry.as<PanelRegistry>(PanelExtensions.Panels);
storageKeysSyncRegistryService.registerStorageKey({ key: PanelPart.PINNED_PANELS, version: 1 });
this.dndHandler = new CompositeDragAndDrop(this.viewDescriptorService, ViewContainerLocation.Panel,
(id: string, focus?: boolean) => (this.openPanel(id, focus) as Promise<IPaneComposite | undefined>).then(panel => panel || null),
@@ -338,7 +335,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
disposables.clear();
this.onDidRegisterExtensions();
this.compositeBar.onDidChange(() => this.saveCachedPanels(), this, disposables);
this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e), this, disposables);
this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e), this, disposables);
}));
}
@@ -670,7 +667,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
return this.toolBar.getItemsWidth();
}
private onDidStorageChange(e: IStorageChangeEvent): void {
private onDidStorageValueChange(e: IStorageValueChangeEvent): void {
if (e.key === PanelPart.PINNED_PANELS && e.scope === StorageScope.GLOBAL
&& this.cachedPanelsValue !== this.getStoredCachedPanelsValue() /* This checks if current window changed the value or not */) {
this._cachedPanelsValue = undefined;
@@ -760,7 +757,7 @@ export class PanelPart extends CompositePart<Panel> implements IPanelService {
}
private setStoredCachedViewletsValue(value: string): void {
this.storageService.store(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL);
this.storageService.store2(PanelPart.PINNED_PANELS, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private getPlaceholderViewContainers(): IPlaceholderViewContainer[] {
@@ -23,7 +23,7 @@ import { isThemeColor } from 'vs/editor/common/editorCommon';
import { Color } from 'vs/base/common/color';
import { EventHelper, createStyleSheet, addDisposableListener, EventType, hide, show, isAncestor, appendChildren } from 'vs/base/browser/dom';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope, IStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { Parts, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { coalesce } from 'vs/base/common/arrays';
@@ -32,7 +32,6 @@ import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/la
import { assertIsDefined } from 'vs/base/common/types';
import { Emitter } from 'vs/base/common/event';
import { Command } from 'vs/editor/common/modes';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
@@ -102,10 +101,10 @@ class StatusbarViewModel extends Disposable {
}
private registerListeners(): void {
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this._register(this.storageService.onDidChangeValue(e => this.onDidStorageValueChange(e)));
}
private onDidStorageChange(event: IStorageChangeEvent): void {
private onDidStorageValueChange(event: IStorageValueChangeEvent): void {
if (event.key === StatusbarViewModel.HIDDEN_ENTRIES_KEY && event.scope === StorageScope.GLOBAL) {
// Keep current hidden entries
@@ -275,7 +274,7 @@ class StatusbarViewModel extends Disposable {
private saveState(): void {
if (this.hidden.size > 0) {
this.storageService.store(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL);
this.storageService.store2(StatusbarViewModel.HIDDEN_ENTRIES_KEY, JSON.stringify(Array.from(this.hidden.values())), StorageScope.GLOBAL, StorageTarget.USER);
} else {
this.storageService.remove(StatusbarViewModel.HIDDEN_ENTRIES_KEY, StorageScope.GLOBAL);
}
@@ -405,12 +404,9 @@ export class StatusbarPart extends Part implements IStatusbarService {
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
@IContextMenuService private contextMenuService: IContextMenuService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
storageKeysSyncRegistryService.registerStorageKey({ key: StatusbarViewModel.HIDDEN_ENTRIES_KEY, version: 1 });
this.registerListeners();
}
@@ -11,7 +11,6 @@ import { registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
/**
* Shows a message when opening a large file which has been memory optimized (and features disabled).
@@ -24,14 +23,9 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC
private readonly _editor: ICodeEditor,
@INotificationService private readonly _notificationService: INotificationService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
const neverShowAgainId = 'editor.contrib.largeFileOptimizationsWarner';
storageKeysSyncRegistryService.registerStorageKey({ key: neverShowAgainId, version: 1 });
this._register(this._editor.onDidChangeModel((e) => {
const model = this._editor.getModel();
if (!model) {
@@ -61,7 +55,7 @@ export class LargeFileOptimizationsWarner extends Disposable implements IEditorC
});
}
}
], { neverShowAgain: { id: neverShowAgainId } });
], { neverShowAgain: { id: 'editor.contrib.largeFileOptimizationsWarner' } });
}
}));
}
@@ -16,9 +16,8 @@ import { areSameExtensions } from 'vs/platform/extensionManagement/common/extens
import { IExtensionRecommendationNotificationService, RecommendationsNotificationResult, RecommendationSource } from 'vs/platform/extensionRecommendations/common/extensionRecommendations';
import { IInstantiationService, optional } from 'vs/platform/instantiation/common/instantiation';
import { INotificationHandle, INotificationService, IPromptChoice, Severity } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IUserDataAutoSyncEnablementService, IUserDataSyncResourceEnablementService, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
import { SearchExtensionsAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IExtension, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions';
@@ -142,12 +141,10 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
@IWorkbenchExtensioManagementService private readonly extensionManagementService: IWorkbenchExtensioManagementService,
@IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService,
@IExtensionIgnoredRecommendationsService private readonly extensionIgnoredRecommendationsService: IExtensionIgnoredRecommendationsService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService,
@IUserDataSyncResourceEnablementService private readonly userDataSyncResourceEnablementService: IUserDataSyncResourceEnablementService,
@optional(ITASExperimentService) tasExperimentService: ITASExperimentService,
) {
storageKeysSyncRegistryService.registerStorageKey({ key: ignoreImportantExtensionRecommendationStorageKey, version: 1 });
this.tasExperimentService = tasExperimentService;
}
@@ -423,7 +420,7 @@ export class ExtensionRecommendationNotificationService implements IExtensionRec
const importantRecommendationsIgnoreList = [...this.ignoredRecommendations];
if (!importantRecommendationsIgnoreList.includes(id.toLowerCase())) {
importantRecommendationsIgnoreList.push(id.toLowerCase());
this.storageService.store(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL);
this.storageService.store2(ignoreImportantExtensionRecommendationStorageKey, JSON.stringify(importantRecommendationsIgnoreList), StorageScope.GLOBAL, StorageTarget.USER);
}
}
@@ -23,7 +23,7 @@ import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileE
import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/contrib/files/browser/fileCommands';
import { INotificationService, INotificationHandle, INotificationActions, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { IProductService } from 'vs/platform/product/common/productService';
import { Event } from 'vs/base/common/event';
@@ -32,7 +32,6 @@ import { isWindows } from 'vs/base/common/platform';
import { Schemas } from 'vs/base/common/network';
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { SaveReason } from 'vs/workbench/common/editor';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext';
export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution';
@@ -56,13 +55,9 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa
@ITextModelService textModelService: ITextModelService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, version: 1 });
const provider = this._register(instantiationService.createInstance(TextFileContentProvider));
this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider));
@@ -225,7 +220,7 @@ class DoNotShowResolveConflictLearnMoreAction extends Action {
}
async run(notification: IDisposable): Promise<void> {
this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL);
this.storageService.store2(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true, StorageScope.GLOBAL, StorageTarget.USER);
// Hide notification
notification.dispose();
@@ -19,13 +19,12 @@ import Severity from 'vs/base/common/severity';
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { VIEWLET_ID as EXTENSIONS_VIEWLET_ID, IExtensionsViewPaneContainer } from 'vs/workbench/contrib/extensions/common/extensions';
import { minimumTranslatedStrings } from 'vs/workbench/contrib/localizations/browser/minimalTranslations';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CancellationToken } from 'vs/base/common/cancellation';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
// Register action to configure locale and related settings
const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActions);
@@ -44,12 +43,9 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
@IExtensionGalleryService private readonly galleryService: IExtensionGalleryService,
@IViewletService private readonly viewletService: IViewletService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
storageKeysSyncRegistryService.registerStorageKey({ key: LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: 'langugage.update.donotask', version: 1 });
this.checkAndInstall();
this._register(this.extensionManagementService.onDidInstallExtension(e => this.onDidInstallExtension(e)));
}
@@ -172,10 +168,11 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo
isSecondary: true,
run: () => {
languagePackSuggestionIgnoreList.push(language);
this.storageService.store(
this.storageService.store2(
LANGUAGEPACK_SUGGESTION_IGNORE_STORAGE_KEY,
JSON.stringify(languagePackSuggestionIgnoreList),
StorageScope.GLOBAL
StorageScope.GLOBAL,
StorageTarget.USER
);
logUserReaction('neverShowAgain');
}
@@ -31,12 +31,11 @@ import { IEditorModel } from 'vs/platform/editor/common/editor';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { badgeBackground, badgeForeground, contrastBorder, editorForeground } from 'vs/platform/theme/common/colorRegistry';
import { attachButtonStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IUserDataAutoSyncEnablementService, IUserDataSyncService, SyncStatus } from 'vs/platform/userDataSync/common/userDataSync';
import { EditorPane } from 'vs/workbench/browser/parts/editor/editorPane';
import { IEditorMemento, IEditorOpenContext, IEditorPane } from 'vs/workbench/common/editor';
@@ -173,7 +172,6 @@ export class SettingsEditor2 extends EditorPane {
@IStorageService private readonly storageService: IStorageService,
@INotificationService private readonly notificationService: INotificationService,
@IEditorGroupsService protected editorGroupService: IEditorGroupsService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IUserDataSyncWorkbenchService private readonly userDataSyncWorkbenchService: IUserDataSyncWorkbenchService,
@IUserDataAutoSyncEnablementService private readonly userDataAutoSyncEnablementService: IUserDataAutoSyncEnablementService
) {
@@ -202,8 +200,6 @@ export class SettingsEditor2 extends EditorPane {
this.onConfigUpdate(e.affectedKeys);
}
}));
storageKeysSyncRegistryService.registerStorageKey({ key: SETTINGS_AUTOSAVE_NOTIFIED_KEY, version: 1 });
}
get minimumWidth(): number { return 375; }
@@ -737,7 +733,7 @@ export class SettingsEditor2 extends EditorPane {
private notifyNoSaveNeeded() {
if (!this.storageService.getBoolean(SETTINGS_AUTOSAVE_NOTIFIED_KEY, StorageScope.GLOBAL, false)) {
this.storageService.store(SETTINGS_AUTOSAVE_NOTIFIED_KEY, true, StorageScope.GLOBAL);
this.storageService.store2(SETTINGS_AUTOSAVE_NOTIFIED_KEY, true, StorageScope.GLOBAL, StorageTarget.USER);
this.notificationService.info(localize('settingsNoSaveNeeded', "Your changes are automatically saved as you edit."));
}
}
@@ -26,9 +26,8 @@ import { languagesExtPoint } from 'vs/workbench/services/mode/common/workbenchMo
import { SnippetCompletionProvider } from './snippetCompletionProvider';
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
import { ResourceMap } from 'vs/base/common/map';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { isStringArray } from 'vs/base/common/types';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
namespace snippetExt {
@@ -136,11 +135,8 @@ class SnippetEnablement {
constructor(
@IStorageService private readonly _storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncService: IStorageKeysSyncRegistryService,
) {
storageKeysSyncService.registerStorageKey({ key: SnippetEnablement._key, version: 1 });
const raw = _storageService.get(SnippetEnablement._key, StorageScope.GLOBAL, '');
let data: string[] | undefined;
try {
@@ -164,7 +160,7 @@ class SnippetEnablement {
changed = true;
}
if (changed) {
this._storageService.store(SnippetEnablement._key, JSON.stringify(Array.from(this._ignored)), StorageScope.GLOBAL);
this._storageService.store2(SnippetEnablement._key, JSON.stringify(Array.from(this._ignored)), StorageScope.GLOBAL, StorageTarget.USER);
}
}
}
@@ -9,8 +9,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ISurveyData, IProductService } from 'vs/platform/product/common/productService';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Severity, INotificationService } from 'vs/platform/notification/common/notification';
@@ -26,7 +25,6 @@ class LanguageSurvey extends Disposable {
constructor(
data: ISurveyData,
storageService: IStorageService,
storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
notificationService: INotificationService,
telemetryService: ITelemetryService,
modelService: IModelService,
@@ -43,14 +41,6 @@ class LanguageSurvey extends Disposable {
const EDITED_LANGUAGE_COUNT_KEY = `${data.surveyId}.editedCount`;
const EDITED_LANGUAGE_DATE_KEY = `${data.surveyId}.editedDate`;
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: SESSION_COUNT_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: LAST_SESSION_DATE_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: SKIP_VERSION_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: IS_CANDIDATE_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: EDITED_LANGUAGE_COUNT_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: EDITED_LANGUAGE_DATE_KEY, version: 1 });
const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, '');
if (skipVersion) {
return;
@@ -65,8 +55,8 @@ class LanguageSurvey extends Disposable {
models.forEach(m => {
if (m.getMode() === data.languageId && date !== storageService.get(EDITED_LANGUAGE_DATE_KEY, StorageScope.GLOBAL)) {
const editedCount = storageService.getNumber(EDITED_LANGUAGE_COUNT_KEY, StorageScope.GLOBAL, 0) + 1;
storageService.store(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL);
storageService.store(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL);
storageService.store2(EDITED_LANGUAGE_COUNT_KEY, editedCount, StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(EDITED_LANGUAGE_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER);
}
});
}, 250));
@@ -80,8 +70,8 @@ class LanguageSurvey extends Disposable {
}
const sessionCount = storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) + 1;
storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL);
storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL);
storageService.store2(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL, StorageTarget.USER);
if (sessionCount < 9) {
return;
@@ -94,10 +84,10 @@ class LanguageSurvey extends Disposable {
const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false)
|| Math.random() < data.userProbability;
storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL);
storageService.store2(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL, StorageTarget.USER);
if (!isCandidate) {
storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL);
storageService.store2(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER);
return;
}
@@ -113,23 +103,23 @@ class LanguageSurvey extends Disposable {
telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`);
telemetryService.getTelemetryInfo().then(info => {
openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`));
storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL);
storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL);
storageService.store2(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER);
});
}
}, {
label: nls.localize('remindLater', "Remind Me later"),
run: () => {
telemetryService.publicLog(`${data.surveyId}.survey/remindMeLater`);
storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL);
storageService.store2(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL, StorageTarget.USER);
}
}, {
label: nls.localize('neverAgain', "Don't Show Again"),
isSecondary: true,
run: () => {
telemetryService.publicLog(`${data.surveyId}.survey/dontShowAgain`);
storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL);
storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL);
storageService.store2(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER);
}
}],
{ sticky: true }
@@ -141,7 +131,6 @@ class LanguageSurveysContribution implements IWorkbenchContribution {
constructor(
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@INotificationService notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService,
@IModelService modelService: IModelService,
@@ -155,7 +144,7 @@ class LanguageSurveysContribution implements IWorkbenchContribution {
productService.surveys
.filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability)
.map(surveyData => new LanguageSurvey(surveyData, storageService, storageKeysSyncRegistryService, notificationService, telemetryService, modelService, textFileService, openerService, productService));
.map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService, productService));
}
}
@@ -8,8 +8,7 @@ import { language } from 'vs/base/common/platform';
import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IProductService } from 'vs/platform/product/common/productService';
import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle';
import { Severity, INotificationService } from 'vs/platform/notification/common/notification';
@@ -27,7 +26,6 @@ class NPSContribution implements IWorkbenchContribution {
constructor(
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@INotificationService notificationService: INotificationService,
@ITelemetryService telemetryService: ITelemetryService,
@IOpenerService openerService: IOpenerService,
@@ -37,12 +35,6 @@ class NPSContribution implements IWorkbenchContribution {
return;
}
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: SESSION_COUNT_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: LAST_SESSION_DATE_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: SKIP_VERSION_KEY, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: IS_CANDIDATE_KEY, version: 1 });
const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, '');
if (skipVersion) {
return;
@@ -56,8 +48,8 @@ class NPSContribution implements IWorkbenchContribution {
}
const sessionCount = (storageService.getNumber(SESSION_COUNT_KEY, StorageScope.GLOBAL, 0) || 0) + 1;
storageService.store(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL);
storageService.store(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL);
storageService.store2(LAST_SESSION_DATE_KEY, date, StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(SESSION_COUNT_KEY, sessionCount, StorageScope.GLOBAL, StorageTarget.USER);
if (sessionCount < 9) {
return;
@@ -66,10 +58,10 @@ class NPSContribution implements IWorkbenchContribution {
const isCandidate = storageService.getBoolean(IS_CANDIDATE_KEY, StorageScope.GLOBAL, false)
|| Math.random() < PROBABILITY;
storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL);
storageService.store2(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL, StorageTarget.USER);
if (!isCandidate) {
storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL);
storageService.store2(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER);
return;
}
@@ -81,18 +73,18 @@ class NPSContribution implements IWorkbenchContribution {
run: () => {
telemetryService.getTelemetryInfo().then(info => {
openerService.open(URI.parse(`${productService.npsSurveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`));
storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL);
storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL);
storageService.store2(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER);
});
}
}, {
label: nls.localize('remindLater', "Remind Me later"),
run: () => storageService.store(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL)
run: () => storageService.store2(SESSION_COUNT_KEY, sessionCount - 3, StorageScope.GLOBAL, StorageTarget.USER)
}, {
label: nls.localize('neverAgain', "Don't Show Again"),
run: () => {
storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL);
storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL);
storageService.store2(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL, StorageTarget.USER);
storageService.store2(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL, StorageTarget.USER);
}
}],
{ sticky: true }
@@ -20,7 +20,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions';
import { IProductService } from 'vs/platform/product/common/productService';
import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
const MINIMUM_FONT_SIZE = 6;
const MAXIMUM_FONT_SIZE = 25;
@@ -51,7 +50,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IProductService private readonly productService: IProductService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
this._updateConfig();
this._configurationService.onDidChangeConfiguration(e => {
@@ -59,9 +57,6 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
this._updateConfig();
}
});
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: 'terminalConfigHelper/launchRecommendationsIgnore', version: 1 });
}
public setLinuxDistro(linuxDistro: LinuxDistro) {
@@ -8,7 +8,6 @@ import { TerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/term
import { EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal';
import { StorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
suite('Workbench - TerminalConfigHelper', () => {
let fixture: HTMLElement;
@@ -30,7 +29,7 @@ suite('Workbench - TerminalConfigHelper', () => {
const configurationService = new TestConfigurationService();
configurationService.setUserConfiguration('editor', { fontFamily: 'foo' });
configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } });
const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.setLinuxDistro(LinuxDistro.Fedora);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontFamily, '\'DejaVu Sans Mono\', monospace', 'Fedora should have its font overridden when terminal.integrated.fontFamily not set');
@@ -40,7 +39,7 @@ suite('Workbench - TerminalConfigHelper', () => {
const configurationService = new TestConfigurationService();
configurationService.setUserConfiguration('editor', { fontFamily: 'foo' });
configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } });
const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.setLinuxDistro(LinuxDistro.Ubuntu);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontFamily, '\'Ubuntu Mono\', monospace', 'Ubuntu should have its font overridden when terminal.integrated.fontFamily not set');
@@ -50,7 +49,7 @@ suite('Workbench - TerminalConfigHelper', () => {
const configurationService = new TestConfigurationService();
configurationService.setUserConfiguration('editor', { fontFamily: 'foo' });
configurationService.setUserConfiguration('terminal', { integrated: { fontFamily: null } });
const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
const configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontFamily, 'foo', 'editor.fontFamily should be the fallback when terminal.integrated.fontFamily not set');
});
@@ -68,7 +67,7 @@ suite('Workbench - TerminalConfigHelper', () => {
fontSize: 10
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontSize, 10, 'terminal.integrated.fontSize should be selected over editor.fontSize');
@@ -81,12 +80,12 @@ suite('Workbench - TerminalConfigHelper', () => {
fontSize: 0
}
});
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.setLinuxDistro(LinuxDistro.Ubuntu);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontSize, 8, 'The minimum terminal font size (with adjustment) should be used when terminal.integrated.fontSize less than it');
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontSize, 6, 'The minimum terminal font size should be used when terminal.integrated.fontSize less than it');
@@ -99,7 +98,7 @@ suite('Workbench - TerminalConfigHelper', () => {
fontSize: 1500
}
});
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontSize, 25, 'The maximum terminal font size should be used when terminal.integrated.fontSize more than it');
@@ -112,12 +111,12 @@ suite('Workbench - TerminalConfigHelper', () => {
fontSize: null
}
});
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.setLinuxDistro(LinuxDistro.Ubuntu);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize + 2, 'The default editor font size (with adjustment) should be used when terminal.integrated.fontSize is not set');
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().fontSize, EDITOR_FONT_DEFAULTS.fontSize, 'The default editor font size should be used when terminal.integrated.fontSize is not set');
});
@@ -135,7 +134,7 @@ suite('Workbench - TerminalConfigHelper', () => {
lineHeight: 2
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().lineHeight, 2, 'terminal.integrated.lineHeight should be selected over editor.lineHeight');
@@ -149,7 +148,7 @@ suite('Workbench - TerminalConfigHelper', () => {
lineHeight: 0
}
});
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.getFont().lineHeight, 1, 'editor.lineHeight should be 1 when terminal.integrated.lineHeight not set');
});
@@ -162,7 +161,7 @@ suite('Workbench - TerminalConfigHelper', () => {
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced');
});
@@ -174,7 +173,7 @@ suite('Workbench - TerminalConfigHelper', () => {
fontFamily: 'sans-serif'
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced');
});
@@ -186,7 +185,7 @@ suite('Workbench - TerminalConfigHelper', () => {
fontFamily: 'serif'
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced');
});
@@ -202,7 +201,7 @@ suite('Workbench - TerminalConfigHelper', () => {
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.configFontIsMonospace(), true, 'monospace is monospaced');
});
@@ -218,7 +217,7 @@ suite('Workbench - TerminalConfigHelper', () => {
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.configFontIsMonospace(), false, 'sans-serif is not monospaced');
});
@@ -234,7 +233,7 @@ suite('Workbench - TerminalConfigHelper', () => {
}
});
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!, new StorageKeysSyncRegistryService());
let configHelper = new TerminalConfigHelper(configurationService, null!, null!, null!, null!, null!, null!);
configHelper.panelContainer = fixture;
assert.equal(configHelper.configFontIsMonospace(), false, 'serif is not monospaced');
});
@@ -28,7 +28,6 @@ import { ShowCurrentReleaseNotesActionId, CheckForVSCodeUpdateActionId } from 'v
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IProductService } from 'vs/platform/product/common/productService';
import product from 'vs/platform/product/common/product';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export const CONTEXT_UPDATE_STATE = new RawContextKey<string>('updateState', StateType.Idle);
@@ -185,15 +184,11 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IProductService private readonly productService: IProductService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
this.state = updateService.state;
this.updateStateContextKey = CONTEXT_UPDATE_STATE.bindTo(this.contextKeyService);
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: 'neverShowAgain:update/win32-fast-updates', version: 1 });
this._register(updateService.onStateChange(this.onUpdateStateChange, this));
this.onUpdateStateChange(this.updateService.state);
@@ -8,7 +8,7 @@ import { localize } from 'vs/nls';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IProductService } from 'vs/platform/product/common/productService';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
@@ -127,10 +127,11 @@ export async function configureOpenerTrustedDomainsHandler(
const itemToTrust = pickedResult.toTrust;
if (trustedDomains.indexOf(itemToTrust) === -1) {
storageService.remove(TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, StorageScope.GLOBAL);
storageService.store(
storageService.store2(
TRUSTED_DOMAINS_STORAGE_KEY,
JSON.stringify([...trustedDomains, itemToTrust]),
StorageScope.GLOBAL
StorageScope.GLOBAL,
StorageTarget.USER
);
return [...trustedDomains, itemToTrust];
@@ -8,11 +8,10 @@ import { parse } from 'vs/base/common/json';
import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import { FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, FileWriteOptions, IFileService, IStat, IWatchOptions, IFileSystemProviderWithFileReadWriteCapability } from 'vs/platform/files/common/files';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { VSBuffer } from 'vs/base/common/buffer';
import { readTrustedDomains, TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, TRUSTED_DOMAINS_STORAGE_KEY } from 'vs/workbench/contrib/url/browser/trustedDomains';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { assertIsDefined } from 'vs/base/common/types';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -97,12 +96,8 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith
@IFileService private readonly fileService: IFileService,
@IStorageService private readonly storageService: IStorageService,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStorageKeysSyncRegistryService private readonly storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
this.fileService.registerProvider(TRUSTED_DOMAINS_SCHEMA, this);
this.storageKeysSyncRegistryService.registerStorageKey({ key: TRUSTED_DOMAINS_STORAGE_KEY, version: 1 });
this.storageKeysSyncRegistryService.registerStorageKey({ key: TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, version: 1 });
}
stat(resource: URI): Promise<IStat> {
@@ -134,11 +129,12 @@ export class TrustedDomainsFileSystemProvider implements IFileSystemProviderWith
const trustedDomainsContent = VSBuffer.wrap(content).toString();
const trustedDomains = parse(trustedDomainsContent);
this.storageService.store(TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, trustedDomainsContent, StorageScope.GLOBAL);
this.storageService.store(
this.storageService.store2(TRUSTED_DOMAINS_CONTENT_STORAGE_KEY, trustedDomainsContent, StorageScope.GLOBAL, StorageTarget.USER);
this.storageService.store2(
TRUSTED_DOMAINS_STORAGE_KEY,
JSON.stringify(trustedDomains) || '',
StorageScope.GLOBAL
StorageScope.GLOBAL,
StorageTarget.USER
);
} catch (err) { }
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
@@ -19,7 +19,6 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution {
@@ -28,7 +27,6 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution
constructor(
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IOpenerService private readonly openerService: IOpenerService,
@INotificationService private readonly notificationService: INotificationService,
@IHostService private readonly hostService: IHostService,
@@ -40,7 +38,6 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IJSONEditingService private readonly jsonEditingService: IJSONEditingService
) {
storageKeysSyncRegistryService.registerStorageKey({ key: AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, version: 1 });
}
protected async handleTelemetryOptOut(): Promise<void> {
@@ -53,7 +50,7 @@ export abstract class AbstractTelemetryOptOut implements IWorkbenchContribution
return; // return early if meanwhile another window opened (we only show the opt-out once)
}
this.storageService.store(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL);
this.storageService.store2(AbstractTelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL, StorageTarget.USER);
this.privacyUrl = this.productService.privacyStatementUrl || this.productService.telemetryOptOutUrl;
@@ -165,7 +162,6 @@ export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut {
constructor(
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IOpenerService openerService: IOpenerService,
@INotificationService notificationService: INotificationService,
@IHostService hostService: IHostService,
@@ -177,7 +173,7 @@ export class BrowserTelemetryOptOut extends AbstractTelemetryOptOut {
@IEnvironmentService environmentService: IEnvironmentService,
@IJSONEditingService jsonEditingService: IJSONEditingService
) {
super(storageService, storageKeysSyncRegistryService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService);
super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService);
this.handleTelemetryOptOut();
}
@@ -16,13 +16,11 @@ import { AbstractTelemetryOptOut } from 'vs/workbench/contrib/welcome/telemetryO
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing';
import { INativeHostService } from 'vs/platform/native/electron-sandbox/native';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export class NativeTelemetryOptOut extends AbstractTelemetryOptOut {
constructor(
@IStorageService storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
@IOpenerService openerService: IOpenerService,
@INotificationService notificationService: INotificationService,
@IHostService hostService: IHostService,
@@ -35,7 +33,7 @@ export class NativeTelemetryOptOut extends AbstractTelemetryOptOut {
@IJSONEditingService jsonEditingService: IJSONEditingService,
@INativeHostService private readonly nativeHostService: INativeHostService
) {
super(storageService, storageKeysSyncRegistryService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService);
super(storageService, openerService, notificationService, hostService, telemetryService, experimentService, configurationService, galleryService, productService, environmentService, jsonEditingService);
this.handleTelemetryOptOut();
}
@@ -677,10 +677,6 @@ class SimpleStorageKeysSyncRegistryService implements IStorageKeysSyncRegistrySe
declare readonly _serviceBrand: undefined;
onDidChangeStorageKeys = Event.None;
storageKeys = [];
registerStorageKey(): void { }
onDidChangeExtensionStorageKeys = Event.None;
@@ -298,7 +298,7 @@ suite('EditorsObserver', function () {
assert.equal(observer.hasEditor(input2.resource), true);
assert.equal(observer.hasEditor(input3.resource), true);
storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN);
const restoredObserver = new EditorsObserver(part, storage);
await part.whenRestored;
@@ -350,7 +350,7 @@ suite('EditorsObserver', function () {
assert.equal(observer.hasEditor(input2.resource), true);
assert.equal(observer.hasEditor(input3.resource), true);
storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN);
const restoredObserver = new EditorsObserver(part, storage);
await part.whenRestored;
@@ -390,7 +390,7 @@ suite('EditorsObserver', function () {
assert.equal(currentEditorsMRU[0].editor, input1);
assert.equal(observer.hasEditor(input1.resource), true);
storage._onWillSaveState.fire({ reason: WillSaveStateReason.SHUTDOWN });
storage.emitWillSaveState(WillSaveStateReason.SHUTDOWN);
const restoredObserver = new EditorsObserver(part, storage);
await part.whenRestored;
@@ -7,8 +7,7 @@ import { distinct } from 'vs/base/common/arrays';
import { Emitter } from 'vs/base/common/event';
import { Disposable } from 'vs/base/common/lifecycle';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IStorageService, IStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IStorageService, IStorageValueChangeEvent, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IExtensionIgnoredRecommendationsService, IgnoredRecommendationChangeNotification } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations';
import { IWorkpsaceExtensionsConfigService } from 'vs/workbench/services/extensionRecommendations/common/workspaceExtensionsConfig';
@@ -35,12 +34,10 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement
constructor(
@IWorkpsaceExtensionsConfigService private readonly workpsaceExtensionsConfigService: IWorkpsaceExtensionsConfigService,
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super();
storageKeysSyncRegistryService.registerStorageKey({ key: ignoredRecommendationsStorageKey, version: 1 });
this._globalIgnoredRecommendations = this.getCachedIgnoredRecommendations();
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this._register(this.storageService.onDidChangeValue(e => this.onDidStorageChange(e)));
this.initIgnoredWorkspaceRecommendations();
}
@@ -72,7 +69,7 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement
return ignoredRecommendations.map(e => e.toLowerCase());
}
private onDidStorageChange(e: IStorageChangeEvent): void {
private onDidStorageChange(e: IStorageValueChangeEvent): void {
if (e.key === ignoredRecommendationsStorageKey && e.scope === StorageScope.GLOBAL
&& this.ignoredRecommendationsValue !== this.getStoredIgnoredRecommendationsValue() /* This checks if current window changed the value or not */) {
this._ignoredRecommendationsValue = undefined;
@@ -106,7 +103,7 @@ export class ExtensionIgnoredRecommendationsService extends Disposable implement
}
private setStoredIgnoredRecommendationsValue(value: string): void {
this.storageService.store(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL);
this.storageService.store2(ignoredRecommendationsStorageKey, value, StorageScope.GLOBAL, StorageTarget.USER);
}
}
@@ -10,7 +10,7 @@ import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecyc
import { Event } from 'vs/base/common/event';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IAction, Action } from 'vs/base/common/actions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
export class NotificationService extends Disposable implements INotificationService {
@@ -83,7 +83,7 @@ export class NotificationService extends Disposable implements INotificationServ
handle.close();
// Remember choice
this.storageService.store(id, true, scope);
this.storageService.store2(id, true, scope, StorageTarget.USER);
return Promise.resolve();
}));
@@ -127,7 +127,7 @@ export class NotificationService extends Disposable implements INotificationServ
const neverShowAgainChoice = {
label: nls.localize('neverShowAgain', "Don't Show Again"),
run: () => this.storageService.store(id, true, scope),
run: () => this.storageService.store2(id, true, scope, StorageTarget.USER),
isSecondary: options.neverShowAgain.isSecondary
};
@@ -38,7 +38,6 @@ import { ColorScheme } from 'vs/platform/theme/common/theme';
import { IHostColorSchemeService } from 'vs/workbench/services/themes/common/hostColorSchemeService';
import { CodiconStyles } from 'vs/base/browser/ui/codicons/codiconStyles';
import { RunOnceScheduler } from 'vs/base/common/async';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
// implementation
@@ -109,11 +108,7 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
@IWorkbenchLayoutService readonly layoutService: IWorkbenchLayoutService,
@ILogService private readonly logService: ILogService,
@IHostColorSchemeService private readonly hostColorService: IHostColorSchemeService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
// roam persisted color theme colors. Don't enable for icons as they contain references to fonts and images.
storageKeysSyncRegistryService.registerStorageKey({ key: ColorThemeData.STORAGE_KEY, version: 1 });
this.container = layoutService.container;
this.settings = new ThemeConfiguration(configurationService);
@@ -21,7 +21,7 @@ import { TokenStyle, SemanticTokenRule, ProbeScope, getTokenClassificationRegist
import { MatcherWithPriority, Matcher, createMatchers } from 'vs/workbench/services/themes/common/textMateScopeMatcher';
import { IExtensionResourceLoaderService } from 'vs/workbench/services/extensionResourceLoader/common/extensionResourceLoader';
import { CharCode } from 'vs/base/common/charCode';
import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage';
import { StorageScope, IStorageService, StorageTarget } from 'vs/platform/storage/common/storage';
import { ThemeConfiguration } from 'vs/workbench/services/themes/common/themeConfiguration';
import { ColorScheme } from 'vs/platform/theme/common/theme';
@@ -523,7 +523,8 @@ export class ColorThemeData implements IWorkbenchColorTheme {
watch: this.watch
});
storageService.store(ColorThemeData.STORAGE_KEY, value, StorageScope.GLOBAL);
// roam persisted color theme colors. Don't enable for icons as they contain references to fonts and images.
storageService.store2(ColorThemeData.STORAGE_KEY, value, StorageScope.GLOBAL, StorageTarget.USER);
}
get baseTheme(): string {
@@ -14,7 +14,7 @@ import { flatten, equals } from 'vs/base/common/arrays';
import { getCurrentAuthenticationSessionInfo, IAuthenticationService } from 'vs/workbench/services/authentication/browser/authenticationService';
import { IUserDataSyncAccountService } from 'vs/platform/userDataSync/common/userDataSyncAccount';
import { IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { IStorageService, IStorageChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, IStorageValueChangeEvent, StorageScope } from 'vs/platform/storage/common/storage';
import { ILogService } from 'vs/platform/log/common/log';
import { IProductService } from 'vs/platform/product/common/productService';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -175,7 +175,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
(() => this.update()));
this._register(Event.filter(this.authenticationService.onDidChangeSessions, e => this.isSupportedAuthenticationProviderId(e.providerId))(({ event }) => this.onDidChangeSessions(event)));
this._register(this.storageService.onDidChangeStorage(e => this.onDidChangeStorage(e)));
this._register(this.storageService.onDidChangeValue(e => this.onDidChangeStorage(e)));
this._register(Event.filter(this.userDataSyncAccountService.onTokenFailed, isSuccessive => isSuccessive)(() => this.onDidSuccessiveAuthFailures()));
}
@@ -584,7 +584,7 @@ export class UserDataSyncWorkbenchService extends Disposable implements IUserDat
this.update();
}
private onDidChangeStorage(e: IStorageChangeEvent): void {
private onDidChangeStorage(e: IStorageValueChangeEvent): void {
if (e.key === UserDataSyncWorkbenchService.CACHED_SESSION_STORAGE_KEY && e.scope === StorageScope.GLOBAL
&& this.currentSessionId !== this.getStoredCachedSessionId() /* This checks if current window changed the value or not */) {
this._cachedCurrentSessionId = null;
@@ -5,7 +5,7 @@
import { ViewContainerLocation, IViewDescriptorService, ViewContainer, IViewsRegistry, IViewContainersRegistry, IViewDescriptor, Extensions as ViewExtensions, ViewVisibilityState } from 'vs/workbench/common/views';
import { IContextKey, RawContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService, StorageScope, IStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { Registry } from 'vs/platform/registry/common/platform';
import { toDisposable, DisposableStore, Disposable, IDisposable } from 'vs/base/common/lifecycle';
@@ -13,7 +13,6 @@ import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneCont
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Event, Emitter } from 'vs/base/common/event';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { generateUuid } from 'vs/base/common/uuid';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@@ -98,12 +97,9 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
@IStorageService private readonly storageService: IStorageService,
@IExtensionService private readonly extensionService: IExtensionService,
@ITelemetryService private readonly telemetryService: ITelemetryService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
) {
super();
storageKeysSyncRegistryService.registerStorageKey({ key: ViewDescriptorService.CACHED_VIEW_POSITIONS, version: 1 });
storageKeysSyncRegistryService.registerStorageKey({ key: ViewDescriptorService.CACHED_VIEW_CONTAINER_LOCATIONS, version: 1 });
this.viewContainerModels = new Map<ViewContainer, { viewContainerModel: ViewContainerModel, disposable: IDisposable; }>();
this.activeViewContextKeys = new Map<string, IContextKey<boolean>>();
this.movableViewContextKeys = new Map<string, IContextKey<boolean>>();
@@ -139,7 +135,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
this.viewContainerModels.clear();
}));
this._register(this.storageService.onDidChangeStorage((e) => { this.onDidStorageChange(e); }));
this._register(this.storageService.onDidChangeValue((e) => { this.onDidStorageChange(e); }));
this._register(this.extensionService.onDidRegisterExtensions(() => this.onDidRegisterExtensions()));
}
@@ -505,7 +501,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
return new Map<string, ViewContainerLocation>(JSON.parse(this.cachedViewContainerLocationsValue));
}
private onDidStorageChange(e: IStorageChangeEvent): void {
private onDidStorageChange(e: IStorageValueChangeEvent): void {
if (e.key === ViewDescriptorService.CACHED_VIEW_POSITIONS && e.scope === StorageScope.GLOBAL
&& this.cachedViewPositionsValue !== this.getStoredCachedViewPositionsValue() /* This checks if current window changed the value or not */) {
this._cachedViewPositionsValue = this.getStoredCachedViewPositionsValue();
@@ -600,7 +596,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
}
private setStoredCachedViewPositionsValue(value: string): void {
this.storageService.store(ViewDescriptorService.CACHED_VIEW_POSITIONS, value, StorageScope.GLOBAL);
this.storageService.store2(ViewDescriptorService.CACHED_VIEW_POSITIONS, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private getStoredCachedViewContainerLocationsValue(): string {
@@ -608,7 +604,7 @@ export class ViewDescriptorService extends Disposable implements IViewDescriptor
}
private setStoredCachedViewContainerLocationsValue(value: string): void {
this.storageService.store(ViewDescriptorService.CACHED_VIEW_CONTAINER_LOCATIONS, value, StorageScope.GLOBAL);
this.storageService.store2(ViewDescriptorService.CACHED_VIEW_CONTAINER_LOCATIONS, value, StorageScope.GLOBAL, StorageTarget.USER);
}
private saveViewPositionsToCache(): void {
@@ -5,11 +5,10 @@
import { ViewContainer, IViewsRegistry, IViewDescriptor, Extensions as ViewExtensions, IViewContainerModel, IAddedViewDescriptorRef, IViewDescriptorRef, IAddedViewDescriptorState } from 'vs/workbench/common/views';
import { IContextKeyService, IReadableSet } from 'vs/platform/contextkey/common/contextkey';
import { IStorageService, StorageScope, IStorageChangeEvent } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, IStorageValueChangeEvent, StorageTarget } from 'vs/platform/storage/common/storage';
import { Registry } from 'vs/platform/registry/common/platform';
import { Disposable } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { URI } from 'vs/base/common/uri';
import { move } from 'vs/base/common/arrays';
@@ -84,14 +83,12 @@ class ViewDescriptorsState extends Disposable {
constructor(
viewContainerStorageId: string,
@IStorageService private readonly storageService: IStorageService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
this.globalViewsStateStorageId = getViewsStateStorageId(viewContainerStorageId);
this.workspaceViewsStateStorageId = viewContainerStorageId;
storageKeysSyncRegistryService.registerStorageKey({ key: this.globalViewsStateStorageId, version: 1 });
this._register(this.storageService.onDidChangeStorage(e => this.onDidStorageChange(e)));
this._register(this.storageService.onDidChangeValue(e => this.onDidStorageChange(e)));
this.state = this.initialize();
}
@@ -143,7 +140,7 @@ class ViewDescriptorsState extends Disposable {
this.setStoredGlobalState(storedGlobalState);
}
private onDidStorageChange(e: IStorageChangeEvent): void {
private onDidStorageChange(e: IStorageValueChangeEvent): void {
if (e.key === this.globalViewsStateStorageId && e.scope === StorageScope.GLOBAL
&& this.globalViewsStatesValue !== this.getStoredGlobalViewsStatesValue() /* This checks if current window changed the value or not */) {
this._globalViewsStatesValue = undefined;
@@ -270,7 +267,7 @@ class ViewDescriptorsState extends Disposable {
}
private setStoredGlobalViewsStatesValue(value: string): void {
this.storageService.store(this.globalViewsStateStorageId, value, StorageScope.GLOBAL);
this.storageService.store2(this.globalViewsStateStorageId, value, StorageScope.GLOBAL, StorageTarget.USER);
}
}
@@ -7,7 +7,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IWorkspacesService, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IEnterWorkspaceResult, IRecentlyOpened, restoreRecentlyOpened, IRecent, isRecentFile, isRecentFolder, toStoreData, IStoredWorkspaceFolder, getStoredWorkspaceFolder, WORKSPACE_EXTENSION, IStoredWorkspace } from 'vs/platform/workspaces/common/workspaces';
import { URI } from 'vs/base/common/uri';
import { Emitter } from 'vs/base/common/event';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -16,7 +16,6 @@ import { IFileService, FileOperationError, FileOperationResult } from 'vs/platfo
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { joinPath } from 'vs/base/common/resources';
import { VSBuffer } from 'vs/base/common/buffer';
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
export class BrowserWorkspacesService extends Disposable implements IWorkspacesService {
@@ -33,13 +32,9 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS
@ILogService private readonly logService: ILogService,
@IFileService private readonly fileService: IFileService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
) {
super();
// opt-in to syncing
storageKeysSyncRegistryService.registerStorageKey({ key: BrowserWorkspacesService.RECENTLY_OPENED_KEY, version: 1 });
// Opening a workspace should push it as most
// recently used to the workspaces history
this.addWorkspaceToRecentlyOpened();
@@ -48,7 +43,7 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS
}
private registerListeners(): void {
this._register(this.storageService.onDidChangeStorage(event => {
this._register(this.storageService.onDidChangeValue(event => {
if (event.key === BrowserWorkspacesService.RECENTLY_OPENED_KEY && event.scope === StorageScope.GLOBAL) {
this._onRecentlyOpenedChange.fire();
}
@@ -116,7 +111,7 @@ export class BrowserWorkspacesService extends Disposable implements IWorkspacesS
}
private async saveRecentlyOpened(data: IRecentlyOpened): Promise<void> {
return this.storageService.store(BrowserWorkspacesService.RECENTLY_OPENED_KEY, JSON.stringify(toStoreData(data)), StorageScope.GLOBAL);
return this.storageService.store2(BrowserWorkspacesService.RECENTLY_OPENED_KEY, JSON.stringify(toStoreData(data)), StorageScope.GLOBAL, StorageTarget.USER);
}
async clearRecentlyOpened(): Promise<void> {
@@ -13,7 +13,7 @@ import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfigurationService';
import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { InMemoryStorageService, IWillSaveStateEvent } from 'vs/platform/storage/common/storage';
import { InMemoryStorageService, WillSaveStateReason } from 'vs/platform/storage/common/storage';
import { WorkingCopyService, IWorkingCopy, IWorkingCopyBackup, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
import { NullExtensionService } from 'vs/workbench/services/extensions/common/extensions';
import { IWorkingCopyFileService, IWorkingCopyFileOperationParticipant, WorkingCopyFileEvent } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
@@ -121,8 +121,10 @@ export class TestContextService implements IWorkspaceContextService {
}
export class TestStorageService extends InMemoryStorageService {
readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState = this._onWillSaveState.event;
emitWillSaveState(reason: WillSaveStateReason): void {
super.emitWillSaveState(reason);
}
}
export class TestWorkingCopyService extends WorkingCopyService { }