提交 0e6bc508 编写于 作者: B Benjamin Pasero

fix #42640

上级 2afc0e92
...@@ -36,7 +36,6 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; ...@@ -36,7 +36,6 @@ import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IInstantiationService, ServicesAccessor, IConstructorSignature2 } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService, ServicesAccessor, IConstructorSignature2 } from 'vs/platform/instantiation/common/instantiation';
import { ITextModel } from 'vs/editor/common/model'; import { ITextModel } from 'vs/editor/common/model';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWindowService } from 'vs/platform/windows/common/windows';
import { COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID } from 'vs/workbench/parts/files/electron-browser/fileCommands'; import { COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, SAVE_ALL_COMMAND_ID, SAVE_ALL_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID } from 'vs/workbench/parts/files/electron-browser/fileCommands';
import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService';
...@@ -289,8 +288,7 @@ class RenameFileAction extends BaseRenameAction { ...@@ -289,8 +288,7 @@ class RenameFileAction extends BaseRenameAction {
element: ExplorerItem, element: ExplorerItem,
@IFileService fileService: IFileService, @IFileService fileService: IFileService,
@INotificationService notificationService: INotificationService, @INotificationService notificationService: INotificationService,
@ITextFileService textFileService: ITextFileService, @ITextFileService textFileService: ITextFileService
@IBackupFileService private backupFileService: IBackupFileService
) { ) {
super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, notificationService, textFileService); super(RenameFileAction.ID, nls.localize('rename', "Rename"), element, fileService, notificationService, textFileService);
...@@ -298,43 +296,10 @@ class RenameFileAction extends BaseRenameAction { ...@@ -298,43 +296,10 @@ class RenameFileAction extends BaseRenameAction {
} }
public runAction(newName: string): TPromise<any> { public runAction(newName: string): TPromise<any> {
const dirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, this.element.resource, !isLinux /* ignorecase */)); const parentResource = this.element.parent.resource;
const dirtyRenamed: URI[] = []; const targetResource = parentResource.with({ path: paths.join(parentResource.path, newName) });
return TPromise.join(dirty.map(d => {
let renamed: URI;
// If the dirty file itself got moved, just reparent it to the target folder
const targetPath = paths.join(this.element.parent.resource.path, newName);
if (this.element.resource.toString() === d.toString()) {
renamed = this.element.parent.resource.with({ path: targetPath });
}
// Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated
else {
renamed = this.element.parent.resource.with({ path: paths.join(targetPath, d.path.substr(this.element.resource.path.length + 1)) });
}
dirtyRenamed.push(renamed); return this.textFileService.move(this.element.resource, targetResource);
const model = this.textFileService.models.get(d);
return this.backupFileService.backupResource(renamed, model.createSnapshot(), model.getVersionId());
}))
// 2. soft revert all dirty since we have backed up their contents
.then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ }))
// 3.) run the rename operation
.then(() => this.fileService.rename(this.element.resource, newName).then(null, (error: Error) => {
return TPromise.join(dirtyRenamed.map(d => this.backupFileService.discardResourceBackup(d))).then(() => {
this.onErrorWithRetry(error, () => this.runAction(newName));
});
}))
// 4.) resolve those that were dirty to load their previous dirty contents from disk
.then(() => {
return TPromise.join(dirtyRenamed.map(t => this.textFileService.models.loadOrCreate(t)));
});
} }
} }
......
...@@ -56,7 +56,6 @@ import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } ...@@ -56,7 +56,6 @@ import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage }
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService';
import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
export class FileDataSource implements IDataSource { export class FileDataSource implements IDataSource {
constructor( constructor(
...@@ -759,7 +758,6 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { ...@@ -759,7 +758,6 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
@IConfigurationService private configurationService: IConfigurationService, @IConfigurationService private configurationService: IConfigurationService,
@IInstantiationService instantiationService: IInstantiationService, @IInstantiationService instantiationService: IInstantiationService,
@ITextFileService private textFileService: ITextFileService, @ITextFileService private textFileService: ITextFileService,
@IBackupFileService private backupFileService: IBackupFileService,
@IWindowService private windowService: IWindowService, @IWindowService private windowService: IWindowService,
@IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService @IWorkspaceEditingService private workspaceEditingService: IWorkspaceEditingService
) { ) {
...@@ -1033,58 +1031,21 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { ...@@ -1033,58 +1031,21 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
} }
private doHandleExplorerDrop(tree: ITree, source: ExplorerItem, target: ExplorerItem | Model, isCopy: boolean): TPromise<void> { private doHandleExplorerDrop(tree: ITree, source: ExplorerItem, target: ExplorerItem | Model, isCopy: boolean): TPromise<void> {
return tree.expand(target).then(() => {
// Reuse duplicate action if user copies
if (isCopy) {
return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run();
}
const dirtyMoved: URI[] = [];
// Success: load all files that are dirty again to restore their dirty contents
// Error: discard any backups created during the process
const onSuccess = () => TPromise.join(dirtyMoved.map(t => this.textFileService.models.loadOrCreate(t)));
const onError = (error?: Error, showError?: boolean) => {
if (showError) {
this.notificationService.error(error);
}
return TPromise.join(dirtyMoved.map(d => this.backupFileService.discardResourceBackup(d)));
};
if (!(target instanceof ExplorerItem)) { if (!(target instanceof ExplorerItem)) {
return TPromise.as(void 0); return TPromise.as(void 0);
} }
// 1. check for dirty files that are being moved and backup to new target return tree.expand(target).then(() => {
const dirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, source.resource, !isLinux /* ignorecase */));
return TPromise.join(dirty.map(d => {
let moved: URI;
// If the dirty file itself got moved, just reparent it to the target folder
if (source.resource.toString() === d.toString()) {
moved = target.resource.with({ path: paths.join(target.resource.path, source.name) });
}
// Otherwise, a parent of the dirty resource got moved, so we have to reparent more complicated. Example: // Reuse duplicate action if user copies
else { if (isCopy) {
moved = target.resource.with({ path: paths.join(target.resource.path, d.path.substr(source.parent.resource.path.length + 1)) }); return this.instantiationService.createInstance(DuplicateFileAction, tree, source, target).run();
} }
dirtyMoved.push(moved); // Otherwise move
const model = this.textFileService.models.get(d);
return this.backupFileService.backupResource(moved, model.createSnapshot(), model.getVersionId());
}))
// 2. soft revert all dirty since we have backed up their contents
.then(() => this.textFileService.revertAll(dirty, { soft: true /* do not attempt to load content from disk */ }))
// 3.) run the move operation
.then(() => {
const targetResource = target.resource.with({ path: paths.join(target.resource.path, source.name) }); const targetResource = target.resource.with({ path: paths.join(target.resource.path, source.name) });
return this.fileService.moveFile(source.resource, targetResource).then(null, error => { return this.textFileService.move(source.resource, targetResource).then(null, error => {
// Conflict // Conflict
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) { if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_MOVE_CONFLICT) {
...@@ -1098,26 +1059,20 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { ...@@ -1098,26 +1059,20 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop {
// Move with overwrite if the user confirms // Move with overwrite if the user confirms
return this.dialogService.confirm(confirm).then(res => { return this.dialogService.confirm(confirm).then(res => {
if (res.confirmed) { if (res.confirmed) {
const targetDirty = this.textFileService.getDirty().filter(d => resources.isEqualOrParent(d, targetResource, !isLinux /* ignorecase */)); return this.textFileService.move(source.resource, targetResource, true /* overwrite */).then(null, error => this.notificationService.error(error));
}
// Make sure to revert all dirty in target first to be able to overwrite properly
return this.textFileService.revertAll(targetDirty, { soft: true /* do not attempt to load content from disk */ }).then(() => {
// Then continue to do the move operation return void 0;
return this.fileService.moveFile(source.resource, targetResource, true).then(onSuccess, error => onError(error, true));
}); });
} }
return onError(); // Any other error
}); else {
this.notificationService.error(error);
} }
return onError(error, true); return void 0;
}); });
})
// 4.) resolve those that were dirty to load their previous dirty contents from disk
.then(onSuccess, onError);
}, errors.onUnexpectedError); }, errors.onUnexpectedError);
} }
} }
...@@ -33,6 +33,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c ...@@ -33,6 +33,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
import { IModelService } from 'vs/editor/common/services/modelService'; import { IModelService } from 'vs/editor/common/services/modelService';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { isEqualOrParent, isEqual } from 'vs/base/common/resources';
export interface IBackupResult { export interface IBackupResult {
didBackup: boolean; didBackup: boolean;
...@@ -702,51 +703,67 @@ export abstract class TextFileService implements ITextFileService { ...@@ -702,51 +703,67 @@ export abstract class TextFileService implements ITextFileService {
} }
public delete(resource: URI, useTrash?: boolean): TPromise<void> { public delete(resource: URI, useTrash?: boolean): TPromise<void> {
return this.revert(resource, { soft: true }).then(() => this.fileService.del(resource, useTrash)); const dirtyFiles = this.getDirty().filter(dirty => isEqualOrParent(dirty, resource, !platform.isLinux /* ignorecase */));
return this.revertAll(dirtyFiles, { soft: true }).then(() => this.fileService.del(resource, useTrash));
} }
public move(source: URI, target: URI, overwrite?: boolean): TPromise<void> { public move(source: URI, target: URI, overwrite?: boolean): TPromise<void> {
// Handle target model if existing // Handle target models if existing (if target URI is a folder, this can be multiple)
let handleTargetModelPromise: TPromise<any> = TPromise.as(void 0); let handleTargetModelPromise: TPromise<any> = TPromise.as(void 0);
const targetModel = this.models.get(target); const dirtyTargetModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), target, !platform.isLinux /* ignorecase */));
if (targetModel) { if (dirtyTargetModels.length) {
if (!overwrite) { handleTargetModelPromise = this.revertAll(dirtyTargetModels.map(targetModel => targetModel.getResource()), { soft: true });
return TPromise.wrapError(new Error('Cannot move file because target file exists and we are not overwriting'));
} }
// Soft revert the target file since we overwrite return handleTargetModelPromise.then(() => {
handleTargetModelPromise = this.revert(target, { soft: true });
// Handle dirty source models if existing (if source URI is a folder, this can be multiple)
let handleDirtySourceModels: TPromise<any>;
const dirtySourceModels = this.getDirtyFileModels().filter(model => isEqualOrParent(model.getResource(), source, !platform.isLinux /* ignorecase */));
const dirtyTargetModels: URI[] = [];
if (dirtySourceModels.length) {
handleDirtySourceModels = TPromise.join(dirtySourceModels.map(sourceModel => {
const sourceModelResource = sourceModel.getResource();
let targetModelResource: URI;
// If the source is the actual model, just use target as new resource
if (isEqual(sourceModelResource, source, !platform.isLinux /* ignorecase */)) {
targetModelResource = target;
} }
return handleTargetModelPromise.then(() => { // Otherwise a parent folder of the source is being moved, so we need
// to compute the target resource based on that
else {
targetModelResource = sourceModelResource.with({ path: paths.join(target.path, sourceModelResource.path.substr(source.path.length + 1)) });
}
// Remember as dirty target model to load after the operation
dirtyTargetModels.push(targetModelResource);
// Handle source model if existing // Backup dirty source model to the target resource it will become later
let handleSourceModelPromise: TPromise<boolean>; return this.backupFileService.backupResource(targetModelResource, sourceModel.createSnapshot(), sourceModel.getVersionId());
const sourceModel = this.models.get(source); }));
if (sourceModel && sourceModel.isDirty()) {
// Backup to target if the source is dirty
handleSourceModelPromise = this.backupFileService.backupResource(target, sourceModel.createSnapshot(), sourceModel.getVersionId()).then((() => true));
} else { } else {
handleSourceModelPromise = TPromise.as(false); handleDirtySourceModels = TPromise.as(void 0);
} }
return handleSourceModelPromise.then(dirty => { return handleDirtySourceModels.then(() => {
// Soft revert the source file // Soft revert the dirty source files if any
return this.revert(source, { soft: true }).then(() => { return this.revertAll(dirtySourceModels.map(dirtySourceModel => dirtySourceModel.getResource()), { soft: true }).then(() => {
// Rename to target // Rename to target
return this.fileService.moveFile(source, target, overwrite).then(() => { return this.fileService.moveFile(source, target, overwrite).then(() => {
// Load if we were dirty before // Load models that were dirty before
if (dirty) { return TPromise.join(dirtyTargetModels.map(dirtyTargetModel => this.models.loadOrCreate(dirtyTargetModel))).then(() => void 0);
return this.models.loadOrCreate(target).then(() => void 0);
}
return void 0;
}, error => { }, error => {
return this.backupFileService.discardResourceBackup(target).then(() => TPromise.wrapError(error));
// In case of an error, discard any dirty target backups that were made
return TPromise.join(dirtyTargetModels.map(dirtyTargetModel => this.backupFileService.discardResourceBackup(dirtyTargetModel)))
.then(() => TPromise.wrapError(error));
}); });
}); });
}); });
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册