Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)

* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c

* remove files we don't want

* fix hygiene

* update distro

* update distro

* fix hygiene

* fix strict nulls

* distro

* distro

* fix tests

* fix tests

* add another edit

* fix viewlet icon

* fix azure dialog

* fix some padding

* fix more padding issues
This commit is contained in:
Anthony Dresser
2019-12-04 19:28:22 -08:00
committed by GitHub
parent a8818ab0df
commit f5ce7fb2a5
1507 changed files with 42813 additions and 27370 deletions

View File

@@ -4,9 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE } from 'vs/platform/userDataSync/common/userDataSync';
import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, IUserDataSyncStore, registerConfiguration, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync';
import { localize } from 'vs/nls';
import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable, MutableDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions';
@@ -25,15 +25,24 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
import { isEqual } from 'vs/base/common/resources';
import { IEditorInput } from 'vs/workbench/common/editor';
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { FalseContext } from 'vs/platform/contextkey/common/contextkeys';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { isWeb } from 'vs/base/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { UserDataAutoSync } from 'vs/workbench/contrib/userDataSync/browser/userDataAutoSync';
import { UserDataSyncTrigger } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncTrigger';
import { timeout } from 'vs/base/common/async';
const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey<string>('authTokenStatus', AuthTokenStatus.Inactive);
const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey<string>('authTokenStatus', AuthTokenStatus.Initializing);
const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`));
const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-dark.svg`));
export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution {
private static readonly ENABLEMENT_SETTING = 'sync.enable';
private readonly userDataSyncStore: IUserDataSyncStore | undefined;
private readonly syncStatusContext: IContextKey<string>;
private readonly authTokenContext: IContextKey<string>;
private readonly badgeDisposable = this._register(new MutableDisposable());
@@ -51,29 +60,43 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
@ITextFileService private readonly textFileService: ITextFileService,
@IHistoryService private readonly historyService: IHistoryService,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IDialogService private readonly dialogService: IDialogService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IInstantiationService instantiationService: IInstantiationService,
) {
super();
this.userDataSyncStore = getUserDataSyncStore(configurationService);
this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
this.authTokenContext = CONTEXT_AUTH_TOKEN_STATE.bindTo(contextKeyService);
this.onDidChangeAuthTokenStatus(this.authTokenService.status);
this.onDidChangeSyncStatus(this.userDataSyncService.status);
this._register(Event.debounce(authTokenService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeAuthTokenStatus(this.authTokenService.status)));
this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status)));
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateBadge()));
this.registerActions();
if (this.userDataSyncStore) {
registerConfiguration();
this.onDidChangeAuthTokenStatus(this.authTokenService.status);
this.onDidChangeSyncStatus(this.userDataSyncService.status);
this._register(Event.debounce(authTokenService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeAuthTokenStatus(this.authTokenService.status)));
this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status)));
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING))(() => this.onDidChangeEnablement()));
this.registerActions();
timeout(2000).then(() => {
if (this.authTokenService.status === AuthTokenStatus.Inactive && configurationService.getValue<boolean>('configurationSync.enable')) {
this.showSignInNotification();
if (isWeb) {
this._register(instantiationService.createInstance(UserDataAutoSync));
} else {
this._register(instantiationService.createInstance(UserDataSyncTrigger).onDidTriggerSync(() => this.triggerSync()));
}
});
}
}
private triggerSync(): void {
if (this.configurationService.getValue<boolean>('sync.enable')
&& this.userDataSyncService.status !== SyncStatus.Uninitialized
&& this.authTokenService.status === AuthTokenStatus.SignedIn) {
this.userDataSyncService.sync();
}
}
private onDidChangeAuthTokenStatus(status: AuthTokenStatus) {
this.authTokenContext.set(status);
if (status === AuthTokenStatus.Active) {
if (status === AuthTokenStatus.SignedIn) {
this.signInNotificationDisposable.clear();
}
this.updateBadge();
@@ -82,7 +105,12 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
private onDidChangeSyncStatus(status: SyncStatus) {
this.syncStatusContext.set(status);
this.updateBadge();
if (status === SyncStatus.Syncing) {
// Show syncing progress if takes more than 1s.
timeout(1000).then(() => this.updateBadge());
} else {
this.updateBadge();
}
if (this.userDataSyncService.status === SyncStatus.HasConflicts) {
if (!this.conflictsWarningDisposable.value) {
@@ -105,47 +133,124 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
private onDidChangeEnablement() {
this.updateBadge();
const enabled = this.configurationService.getValue<boolean>(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING);
if (enabled) {
if (this.authTokenService.status === AuthTokenStatus.SignedOut) {
const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.userDataSyncStore!.account),
[
{
label: localize('Sign in', "Sign in"),
run: () => this.signIn()
}
]);
this.signInNotificationDisposable.value = toDisposable(() => handle.close());
handle.onDidClose(() => this.signInNotificationDisposable.clear());
}
} else {
this.signInNotificationDisposable.clear();
}
}
private updateBadge(): void {
this.badgeDisposable.clear();
let badge: IBadge | undefined = undefined;
let clazz: string | undefined;
let priority: number | undefined = undefined;
if (this.authTokenService.status === AuthTokenStatus.Inactive && this.configurationService.getValue<boolean>('configurationSync.enable')) {
badge = new NumberBadge(1, () => localize('sign in', "Sign in..."));
if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue<boolean>(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING) && this.authTokenService.status === AuthTokenStatus.SignedOut) {
badge = new NumberBadge(1, () => localize('sign in', "Sync: Sign in..."));
} else if (this.authTokenService.status === AuthTokenStatus.SigningIn) {
badge = new ProgressBadge(() => localize('signing in', "Signin in..."));
clazz = 'progress-badge';
priority = 1;
} else if (this.userDataSyncService.status === SyncStatus.HasConflicts) {
badge = new NumberBadge(1, () => localize('resolve conflicts', "Resolve Conflicts"));
} else if (this.userDataSyncService.status === SyncStatus.Syncing) {
badge = new ProgressBadge(() => localize('syncing', "Synchronizing User Configuration..."));
clazz = 'progress-badge';
priority = 1;
}
if (badge) {
this.badgeDisposable.value = this.activityService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz);
this.badgeDisposable.value = this.activityService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz, priority);
}
}
private showSignInNotification(): void {
const handle = this.notificationService.prompt(Severity.Info, localize('show sign in', "Please sign in to Settings Sync service to start syncing configuration."),
[
{
label: localize('sign in', "Sign in..."),
run: () => this.signIn()
private async turnOn(): Promise<void> {
if (this.authTokenService.status === AuthTokenStatus.SignedOut) {
const result = await this.dialogService.confirm({
type: 'info',
message: localize('sign in to account', "Sign in to {0}", this.userDataSyncStore!.name),
detail: localize('ask to sign in', "Please sign in with your {0} account to sync configuration across all your machines", this.userDataSyncStore!.account),
primaryButton: localize('Sign in', "Sign in")
});
if (!result.confirmed) {
return;
}
await this.signIn();
}
await this.configureSyncOptions();
await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true);
this.notificationService.info(localize('Sync Started', "Sync Started."));
}
private async configureSyncOptions(): Promise<void> {
return new Promise((c, e) => {
const disposables: DisposableStore = new DisposableStore();
const quickPick = this.quickInputService.createQuickPick();
disposables.add(quickPick);
quickPick.title = localize('configure sync title', "Sync: Configure");
quickPick.placeholder = localize('select configurations to sync', "Choose what to sync");
quickPick.canSelectMany = true;
quickPick.ignoreFocusOut = true;
const items = [{
id: 'sync.enableSettings',
label: localize('user settings', "User Settings")
}, {
id: 'sync.enableKeybindings',
label: localize('user keybindings', "User Keybindings")
}, {
id: 'sync.enableExtensions',
label: localize('extensions', "Extensions")
}];
quickPick.items = items;
quickPick.selectedItems = items.filter(item => this.configurationService.getValue(item.id));
disposables.add(quickPick.onDidAccept(() => {
for (const item of items) {
const wasEnabled = this.configurationService.getValue(item.id);
const isEnabled = !!quickPick.selectedItems.filter(selected => selected.id === item.id)[0];
if (wasEnabled !== isEnabled) {
this.configurationService.updateValue(item.id!, isEnabled);
}
}
]);
this.signInNotificationDisposable.value = toDisposable(() => handle.close());
handle.onDidClose(() => this.signInNotificationDisposable.clear());
quickPick.hide();
}));
disposables.add(quickPick.onDidHide(() => {
disposables.dispose();
c();
}));
quickPick.show();
});
}
private async turnOff(): Promise<void> {
await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, false);
}
private async signIn(): Promise<void> {
const token = await this.quickInputService.input({ placeHolder: localize('enter token', "Please provide the auth bearer token"), ignoreFocusLost: true, });
if (token) {
await this.authTokenService.updateToken(token);
try {
await this.authTokenService.login();
} catch (e) {
this.notificationService.error(e);
throw e;
}
}
private async signOut(): Promise<void> {
await this.authTokenService.deleteToken();
await this.authTokenService.logout();
}
private async continueSync(): Promise<void> {
@@ -169,13 +274,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
private getPreviewEditorInput(): IEditorInput | undefined {
return this.editorService.editors.filter(input => isEqual(input.getResource(), this.workbenchEnvironmentService.settingsSyncPreviewResource))[0];
return this.editorService.editors.filter(input => isEqual(input.getResource(), this.workbenchEnvironmentService.settingsSyncPreviewResource) || isEqual(input.getResource(), this.workbenchEnvironmentService.keybindingsSyncPreviewResource))[0];
}
private async handleConflicts(): Promise<void> {
if (this.userDataSyncService.conflictsSource === SyncSource.Settings) {
const conflictsResource = this.getConflictsResource();
if (conflictsResource) {
const resourceInput = {
resource: this.workbenchEnvironmentService.settingsSyncPreviewResource,
resource: conflictsResource,
options: {
preserveFocus: false,
pinned: false,
@@ -197,80 +303,109 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
private getConflictsResource(): URI | null {
if (this.userDataSyncService.conflictsSource === SyncSource.Settings) {
return this.workbenchEnvironmentService.settingsSyncPreviewResource;
}
if (this.userDataSyncService.conflictsSource === SyncSource.Keybindings) {
return this.workbenchEnvironmentService.keybindingsSyncPreviewResource;
}
return null;
}
private registerActions(): void {
const signInMenuItem: IMenuItem = {
group: '5_sync',
command: {
id: 'workbench.userData.actions.login',
title: localize('sign in', "Sign in...")
},
when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Inactive), ContextKeyExpr.has('config.configurationSync.enable')),
};
CommandsRegistry.registerCommand(signInMenuItem.command.id, () => this.signIn());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, signInMenuItem);
MenuRegistry.appendMenuItem(MenuId.CommandPalette, signInMenuItem);
const signOutMenuItem: IMenuItem = {
command: {
id: 'workbench.userData.actions.logout',
title: localize('sign out', "Sign Out")
},
when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Active)),
};
CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut());
MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem);
const startSyncMenuItem: IMenuItem = {
group: '5_sync',
command: {
id: 'workbench.userData.actions.syncStart',
title: localize('start sync', "Configuration Sync: Turn On")
title: localize('start sync', "Sync: Turn On")
},
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not('config.configurationSync.enable')),
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.notEqualsTo(AuthTokenStatus.SigningIn)),
};
CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', true));
CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.turnOn());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, startSyncMenuItem);
MenuRegistry.appendMenuItem(MenuId.CommandPalette, startSyncMenuItem);
const stopSyncMenuItem: IMenuItem = {
const signInCommandId = 'workbench.userData.actions.signin';
const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedOut));
CommandsRegistry.registerCommand(signInCommandId, () => this.signIn());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_sync',
command: {
id: 'workbench.userData.actions.stopSync',
title: localize('stop sync', "Configuration Sync: Turn Off")
id: signInCommandId,
title: localize('global activity sign in', "Sync: Sign in... (1)")
},
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has('config.configurationSync.enable')),
};
CommandsRegistry.registerCommand(stopSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', false));
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, stopSyncMenuItem);
MenuRegistry.appendMenuItem(MenuId.CommandPalette, stopSyncMenuItem);
when: signInWhenContext,
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: signInCommandId,
title: localize('sign in', "Sync: Sign in...")
},
when: signInWhenContext,
});
const resolveConflictsMenuItem: IMenuItem = {
const signingInCommandId = 'workbench.userData.actions.signingin';
CommandsRegistry.registerCommand(signingInCommandId, () => null);
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_sync',
command: {
id: 'sync.resolveConflicts',
title: localize('resolveConflicts', "Configuration Sync: Resolve Conflicts"),
id: signingInCommandId,
title: localize('signinig in', "Sync: Signing in..."),
precondition: FalseContext
},
when: CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts),
when: CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SigningIn)
});
const stopSyncCommand = {
id: 'workbench.userData.actions.stopSync',
title: localize('stop sync', "Sync: Turn Off")
};
CommandsRegistry.registerCommand(resolveConflictsMenuItem.command.id, () => this.handleConflicts());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, resolveConflictsMenuItem);
MenuRegistry.appendMenuItem(MenuId.CommandPalette, resolveConflictsMenuItem);
CommandsRegistry.registerCommand(stopSyncCommand.id, () => this.turnOff());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_sync',
command: stopSyncCommand,
when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts))
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: stopSyncCommand,
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`)),
});
const resolveConflictsCommandId = 'workbench.userData.actions.resolveConflicts';
const resolveConflictsWhenContext = CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts);
CommandsRegistry.registerCommand(resolveConflictsCommandId, () => this.handleConflicts());
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_sync',
command: {
id: resolveConflictsCommandId,
title: localize('resolveConflicts_global', "Sync: Resolve Conflicts (1)"),
},
when: resolveConflictsWhenContext,
});
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: resolveConflictsCommandId,
title: localize('resolveConflicts', "Sync: Resolve Conflicts"),
},
when: resolveConflictsWhenContext,
});
const continueSyncCommandId = 'workbench.userData.actions.continueSync';
CommandsRegistry.registerCommand(continueSyncCommandId, () => this.continueSync());
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: {
id: continueSyncCommandId,
title: localize('continue sync', "Configuration Sync: Continue")
title: localize('continue sync', "Sync: Continue")
},
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts)),
});
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: continueSyncCommandId,
title: localize('continue sync', "Configuration Sync: Continue"),
iconLocation: {
title: localize('continue sync', "Sync: Continue"),
icon: {
light: SYNC_PUSH_LIGHT_ICON_URI,
dark: SYNC_PUSH_DARK_ICON_URI
}
@@ -279,5 +414,29 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
order: 1,
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts), ResourceContextKey.Resource.isEqualTo(this.workbenchEnvironmentService.settingsSyncPreviewResource.toString())),
});
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
command: {
id: continueSyncCommandId,
title: localize('continue sync', "Sync: Continue"),
icon: {
light: SYNC_PUSH_LIGHT_ICON_URI,
dark: SYNC_PUSH_DARK_ICON_URI
}
},
group: 'navigation',
order: 1,
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts), ResourceContextKey.Resource.isEqualTo(this.workbenchEnvironmentService.keybindingsSyncPreviewResource.toString())),
});
const signOutMenuItem: IMenuItem = {
group: '5_sync',
command: {
id: 'workbench.userData.actions.signout',
title: localize('sign out', "Sign Out")
},
when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.SignedIn)),
};
CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut());
MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem);
}
}