This commit is contained in:
Johannes Rieken 2020-06-22 17:29:05 +02:00
parent 83ed704964
commit 506c6d02b3
3 changed files with 121 additions and 29 deletions

View file

@ -96,13 +96,13 @@ class BulkEdit {
private async _performFileEdits(edits: WorkspaceFileEdit[], progress: IProgress<void>) {
this._logService.debug('_performFileEdits', JSON.stringify(edits));
const model = this._instaService.createInstance(BulkFileEdits, progress, edits);
const model = this._instaService.createInstance(BulkFileEdits, this._label || localize('workspaceEdit', "Workspace Edit"), progress, edits);
await model.apply();
}
private async _performTextEdits(edits: WorkspaceTextEdit[], progress: IProgress<void>): Promise<void> {
this._logService.debug('_performTextEdits', JSON.stringify(edits));
const model = this._instaService.createInstance(BulkTextEdits, this._label, this._editor, progress, edits);
const model = this._instaService.createInstance(BulkTextEdits, this._label || localize('workspaceEdit', "Workspace Edit"), this._editor, progress, edits);
await model.apply();
}
}

View file

@ -4,55 +4,148 @@
*--------------------------------------------------------------------------------------------*/
import { WorkspaceFileEdit } from 'vs/editor/common/modes';
import { WorkspaceFileEdit, WorkspaceFileEditOptions } from 'vs/editor/common/modes';
import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { IProgress } from 'vs/platform/progress/common/progress';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
import { IWorkspaceUndoRedoElement, UndoRedoElementType, IResourceUndoRedoElement, IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
interface IFileOperation {
perform(): Promise<IFileOperation>;
}
class Noop implements IFileOperation {
async perform() { return this; }
}
class RenameOperation implements IFileOperation {
constructor(
readonly newUri: URI,
readonly oldUri: URI,
readonly options: WorkspaceFileEditOptions,
@IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService,
@IFileService private readonly _fileService: IFileService,
) { }
async perform(): Promise<IFileOperation> {
// rename
if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) {
return new Noop(); // not overwriting, but ignoring, and the target file exists
}
await this._workingCopyFileService.move(this.oldUri, this.newUri, this.options.overwrite);
return new RenameOperation(this.oldUri, this.newUri, this.options, this._workingCopyFileService, this._fileService);
}
}
class CreateOperation implements IFileOperation {
constructor(
readonly newUri: URI,
readonly options: WorkspaceFileEditOptions,
@ITextFileService private readonly _textFileService: ITextFileService,
@IFileService private readonly _fileService: IFileService,
@IInstantiationService private readonly _instaService: IInstantiationService,
) { }
async perform(): Promise<IFileOperation> {
// create file
if (this.options.overwrite === undefined && this.options.ignoreIfExists && await this._fileService.exists(this.newUri)) {
return new Noop(); // not overwriting, but ignoring, and the target file exists
}
await this._textFileService.create(this.newUri, undefined, { overwrite: this.options.overwrite });
return this._instaService.createInstance(DeleteOperation, this.newUri, this.options);
}
}
class DeleteOperation implements IFileOperation {
constructor(
readonly oldUri: URI,
readonly options: WorkspaceFileEditOptions,
@IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService,
@IFileService private readonly _fileService: IFileService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IInstantiationService private readonly _instaService: IInstantiationService,
) { }
async perform(): Promise<IFileOperation> {
// delete file
if (await this._fileService.exists(this.oldUri)) {
let useTrash = this._configurationService.getValue<boolean>('files.enableTrash');
if (useTrash && !(this._fileService.hasCapability(this.oldUri, FileSystemProviderCapabilities.Trash))) {
useTrash = false; // not supported by provider
}
await this._workingCopyFileService.delete(this.oldUri, { useTrash, recursive: this.options.recursive });
} else if (!this.options.ignoreIfNotExists) {
throw new Error(`${this.oldUri} does not exist and can not be deleted`);
}
return this._instaService.createInstance(CreateOperation, this.oldUri, this.options);
}
}
class FileUndoRedoElement implements IWorkspaceUndoRedoElement {
readonly type = UndoRedoElementType.Workspace;
readonly resources: readonly URI[] = [];
constructor(
readonly label: string,
readonly operations: IFileOperation[]
) { }
undo(): void | Promise<void> {
// throw new Error('Method not implemented.');
}
redo(): void | Promise<void> {
// throw new Error('Method not implemented.');
}
split(): IResourceUndoRedoElement[] {
return [];
}
}
export class BulkFileEdits {
constructor(
private readonly _label: string,
private readonly _progress: IProgress<void>,
private readonly _edits: WorkspaceFileEdit[],
@IFileService private readonly _fileService: IFileService,
@ITextFileService private readonly _textFileService: ITextFileService,
@IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService,
@IConfigurationService private readonly _configurationService: IConfigurationService
@IInstantiationService private readonly _instaService: IInstantiationService,
@IUndoRedoService private readonly _undoRedoService: IUndoRedoService,
) { }
async apply(): Promise<void> {
const undoOperations: IFileOperation[] = [];
for (const edit of this._edits) {
this._progress.report(undefined);
const options = edit.options || {};
let op: IFileOperation | undefined;
if (edit.newUri && edit.oldUri) {
// rename
if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.exists(edit.newUri)) {
continue; // not overwriting, but ignoring, and the target file exists
}
await this._workingCopyFileService.move(edit.oldUri, edit.newUri, options.overwrite);
op = this._instaService.createInstance(RenameOperation, edit.newUri, edit.oldUri, options);
} else if (!edit.newUri && edit.oldUri) {
// delete file
if (await this._fileService.exists(edit.oldUri)) {
let useTrash = this._configurationService.getValue<boolean>('files.enableTrash');
if (useTrash && !(this._fileService.hasCapability(edit.oldUri, FileSystemProviderCapabilities.Trash))) {
useTrash = false; // not supported by provider
}
await this._workingCopyFileService.delete(edit.oldUri, { useTrash, recursive: options.recursive });
} else if (!options.ignoreIfNotExists) {
throw new Error(`${edit.oldUri} does not exist and can not be deleted`);
}
op = this._instaService.createInstance(DeleteOperation, edit.oldUri, options);
} else if (edit.newUri && !edit.oldUri) {
// create file
if (options.overwrite === undefined && options.ignoreIfExists && await this._fileService.exists(edit.newUri)) {
continue; // not overwriting, but ignoring, and the target file exists
}
await this._textFileService.create(edit.newUri, undefined, { overwrite: options.overwrite });
op = this._instaService.createInstance(CreateOperation, edit.newUri, options);
}
if (op) {
await op.perform();
undoOperations.push(op);
}
}
this._undoRedoService.pushElement(new FileUndoRedoElement(this._label, undoOperations));
}
}

View file

@ -13,7 +13,6 @@ import { Selection } from 'vs/editor/common/core/selection';
import { EndOfLineSequence, IIdentifiedSingleEditOperation, ITextModel } from 'vs/editor/common/model';
import { WorkspaceTextEdit } from 'vs/editor/common/modes';
import { ITextModelService, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { localize } from 'vs/nls';
import { IProgress } from 'vs/platform/progress/common/progress';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo';
@ -120,7 +119,7 @@ export class BulkTextEdits {
private readonly _edits = new ResourceMap<WorkspaceTextEdit[]>();
constructor(
private readonly _label: string | undefined,
private readonly _label: string,
private readonly _editor: ICodeEditor | undefined,
private readonly _progress: IProgress<void>,
edits: WorkspaceTextEdit[],
@ -228,7 +227,7 @@ export class BulkTextEdits {
} else {
// prepare multi model undo element
const multiModelEditStackElement = new MultiModelEditStackElement(
this._label || localize('workspaceEdit', "Workspace Edit"),
this._label,
tasks.map(t => new SingleModelEditStackElement(t.model, t.getBeforeCursorState()))
);
this._undoRedoService.pushElement(multiModelEditStackElement);