提交 d7a5944f 编写于 作者: B Benjamin Pasero

notifications - clean up save error handler

上级 ec900254
......@@ -12,7 +12,6 @@ import paths = require('vs/base/common/paths');
import { Action } from 'vs/base/common/actions';
import URI from 'vs/base/common/uri';
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
......@@ -28,15 +27,20 @@ import { IContextKeyService, IContextKey, RawContextKey } from 'vs/platform/cont
import { FileOnDiskContentProvider } from 'vs/workbench/parts/files/common/files';
import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput';
import { IModelService } from 'vs/editor/common/services/modelService';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { SAVE_FILE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL } from 'vs/workbench/parts/files/electron-browser/fileCommands';
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
import { INotificationService, INotificationHandle, INotificationActions, Severity } from 'vs/platform/notification/common/notification';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IStorageService } from 'vs/platform/storage/common/storage';
import { ExecuteCommandAction } from 'vs/platform/actions/common/actions';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
export const CONFLICT_RESOLUTION_CONTEXT = 'saveConflictResolutionContext';
export const CONFLICT_RESOLUTION_SCHEME = 'conflictResolution';
const conflictEditorHelp = nls.localize('userGuide', "Use the actions in the editor tool bar to either undo your changes or overwrite the content on disk with your changes");
const LEARN_MORE_DIRTY_WRITE_IGNORE_KEY = 'learnMoreDirtyWriteError';
const conflictEditorHelp = nls.localize('userGuide', "Use the actions in the editor tool bar to either undo your changes or overwrite the content on disk with your changes.");
// A handler for save error happening with conflict resolution actions
export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContribution {
......@@ -48,13 +52,12 @@ export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContributi
constructor(
@INotificationService private notificationService: INotificationService,
@ITextFileService private textFileService: ITextFileService,
@IEnvironmentService private environmentService: IEnvironmentService,
@IEditorGroupService private editorGroupService: IEditorGroupService,
@IContextKeyService contextKeyService: IContextKeyService,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@ITextModelService textModelService: ITextModelService,
@ICommandService private commandService: ICommandService,
@IInstantiationService instantiationService: IInstantiationService
@IInstantiationService private instantiationService: IInstantiationService,
@IStorageService private storageService: IStorageService
) {
this.toUnbind = [];
this.messages = new ResourceMap<INotificationHandle>();
......@@ -114,24 +117,22 @@ export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContributi
if (fileOperationError.fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) {
// If the user tried to save from the opened conflict editor, show its message again
// Otherwise show the message that will lead the user into the save conflict editor.
if (this.activeConflictResolutionResource && this.activeConflictResolutionResource.toString() === model.getResource().toString()) {
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY)) {
return; // return if this message is ignored
}
message = conflictEditorHelp;
} else {
message = nls.localize('staleSaveError', "Failed to save '{0}': The content on disk is newer. Please compare your version with the one on disk.", paths.basename(resource.fsPath));
actions.primary.push(new Action('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare"), null, true, () => {
if (!model.isDisposed()) {
const name = paths.basename(resource.fsPath);
const editorLabel = nls.localize('saveConflictDiffLabel', "{0} (on disk) ↔ {1} (in {2}) - Resolve save conflict", name, name, this.environmentService.appNameLong);
actions.primary.push(this.instantiationService.createInstance(ResolveConflictLearnMoreAction));
actions.secondary.push(this.instantiationService.createInstance(DoNotShowResolveConflictLearnMoreAction));
}
return this.editorService.openEditor({ leftResource: URI.from({ scheme: CONFLICT_RESOLUTION_SCHEME, path: resource.fsPath }), rightResource: resource, label: editorLabel, options: { pinned: true } }).then(() => {
pendingResolveSaveConflictMessages.push(this.notificationService.notify({ severity: Severity.Info, message: conflictEditorHelp })); // Inform user
});
}
// Otherwise show the message that will lead the user into the save conflict editor.
else {
message = nls.localize('staleSaveError', "Failed to save '{0}': The content on disk is newer. Please compare your version with the one on disk.", paths.basename(resource.fsPath));
return TPromise.as(true);
}));
actions.primary.push(this.instantiationService.createInstance(ResolveSaveConflictAction, model));
}
}
......@@ -143,45 +144,24 @@ export class SaveErrorHandler implements ISaveErrorHandler, IWorkbenchContributi
// Save Elevated
if (isPermissionDenied || triedToMakeWriteable) {
actions.primary.push(new Action('workbench.files.action.saveElevated', triedToMakeWriteable ? nls.localize('overwriteElevated', "Overwrite as Admin...") : nls.localize('saveElevated', "Retry as Admin..."), null, true, () => {
if (!model.isDisposed()) {
model.save({
writeElevated: true,
overwriteReadonly: triedToMakeWriteable
}).done(null, errors.onUnexpectedError);
}
return TPromise.as(true);
}));
actions.primary.push(this.instantiationService.createInstance(SaveElevatedAction, model, triedToMakeWriteable));
}
// Overwrite
else if (isReadonly) {
actions.primary.push(new Action('workbench.files.action.overwrite', nls.localize('overwrite', "Overwrite"), null, true, () => {
if (!model.isDisposed()) {
model.save({ overwriteReadonly: true }).done(null, errors.onUnexpectedError);
}
return TPromise.as(true);
}));
actions.primary.push(this.instantiationService.createInstance(OverwriteReadonlyAction, model));
}
// Retry
else {
actions.primary.push(new Action('workbench.files.action.retry', nls.localize('retry', "Retry"), null, true, () => {
return this.commandService.executeCommand(SAVE_FILE_COMMAND_ID, resource);
}));
actions.primary.push(this.instantiationService.createInstance(ExecuteCommandAction, SAVE_FILE_COMMAND_ID, nls.localize('retry', "Retry")));
}
// Save As
actions.primary.push(new Action('workbench.files.action.saveAs', SAVE_FILE_AS_LABEL, null, true, () => {
return this.commandService.executeCommand(SAVE_FILE_AS_COMMAND_ID, resource);
}));
actions.primary.push(this.instantiationService.createInstance(ExecuteCommandAction, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL));
// Discard
actions.primary.push(new Action('workbench.files.action.discard', nls.localize('discard', "Discard"), null, true, () => {
return this.commandService.executeCommand(REVERT_FILE_COMMAND_ID, resource);
}));
actions.primary.push(this.instantiationService.createInstance(ExecuteCommandAction, REVERT_FILE_COMMAND_ID, nls.localize('discard', "Discard")));
if (isReadonly) {
if (triedToMakeWriteable) {
......@@ -214,6 +194,117 @@ function clearPendingResolveSaveConflictMessages(): void {
}
}
class ResolveConflictLearnMoreAction extends Action {
constructor(
@IOpenerService private openerService: IOpenerService
) {
super('workbench.files.action.resolveConflictLearnMore', nls.localize('learnMore', "Learn More"));
}
public run(): TPromise<any> {
return this.openerService.open(URI.parse('https://go.microsoft.com/fwlink/?linkid=868264'));
}
}
class DoNotShowResolveConflictLearnMoreAction extends Action {
constructor(
@IStorageService private storageService: IStorageService
) {
super('workbench.files.action.resolveConflictLearnMoreDoNotShowAgain', nls.localize('dontShowAgain', "Don't Show Again"));
}
public run(): TPromise<any> {
this.storageService.store(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY, true);
return TPromise.as(void 0);
}
}
class ResolveSaveConflictAction extends Action {
constructor(
private model: ITextFileEditorModel,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@INotificationService private notificationService: INotificationService,
@IInstantiationService private instantiationService: IInstantiationService,
@IStorageService private storageService: IStorageService,
@IEnvironmentService private environmentService: IEnvironmentService
) {
super('workbench.files.action.resolveConflict', nls.localize('compareChanges', "Compare"));
}
public run(): TPromise<any> {
if (!this.model.isDisposed()) {
const resource = this.model.getResource();
const name = paths.basename(resource.fsPath);
const editorLabel = nls.localize('saveConflictDiffLabel', "{0} (on disk) ↔ {1} (in {2}) - Resolve save conflict", name, name, this.environmentService.appNameLong);
return this.editorService.openEditor(
{
leftResource: URI.from({ scheme: CONFLICT_RESOLUTION_SCHEME, path: resource.fsPath }),
rightResource: resource,
label: editorLabel,
options: { pinned: true }
}
).then(() => {
if (this.storageService.getBoolean(LEARN_MORE_DIRTY_WRITE_IGNORE_KEY)) {
return; // return if this message is ignored
}
// Show additional help how to resolve the save conflict
const actions: INotificationActions = { primary: [], secondary: [] };
actions.primary.push(this.instantiationService.createInstance(ResolveConflictLearnMoreAction));
actions.secondary.push(this.instantiationService.createInstance(DoNotShowResolveConflictLearnMoreAction));
const handle = this.notificationService.notify({ severity: Severity.Info, message: conflictEditorHelp, actions });
pendingResolveSaveConflictMessages.push(handle);
});
}
return TPromise.as(true);
}
}
class SaveElevatedAction extends Action {
constructor(
private model: ITextFileEditorModel,
private triedToMakeWriteable: boolean
) {
super('workbench.files.action.saveElevated', triedToMakeWriteable ? nls.localize('overwriteElevated', "Overwrite as Admin...") : nls.localize('saveElevated', "Retry as Admin..."));
}
public run(): TPromise<any> {
if (!this.model.isDisposed()) {
this.model.save({
writeElevated: true,
overwriteReadonly: this.triedToMakeWriteable
}).done(null, errors.onUnexpectedError);
}
return TPromise.as(true);
}
}
class OverwriteReadonlyAction extends Action {
constructor(
private model: ITextFileEditorModel
) {
super('workbench.files.action.overwrite', nls.localize('overwrite', "Overwrite"));
}
public run(): TPromise<any> {
if (!this.model.isDisposed()) {
this.model.save({ overwriteReadonly: true }).done(null, errors.onUnexpectedError);
}
return TPromise.as(true);
}
}
export const acceptLocalChangesCommand = (accessor: ServicesAccessor, resource: URI) => {
const editorService = accessor.get(IWorkbenchEditorService);
const resolverService = accessor.get(ITextModelService);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册