mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode 073a24de05773f2261f89172987002dc0ae2f1cd (#9711)
This commit is contained in:
@@ -15,7 +15,7 @@ import type { ICodeEditor } from 'vs/editor/browser/editorBrowser';
|
||||
import { registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
|
||||
import type { IEditorContribution } from 'vs/editor/common/editorCommon';
|
||||
import type { ITextModel } from 'vs/editor/common/model';
|
||||
import { AuthenticationSession } from 'vs/editor/common/modes';
|
||||
import { AuthenticationSession, AuthenticationSessionsChangeEvent } from 'vs/editor/common/modes';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService';
|
||||
@@ -63,6 +63,8 @@ const enum AuthStatus {
|
||||
const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey<string>('authTokenStatus', AuthStatus.Initializing);
|
||||
const CONTEXT_CONFLICTS_SOURCES = new RawContextKey<string>('conflictsSources', '');
|
||||
|
||||
const USER_DATA_SYNC_ACCOUNT_PREFERENCE_KEY = 'userDataSyncAccountPreference';
|
||||
|
||||
type ConfigureSyncQuickPickItem = { id: SyncResource, label: string, description?: string };
|
||||
|
||||
function getSyncAreaLabel(source: SyncResource): string {
|
||||
@@ -195,18 +197,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedAccount = await this.quickInputService.pick(sessions.map(session => {
|
||||
return {
|
||||
id: session.id,
|
||||
label: session.accountName
|
||||
};
|
||||
}), { canPickMany: false });
|
||||
|
||||
if (selectedAccount) {
|
||||
const selected = sessions.filter(account => selectedAccount.id === account.id)[0];
|
||||
this.logAuthenticatedEvent(selected);
|
||||
await this.setActiveAccount(selected);
|
||||
const accountPreference = this.storageService.get(USER_DATA_SYNC_ACCOUNT_PREFERENCE_KEY, StorageScope.GLOBAL);
|
||||
if (accountPreference) {
|
||||
const matchingSession = sessions.find(session => session.id === accountPreference);
|
||||
if (matchingSession) {
|
||||
this.setActiveAccount(matchingSession);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
await this.showSwitchAccountPicker(sessions);
|
||||
}
|
||||
|
||||
private logAuthenticatedEvent(session: AuthenticationSession): void {
|
||||
@@ -246,15 +246,80 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
||||
this.updateBadge();
|
||||
}
|
||||
|
||||
private async onDidChangeSessions(providerId: string): Promise<void> {
|
||||
private async showSwitchAccountPicker(sessions: readonly AuthenticationSession[]): Promise<void> {
|
||||
return new Promise((resolve, _) => {
|
||||
const quickPick = this.quickInputService.createQuickPick<{ label: string, session: AuthenticationSession }>();
|
||||
quickPick.title = localize('chooseAccountTitle', "Sync: Choose Account");
|
||||
quickPick.placeholder = localize('chooseAccount', "Choose an account you would like to use for settings sync");
|
||||
quickPick.items = sessions.map(session => {
|
||||
return {
|
||||
label: session.accountName,
|
||||
session: session
|
||||
};
|
||||
});
|
||||
|
||||
quickPick.onDidHide(() => {
|
||||
quickPick.dispose();
|
||||
resolve();
|
||||
});
|
||||
|
||||
quickPick.onDidAccept(() => {
|
||||
const selected = quickPick.selectedItems[0];
|
||||
this.setActiveAccount(selected.session);
|
||||
this.storageService.store(USER_DATA_SYNC_ACCOUNT_PREFERENCE_KEY, selected.session.id, StorageScope.GLOBAL);
|
||||
quickPick.dispose();
|
||||
resolve();
|
||||
});
|
||||
|
||||
quickPick.show();
|
||||
});
|
||||
}
|
||||
|
||||
private async onDidChangeSessions(e: { providerId: string, event: AuthenticationSessionsChangeEvent }): Promise<void> {
|
||||
const { providerId, event } = e;
|
||||
if (providerId === this.userDataSyncStore!.authenticationProviderId) {
|
||||
if (this.activeAccount) {
|
||||
// Try to update existing account, case where access token has been refreshed
|
||||
const accounts = (await this.authenticationService.getSessions(this.userDataSyncStore!.authenticationProviderId) || []);
|
||||
const matchingAccount = accounts.filter(a => a.id === this.activeAccount?.id)[0];
|
||||
this.setActiveAccount(matchingAccount);
|
||||
if (event.removed.length) {
|
||||
const activeWasRemoved = !!event.removed.find(removed => removed === this.activeAccount!.id);
|
||||
|
||||
// If the current account was removed, check if another account can be used, otherwise offer to turn off sync
|
||||
if (activeWasRemoved) {
|
||||
const accounts = (await this.authenticationService.getSessions(this.userDataSyncStore!.authenticationProviderId) || []);
|
||||
if (accounts.length) {
|
||||
// Show switch dialog here
|
||||
await this.showSwitchAccountPicker(accounts);
|
||||
} else {
|
||||
await this.turnOff();
|
||||
this.setActiveAccount(undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (event.added.length) {
|
||||
// Offer to switch accounts
|
||||
const accounts = (await this.authenticationService.getSessions(this.userDataSyncStore!.authenticationProviderId) || []);
|
||||
await this.showSwitchAccountPicker(accounts);
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.changed.length) {
|
||||
const activeWasChanged = !!event.changed.find(changed => changed === this.activeAccount!.id);
|
||||
if (activeWasChanged) {
|
||||
// Try to update existing account, case where access token has been refreshed
|
||||
const accounts = (await this.authenticationService.getSessions(this.userDataSyncStore!.authenticationProviderId) || []);
|
||||
const matchingAccount = accounts.filter(a => a.id === this.activeAccount?.id)[0];
|
||||
this.setActiveAccount(matchingAccount);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.initializeActiveAccount();
|
||||
await this.initializeActiveAccount();
|
||||
|
||||
// If logged in for the first time from accounts menu, prompt if sync should be turned on
|
||||
if (this.activeAccount) {
|
||||
this.turnOn(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,7 +585,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
||||
}
|
||||
}
|
||||
|
||||
private async turnOn(): Promise<void> {
|
||||
private async turnOn(skipAccountPick?: boolean): Promise<void> {
|
||||
if (!this.storageService.getBoolean('sync.donotAskPreviewConfirmation', StorageScope.GLOBAL, false)) {
|
||||
const result = await this.dialogService.show(
|
||||
Severity.Info,
|
||||
@@ -562,7 +627,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
||||
disposables.add(Event.any(quickPick.onDidAccept, quickPick.onDidCustom)(async () => {
|
||||
if (quickPick.selectedItems.length) {
|
||||
this.updateConfiguration(items, quickPick.selectedItems);
|
||||
this.doTurnOn().then(c, e);
|
||||
this.doTurnOn(skipAccountPick).then(c, e);
|
||||
quickPick.hide();
|
||||
}
|
||||
}));
|
||||
@@ -571,8 +636,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
||||
});
|
||||
}
|
||||
|
||||
private async doTurnOn(): Promise<void> {
|
||||
if (this.authenticationState.get() === AuthStatus.SignedIn) {
|
||||
private async doTurnOn(skipAccountPick?: boolean): Promise<void> {
|
||||
if (this.authenticationState.get() === AuthStatus.SignedIn && !skipAccountPick) {
|
||||
await new Promise((c, e) => {
|
||||
const disposables: DisposableStore = new DisposableStore();
|
||||
const displayName = this.authenticationService.getDisplayName(this.userDataSyncStore!.authenticationProviderId);
|
||||
@@ -706,7 +771,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
||||
private async turnOff(): Promise<void> {
|
||||
const result = await this.dialogService.confirm({
|
||||
type: 'info',
|
||||
message: localize('turn off sync confirmation', "Turn off Sync"),
|
||||
message: localize('turn off sync confirmation', "Do you want to turn off sync?"),
|
||||
detail: localize('turn off sync detail', "Your settings, keybindings, extensions and UI State will no longer be synced."),
|
||||
primaryButton: localize('turn off', "Turn Off"),
|
||||
checkbox: {
|
||||
@@ -750,14 +815,14 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
||||
private getConflictsEditorInputs(syncResource: SyncResource): DiffEditorInput[] {
|
||||
return this.editorService.editors.filter(input => {
|
||||
const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource;
|
||||
return getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) === syncResource;
|
||||
return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) === syncResource;
|
||||
}) as DiffEditorInput[];
|
||||
}
|
||||
|
||||
private getAllConflictsEditorInputs(): IEditorInput[] {
|
||||
return this.editorService.editors.filter(input => {
|
||||
const resource = input instanceof DiffEditorInput ? input.master.resource : input.resource;
|
||||
return getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) !== undefined;
|
||||
return resource && getSyncResourceFromLocalPreview(resource!, this.workbenchEnvironmentService) !== undefined;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -10,13 +10,12 @@ import { localize } from 'vs/nls';
|
||||
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { TreeViewPane, TreeView } from 'vs/workbench/browser/parts/views/treeView';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ALL_SYNC_RESOURCES, CONTEXT_SYNC_ENABLEMENT, IUserDataSyncStoreService, toRemoteBackupSyncResource, resolveBackupSyncResource, IUserDataSyncBackupStoreService, IResourceRefHandle, toLocalBackupSyncResource, SyncResource } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { ALL_SYNC_RESOURCES, CONTEXT_SYNC_ENABLEMENT, SyncResource, IUserDataSyncService, ISyncResourceHandle } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { registerAction2, Action2, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService, RawContextKey, ContextKeyExpr, ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { FolderThemeIcon, FileThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { FolderThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { fromNow } from 'vs/base/common/date';
|
||||
import { pad } from 'vs/base/common/strings';
|
||||
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
|
||||
@@ -26,8 +25,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
|
||||
constructor(
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IContextKeyService private readonly contextKeyService: IContextKeyService,
|
||||
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
|
||||
@IUserDataSyncBackupStoreService private readonly userDataSyncBackupStoreService: IUserDataSyncBackupStoreService,
|
||||
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
|
||||
) {
|
||||
const container = this.registerSyncViewContainer();
|
||||
this.registerBackupView(container, true);
|
||||
@@ -59,9 +57,7 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
|
||||
const disposable = treeView.onDidChangeVisibility(visible => {
|
||||
if (visible && !treeView.dataProvider) {
|
||||
disposable.dispose();
|
||||
treeView.dataProvider = this.instantiationService.createInstance(UserDataSyncHistoryViewDataProvider, id,
|
||||
(resource: SyncResource) => remote ? this.userDataSyncStoreService.getAllRefs(resource) : this.userDataSyncBackupStoreService.getAllRefs(resource),
|
||||
(resource: SyncResource, ref: string) => remote ? toRemoteBackupSyncResource(resource, ref) : toLocalBackupSyncResource(resource, ref));
|
||||
treeView.dataProvider = new UserDataSyncHistoryViewDataProvider(remote, this.userDataSyncService);
|
||||
}
|
||||
});
|
||||
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
|
||||
@@ -104,28 +100,11 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: `workbench.actions.sync.${viewId}.resolveResourceRef`,
|
||||
title: localize('workbench.actions.sync.resolveResourceRef', "Resolve Resource Ref"),
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise<void> {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
let resource = URI.parse(handle.$treeItemHandle);
|
||||
const result = resolveBackupSyncResource(resource);
|
||||
if (result) {
|
||||
resource = resource.with({ fragment: result.resource });
|
||||
await editorService.openEditor({ resource });
|
||||
}
|
||||
}
|
||||
});
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: `workbench.actions.sync.${viewId}.resolveResourceRefCompletely`,
|
||||
title: localize('workbench.actions.sync.resolveResourceRefCompletely', "Show full content"),
|
||||
id: `workbench.actions.sync.resolveResource`,
|
||||
title: localize('workbench.actions.sync.resolveResourceRef', "Show full content"),
|
||||
menu: {
|
||||
id: MenuId.ViewItemContext,
|
||||
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /syncref-.*/i))
|
||||
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /sync-resource-.*/i))
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -134,34 +113,28 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
|
||||
await editorService.openEditor({ resource: URI.parse(handle.$treeItemHandle) });
|
||||
}
|
||||
});
|
||||
|
||||
registerAction2(class extends Action2 {
|
||||
constructor() {
|
||||
super({
|
||||
id: `workbench.actions.${viewId}.commpareWithLocal`,
|
||||
title: localize('workbench.action.deleteRef', "Open Changes"),
|
||||
menu: {
|
||||
id: MenuId.ViewItemContext,
|
||||
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /syncref-(settings|keybindings).*/i))
|
||||
},
|
||||
id: `workbench.actions.sync.commpareWithLocal`,
|
||||
title: localize('workbench.actions.sync.commpareWithLocal', "Open Changes"),
|
||||
});
|
||||
}
|
||||
async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise<void> {
|
||||
const editorService = accessor.get(IEditorService);
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
const resource = URI.parse(handle.$treeItemHandle);
|
||||
const result = resolveBackupSyncResource(resource);
|
||||
if (result) {
|
||||
const leftResource: URI = resource.with({ fragment: result.resource });
|
||||
const rightResource: URI = result.resource === 'settings' ? environmentService.settingsResource : environmentService.keybindingsResource;
|
||||
const { resource, comparableResource } = <{ resource: string, comparableResource?: string }>JSON.parse(handle.$treeItemHandle);
|
||||
if (comparableResource) {
|
||||
await editorService.openEditor({
|
||||
leftResource,
|
||||
rightResource,
|
||||
leftResource: URI.parse(resource),
|
||||
rightResource: URI.parse(comparableResource),
|
||||
options: {
|
||||
preserveFocus: false,
|
||||
pinned: true,
|
||||
preserveFocus: true,
|
||||
revealIfVisible: true,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
await editorService.openEditor({ resource: URI.parse(resource) });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -169,48 +142,55 @@ export class UserDataSyncViewContribution implements IWorkbenchContribution {
|
||||
|
||||
}
|
||||
|
||||
interface SyncResourceTreeItem extends ITreeItem {
|
||||
resource: SyncResource;
|
||||
resourceHandle: ISyncResourceHandle;
|
||||
}
|
||||
|
||||
class UserDataSyncHistoryViewDataProvider implements ITreeViewDataProvider {
|
||||
|
||||
constructor(
|
||||
private readonly viewId: string,
|
||||
private getAllRefs: (resource: SyncResource) => Promise<IResourceRefHandle[]>,
|
||||
private toResource: (resource: SyncResource, ref: string) => URI
|
||||
) {
|
||||
}
|
||||
constructor(private readonly remote: boolean, private userDataSyncService: IUserDataSyncService) { }
|
||||
|
||||
async getChildren(element?: ITreeItem): Promise<ITreeItem[]> {
|
||||
if (element) {
|
||||
return this.getResources(element.handle);
|
||||
if (!element) {
|
||||
return ALL_SYNC_RESOURCES.map(resourceKey => ({
|
||||
handle: resourceKey,
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed,
|
||||
label: { label: resourceKey },
|
||||
themeIcon: FolderThemeIcon,
|
||||
}));
|
||||
}
|
||||
return ALL_SYNC_RESOURCES.map(resourceKey => ({
|
||||
handle: resourceKey,
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed,
|
||||
label: { label: resourceKey },
|
||||
themeIcon: FolderThemeIcon,
|
||||
contextValue: `sync-${resourceKey}`
|
||||
}));
|
||||
}
|
||||
|
||||
private async getResources(handle: string): Promise<ITreeItem[]> {
|
||||
const resourceKey = ALL_SYNC_RESOURCES.filter(key => key === handle)[0];
|
||||
const resourceKey = ALL_SYNC_RESOURCES.filter(key => key === element.handle)[0] as SyncResource;
|
||||
if (resourceKey) {
|
||||
const refHandles = await this.getAllRefs(resourceKey);
|
||||
return refHandles.map(({ ref, created }) => {
|
||||
const handle = this.toResource(resourceKey, ref).toString();
|
||||
const refHandles = this.remote ? await this.userDataSyncService.getRemoteSyncResourceHandles(resourceKey) : await this.userDataSyncService.getLocalSyncResourceHandles(resourceKey);
|
||||
return refHandles.map(({ uri, created }) => {
|
||||
return <SyncResourceTreeItem>{
|
||||
handle: uri.toString(),
|
||||
collapsibleState: TreeItemCollapsibleState.Collapsed,
|
||||
label: { label: label(new Date(created)) },
|
||||
description: fromNow(created, true),
|
||||
resourceUri: uri,
|
||||
resource: resourceKey,
|
||||
resourceHandle: { uri, created },
|
||||
contextValue: `sync-resource-${resourceKey}`
|
||||
};
|
||||
});
|
||||
}
|
||||
if ((<SyncResourceTreeItem>element).resourceHandle) {
|
||||
const associatedResources = await this.userDataSyncService.getAssociatedResources((<SyncResourceTreeItem>element).resource, (<SyncResourceTreeItem>element).resourceHandle);
|
||||
return associatedResources.map(({ resource, comparableResource }) => {
|
||||
const handle = JSON.stringify({ resource: resource.toString(), comparableResource: comparableResource?.toString() });
|
||||
return {
|
||||
handle,
|
||||
collapsibleState: TreeItemCollapsibleState.None,
|
||||
label: { label: label(new Date(created)) },
|
||||
description: fromNow(created, true),
|
||||
command: { id: `workbench.actions.sync.${this.viewId}.resolveResourceRef`, title: '', arguments: [<TreeViewItemHandleArg>{ $treeItemHandle: handle, $treeViewId: '' }] },
|
||||
themeIcon: FileThemeIcon,
|
||||
contextValue: `syncref-${resourceKey}`
|
||||
resourceUri: resource,
|
||||
command: { id: `workbench.actions.sync.commpareWithLocal`, title: '', arguments: [<TreeViewItemHandleArg>{ $treeViewId: '', $treeItemHandle: handle }] },
|
||||
contextValue: `sync-associatedResource-${(<SyncResourceTreeItem>element).resource}`
|
||||
};
|
||||
});
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function label(date: Date): string {
|
||||
|
||||
Reference in New Issue
Block a user