in src/vs/workbench/services/preferences/common/preferencesModels.ts [1025:1129]
export function createValidator(prop: IConfigurationPropertySchema): (value: any) => (string | null) {
return value => {
let exclusiveMax: number | undefined;
let exclusiveMin: number | undefined;
if (typeof prop.exclusiveMaximum === 'boolean') {
exclusiveMax = prop.exclusiveMaximum ? prop.maximum : undefined;
} else {
exclusiveMax = prop.exclusiveMaximum;
}
if (typeof prop.exclusiveMinimum === 'boolean') {
exclusiveMin = prop.exclusiveMinimum ? prop.minimum : undefined;
} else {
exclusiveMin = prop.exclusiveMinimum;
}
let patternRegex: RegExp | undefined;
if (typeof prop.pattern === 'string') {
patternRegex = new RegExp(prop.pattern);
}
const type = Array.isArray(prop.type) ? prop.type : [prop.type];
const canBeType = (t: string) => type.indexOf(t) > -1;
const isNullable = canBeType('null');
const isNumeric = (canBeType('number') || canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable);
const isIntegral = (canBeType('integer')) && (type.length === 1 || type.length === 2 && isNullable);
type Validator<T> = { enabled: boolean, isValid: (value: T) => boolean; message: string };
const numericValidations: Validator<number>[] = isNumeric ? [
{
enabled: exclusiveMax !== undefined && (prop.maximum === undefined || exclusiveMax <= prop.maximum),
isValid: ((value: number) => value < exclusiveMax!),
message: nls.localize('validations.exclusiveMax', "Value must be strictly less than {0}.", exclusiveMax)
},
{
enabled: exclusiveMin !== undefined && (prop.minimum === undefined || exclusiveMin >= prop.minimum),
isValid: ((value: number) => value > exclusiveMin!),
message: nls.localize('validations.exclusiveMin', "Value must be strictly greater than {0}.", exclusiveMin)
},
{
enabled: prop.maximum !== undefined && (exclusiveMax === undefined || exclusiveMax > prop.maximum),
isValid: ((value: number) => value <= prop.maximum!),
message: nls.localize('validations.max', "Value must be less than or equal to {0}.", prop.maximum)
},
{
enabled: prop.minimum !== undefined && (exclusiveMin === undefined || exclusiveMin < prop.minimum),
isValid: ((value: number) => value >= prop.minimum!),
message: nls.localize('validations.min', "Value must be greater than or equal to {0}.", prop.minimum)
},
{
enabled: prop.multipleOf !== undefined,
isValid: ((value: number) => value % prop.multipleOf! === 0),
message: nls.localize('validations.multipleOf', "Value must be a multiple of {0}.", prop.multipleOf)
},
{
enabled: isIntegral,
isValid: ((value: number) => value % 1 === 0),
message: nls.localize('validations.expectedInteger', "Value must be an integer.")
},
].filter(validation => validation.enabled) : [];
const stringValidations: Validator<string>[] = [
{
enabled: prop.maxLength !== undefined,
isValid: ((value: { length: number; }) => value.length <= prop.maxLength!),
message: nls.localize('validations.maxLength', "Value must be {0} or fewer characters long.", prop.maxLength)
},
{
enabled: prop.minLength !== undefined,
isValid: ((value: { length: number; }) => value.length >= prop.minLength!),
message: nls.localize('validations.minLength', "Value must be {0} or more characters long.", prop.minLength)
},
{
enabled: patternRegex !== undefined,
isValid: ((value: string) => patternRegex!.test(value)),
message: prop.patternErrorMessage || nls.localize('validations.regex', "Value must match regex `{0}`.", prop.pattern)
},
].filter(validation => validation.enabled);
if (prop.type === 'string' && stringValidations.length === 0) { return null; }
if (isNullable && value === '') { return ''; }
const errors: string[] = [];
if (isNumeric) {
if (value === '' || isNaN(+value)) {
errors.push(nls.localize('validations.expectedNumeric', "Value must be a number."));
} else {
errors.push(...numericValidations.filter(validator => !validator.isValid(+value)).map(validator => validator.message));
}
}
if (prop.type === 'string') {
errors.push(...stringValidations.filter(validator => !validator.isValid('' + value)).map(validator => validator.message));
}
if (errors.length) {
return prop.errorMessage ? [prop.errorMessage, ...errors].join(' ') : errors.join(' ');
}
return '';
};
}