Merge from vscode cfc1ab4c5f816765b91fb7ead3c3427a7c8581a3

This commit is contained in:
ADS Merger
2020-03-11 04:19:23 +00:00
parent 16fab722d5
commit 4c3e48773d
880 changed files with 20441 additions and 11232 deletions

View File

@@ -7,6 +7,8 @@ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } fr
import { Registry } from 'vs/platform/registry/common/platform';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { UserDataSyncWorkbenchContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSync';
import { UserDataSyncViewContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSyncView';
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Ready);
workbenchRegistry.registerWorkbenchContribution(UserDataSyncViewContribution, LifecyclePhase.Ready);

View File

@@ -23,18 +23,18 @@ import { localize } from 'vs/nls';
import { MenuId, MenuRegistry, registerAction2, Action2 } from 'vs/platform/actions/common/actions';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey, ContextKeyRegexExpr } from 'vs/platform/contextkey/common/contextkey';
import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IFileService } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { CONTEXT_SYNC_STATE, getSyncSourceFromRemoteContentResource, getUserDataSyncStore, ISyncConfiguration, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, registerConfiguration, SyncSource, SyncStatus, toRemoteContentResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncEnablementService, ResourceKey, getSyncSourceFromPreviewResource, CONTEXT_SYNC_ENABLEMENT } from 'vs/platform/userDataSync/common/userDataSync';
import { CONTEXT_SYNC_STATE, getUserDataSyncStore, ISyncConfiguration, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, registerConfiguration, SyncSource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncEnablementService, ResourceKey, getSyncSourceFromPreviewResource, CONTEXT_SYNC_ENABLEMENT, toRemoteSyncResourceFromSource, PREVIEW_QUERY, resolveSyncResource, getSyncSourceFromResourceKey } from 'vs/platform/userDataSync/common/userDataSync';
import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets';
import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import type { IEditorInput } from 'vs/workbench/common/editor';
import { IEditorInput, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
@@ -65,7 +65,7 @@ type ConfigureSyncQuickPickItem = { id: ResourceKey, label: string, description?
function getSyncAreaLabel(source: SyncSource): string {
switch (source) {
case SyncSource.Settings: return localize('settings', "Settings");
case SyncSource.Keybindings: return localize('keybindings', "Keybindings");
case SyncSource.Keybindings: return localize('keybindings', "Keyboard Shortcuts");
case SyncSource.Extensions: return localize('extensions', "Extensions");
case SyncSource.GlobalState: return localize('ui state label', "UI State");
}
@@ -155,7 +155,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status)));
this._register(userDataSyncService.onDidChangeConflicts(() => this.onDidChangeConflicts(this.userDataSyncService.conflictsSources)));
this._register(userDataSyncService.onSyncErrors(errors => this.onSyncErrors(errors)));
this._register(this.authTokenService.onTokenFailed(_ => this.authenticationService.getSessions(this.userDataSyncStore!.authenticationProviderId)));
this._register(this.authTokenService.onTokenFailed(_ => this.onTokenFailed()));
this._register(this.userDataSyncEnablementService.onDidChangeEnablement(enabled => this.onDidChangeEnablement(enabled)));
this._register(this.authenticationService.onDidRegisterAuthenticationProvider(e => this.onDidRegisterAuthenticationProvider(e)));
this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(e => this.onDidUnregisterAuthenticationProvider(e)));
@@ -255,6 +255,16 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
private async onTokenFailed(): Promise<void> {
if (this.activeAccount) {
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.setActiveAccount(undefined);
}
}
private async onDidRegisterAuthenticationProvider(providerId: string) {
if (providerId === this.userDataSyncStore!.authenticationProviderId) {
await this.initializeActiveAccount();
@@ -344,7 +354,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
private async acceptRemote(syncSource: SyncSource) {
try {
const contents = await this.userDataSyncService.getRemoteContent(syncSource, false);
const contents = await this.userDataSyncService.resolveContent(toRemoteSyncResourceFromSource(syncSource).with({ query: PREVIEW_QUERY }));
if (contents) {
await this.userDataSyncService.accept(syncSource, contents);
}
@@ -458,7 +468,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
return;
}
const resource = source === SyncSource.Settings ? this.workbenchEnvironmentService.settingsResource : this.workbenchEnvironmentService.keybindingsResource;
if (isEqual(resource, this.editorService.activeEditor?.resource)) {
if (isEqual(resource, toResource(this.editorService.activeEditor, { supportSideBySide: SideBySideEditor.MASTER }))) {
// Do not show notification if the file in error is active
return;
}
@@ -744,7 +754,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
label = localize('keybindings conflicts preview', "Keybindings Conflicts (Remote ↔ Local)");
}
if (previewResource) {
const remoteContentResource = toRemoteContentResource(source);
const remoteContentResource = toRemoteSyncResourceFromSource(source).with({ query: PREVIEW_QUERY });
await this.editorService.openEditor({
leftResource: remoteContentResource,
rightResource: previewResource,
@@ -835,7 +845,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
private registerShowSettingsConflictsAction(): void {
const resolveSettingsConflictsWhenContext = ContextKeyRegexExpr.create(CONTEXT_CONFLICTS_SOURCES.keys()[0], /.*settings.*/i);
const resolveSettingsConflictsWhenContext = ContextKeyExpr.regex(CONTEXT_CONFLICTS_SOURCES.keys()[0], /.*settings.*/i);
CommandsRegistry.registerCommand(resolveSettingsConflictsCommand.id, () => this.handleConflicts(SyncSource.Settings));
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_sync',
@@ -862,7 +872,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
private registerShowKeybindingsConflictsAction(): void {
const resolveKeybindingsConflictsWhenContext = ContextKeyRegexExpr.create(CONTEXT_CONFLICTS_SOURCES.keys()[0], /.*keybindings.*/i);
const resolveKeybindingsConflictsWhenContext = ContextKeyExpr.regex(CONTEXT_CONFLICTS_SOURCES.keys()[0], /.*keybindings.*/i);
CommandsRegistry.registerCommand(resolveKeybindingsConflictsCommand.id, () => this.handleConflicts(SyncSource.Keybindings));
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_sync',
@@ -1046,15 +1056,8 @@ class UserDataRemoteContentProvider implements ITextModelContentProvider {
}
provideTextContent(uri: URI): Promise<ITextModel> | null {
let promise: Promise<string | null> | undefined;
if (isEqual(uri, toRemoteContentResource(SyncSource.Settings))) {
promise = this.userDataSyncService.getRemoteContent(SyncSource.Settings, true);
}
if (isEqual(uri, toRemoteContentResource(SyncSource.Keybindings))) {
promise = this.userDataSyncService.getRemoteContent(SyncSource.Keybindings, true);
}
if (promise) {
return promise.then(content => this.modelService.createModel(content || '', this.modeService.create('jsonc'), uri));
if (uri.scheme === USER_DATA_SYNC_SCHEME) {
return this.userDataSyncService.resolveContent(uri).then(content => this.modelService.createModel(content || '', this.modeService.create('jsonc'), uri));
}
return null;
}
@@ -1110,7 +1113,7 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
return true;
}
if (getSyncSourceFromRemoteContentResource(model.uri) !== undefined) {
if (resolveSyncResource(model.uri) !== null && model.uri.query === PREVIEW_QUERY) {
return this.configurationService.getValue<boolean>('diffEditor.renderSideBySide');
}
@@ -1120,14 +1123,14 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
private createAcceptChangesWidgetRenderer(): void {
if (!this.acceptChangesButton) {
const isRemote = getSyncSourceFromRemoteContentResource(this.editor.getModel()!.uri) !== undefined;
const isRemote = resolveSyncResource(this.editor.getModel()!.uri) !== null;
const acceptRemoteLabel = localize('accept remote', "Accept Remote");
const acceptLocalLabel = localize('accept local', "Accept Local");
this.acceptChangesButton = this.instantiationService.createInstance(FloatingClickWidget, this.editor, isRemote ? acceptRemoteLabel : acceptLocalLabel, null);
this._register(this.acceptChangesButton.onClick(async () => {
const model = this.editor.getModel();
if (model) {
const conflictsSource = (getSyncSourceFromPreviewResource(model.uri, this.environmentService) || getSyncSourceFromRemoteContentResource(model.uri))!;
const conflictsSource = (getSyncSourceFromPreviewResource(model.uri, this.environmentService) || getSyncSourceFromResourceKey(resolveSyncResource(model.uri)!.resourceKey))!;
this.telemetryService.publicLog2<{ source: string, action: string }, SyncConflictsClassification>('sync/handleConflicts', { source: conflictsSource, action: isRemote ? 'acceptRemote' : 'acceptLocal' });
const syncAreaLabel = getSyncAreaLabel(conflictsSource);
const result = await this.dialogService.confirm({
@@ -1136,8 +1139,8 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
? localize('Sync accept remote', "Sync: {0}", acceptRemoteLabel)
: localize('Sync accept local', "Sync: {0}", acceptLocalLabel),
message: isRemote
? localize('confirm replace and overwrite local', "Would you like to accept Remote {0} and replace Local {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase())
: localize('confirm replace and overwrite remote', "Would you like to accept Local {0} and replace Remote {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase()),
? localize('confirm replace and overwrite local', "Would you like to accept remote {0} and replace local {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase())
: localize('confirm replace and overwrite remote', "Would you like to accept local {0} and replace remote {1}?", syncAreaLabel.toLowerCase(), syncAreaLabel.toLowerCase()),
primaryButton: isRemote ? acceptRemoteLabel : acceptLocalLabel
});
if (result.confirmed) {

View File

@@ -0,0 +1,223 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IViewsRegistry, Extensions, ITreeViewDescriptor, ITreeViewDataProvider, ITreeItem, TreeItemCollapsibleState, IViewsService, TreeViewItemHandleArg, IViewContainersRegistry, ViewContainerLocation, ViewContainer } from 'vs/workbench/common/views';
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_RESOURCE_KEYS, CONTEXT_SYNC_ENABLEMENT, IUserDataSyncStoreService, toRemoteSyncResource, resolveSyncResource, IUserDataSyncBackupStoreService, IResourceRefHandle, ResourceKey, toLocalBackupSyncResource } 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 { fromNow } from 'vs/base/common/date';
import { pad } from 'vs/base/common/strings';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
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,
) {
const container = this.registerSyncViewContainer();
// Disable remote backup view until server is upgraded.
// this.registerBackupView(container, true);
this.registerBackupView(container, false);
}
private registerSyncViewContainer(): ViewContainer {
return Registry.as<IViewContainersRegistry>(Extensions.ViewContainersRegistry).registerViewContainer(
{
id: 'workbench.view.sync',
name: localize('sync', "Sync"),
ctorDescriptor: new SyncDescriptor(
ViewPaneContainer,
['workbench.view.sync', `workbench.view.sync.state`, { mergeViewWithContainerWhenSingleView: true }]
),
icon: 'codicon-sync',
hideIfEmpty: true,
}, ViewContainerLocation.Sidebar);
}
private registerBackupView(container: ViewContainer, remote: boolean): void {
const id = `workbench.views.sync.${remote ? 'remote' : 'local'}BackupView`;
const name = remote ? localize('remote title', "Remote Backup") : localize('local title', "Local Backup");
const contextKey = new RawContextKey<boolean>(`showUserDataSync${remote ? 'Remote' : 'Local'}BackupView`, false);
const viewEnablementContext = contextKey.bindTo(this.contextKeyService);
const treeView = this.instantiationService.createInstance(TreeView, id, name);
treeView.showCollapseAllAction = true;
treeView.showRefreshAction = true;
const disposable = treeView.onDidChangeVisibility(visible => {
if (visible && !treeView.dataProvider) {
disposable.dispose();
treeView.dataProvider = this.instantiationService.createInstance(UserDataSyncHistoryViewDataProvider, id,
(resourceKey: ResourceKey) => remote ? this.userDataSyncStoreService.getAllRefs(resourceKey) : this.userDataSyncBackupStoreService.getAllRefs(resourceKey),
(resourceKey: ResourceKey, ref: string) => remote ? toRemoteSyncResource(resourceKey, ref) : toLocalBackupSyncResource(resourceKey, ref));
}
});
const viewsRegistry = Registry.as<IViewsRegistry>(Extensions.ViewsRegistry);
viewsRegistry.registerViews([<ITreeViewDescriptor>{
id,
name,
ctorDescriptor: new SyncDescriptor(TreeViewPane),
when: ContextKeyExpr.and(CONTEXT_SYNC_ENABLEMENT, contextKey),
canToggleVisibility: true,
canMoveView: true,
treeView,
collapsed: false,
order: 100,
}], container);
registerAction2(class extends Action2 {
constructor() {
super({
id: `workbench.actions.showSync${remote ? 'Remote' : 'Local'}BackupView`,
title: remote ?
{ value: localize('workbench.action.showSyncRemoteBackup', "Show Remote Backup"), original: `Show Remote Backup` }
: { value: localize('workbench.action.showSyncLocalBackup', "Show Local Backup"), original: `Show Local Backup` },
category: { value: localize('sync', "Sync"), original: `Sync` },
menu: {
id: MenuId.CommandPalette,
when: CONTEXT_SYNC_ENABLEMENT
},
});
}
async run(accessor: ServicesAccessor): Promise<void> {
viewEnablementContext.set(true);
accessor.get(IViewsService).openView(id, true);
}
});
this.registerActions(id);
}
private registerActions(viewId: string) {
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 = resolveSyncResource(resource);
if (result) {
resource = resource.with({ fragment: result.resourceKey });
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"),
menu: {
id: MenuId.ViewItemContext,
when: ContextKeyExpr.and(ContextKeyEqualsExpr.create('view', viewId), ContextKeyExpr.regex('viewItem', /syncref-.*/i))
},
});
}
async run(accessor: ServicesAccessor, handle: TreeViewItemHandleArg): Promise<void> {
const editorService = accessor.get(IEditorService);
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))
},
});
}
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 = resolveSyncResource(resource);
if (result) {
const leftResource: URI = resource.with({ fragment: result.resourceKey });
const rightResource: URI = result.resourceKey === 'settings' ? environmentService.settingsResource : environmentService.keybindingsResource;
await editorService.openEditor({
leftResource,
rightResource,
options: {
preserveFocus: false,
pinned: true,
revealIfVisible: true,
},
});
}
}
});
}
}
class UserDataSyncHistoryViewDataProvider implements ITreeViewDataProvider {
constructor(
private readonly viewId: string,
private getAllRefs: (resourceKey: ResourceKey) => Promise<IResourceRefHandle[]>,
private toResource: (resourceKey: ResourceKey, ref: string) => URI
) {
}
async getChildren(element?: ITreeItem): Promise<ITreeItem[]> {
if (element) {
return this.getResources(element.handle);
}
return ALL_RESOURCE_KEYS.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_RESOURCE_KEYS.filter(key => key === handle)[0];
if (resourceKey) {
const refHandles = await this.getAllRefs(resourceKey);
return refHandles.map(({ ref, created }) => {
const handle = this.toResource(resourceKey, ref).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}`
};
});
}
return [];
}
}
function label(date: Date): string {
return date.toLocaleDateString() +
' ' + pad(date.getHours(), 2) +
':' + pad(date.getMinutes(), 2) +
':' + pad(date.getSeconds(), 2);
}