"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = _default; var _isWhitespace = _interopRequireDefault(require("./isWhitespace")); var _isSingleLineString = _interopRequireDefault(require("./isSingleLineString")); var _configurationError = _interopRequireDefault(require("./configurationError")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } /** * Create a whitespaceChecker, which exposes the following functions: * - `before()` * - `beforeAllowingIndentation()` * - `after()` * - `afterOneOnly()` * * @param {"space"|"newline"} targetWhitespace - This is a keyword instead * of the actual character (e.g. " ") in order to accommodate * different styles of newline ("\n" vs "\r\n") * @param { * "always"|"never" * |"always-single-line"|"always-multi-line" * | "never-single-line"|"never-multi-line" * } expectation * @param {object} messages - An object of message functions; * calling `before*()` or `after*()` and the `expectation` that is passed * determines which message functions are required * @param {function} [messages.expectedBefore] * @param {function} [messages.rejectedBefore] * @param {function} [messages.expectedAfter] * @param {function} [messages.rejectedAfter] * @param {function} [messages.expectedBeforeSingleLine] * @param {function} [messages.rejectedBeforeSingleLine] * @param {function} [messages.expectedBeforeMultiLine] * @param {function} [messages.rejectedBeforeMultiLine] * @return {object} The checker, with its exposed checking functions */ function _default(targetWhitespace, expectation, messages) { // Keep track of active arguments in order to avoid passing // too much stuff around, making signatures long and confusing. // This variable gets reset anytime a checking function is called. var activeArgs; /** * Check for whitespace *before* a character. * * @param {object} args - Named arguments object * @param {string} args.source - The source string * @param {number} args.index - The index of the character to check before * @param {function} args.err - If a violation is found, this callback * will be invoked with the relevant warning message. * Typically this callback will report() the violation. * @param {function} args.errTarget - If a violation is found, this string * will be sent to the relevant warning message. * @param {string} [args.lineCheckStr] - Single- and multi-line checkers * will use this string to determine whether they should proceed, * i.e. if this string is one line only, single-line checkers will check, * multi-line checkers will ignore. * If none is passed, they will use `source`. * @param {boolean} [args.onlyOneChar=false] - Only check *one* character before. * By default, "always-*" checks will look for the `targetWhitespace` one * before and then ensure there is no whitespace two before. This option * bypasses that second check. * @param {boolean} [args.allowIndentation=false] - Allow arbitrary indentation * between the `targetWhitespace` (almost definitely a newline) and the `index`. * With this option, the checker will see if a newline *begins* the whitespace before * the `index`. */ function before(_ref) { var source = _ref.source, index = _ref.index, err = _ref.err, errTarget = _ref.errTarget, lineCheckStr = _ref.lineCheckStr, _ref$onlyOneChar = _ref.onlyOneChar, onlyOneChar = _ref$onlyOneChar === void 0 ? false : _ref$onlyOneChar, _ref$allowIndentation = _ref.allowIndentation, allowIndentation = _ref$allowIndentation === void 0 ? false : _ref$allowIndentation; activeArgs = { source: source, index: index, err: err, errTarget: errTarget, onlyOneChar: onlyOneChar, allowIndentation: allowIndentation }; switch (expectation) { case "always": expectBefore(); break; case "never": rejectBefore(); break; case "always-single-line": if (!(0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } expectBefore(messages.expectedBeforeSingleLine); break; case "never-single-line": if (!(0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } rejectBefore(messages.rejectedBeforeSingleLine); break; case "always-multi-line": if ((0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } expectBefore(messages.expectedBeforeMultiLine); break; case "never-multi-line": if ((0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } rejectBefore(messages.rejectedBeforeMultiLine); break; default: throw (0, _configurationError["default"])("Unknown expectation \"".concat(expectation, "\"")); } } /** * Check for whitespace *after* a character. * * Parameters are pretty much the same as for `before()`, above, just substitute * the word "after" for "before". */ function after(_ref2) { var source = _ref2.source, index = _ref2.index, err = _ref2.err, errTarget = _ref2.errTarget, lineCheckStr = _ref2.lineCheckStr, _ref2$onlyOneChar = _ref2.onlyOneChar, onlyOneChar = _ref2$onlyOneChar === void 0 ? false : _ref2$onlyOneChar; activeArgs = { source: source, index: index, err: err, errTarget: errTarget, onlyOneChar: onlyOneChar }; switch (expectation) { case "always": expectAfter(); break; case "never": rejectAfter(); break; case "always-single-line": if (!(0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } expectAfter(messages.expectedAfterSingleLine); break; case "never-single-line": if (!(0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } rejectAfter(messages.rejectedAfterSingleLine); break; case "always-multi-line": if ((0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } expectAfter(messages.expectedAfterMultiLine); break; case "never-multi-line": if ((0, _isSingleLineString["default"])(lineCheckStr || source)) { return; } rejectAfter(messages.rejectedAfterMultiLine); break; case "at-least-one-space": expectAfter(messages.expectedAfterAtLeast); break; default: throw (0, _configurationError["default"])("Unknown expectation \"".concat(expectation, "\"")); } } function beforeAllowingIndentation(obj) { before(Object.assign({}, obj, { allowIndentation: true })); } function expectBefore() { var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedBefore; if (activeArgs.allowIndentation) { expectBeforeAllowingIndentation(messageFunc); return; } var _activeArgs = activeArgs, source = _activeArgs.source, index = _activeArgs.index; var oneCharBefore = source[index - 1]; var twoCharsBefore = source[index - 2]; if (!isValue(oneCharBefore)) { return; } if (targetWhitespace === "newline") { // If index is preceeded by a Windows CR-LF ... if (oneCharBefore === "\n" && twoCharsBefore === "\r") { if (activeArgs.onlyOneChar || !(0, _isWhitespace["default"])(source[index - 3])) { return; } } // If index is followed by a Unix LF ... if (oneCharBefore === "\n" && twoCharsBefore !== "\r") { if (activeArgs.onlyOneChar || !(0, _isWhitespace["default"])(twoCharsBefore)) { return; } } } if (targetWhitespace === "space" && oneCharBefore === " ") { if (activeArgs.onlyOneChar || !(0, _isWhitespace["default"])(twoCharsBefore)) { return; } } activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])); } function expectBeforeAllowingIndentation() { var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedBefore; var _activeArgs2 = activeArgs, source = _activeArgs2.source, index = _activeArgs2.index, err = _activeArgs2.err; var expectedChar = function () { if (targetWhitespace === "newline") { return "\n"; } if (targetWhitespace === "space") { return " "; } }(); var i = index - 1; while (source[i] !== expectedChar) { if (source[i] === "\t" || source[i] === " ") { i--; continue; } err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])); return; } } function rejectBefore() { var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.rejectedBefore; var _activeArgs3 = activeArgs, source = _activeArgs3.source, index = _activeArgs3.index; var oneCharBefore = source[index - 1]; if (isValue(oneCharBefore) && (0, _isWhitespace["default"])(oneCharBefore)) { activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])); } } function afterOneOnly(obj) { after(Object.assign({}, obj, { onlyOneChar: true })); } function expectAfter() { var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedAfter; var _activeArgs4 = activeArgs, source = _activeArgs4.source, index = _activeArgs4.index; var oneCharAfter = index + 1 < source.length ? source[index + 1] : ""; var twoCharsAfter = index + 2 < source.length ? source[index + 2] : ""; if (!isValue(oneCharAfter)) { return; } if (targetWhitespace === "newline") { // If index is followed by a Windows CR-LF ... if (oneCharAfter === "\r" && twoCharsAfter === "\n") { var threeCharsAfter = index + 3 < source.length ? source[index + 3] : ""; if (activeArgs.onlyOneChar || !(0, _isWhitespace["default"])(threeCharsAfter)) { return; } } // If index is followed by a Unix LF ... if (oneCharAfter === "\n") { if (activeArgs.onlyOneChar || !(0, _isWhitespace["default"])(twoCharsAfter)) { return; } } } if (targetWhitespace === "space" && oneCharAfter === " ") { if (expectation === "at-least-one-space" || activeArgs.onlyOneChar || !(0, _isWhitespace["default"])(twoCharsAfter)) { return; } } activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])); } function rejectAfter() { var messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.rejectedAfter; var _activeArgs5 = activeArgs, source = _activeArgs5.source, index = _activeArgs5.index; var oneCharAfter = index + 1 < source.length ? source[index + 1] : ""; if (isValue(oneCharAfter) && (0, _isWhitespace["default"])(oneCharAfter)) { activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])); } } return { before: before, beforeAllowingIndentation: beforeAllowingIndentation, after: after, afterOneOnly: afterOneOnly }; } function isValue(x) { return x !== undefined && x !== null; }