"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports._testMethods = exports.hasWordCheck = exports.isWordValid = exports.calcTextInclusionRanges = exports.validateText = exports.minWordSplitLen = exports.defaultMinWordLength = exports.defaultMaxDuplicateProblems = exports.defaultMaxNumberOfProblems = void 0; const Text = __importStar(require("./util/text")); const TextRange = __importStar(require("./util/TextRange")); const RxPat = __importStar(require("./Settings/RegExpPatterns")); exports.defaultMaxNumberOfProblems = 200; exports.defaultMaxDuplicateProblems = 5; exports.defaultMinWordLength = 4; exports.minWordSplitLen = 3; function validateText(text, dict, options) { const { maxNumberOfProblems = exports.defaultMaxNumberOfProblems, maxDuplicateProblems = exports.defaultMaxDuplicateProblems, } = options; const mapOfProblems = new Map(); const includeRanges = calcTextInclusionRanges(text, options); const validator = lineValidator(dict, options); return Text.extractLinesOfText(text) .concatMap(mapTextOffsetsAgainstRanges(includeRanges)) .concatMap(validator) .filter((wo) => { const word = wo.text; // Keep track of the number of times we have seen the same problem mapOfProblems.set(word, (mapOfProblems.get(word) || 0) + 1); // Filter out if there is too many return mapOfProblems.get(word) < maxDuplicateProblems; }) .take(maxNumberOfProblems); } exports.validateText = validateText; function calcTextInclusionRanges(text, options) { const { ignoreRegExpList = [], includeRegExpList = [] } = options; const filteredIncludeList = includeRegExpList.filter((a) => !!a); const finalIncludeList = filteredIncludeList.length ? filteredIncludeList : ['.*']; const includeRanges = TextRange.excludeRanges(TextRange.findMatchingRangesForPatterns(finalIncludeList, text), TextRange.findMatchingRangesForPatterns(ignoreRegExpList, text)); return includeRanges; } exports.calcTextInclusionRanges = calcTextInclusionRanges; function lineValidator(dict, options) { const { minWordLength = exports.defaultMinWordLength, flagWords = [], ignoreWords = [], allowCompoundWords = false, ignoreCase = true, caseSensitive = false, } = options; const checkOptions = { ...options, allowCompoundWords, ignoreCase, caseSensitive, }; const setOfFlagWords = new Set(flagWords); const mappedIgnoreWords = options.caseSensitive ? ignoreWords : ignoreWords.concat(ignoreWords.map((a) => a.toLowerCase())); const ignoreWordsSet = new Set(mappedIgnoreWords); const setOfKnownSuccessfulWords = new Set(); const rememberFilter = (fn) => (v) => { const keep = fn(v); if (!keep) { setOfKnownSuccessfulWords.add(v.text); } return keep; }; const filterAlreadyChecked = (wo) => { return !setOfKnownSuccessfulWords.has(wo.text); }; function testForFlaggedWord(wo) { return setOfFlagWords.has(wo.text) || setOfFlagWords.has(wo.text.toLowerCase()); } function checkFlagWords(word) { const isFlagged = testForFlaggedWord(word); word.isFlagged = isFlagged; return word; } function checkWord(word, options) { const isFlagged = testForFlaggedWord(word); const isFound = isFlagged ? undefined : isWordValid(dict, word, word.line, options); return { ...word, isFlagged, isFound }; } const fn = (lineSegment) => { function checkFullWord(vr) { if (vr.isFlagged) { return [vr]; } const codeWordResults = Text.extractWordsFromCodeTextOffset(vr) .filter(filterAlreadyChecked) .filter(rememberFilter((wo) => wo.text.length >= minWordLength)) .map((t) => ({ ...t, line: vr.line })) .map((wo) => { const vr = { ...wo, text: wo.text.toLowerCase() }; return vr; }) .map((wo) => (wo.isFlagged ? wo : checkWord(wo, checkOptions))) .filter(rememberFilter((wo) => wo.isFlagged || !wo.isFound)) .filter(rememberFilter((wo) => !ignoreWordsSet.has(wo.text))) .filter(rememberFilter((wo) => !RxPat.regExHexDigits.test(wo.text))) // Filter out any hex numbers .filter(rememberFilter((wo) => !RxPat.regExRepeatedChar.test(wo.text))) // Filter out any repeated characters like xxxxxxxxxx // get back the original text. .map((wo) => ({ ...wo, text: Text.extractText(lineSegment, wo.offset, wo.offset + wo.text.length) })) .toArray(); if (!codeWordResults.length || ignoreWordsSet.has(vr.text) || checkWord(vr, checkOptions).isFound) { rememberFilter((_) => false)(vr); return []; } return codeWordResults; } // Check the whole words, not yet split const checkedWords = Text.extractWordsFromTextOffset(lineSegment) .filter(filterAlreadyChecked) .filter(rememberFilter((wo) => wo.text.length >= minWordLength)) .map((wo) => ({ ...wo, line: lineSegment })) .map(checkFlagWords) .concatMap(checkFullWord); return checkedWords; }; return fn; } function isWordValid(dict, wo, line, options) { const firstTry = hasWordCheck(dict, wo.text, options); return (firstTry || // Drop the first letter if it is preceded by a '\'. (line.text[wo.offset - line.offset - 1] === '\\' && hasWordCheck(dict, wo.text.slice(1), options))); } exports.isWordValid = isWordValid; function hasWordCheck(dict, word, options) { word = word.replace(/\\/g, ''); // Do not pass allowCompounds down if it is false, that allows for the dictionary to override the value based upon its own settings. return dict.has(word, convertCheckOptionsToHasOptions(options)); } exports.hasWordCheck = hasWordCheck; function convertCheckOptionsToHasOptions(opt) { const { ignoreCase, allowCompoundWords } = opt; return { ignoreCase, useCompounds: allowCompoundWords || undefined, }; } /** * Returns a mapper function that will * @param includeRanges Allowed ranges for words. */ function mapTextOffsetsAgainstRanges(includeRanges) { let rangePos = 0; const mapper = (textOffset) => { if (!includeRanges.length) { return []; } const parts = []; const { text, offset } = textOffset; const wordEndPos = offset + text.length; let wordStartPos = offset; while (rangePos && (rangePos >= includeRanges.length || includeRanges[rangePos].startPos > wordStartPos)) { rangePos -= 1; } while (wordStartPos < wordEndPos) { while (includeRanges[rangePos] && includeRanges[rangePos].endPos <= wordStartPos) { rangePos += 1; } if (!includeRanges[rangePos] || wordEndPos < includeRanges[rangePos].startPos) { break; } const { startPos, endPos } = includeRanges[rangePos]; const a = Math.max(wordStartPos, startPos); const b = Math.min(wordEndPos, endPos); parts.push({ offset: a, text: text.slice(a - offset, b - offset) }); wordStartPos = b; } return parts.filter((wo) => !!wo.text); }; return mapper; } exports._testMethods = { mapWordsAgainstRanges: mapTextOffsetsAgainstRanges, }; //# sourceMappingURL=textValidator.js.map