diff --git a/packages/core/src/browser/dialogs.ts b/packages/core/src/browser/dialogs.ts index 73ed510271999..58f83ac166298 100644 --- a/packages/core/src/browser/dialogs.ts +++ b/packages/core/src/browser/dialogs.ts @@ -194,7 +194,9 @@ export abstract class AbstractDialog extends BaseWidget { this.activeElement = undefined; super.close(); } - + error(error: string): void { + this.setErrorMessage(error); + } protected onUpdateRequest(msg: Message): void { super.onUpdateRequest(msg); this.validate(); diff --git a/packages/workspace/src/browser/workspace-commands.ts b/packages/workspace/src/browser/workspace-commands.ts index 070bcb7dcf7a3..070e21b0eb9bd 100644 --- a/packages/workspace/src/browser/workspace-commands.ts +++ b/packages/workspace/src/browser/workspace-commands.ts @@ -16,6 +16,7 @@ import { inject, injectable } from 'inversify'; import URI from '@theia/core/lib/common/uri'; +import * as path from 'path'; import { SelectionService } from '@theia/core/lib/common/selection-service'; import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command'; import { MenuContribution, MenuModelRegistry } from '@theia/core/lib/common/menu'; @@ -182,11 +183,13 @@ export class WorkspaceCommandContribution implements CommandContribution { const parentUri = new URI(parent.uri); const { fileName, fileExtension } = this.getDefaultFileConfig(); const vacantChildUri = FileSystemUtils.generateUniqueResourceURI(parentUri, parent, fileName, fileExtension); - const dialog = new SingleTextInputDialog({ + + const dialog: SingleTextInputDialog = new SingleTextInputDialog({ title: 'New File', initialValue: vacantChildUri.path.base, - validate: name => this.validateFileName(name, parent) + validate: name => this.validateFileName(name, parent, true, dialog) }); + dialog.open().then(name => { if (name) { const fileUri = parentUri.resolve(name); @@ -203,10 +206,10 @@ export class WorkspaceCommandContribution implements CommandContribution { if (parent) { const parentUri = new URI(parent.uri); const vacantChildUri = FileSystemUtils.generateUniqueResourceURI(parentUri, parent, 'Untitled'); - const dialog = new SingleTextInputDialog({ + const dialog: SingleTextInputDialog = new SingleTextInputDialog({ title: 'New Folder', initialValue: vacantChildUri.path.base, - validate: name => this.validateFileName(name, parent) + validate: name => this.validateFileName(name, parent, true, dialog) }); dialog.open().then(name => { if (name) { @@ -240,7 +243,7 @@ export class WorkspaceCommandContribution implements CommandContribution { if (initialValue === name && mode === 'preview') { return false; } - return this.validateFileName(name, parent); + return this.validateFileName(name, parent, false); } }); dialog.open().then(name => { @@ -308,18 +311,26 @@ export class WorkspaceCommandContribution implements CommandContribution { * * @param name the simple file name of the file to validate. * @param parent the parent directory's file stat. + * @param recursive allow file or folder creation using recursive path + * @param dialog optional SingleTextInputDialog instance */ - protected validateFileName(name: string, parent: FileStat): string { - if (!validFilename(name)) { - return 'Invalid name, try other'; + protected validateFileName(name: string, parent: FileStat, recursive: boolean = false, dialog?: SingleTextInputDialog): string { + if (!name) { + return ''; } - if (parent.children) { - for (const child of parent.children) { - if (new URI(child.uri).path.base === name) { - return 'A file with this name already exists.'; - } - } + if (!recursive && !validFilename(name)) { + return 'Invalid file or folder name'; } + const fileName = path.basename(name); + if (!validFilename(fileName) || name.startsWith('/') || name.startsWith(' ') || name.endsWith(' ')) { + return `The name ${fileName} is not a valid file or folder name.`; + } + const childUri = new URI(parent.uri).resolve(name).toString(); + this.fileSystem.exists(childUri).then(exists => { + if (exists && dialog) { + dialog.error(`A file or folder ${fileName} already exists at this location.`); + } + }); return ''; }