diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 4bee5f56d39fb..6ac2dd010fb8e 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -11,7 +11,7 @@ import { URI } from 'vs/base/common/uri'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Action } from 'vs/base/common/actions'; import { dispose, IDisposable } from 'vs/base/common/lifecycle'; -import { VIEWLET_ID, IFilesConfiguration, VIEW_ID, UndoEnablement } from 'vs/workbench/contrib/files/common/files'; +import { VIEWLET_ID, IFilesConfiguration, VIEW_ID, UndoConfirmLevel } from 'vs/workbench/contrib/files/common/files'; import { IFileService } from 'vs/platform/files/common/files'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput'; @@ -870,6 +870,7 @@ export const renameHandler = async (accessor: ServicesAccessor) => { const notificationService = accessor.get(INotificationService); const remoteAgentService = accessor.get(IRemoteAgentService); const pathService = accessor.get(IPathService); + const configurationService = accessor.get(IConfigurationService); const stats = explorerService.getContext(false); const stat = stats.length > 0 ? stats[0] : undefined; @@ -888,6 +889,7 @@ export const renameHandler = async (accessor: ServicesAccessor) => { if (stat.resource.toString() !== targetResource.toString()) { try { await explorerService.applyBulkEdit([new ResourceFileEdit(stat.resource, targetResource)], { + confirmBeforeUndo: configurationService.getValue().explorer.confirmUndo === UndoConfirmLevel.Verbose, undoLabel: nls.localize('renameBulkEdit', "Rename {0} to {1}", stat.name, value), progressLabel: nls.localize('renamingBulkEdit', "Renaming {0} to {1}", stat.name, value), }); @@ -1029,6 +1031,7 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { if (pasteShouldMove) { const resourceFileEdits = sourceTargetPairs.map(pair => new ResourceFileEdit(pair.source, pair.target)); const options = { + confirmBeforeUndo: configurationService.getValue().explorer.confirmUndo === UndoConfirmLevel.Verbose, progressLabel: sourceTargetPairs.length > 1 ? nls.localize({ key: 'movingBulkEdit', comment: ['Placeholder will be replaced by the number of files being moved'] }, "Moving {0} files", sourceTargetPairs.length) : nls.localize({ key: 'movingFileBulkEdit', comment: ['Placeholder will be replaced by the name of the file moved.'] }, "Moving {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)), undoLabel: sourceTargetPairs.length > 1 ? nls.localize({ key: 'moveBulkEdit', comment: ['Placeholder will be replaced by the number of files being moved'] }, "Move {0} files", sourceTargetPairs.length) @@ -1037,8 +1040,9 @@ export const pasteFileHandler = async (accessor: ServicesAccessor) => { await explorerService.applyBulkEdit(resourceFileEdits, options); } else { const resourceFileEdits = sourceTargetPairs.map(pair => new ResourceFileEdit(pair.source, pair.target, { copy: true })); + const undoLevel = configurationService.getValue().explorer.confirmUndo; const options = { - confirmBeforeUndo: configurationService.getValue().explorer.enableUndo === UndoEnablement.Warn, + confirmBeforeUndo: undoLevel === UndoConfirmLevel.Default || undoLevel === UndoConfirmLevel.Verbose, progressLabel: sourceTargetPairs.length > 1 ? nls.localize({ key: 'copyingBulkEdit', comment: ['Placeholder will be replaced by the number of files being copied'] }, "Copying {0} files", sourceTargetPairs.length) : nls.localize({ key: 'copyingFileBulkEdit', comment: ['Placeholder will be replaced by the name of the file copied.'] }, "Copying {0}", resources.basenameOrAuthority(sourceTargetPairs[0].target)), undoLabel: sourceTargetPairs.length > 1 ? nls.localize({ key: 'copyBulkEdit', comment: ['Placeholder will be replaced by the number of files being copied'] }, "Paste {0} files", sourceTargetPairs.length) diff --git a/src/vs/workbench/contrib/files/browser/fileImportExport.ts b/src/vs/workbench/contrib/files/browser/fileImportExport.ts index c054249caa61d..f9bd8a4575c66 100644 --- a/src/vs/workbench/contrib/files/browser/fileImportExport.ts +++ b/src/vs/workbench/contrib/files/browser/fileImportExport.ts @@ -10,7 +10,7 @@ import { ByteSize, FileSystemProviderCapabilities, IFileService, IFileStatWithMe import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IProgress, IProgressService, IProgressStep, ProgressLocation } from 'vs/platform/progress/common/progress'; import { IExplorerService } from 'vs/workbench/contrib/files/browser/files'; -import { IFilesConfiguration, UndoEnablement, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; +import { IFilesConfiguration, UndoConfirmLevel, VIEW_ID } from 'vs/workbench/contrib/files/common/files'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Limiter, Promises, RunOnceWorker } from 'vs/base/common/async'; import { newWriteableBufferStream, VSBuffer } from 'vs/base/common/buffer'; @@ -530,6 +530,7 @@ export class ExternalFileImport { return new ResourceFileEdit(resource, targetFile, { overwrite: true, copy: true }); }); + const undoLevel = this.configurationService.getValue().explorer.confirmUndo; await this.explorerService.applyBulkEdit(resourceFileEdits, { undoLabel: resourcesFiltered.length === 1 ? localize('importFile', "Import {0}", basename(resourcesFiltered[0])) : @@ -538,7 +539,7 @@ export class ExternalFileImport { localize('copyingFile', "Copying {0}", basename(resourcesFiltered[0])) : localize('copyingnFile', "Copying {0} resources", resourcesFiltered.length), progressLocation: ProgressLocation.Window, - confirmBeforeUndo: this.configurationService.getValue().explorer.enableUndo === UndoEnablement.Warn, + confirmBeforeUndo: undoLevel === UndoConfirmLevel.Verbose || undoLevel === UndoConfirmLevel.Default, }); // if we only add one file, just open it directly diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 951fbdc867fb1..9c295cc41ab9c 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -10,7 +10,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IFileEditorInput, IEditorFactoryRegistry, EditorExtensions } from 'vs/workbench/common/editor'; import { AutoSaveConfiguration, HotExitConfiguration, FILES_EXCLUDE_CONFIG, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; -import { SortOrder, LexicographicOptions, FILE_EDITOR_INPUT_ID, BINARY_TEXT_FILE_MODE, UndoEnablement, IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; +import { SortOrder, LexicographicOptions, FILE_EDITOR_INPUT_ID, BINARY_TEXT_FILE_MODE, UndoConfirmLevel, IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; import { TextFileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/textFileEditorTracker'; import { TextFileSaveErrorHandler } from 'vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler'; import { FileEditorInput } from 'vs/workbench/contrib/files/browser/editors/fileEditorInput'; @@ -372,14 +372,19 @@ configurationRegistry.registerConfiguration({ 'default': true }, 'explorer.enableUndo': { + 'type': 'boolean', + 'description': nls.localize('enableUndo', "Controls whether the explorer should support undoing file and folder operations."), + 'default': true + }, + 'explorer.confirmUndo': { 'type': 'string', - 'enum': [UndoEnablement.Warn, UndoEnablement.Allow, UndoEnablement.Disable], - 'description': nls.localize('confirmUndo', "Controls how the explorer participates in undoing file and folder edits."), - 'default': UndoEnablement.Warn, + 'enum': [UndoConfirmLevel.Verbose, UndoConfirmLevel.Default, UndoConfirmLevel.Light], + 'description': nls.localize('confirmUndo', "Controls whether the explorer should ask for confirmation when undoing."), + 'default': UndoConfirmLevel.Default, 'enumDescriptions': [ - nls.localize('enableUndo.warn', 'Explorer will prompt before undoing all file and folder creation events.'), - nls.localize('enableUndo.allow', 'Explorer will undo file and folder creation events without prompting.'), - nls.localize('enableUndo.disable', 'Undo will be disabled in the explorer.'), + nls.localize('enableUndo.verbose', 'Explorer will prompt before all undo operations.'), + nls.localize('enableUndo.default', 'Explorer will prompt before destructive undo operations.'), + nls.localize('enableUndo.light', 'Explorer will not prompt before undo operations when focused.'), ], }, 'explorer.expandSingleFolderWorkspaces': { @@ -396,7 +401,7 @@ configurationRegistry.registerConfiguration({ nls.localize('sortOrder.mixed', 'Files and folders are sorted by their names. Files are interwoven with folders.'), nls.localize('sortOrder.filesFirst', 'Files and folders are sorted by their names. Files are displayed before folders.'), nls.localize('sortOrder.type', 'Files and folders are grouped by extension type then sorted by their names. Folders are displayed before files.'), - nls.localize('sortOrder.modified', 'Files and folders are sorted by last modified date in descending order. Folders are displayed before files.'), + nls.localize('sortOrder.modified', 'Files and folders are sorted by last modified date in descending order. Folders are displayed before files.'), nls.localize('sortOrder.foldersNestsFiles', 'Files and folders are sorted by their names. Folders are displayed before files. Files with nested children are displayed before other files.') ], 'description': nls.localize('sortOrder', "Controls the property-based sorting of files and folders in the explorer.") @@ -491,7 +496,7 @@ UndoCommand.addImplementation(110, 'explorer', (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); const configurationService = accessor.get(IConfigurationService); - const explorerCanUndo = configurationService.getValue().explorer.enableUndo !== UndoEnablement.Disable; + const explorerCanUndo = configurationService.getValue().explorer.enableUndo; if (explorerService.hasViewFocus() && undoRedoService.canUndo(UNDO_REDO_SOURCE) && explorerCanUndo) { undoRedoService.undo(UNDO_REDO_SOURCE); return true; @@ -505,7 +510,7 @@ RedoCommand.addImplementation(110, 'explorer', (accessor: ServicesAccessor) => { const explorerService = accessor.get(IExplorerService); const configurationService = accessor.get(IConfigurationService); - const explorerCanUndo = configurationService.getValue().explorer.enableUndo !== UndoEnablement.Disable; + const explorerCanUndo = configurationService.getValue().explorer.enableUndo; if (explorerService.hasViewFocus() && undoRedoService.canRedo(UNDO_REDO_SOURCE) && explorerCanUndo) { undoRedoService.redo(UNDO_REDO_SOURCE); return true; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index 5a61a73b3ac85..ecb3a22c7469c 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -19,7 +19,7 @@ import { ITreeNode, ITreeFilter, TreeVisibility, IAsyncDataSource, ITreeSorter, import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IFilesConfiguration } from 'vs/workbench/contrib/files/common/files'; +import { IFilesConfiguration, UndoConfirmLevel } from 'vs/workbench/contrib/files/common/files'; import { dirname, joinPath, distinctParents } from 'vs/base/common/resources'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { localize } from 'vs/nls'; @@ -1102,10 +1102,12 @@ export class FileDragAndDrop implements ITreeDragAndDrop { private async doHandleExplorerDropOnCopy(sources: ExplorerItem[], target: ExplorerItem): Promise { // Reuse duplicate action when user copies - const incrementalNaming = this.configurationService.getValue().explorer.incrementalNaming; - const resourceFileEdits = sources.map(({ resource, isDirectory }) => (new ResourceFileEdit(resource, findValidPasteFileTarget(this.explorerService, target, { resource, isDirectory, allowOverwrite: false }, incrementalNaming), { copy: true }))); + const explorerConfig = this.configurationService.getValue().explorer; + const resourceFileEdits = sources.map(({ resource, isDirectory }) => + (new ResourceFileEdit(resource, findValidPasteFileTarget(this.explorerService, target, { resource, isDirectory, allowOverwrite: false }, explorerConfig.incrementalNaming), { copy: true }))); const labelSufix = getFileOrFolderLabelSufix(sources); await this.explorerService.applyBulkEdit(resourceFileEdits, { + confirmBeforeUndo: explorerConfig.confirmUndo === UndoConfirmLevel.Default || explorerConfig.confirmUndo === UndoConfirmLevel.Verbose, undoLabel: localize('copy', "Copy {0}", labelSufix), progressLabel: localize('copying', "Copying {0}", labelSufix), }); @@ -1124,6 +1126,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop { const resourceFileEdits = sources.filter(source => !source.isReadonly).map(source => new ResourceFileEdit(source.resource, joinPath(target.resource, source.name))); const labelSufix = getFileOrFolderLabelSufix(sources); const options = { + confirmBeforeUndo: this.configurationService.getValue().explorer.confirmUndo === UndoConfirmLevel.Verbose, undoLabel: localize('move', "Move {0}", labelSufix), progressLabel: localize('moving', "Moving {0}", labelSufix) }; diff --git a/src/vs/workbench/contrib/files/common/files.ts b/src/vs/workbench/contrib/files/common/files.ts index 3f74e61ed3b5b..1dc0604f0a286 100644 --- a/src/vs/workbench/contrib/files/common/files.ts +++ b/src/vs/workbench/contrib/files/common/files.ts @@ -88,7 +88,8 @@ export interface IFilesConfiguration extends PlatformIFilesConfiguration, IWorkb autoReveal: boolean | 'focusNoScroll'; enableDragAndDrop: boolean; confirmDelete: boolean; - enableUndo: UndoEnablement; + enableUndo: boolean; + confirmUndo: UndoConfirmLevel; expandSingleFolderWorkspaces: boolean; sortOrder: SortOrder; sortOrderLexicographicOptions: LexicographicOptions; @@ -122,10 +123,10 @@ export const enum SortOrder { FoldersNestsFiles = 'foldersNestsFiles', } -export const enum UndoEnablement { - Warn = 'warn', - Allow = 'allow', - Disable = 'disable', +export const enum UndoConfirmLevel { + Verbose = 'verbose', + Default = 'default', + Light = 'light', } export const enum LexicographicOptions {