Merge from vscode 5d18ad4c5902e3bddbc9f78da82dfc2ac349e908 (#9683)

This commit is contained in:
Anthony Dresser
2020-03-20 01:17:27 -07:00
committed by GitHub
parent 1520441b84
commit dd8fb9433b
89 changed files with 3095 additions and 445 deletions

View File

@@ -9,7 +9,7 @@ import { canceled, isPromiseCanceledError } from 'vs/base/common/errors';
import { Event } from 'vs/base/common/event';
import { Disposable, DisposableStore, dispose, MutableDisposable, toDisposable, IDisposable } from 'vs/base/common/lifecycle';
import { isWeb } from 'vs/base/common/platform';
import { isEqual } from 'vs/base/common/resources';
import { isEqual, basename } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import type { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { registerEditorContribution, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
@@ -32,7 +32,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import {
CONTEXT_SYNC_STATE, getUserDataSyncStore, ISyncConfiguration, IUserDataAutoSyncService, IUserDataSyncService, IUserDataSyncStore, registerConfiguration,
SyncResource, SyncStatus, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_SCHEME, IUserDataSyncEnablementService, CONTEXT_SYNC_ENABLEMENT,
SyncResourceConflicts, Conflict, getSyncResourceFromLocalPreview, getSyncResourceFromRemotePreview
SyncResourceConflicts, Conflict, getSyncResourceFromLocalPreview
} from 'vs/platform/userDataSync/common/userDataSync';
import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets';
import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity';
@@ -69,6 +69,7 @@ function getSyncAreaLabel(source: SyncResource): string {
switch (source) {
case SyncResource.Settings: return localize('settings', "Settings");
case SyncResource.Keybindings: return localize('keybindings', "Keyboard Shortcuts");
case SyncResource.Snippets: return localize('snippets', "User Snippets");
case SyncResource.Extensions: return localize('extensions', "Extensions");
case SyncResource.GlobalState: return localize('ui state label', "UI State");
}
@@ -100,6 +101,7 @@ const signInCommand = { id: 'workbench.userData.actions.signin', title: localize
const stopSyncCommand = { id: 'workbench.userData.actions.stopSync', title(authenticationProviderId: string, account: AuthenticationSession | undefined, authenticationService: IAuthenticationService) { return getIdentityTitle(localize('stop sync', "Sync: Turn off Sync"), authenticationProviderId, account, authenticationService); } };
const resolveSettingsConflictsCommand = { id: 'workbench.userData.actions.resolveSettingsConflicts', title: localize('showConflicts', "Sync: Show Settings Conflicts") };
const resolveKeybindingsConflictsCommand = { id: 'workbench.userData.actions.resolveKeybindingsConflicts', title: localize('showKeybindingsConflicts', "Sync: Show Keybindings Conflicts") };
const resolveSnippetsConflictsCommand = { id: 'workbench.userData.actions.resolveSnippetsConflicts', title: localize('showSnippetsConflicts', "Sync: Show User Snippets Conflicts") };
const configureSyncCommand = { id: 'workbench.userData.actions.configureSync', title: localize('configure sync', "Sync: Configure") };
const showSyncActivityCommand = {
id: 'workbench.userData.actions.showSyncActivity', title(userDataSyncService: IUserDataSyncService): string {
@@ -291,6 +293,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
if (conflicts.length) {
const conflictsSources: SyncResource[] = conflicts.map(conflict => conflict.syncResource);
this.conflictsSources.set(conflictsSources.join(','));
if (conflictsSources.indexOf(SyncResource.Snippets) !== -1) {
this.registerShowSnippetsConflictsAction();
}
// Clear and dispose conflicts those were cleared
this.conflictsDisposables.forEach((disposable, conflictsSource) => {
@@ -301,8 +306,19 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
});
for (const { syncResource, conflicts } of this.userDataSyncService.conflicts) {
const conflictsEditorInput = this.getConflictsEditorInput(syncResource);
if (!conflictsEditorInput && !this.conflictsDisposables.has(syncResource)) {
const conflictsEditorInputs = this.getConflictsEditorInputs(syncResource);
// close stale conflicts editor previews
if (conflictsEditorInputs.length) {
conflictsEditorInputs.forEach(input => {
if (!conflicts.some(({ local }) => isEqual(local, input.master.resource))) {
input.dispose();
}
});
}
// Show conflicts notification if not shown before
else if (!this.conflictsDisposables.has(syncResource)) {
const conflictsArea = getSyncAreaLabel(syncResource);
const handle = this.notificationService.prompt(Severity.Warning, localize('conflicts detected', "Unable to sync due to conflicts in {0}. Please resolve them to continue.", conflictsArea.toLowerCase()),
[
@@ -338,9 +354,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
handle.close();
// close opened conflicts editor previews
const conflictsEditorInput = this.getConflictsEditorInput(syncResource);
if (conflictsEditorInput) {
conflictsEditorInput.dispose();
const conflictsEditorInputs = this.getConflictsEditorInputs(syncResource);
if (conflictsEditorInputs.length) {
conflictsEditorInputs.forEach(input => input.dispose());
}
this.conflictsDisposables.delete(syncResource);
@@ -496,7 +512,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.userDataSyncEnablementService.isEnabled() && this.authenticationState.get() === AuthStatus.SignedOut) {
badge = new NumberBadge(1, () => localize('sign in to sync', "Sign in to Sync"));
} else if (this.userDataSyncService.conflicts.length) {
badge = new NumberBadge(this.userDataSyncService.conflicts.length, () => localize('has conflicts', "Sync: Conflicts Detected"));
badge = new NumberBadge(this.userDataSyncService.conflicts.reduce((result, syncResourceConflict) => { return result + syncResourceConflict.conflicts.length; }, 0), () => localize('has conflicts', "Sync: Conflicts Detected"));
}
if (badge) {
@@ -605,6 +621,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}, {
id: SyncResource.Keybindings,
label: getSyncAreaLabel(SyncResource.Keybindings)
}, {
id: SyncResource.Snippets,
label: getSyncAreaLabel(SyncResource.Snippets)
}, {
id: SyncResource.Extensions,
label: getSyncAreaLabel(SyncResource.Extensions)
@@ -712,6 +731,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
switch (source) {
case SyncResource.Settings: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Settings, false);
case SyncResource.Keybindings: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Keybindings, false);
case SyncResource.Snippets: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Snippets, false);
case SyncResource.Extensions: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.Extensions, false);
case SyncResource.GlobalState: return this.userDataSyncEnablementService.setResourceEnablement(SyncResource.GlobalState, false);
}
@@ -727,8 +747,11 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
}
}
private getConflictsEditorInput(syncResource: SyncResource): IEditorInput | undefined {
return this.editorService.editors.filter(input => input instanceof DiffEditorInput && getSyncResourceFromLocalPreview(input.master.resource!, this.workbenchEnvironmentService) === syncResource)[0];
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;
}) as DiffEditorInput[];
}
private getAllConflictsEditorInputs(): IEditorInput[] {
@@ -752,6 +775,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
label = localize('settings conflicts preview', "Settings Conflicts (Remote ↔ Local)");
} else if (syncResource === SyncResource.Keybindings) {
label = localize('keybindings conflicts preview', "Keybindings Conflicts (Remote ↔ Local)");
} else if (syncResource === SyncResource.Snippets) {
label = localize('snippets conflicts preview', "User Snippet Conflicts (Remote ↔ Local) - {0}", basename(conflict.local));
}
await this.editorService.openEditor({
leftResource: conflict.remote,
@@ -775,6 +800,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
this.registerSignInAction();
this.registerShowSettingsConflictsAction();
this.registerShowKeybindingsConflictsAction();
this.registerShowSnippetsConflictsAction();
this.registerSyncStatusAction();
this.registerTurnOffSyncAction();
@@ -894,7 +920,36 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
command: resolveKeybindingsConflictsCommand,
when: resolveKeybindingsConflictsWhenContext,
});
}
private _snippetsConflictsActionsDisposable: DisposableStore = new DisposableStore();
private registerShowSnippetsConflictsAction(): void {
this._snippetsConflictsActionsDisposable.clear();
const resolveSnippetsConflictsWhenContext = ContextKeyExpr.regex(CONTEXT_CONFLICTS_SOURCES.keys()[0], /.*snippets.*/i);
const conflicts: Conflict[] | undefined = this.userDataSyncService.conflicts.filter(({ syncResource }) => syncResource === SyncResource.Snippets)[0]?.conflicts;
this._snippetsConflictsActionsDisposable.add(CommandsRegistry.registerCommand(resolveSnippetsConflictsCommand.id, () => this.handleSyncResourceConflicts(SyncResource.Snippets)));
this._snippetsConflictsActionsDisposable.add(MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
group: '5_sync',
command: {
id: resolveSnippetsConflictsCommand.id,
title: localize('resolveSnippetsConflicts_global', "Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1),
},
when: resolveSnippetsConflictsWhenContext,
order: 2
}));
this._snippetsConflictsActionsDisposable.add(MenuRegistry.appendMenuItem(MenuId.MenubarPreferencesMenu, {
group: '5_sync',
command: {
id: resolveSnippetsConflictsCommand.id,
title: localize('resolveSnippetsConflicts_global', "Sync: Show User Snippets Conflicts ({0})", conflicts?.length || 1),
},
when: resolveSnippetsConflictsWhenContext,
order: 2
}));
this._snippetsConflictsActionsDisposable.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
command: resolveSnippetsConflictsCommand,
when: resolveSnippetsConflictsWhenContext,
}));
}
private registerSyncStatusAction(): void {
@@ -938,6 +993,9 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
case SyncResource.Keybindings:
items.push({ id: resolveKeybindingsConflictsCommand.id, label: resolveKeybindingsConflictsCommand.title });
break;
case SyncResource.Snippets:
items.push({ id: resolveSnippetsConflictsCommand.id, label: resolveSnippetsConflictsCommand.title });
break;
}
}
items.push({ type: 'separator' });
@@ -1074,7 +1132,6 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
constructor(
private editor: ICodeEditor,
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
@INotificationService private readonly notificationService: INotificationService,
@IDialogService private readonly dialogService: IDialogService,
@@ -1088,7 +1145,8 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
}
private registerListeners(): void {
this._register(this.editor.onDidChangeModel(e => this.update()));
this._register(this.editor.onDidChangeModel(() => this.update()));
this._register(this.userDataSyncService.onDidChangeConflicts(() => this.update()));
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('diffEditor.renderSideBySide'))(() => this.update()));
}
@@ -1107,11 +1165,16 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
return false; // we need a model
}
if (getSyncResourceFromLocalPreview(model.uri, this.environmentService) !== undefined) {
const syncResourceConflicts = this.getSyncResourceConflicts(model.uri);
if (!syncResourceConflicts) {
return false;
}
if (syncResourceConflicts.conflicts.some(({ local }) => isEqual(local, model.uri))) {
return true;
}
if (getSyncResourceFromRemotePreview(model.uri, this.environmentService) !== undefined) {
if (syncResourceConflicts.conflicts.some(({ remote }) => isEqual(remote, model.uri))) {
return this.configurationService.getValue<boolean>('diffEditor.renderSideBySide');
}
@@ -1121,16 +1184,17 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
private createAcceptChangesWidgetRenderer(): void {
if (!this.acceptChangesButton) {
const isRemote = getSyncResourceFromRemotePreview(this.editor.getModel()!.uri, this.environmentService) !== undefined;
const resource = this.editor.getModel()!.uri;
const syncResourceConflicts = this.getSyncResourceConflicts(resource)!;
const isRemote = syncResourceConflicts.conflicts.some(({ remote }) => isEqual(remote, resource));
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 = (getSyncResourceFromLocalPreview(model.uri, this.environmentService) || getSyncResourceFromRemotePreview(model.uri, this.environmentService))!;
this.telemetryService.publicLog2<{ source: string, action: string }, SyncConflictsClassification>('sync/handleConflicts', { source: conflictsSource, action: isRemote ? 'acceptRemote' : 'acceptLocal' });
const syncAreaLabel = getSyncAreaLabel(conflictsSource);
this.telemetryService.publicLog2<{ source: string, action: string }, SyncConflictsClassification>('sync/handleConflicts', { source: syncResourceConflicts.syncResource, action: isRemote ? 'acceptRemote' : 'acceptLocal' });
const syncAreaLabel = getSyncAreaLabel(syncResourceConflicts.syncResource);
const result = await this.dialogService.confirm({
type: 'info',
title: isRemote
@@ -1146,7 +1210,7 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
await this.userDataSyncService.acceptConflict(model.uri, model.getValue());
} catch (e) {
if (e instanceof UserDataSyncError && e.code === UserDataSyncErrorCode.LocalPreconditionFailed) {
const syncResourceCoflicts = this.userDataSyncService.conflicts.filter(({ syncResource }) => syncResource === conflictsSource)[0];
const syncResourceCoflicts = this.userDataSyncService.conflicts.filter(({ syncResource }) => syncResource === syncResourceConflicts.syncResource)[0];
if (syncResourceCoflicts && syncResourceCoflicts.conflicts.some(conflict => isEqual(conflict.local, model.uri) || isEqual(conflict.remote, model.uri))) {
this.notificationService.warn(localize('update conflicts', "Could not resolve conflicts as there is new local version available. Please try again."));
}
@@ -1162,6 +1226,10 @@ class AcceptChangesContribution extends Disposable implements IEditorContributio
}
}
private getSyncResourceConflicts(resource: URI): SyncResourceConflicts | undefined {
return this.userDataSyncService.conflicts.filter(({ conflicts }) => conflicts.some(({ local, remote }) => isEqual(local, resource) || isEqual(remote, resource)))[0];
}
private disposeAcceptChangesWidgetRenderer(): void {
dispose(this.acceptChangesButton);
this.acceptChangesButton = undefined;