未验证 提交 a17259e1 编写于 作者: I Isidor Nikolic 提交者: GitHub

Merge pull request #92221 from jeanp413/fix-91187

Show warning when filename start/end with whitespace in explorer inputbox
......@@ -21,6 +21,7 @@ import { flatten, mergeSort } from 'vs/base/common/arrays';
import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
import { SetMap } from 'vs/base/common/collections';
import { IProgressIndicator } from 'vs/platform/progress/common/progress';
import Severity from 'vs/base/common/severity';
export const TEST_VIEW_CONTAINER_ID = 'workbench.view.extension.test';
......@@ -583,7 +584,7 @@ export interface ITreeViewDataProvider {
}
export interface IEditableData {
validationMessage: (value: string) => string | null;
validationMessage: (value: string) => { content: string, severity: Severity } | null;
placeholder?: string | null;
startingValue?: string | null;
onFinish: (value: string, success: boolean) => void;
......
......@@ -733,18 +733,24 @@ export class ShowOpenedFileInNewWindow extends Action {
}
}
export function validateFileName(item: ExplorerItem, name: string): string | null {
export function validateFileName(item: ExplorerItem, name: string): { content: string, severity: Severity } | null {
// Produce a well formed file name
name = getWellFormedFileName(name);
// Name not provided
if (!name || name.length === 0 || /^\s+$/.test(name)) {
return nls.localize('emptyFileNameError', "A file or folder name must be provided.");
return {
content: nls.localize('emptyFileNameError', "A file or folder name must be provided."),
severity: Severity.Error
};
}
// Relative paths only
if (name[0] === '/' || name[0] === '\\') {
return nls.localize('fileNameStartsWithSlashError', "A file or folder name cannot start with a slash.");
return {
content: nls.localize('fileNameStartsWithSlashError', "A file or folder name cannot start with a slash."),
severity: Severity.Error
};
}
const names = coalesce(name.split(/[\\/]/));
......@@ -754,14 +760,27 @@ export function validateFileName(item: ExplorerItem, name: string): string | nul
// Do not allow to overwrite existing file
const child = parent?.getChild(name);
if (child && child !== item) {
return nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name);
return {
content: nls.localize('fileNameExistsError', "A file or folder **{0}** already exists at this location. Please choose a different name.", name),
severity: Severity.Error
};
}
}
// Invalid File name
const windowsBasenameValidity = item.resource.scheme === Schemas.file && isWindows;
if (names.some((folderName) => !extpath.isValidBasename(folderName, windowsBasenameValidity))) {
return nls.localize('invalidFileNameError', "The name **{0}** is not valid as a file or folder name. Please choose a different name.", trimLongName(name));
return {
content: nls.localize('invalidFileNameError', "The name **{0}** is not valid as a file or folder name. Please choose a different name.", trimLongName(name)),
severity: Severity.Error
};
}
if (names.some(name => /^\s|\s$/.test(name))) {
return {
content: nls.localize('fileNameWhitespaceWarning', "Leading or trailing whitespace detected in file or folder name."),
severity: Severity.Warning
};
}
return null;
......@@ -783,7 +802,7 @@ export function getWellFormedFileName(filename: string): string {
// Trim tabs
filename = strings.trim(filename, '\t');
// Remove trailing dots, slashes, and spaces
// Remove trailing dots and slashes
filename = strings.rtrim(filename, '.');
filename = strings.rtrim(filename, '/');
filename = strings.rtrim(filename, '\\');
......
......@@ -376,13 +376,13 @@ export class FilesRenderer implements ICompressibleTreeRenderer<ExplorerItem, Fu
const inputBox = new InputBox(label.element, this.contextViewService, {
validationOptions: {
validation: (value) => {
const content = editableData.validationMessage(value);
if (!content) {
const message = editableData.validationMessage(value);
if (!message || message.severity !== Severity.Error) {
return null;
}
return {
content,
content: message.content,
formatContent: true,
type: MessageType.ERROR
};
......@@ -392,10 +392,6 @@ export class FilesRenderer implements ICompressibleTreeRenderer<ExplorerItem, Fu
});
const styler = attachInputBoxStyler(inputBox, this.themeService);
inputBox.onDidChange(value => {
label.setFile(joinPath(parent, value || ' '), labelOptions); // update label icon while typing!
});
const lastDot = value.lastIndexOf('.');
inputBox.value = value;
......@@ -412,8 +408,27 @@ export class FilesRenderer implements ICompressibleTreeRenderer<ExplorerItem, Fu
}
});
const showInputBoxNotification = () => {
if (inputBox.isInputValid()) {
const message = editableData.validationMessage(inputBox.value);
if (message) {
inputBox.showMessage({
content: message.content,
formatContent: true,
type: message.severity === Severity.Info ? MessageType.INFO : message.severity === Severity.Warning ? MessageType.WARNING : MessageType.ERROR
});
} else {
inputBox.hideMessage();
}
}
};
showInputBoxNotification();
const toDispose = [
inputBox,
inputBox.onDidChange(value => {
label.setFile(joinPath(parent, value || ' '), labelOptions); // update label icon while typing!
}),
DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_DOWN, (e: IKeyboardEvent) => {
if (e.equals(KeyCode.Enter)) {
if (inputBox.validate()) {
......@@ -423,6 +438,9 @@ export class FilesRenderer implements ICompressibleTreeRenderer<ExplorerItem, Fu
done(false, true);
}
}),
DOM.addStandardDisposableListener(inputBox.inputElement, DOM.EventType.KEY_UP, (e: IKeyboardEvent) => {
showInputBoxNotification();
}),
DOM.addDisposableListener(inputBox.inputElement, DOM.EventType.BLUR, () => {
done(inputBox.isInputValid(), true);
}),
......
......@@ -28,7 +28,7 @@ import { IMenuService, MenuId, IMenu, MenuRegistry, MenuItemAction } from 'vs/pl
import { createAndFillInContextMenuActions, createAndFillInActionBarActions, ContextAwareMenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryActionViewItem';
import { IRemoteExplorerService, TunnelModel, MakeAddress, TunnelType, ITunnelItem, Tunnel } from 'vs/workbench/services/remote/common/remoteExplorerService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox';
import { attachInputBoxStyler } from 'vs/platform/theme/common/styler';
import { once } from 'vs/base/common/functional';
......@@ -282,13 +282,13 @@ class TunnelTreeRenderer extends Disposable implements ITreeRenderer<ITunnelGrou
ariaLabel: nls.localize('remote.tunnelsView.input', "Press Enter to confirm or Escape to cancel."),
validationOptions: {
validation: (value) => {
const content = editableData.validationMessage(value);
if (!content) {
const message = editableData.validationMessage(value);
if (!message || message.severity !== Severity.Error) {
return null;
}
return {
content,
content: message.content,
formatContent: true,
type: MessageType.ERROR
};
......@@ -657,6 +657,17 @@ export class TunnelPanelDescriptor implements IViewDescriptor {
}
}
function validationMessage(validationString: string | null): { content: string, severity: Severity } | null {
if (!validationString) {
return null;
}
return {
content: validationString,
severity: Severity.Error
};
}
namespace LabelTunnelAction {
export const ID = 'remote.tunnel.label';
export const LABEL = nls.localize('remote.tunnel.label', "Set Label");
......@@ -733,7 +744,7 @@ namespace ForwardPortAction {
}
remoteExplorerService.setEditable(undefined, null);
},
validationMessage: validateInput,
validationMessage: (value) => validationMessage(validateInput(value)),
placeholder: forwardPrompt
});
}
......@@ -916,7 +927,7 @@ namespace ChangeLocalPortAction {
}
}
},
validationMessage: validateInput,
validationMessage: (value) => validationMessage(validateInput(value)),
placeholder: nls.localize('remote.tunnelsView.changePort', "New local port")
});
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册