eslint-plugin-regexp
Advanced tools
Comparing version
@@ -14,2 +14,12 @@ "use strict"; | ||
]); | ||
function isRegExpLiteralAt({ node, patternSource }, at) { | ||
if (utils_2.isRegexpLiteral(node)) { | ||
return true; | ||
} | ||
const replaceRange = patternSource.getReplaceRange(at); | ||
if (replaceRange && replaceRange.type === "RegExp") { | ||
return true; | ||
} | ||
return false; | ||
} | ||
exports.default = utils_1.createRule("control-character-escape", { | ||
@@ -30,3 +40,4 @@ meta: { | ||
create(context) { | ||
function createVisitor({ node, getRegexpLocation, fixReplaceNode, }) { | ||
function createVisitor(regexpContext) { | ||
const { node, getRegexpLocation, fixReplaceNode } = regexpContext; | ||
return { | ||
@@ -44,3 +55,3 @@ onCharacterEnter(cNode) { | ||
} | ||
if (!utils_2.isRegexpLiteral(node) && | ||
if (!isRegExpLiteralAt(regexpContext, cNode) && | ||
cNode.raw === String.fromCodePoint(cNode.value)) { | ||
@@ -47,0 +58,0 @@ return; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
const utils_1 = require("../utils"); | ||
@@ -19,3 +20,3 @@ exports.default = utils_1.createRule("negation", { | ||
create(context) { | ||
function createVisitor({ node, getRegexpLocation, fixReplaceNode, toCharSet, flags, }) { | ||
function createVisitor({ node, getRegexpLocation, fixReplaceNode, flags, }) { | ||
return { | ||
@@ -31,4 +32,4 @@ onCharacterClassEnter(ccNode) { | ||
if (flags.ignoreCase && element.kind === "property") { | ||
const ccSet = toCharSet(ccNode); | ||
const negatedElementSet = toCharSet(Object.assign(Object.assign({}, element), { negate: !element.negate })); | ||
const ccSet = regexp_ast_analysis_1.toCharSet(ccNode, flags); | ||
const negatedElementSet = regexp_ast_analysis_1.toCharSet(Object.assign(Object.assign({}, element), { negate: !element.negate }), flags); | ||
if (!ccSet.equals(negatedElementSet)) { | ||
@@ -35,0 +36,0 @@ return; |
@@ -7,3 +7,3 @@ "use strict"; | ||
const mention_1 = require("../utils/mention"); | ||
function groupElements(elements, { toCharSet }) { | ||
function groupElements(elements, flags) { | ||
const duplicates = []; | ||
@@ -23,3 +23,3 @@ const characters = new Map(); | ||
for (const e of elements) { | ||
const charSet = toCharSet(e); | ||
const charSet = regexp_ast_analysis_1.toCharSet(e, flags); | ||
if (e.type === "Character") { | ||
@@ -168,6 +168,6 @@ const key = charSet.ranges[0].min; | ||
function createVisitor(regexpContext) { | ||
const { toCharSet, flags } = regexpContext; | ||
const { flags } = regexpContext; | ||
return { | ||
onCharacterClassEnter(ccNode) { | ||
const { duplicates, characters, characterRanges, characterSets, } = groupElements(ccNode.elements, regexpContext); | ||
const { duplicates, characters, characterRanges, characterSets, } = groupElements(ccNode.elements, flags); | ||
const rangesAndSets = [...characterRanges, ...characterSets]; | ||
@@ -181,3 +181,3 @@ const subsets = new Set(); | ||
for (const other of rangesAndSets) { | ||
if (toCharSet(other).has(char.value)) { | ||
if (regexp_ast_analysis_1.toCharSet(other, flags).has(char.value)) { | ||
reportSubset(regexpContext, char, other); | ||
@@ -194,3 +194,3 @@ subsets.add(char); | ||
} | ||
if (toCharSet(element).isSubsetOf(toCharSet(other))) { | ||
if (regexp_ast_analysis_1.toCharSet(element, flags).isSubsetOf(regexp_ast_analysis_1.toCharSet(other, flags))) { | ||
reportSubset(regexpContext, element, other); | ||
@@ -209,8 +209,8 @@ subsets.add(element); | ||
.filter((e) => !subsets.has(e) && e !== element) | ||
.map((e) => toCharSet(e))); | ||
const elementCharSet = toCharSet(element); | ||
.map((e) => regexp_ast_analysis_1.toCharSet(e, flags))); | ||
const elementCharSet = regexp_ast_analysis_1.toCharSet(element, flags); | ||
if (elementCharSet.isSubsetOf(totalOthers)) { | ||
const superSetElements = ccNode.elements | ||
.filter((e) => !subsets.has(e) && e !== element) | ||
.filter((e) => !toCharSet(e).isDisjointWith(elementCharSet)); | ||
.filter((e) => !regexp_ast_analysis_1.toCharSet(e, flags).isDisjointWith(elementCharSet)); | ||
reportSubsetOfMany(regexpContext, element, superSetElements); | ||
@@ -230,3 +230,3 @@ subsets.add(element); | ||
} | ||
const intersection = toCharSet(range).intersect(toCharSet(other)); | ||
const intersection = regexp_ast_analysis_1.toCharSet(range, flags).intersect(regexp_ast_analysis_1.toCharSet(other, flags)); | ||
if (intersection.isEmpty) { | ||
@@ -233,0 +233,0 @@ continue; |
@@ -200,3 +200,3 @@ "use strict"; | ||
} | ||
function* findDuplicationAstFast(alternatives, { toCharSet }) { | ||
function* findDuplicationAstFast(alternatives, flags) { | ||
const shortCircuit = (a) => { | ||
@@ -209,3 +209,3 @@ return a.type === "CapturingGroup" ? false : null; | ||
const other = alternatives[j]; | ||
if (regexp_ast_1.isEqualNodes(other, alternative, toCharSet, shortCircuit)) { | ||
if (regexp_ast_1.isEqualNodes(other, alternative, flags, shortCircuit)) { | ||
yield { type: "Duplicate", alternative, others: [other] }; | ||
@@ -216,8 +216,6 @@ } | ||
} | ||
function* findDuplicationAst(alternatives, context, hasNothingAfter) { | ||
const { flags, toCharSet } = context; | ||
function* findDuplicationAst(alternatives, flags, hasNothingAfter) { | ||
const isCoveredOptions = { | ||
flags, | ||
canOmitRight: hasNothingAfter, | ||
toCharSet, | ||
}; | ||
@@ -227,3 +225,2 @@ const isCoveredOptionsNoPrefix = { | ||
canOmitRight: false, | ||
toCharSet, | ||
}; | ||
@@ -235,3 +232,3 @@ for (let i = 0; i < alternatives.length; i++) { | ||
if (regexp_ast_1.isCoveredNode(other, alternative, isCoveredOptions)) { | ||
if (regexp_ast_1.isEqualNodes(other, alternative, toCharSet)) { | ||
if (regexp_ast_1.isEqualNodes(other, alternative, flags)) { | ||
yield { | ||
@@ -280,3 +277,3 @@ type: "Duplicate", | ||
} | ||
function* findDuplicationNfa(alternatives, context, { hasNothingAfter, parser, ignoreOverlap }) { | ||
function* findDuplicationNfa(alternatives, flags, { hasNothingAfter, parser, ignoreOverlap }) { | ||
const previous = []; | ||
@@ -309,3 +306,3 @@ for (let i = 0; i < alternatives.length; i++) { | ||
case 3: { | ||
const reorder = reorder_alternatives_1.canReorder([alternative, ...others], context); | ||
const reorder = reorder_alternatives_1.canReorder([alternative, ...others], flags); | ||
if (reorder) { | ||
@@ -346,11 +343,11 @@ for (const other of others) { | ||
} | ||
function* findDuplication(alternatives, context, options) { | ||
function* findDuplication(alternatives, flags, options) { | ||
if (options.fastAst) { | ||
yield* findDuplicationAstFast(alternatives, context); | ||
yield* findDuplicationAstFast(alternatives, flags); | ||
} | ||
else { | ||
yield* findDuplicationAst(alternatives, context, options.hasNothingAfter); | ||
yield* findDuplicationAst(alternatives, flags, options.hasNothingAfter); | ||
} | ||
if (!options.noNfa) { | ||
yield* findDuplicationNfa(alternatives, context, options); | ||
yield* findDuplicationNfa(alternatives, flags, options); | ||
} | ||
@@ -487,3 +484,3 @@ } | ||
const info = getFilterInfo(parentNode); | ||
const rawResults = findDuplication(parentNode.alternatives, regexpContext, { | ||
const rawResults = findDuplication(parentNode.alternatives, flags, { | ||
fastAst: false, | ||
@@ -490,0 +487,0 @@ noNfa: false, |
@@ -108,6 +108,3 @@ "use strict"; | ||
} | ||
const firstOf = regexp_ast_analysis_1.getFirstConsumedChar(assertion.alternatives, direction, flags); | ||
if (firstOf.empty) { | ||
return; | ||
} | ||
const firstOf = regexp_ast_analysis_1.FirstConsumedChars.toLook(regexp_ast_analysis_1.getFirstConsumedChar(assertion.alternatives, direction, flags)); | ||
const accept = assertion.negate ? "reject" : "accept"; | ||
@@ -114,0 +111,0 @@ const reject = assertion.negate ? "accept" : "reject"; |
@@ -6,2 +6,3 @@ "use strict"; | ||
const type_tracker_1 = require("../utils/type-tracker"); | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
class RegExpReference { | ||
@@ -94,3 +95,3 @@ constructor(regExpContext) { | ||
createVisitor(regExpContext) { | ||
const { flags, regexpNode, toCharSet, ownsFlags, getFlagLocation, } = regExpContext; | ||
const { flags, regexpNode, ownsFlags, getFlagLocation, } = regExpContext; | ||
if (!flags.ignoreCase || !ownsFlags) { | ||
@@ -111,3 +112,3 @@ return {}; | ||
if (unnecessary) { | ||
if (toCharSet(cNode).size > 1) { | ||
if (regexp_ast_analysis_1.toCharSet(cNode, flags).size > 1) { | ||
unnecessary = false; | ||
@@ -123,4 +124,4 @@ } | ||
if (cNode.kind === "property") { | ||
const caseInsensitive = toCharSet(cNode); | ||
const caseSensitive = toCharSet(cNode, flagsNoI); | ||
const caseInsensitive = regexp_ast_analysis_1.toCharSet(cNode, flags); | ||
const caseSensitive = regexp_ast_analysis_1.toCharSet(cNode, flagsNoI); | ||
if (!caseInsensitive.equals(caseSensitive)) { | ||
@@ -127,0 +128,0 @@ unnecessary = false; |
@@ -18,8 +18,8 @@ "use strict"; | ||
}; | ||
function getSingleConsumedChar(element, context) { | ||
const empty = context.flags.unicode ? EMPTY_UNICODE : EMPTY_UTF16; | ||
function getSingleConsumedChar(element, flags) { | ||
const empty = flags.unicode ? EMPTY_UNICODE : EMPTY_UTF16; | ||
switch (element.type) { | ||
case "Alternative": | ||
if (element.elements.length === 1) { | ||
return getSingleConsumedChar(element.elements[0], context); | ||
return getSingleConsumedChar(element.elements[0], flags); | ||
} | ||
@@ -31,3 +31,3 @@ return empty; | ||
return { | ||
char: context.toCharSet(element), | ||
char: regexp_ast_analysis_1.toCharSet(element, flags), | ||
complete: true, | ||
@@ -37,3 +37,3 @@ }; | ||
case "CapturingGroup": { | ||
const results = element.alternatives.map((a) => getSingleConsumedChar(a, context)); | ||
const results = element.alternatives.map((a) => getSingleConsumedChar(a, flags)); | ||
return { | ||
@@ -76,3 +76,3 @@ char: empty.char.union(...results.map((r) => r.char)), | ||
} | ||
function getQuantifiersReplacement(left, right, context) { | ||
function getQuantifiersReplacement(left, right, flags) { | ||
if (left.min === left.max || right.min === right.max) { | ||
@@ -84,10 +84,10 @@ return null; | ||
} | ||
const lSingle = getSingleConsumedChar(left.element, context); | ||
const rSingle = getSingleConsumedChar(right.element, context); | ||
const lSingle = getSingleConsumedChar(left.element, flags); | ||
const rSingle = getSingleConsumedChar(right.element, flags); | ||
const lPossibleChar = lSingle.complete | ||
? lSingle.char | ||
: regexp_ast_1.getPossiblyConsumedChar(left.element, context).char; | ||
: regexp_ast_1.getPossiblyConsumedChar(left.element, flags).char; | ||
const rPossibleChar = rSingle.complete | ||
? rSingle.char | ||
: regexp_ast_1.getPossiblyConsumedChar(right.element, context).char; | ||
: regexp_ast_1.getPossiblyConsumedChar(right.element, flags).char; | ||
const greedy = left.greedy; | ||
@@ -160,9 +160,9 @@ let lQuant, rQuant; | ||
} | ||
function getQuantifierRepeatedElementReplacement(pair, context) { | ||
function getQuantifierRepeatedElementReplacement(pair, flags) { | ||
const [left, right] = pair; | ||
const lSingle = getSingleConsumedChar(left.element, context); | ||
const lSingle = getSingleConsumedChar(left.element, flags); | ||
if (!lSingle.complete) { | ||
return null; | ||
} | ||
const rSingle = getSingleConsumedChar(right.element, context); | ||
const rSingle = getSingleConsumedChar(right.element, flags); | ||
if (!rSingle.complete) { | ||
@@ -189,3 +189,3 @@ return null; | ||
} | ||
function getNestedReplacement(dominate, nested, context) { | ||
function getNestedReplacement(dominate, nested, flags) { | ||
if (dominate.greedy !== nested.greedy) { | ||
@@ -197,7 +197,7 @@ return null; | ||
} | ||
const single = getSingleConsumedChar(dominate.element, context); | ||
const single = getSingleConsumedChar(dominate.element, flags); | ||
if (single.char.isEmpty) { | ||
return null; | ||
} | ||
const nestedPossible = regexp_ast_1.getPossiblyConsumedChar(nested.element, context); | ||
const nestedPossible = regexp_ast_1.getPossiblyConsumedChar(nested.element, flags); | ||
if (single.char.isSupersetOf(nestedPossible.char)) { | ||
@@ -267,5 +267,5 @@ const { min } = nested; | ||
} | ||
function getReplacement(left, right, context) { | ||
function getReplacement(left, right, flags) { | ||
if (left.type === "Quantifier" && right.type === "Quantifier") { | ||
const result = getQuantifiersReplacement(left, right, context); | ||
const result = getQuantifiersReplacement(left, right, flags); | ||
if (result && !ignoreReplacement(left, right, result)) | ||
@@ -277,3 +277,3 @@ return result; | ||
if (rightRep) { | ||
const result = getQuantifierRepeatedElementReplacement([left, rightRep], context); | ||
const result = getQuantifierRepeatedElementReplacement([left, rightRep], flags); | ||
if (result && !ignoreReplacement(left, right, result)) | ||
@@ -286,3 +286,3 @@ return result; | ||
if (leftRep) { | ||
const result = getQuantifierRepeatedElementReplacement([leftRep, right], context); | ||
const result = getQuantifierRepeatedElementReplacement([leftRep, right], flags); | ||
if (result && !ignoreReplacement(left, right, result)) | ||
@@ -294,3 +294,3 @@ return result; | ||
for (const nested of nestedQuantifiers(right, "start")) { | ||
const result = getNestedReplacement(left, nested, context); | ||
const result = getNestedReplacement(left, nested, flags); | ||
if (result) | ||
@@ -302,3 +302,3 @@ return result; | ||
for (const nested of nestedQuantifiers(left, "end")) { | ||
const result = getNestedReplacement(right, nested, context); | ||
const result = getNestedReplacement(right, nested, flags); | ||
if (result) | ||
@@ -337,3 +337,3 @@ return result; | ||
function createVisitor(regexpContext) { | ||
const { node, getRegexpLocation, fixReplaceNode } = regexpContext; | ||
const { node, flags, getRegexpLocation, fixReplaceNode, } = regexpContext; | ||
return { | ||
@@ -344,3 +344,3 @@ onAlternativeEnter(aNode) { | ||
const right = aNode.elements[i + 1]; | ||
const replacement = getReplacement(left, right, regexpContext); | ||
const replacement = getReplacement(left, right, flags); | ||
if (!replacement) { | ||
@@ -347,0 +347,0 @@ continue; |
@@ -62,3 +62,3 @@ "use strict"; | ||
} | ||
function categorizeRawAlts(alternatives, { toCharSet }) { | ||
function categorizeRawAlts(alternatives, flags) { | ||
return alternatives.map((alternative) => { | ||
@@ -74,3 +74,3 @@ if (alternative.elements.length === 1) { | ||
element, | ||
char: toCharSet(element), | ||
char: regexp_ast_analysis_1.toCharSet(element, flags), | ||
}; | ||
@@ -114,3 +114,3 @@ } | ||
} | ||
function parseRawAlts(alternatives, { flags, toCharSet }) { | ||
function parseRawAlts(alternatives, flags) { | ||
return alternatives.map((a) => { | ||
@@ -123,3 +123,3 @@ if (a.isCharacter) { | ||
elements, | ||
char: toCharSet(a.element), | ||
char: regexp_ast_analysis_1.toCharSet(a.element, flags), | ||
raw: a.alternative.raw, | ||
@@ -202,3 +202,3 @@ }; | ||
} | ||
function totalIsAll(alternatives, { toCharSet }) { | ||
function totalIsAll(alternatives, { flags }) { | ||
let total = undefined; | ||
@@ -208,6 +208,6 @@ for (const a of alternatives) { | ||
if (total === undefined) { | ||
total = toCharSet(a.element); | ||
total = regexp_ast_analysis_1.toCharSet(a.element, flags); | ||
} | ||
else { | ||
total = total.union(toCharSet(a.element)); | ||
total = total.union(regexp_ast_analysis_1.toCharSet(a.element, flags)); | ||
} | ||
@@ -272,3 +272,3 @@ } | ||
function createVisitor(regexpContext) { | ||
const { node, getRegexpLocation, fixReplaceNode } = regexpContext; | ||
const { node, flags, getRegexpLocation, fixReplaceNode, } = regexpContext; | ||
function fixReplaceAlternatives(n, newAlternatives) { | ||
@@ -291,3 +291,3 @@ const [prefix, suffix] = getParentPrefixAndSuffix(n); | ||
} | ||
const alts = categorizeRawAlts(n.alternatives, regexpContext); | ||
const alts = categorizeRawAlts(n.alternatives, flags); | ||
const characterAltsCount = alts.filter((a) => a.isCharacter) | ||
@@ -308,3 +308,3 @@ .length; | ||
} | ||
const parsedAlts = parseRawAlts(alts, regexpContext); | ||
const parsedAlts = parseRawAlts(alts, flags); | ||
if (characterAltsCount >= 3 || | ||
@@ -311,0 +311,0 @@ containsCharacterClass(alts) || |
@@ -21,7 +21,7 @@ "use strict"; | ||
create(context) { | ||
function createVisitor({ node, flags, getRegexpLocation, fixReplaceNode, toCharSet, }) { | ||
function createVisitor({ node, flags, getRegexpLocation, fixReplaceNode, }) { | ||
let reportedCharacterClass = false; | ||
return { | ||
onCharacterClassEnter(ccNode) { | ||
const charSet = toCharSet(ccNode); | ||
const charSet = regexp_ast_analysis_1.toCharSet(ccNode, flags); | ||
let predefined = undefined; | ||
@@ -28,0 +28,0 @@ if (charSet.equals(regexp_ast_analysis_1.Chars.digit(flags))) { |
@@ -34,3 +34,3 @@ "use strict"; | ||
function createVisitor(regexpContext) { | ||
const { node, flags, getRegexpLocation, toCharSet, fixReplaceNode, } = regexpContext; | ||
const { node, flags, getRegexpLocation, fixReplaceNode, } = regexpContext; | ||
const word = regexp_ast_analysis_1.Chars.word(flags); | ||
@@ -120,3 +120,3 @@ const nonWord = regexp_ast_analysis_1.Chars.word(flags).negate(); | ||
} | ||
const charSet = toCharSet(chars); | ||
const charSet = regexp_ast_analysis_1.toCharSet(chars, flags); | ||
if (charSet.isAll) { | ||
@@ -123,0 +123,0 @@ replaceEdgeAssertion(aNode, false); |
@@ -40,6 +40,6 @@ "use strict"; | ||
create(context) { | ||
function createVisitor({ node, flags, getRegexpLocation, fixReplaceNode, patternSource, toCharSet, }) { | ||
function createVisitor({ node, flags, getRegexpLocation, fixReplaceNode, patternSource, }) { | ||
return { | ||
onCharacterClassEnter(ccNode) { | ||
const charSet = toCharSet(ccNode); | ||
const charSet = regexp_ast_analysis_1.toCharSet(ccNode, flags); | ||
let predefined = undefined; | ||
@@ -46,0 +46,0 @@ if (charSet.equals(regexp_ast_analysis_1.Chars.word(flags))) { |
@@ -8,2 +8,3 @@ "use strict"; | ||
const regexp_ast_1 = require("../utils/regexp-ast"); | ||
const alternative_prefix_1 = require("../utils/regexp-ast/alternative-prefix"); | ||
const cache = new Map(); | ||
@@ -40,5 +41,31 @@ function getAllowedChars(flags) { | ||
function compareByteOrder(a, b) { | ||
if (a === b) { | ||
return 0; | ||
} | ||
return a < b ? -1 : +1; | ||
} | ||
function compareCharSets(a, b) { | ||
if (a.isEmpty) { | ||
return 1; | ||
} | ||
else if (b.isEmpty) { | ||
return -1; | ||
} | ||
if (a.ranges[0].min !== b.ranges[0].min) { | ||
return a.ranges[0].min - b.ranges[0].min; | ||
} | ||
const symDiff = a.union(b).without(a.intersect(b)); | ||
if (symDiff.isEmpty) { | ||
return 0; | ||
} | ||
const min = symDiff.ranges[0].min; | ||
if (a.has(min)) { | ||
return -1; | ||
} | ||
return 1; | ||
} | ||
function compareCharSetStrings(a, b) { | ||
const l = Math.min(a.length, b.length); | ||
for (let i = 0; i < l; i++) { | ||
const diff = a.charCodeAt(i) - b.charCodeAt(i); | ||
const diff = compareCharSets(a[i], b[i]); | ||
if (diff !== 0) { | ||
@@ -50,6 +77,6 @@ return diff; | ||
} | ||
function sortAlternatives(alternatives, context) { | ||
function sortAlternatives(alternatives, flags) { | ||
const firstChars = new Map(); | ||
for (const a of alternatives) { | ||
const chars = regexp_ast_analysis_1.getFirstConsumedChar(a, "ltr", context.flags); | ||
const chars = regexp_ast_analysis_1.getFirstConsumedChar(a, "ltr", flags); | ||
const char = chars.empty || chars.char.isEmpty | ||
@@ -61,8 +88,7 @@ ? Infinity | ||
alternatives.sort((a, b) => { | ||
const firstA = firstChars.get(a); | ||
const firstB = firstChars.get(b); | ||
if (firstA !== firstB) { | ||
return firstA - firstB; | ||
const prefixDiff = compareCharSetStrings(alternative_prefix_1.getLongestPrefix(a, "ltr", flags), alternative_prefix_1.getLongestPrefix(b, "ltr", flags)); | ||
if (prefixDiff !== 0) { | ||
return prefixDiff; | ||
} | ||
if (context.flags.ignoreCase) { | ||
if (flags.ignoreCase) { | ||
return (compareByteOrder(a.raw.toUpperCase(), b.raw.toUpperCase()) || | ||
@@ -147,3 +173,3 @@ compareByteOrder(a.raw, b.raw)); | ||
if (chars === undefined) { | ||
chars = regexp_ast_1.getPossiblyConsumedChar(a, regexpContext).char; | ||
chars = regexp_ast_1.getPossiblyConsumedChar(a, flags).char; | ||
possibleCharsCache.set(a, chars); | ||
@@ -155,4 +181,4 @@ } | ||
const alternatives = run.elements; | ||
if (reorder_alternatives_1.canReorder(alternatives, regexpContext)) { | ||
sortAlternatives(alternatives, regexpContext); | ||
if (reorder_alternatives_1.canReorder(alternatives, flags)) { | ||
sortAlternatives(alternatives, flags); | ||
trySortNumberAlternatives(alternatives); | ||
@@ -166,3 +192,3 @@ } | ||
if (elements.length > 1 && | ||
reorder_alternatives_1.canReorder(elements, regexpContext)) { | ||
reorder_alternatives_1.canReorder(elements, flags)) { | ||
trySortNumberAlternatives(elements); | ||
@@ -169,0 +195,0 @@ alternatives.splice(index, elements.length, ...elements); |
@@ -167,11 +167,17 @@ "use strict"; | ||
if (flagsArg.type !== "SpreadElement") { | ||
flagsNode = flagsArg; | ||
flagsString = ast_utils_1.getStringIfConstant(context, flagsArg); | ||
ownsFlags = utils_1.isStringLiteral(flagsArg); | ||
flagsNode = utils_1.dereferenceOwnedVariable(context, flagsArg); | ||
flagsString = ast_utils_1.getStringIfConstant(context, flagsNode); | ||
ownsFlags = utils_1.isStringLiteral(flagsNode); | ||
} | ||
} | ||
else if (patternSource && patternSource.regexpValue) { | ||
flagsString = patternSource.regexpValue.flags; | ||
ownsFlags = Boolean(patternSource.regexpValue.ownedNode); | ||
flagsNode = patternSource.regexpValue.ownedNode; | ||
else { | ||
if (patternSource && patternSource.regexpValue) { | ||
flagsString = patternSource.regexpValue.flags; | ||
ownsFlags = Boolean(patternSource.regexpValue.ownedNode); | ||
flagsNode = patternSource.regexpValue.ownedNode; | ||
} | ||
else { | ||
flagsString = ""; | ||
ownsFlags = true; | ||
} | ||
} | ||
@@ -265,17 +271,4 @@ regexpDataList.push({ | ||
const sourceCode = context.getSourceCode(); | ||
const cacheCharSet = new WeakMap(); | ||
let cacheUsageOfPattern = null; | ||
return { | ||
toCharSet: (node, optionFlags) => { | ||
if (optionFlags) { | ||
return regexp_ast_analysis_1.toCharSet(node, optionFlags); | ||
} | ||
let charSet = cacheCharSet.get(node); | ||
if (charSet) { | ||
return charSet; | ||
} | ||
charSet = regexp_ast_analysis_1.toCharSet(node, flags); | ||
cacheCharSet.set(node, charSet); | ||
return charSet; | ||
}, | ||
getRegexpLocation: (range, offsets) => { | ||
@@ -305,7 +298,8 @@ if (offsets) { | ||
patternSource, | ||
flags, | ||
flags: regexp_ast_analysis_1.toCache(flags), | ||
}; | ||
} | ||
function buildUnparsableRegExpContextBase({ patternSource, patternNode, regexpNode, context, flags, flagsString, flagsNode, ownsFlags, }) { | ||
function buildUnparsableRegExpContextBase({ patternSource, patternNode, regexpNode, context, flags: originalFlags, flagsString, flagsNode, ownsFlags, }) { | ||
const sourceCode = context.getSourceCode(); | ||
const flags = regexp_ast_analysis_1.toCache(originalFlags); | ||
return { | ||
@@ -349,2 +343,5 @@ regexpNode, | ||
} | ||
if (range[0] === range[1]) { | ||
range[0]--; | ||
} | ||
return { | ||
@@ -455,6 +452,17 @@ start: sourceCode.getLocFromIndex(range[0]), | ||
} | ||
const range = getFlagsRange(flagsNode); | ||
if (range == null) { | ||
return null; | ||
let flagsFix; | ||
if (flagsNode) { | ||
const range = getFlagsRange(flagsNode); | ||
if (range == null) { | ||
return null; | ||
} | ||
flagsFix = fixer.replaceTextRange(range, newFlags); | ||
} | ||
else { | ||
if (regexpNode.arguments.length !== 1) { | ||
return null; | ||
} | ||
const end = regexpNode.range[1]; | ||
flagsFix = fixer.replaceTextRange([end - 1, end], `, "${newFlags}")`); | ||
} | ||
const patternRange = patternSource.getReplaceRange({ | ||
@@ -467,6 +475,3 @@ start: 0, | ||
} | ||
return [ | ||
patternRange.replace(fixer, patternSource.value), | ||
fixer.replaceTextRange(range, newFlags), | ||
]; | ||
return [patternRange.replace(fixer, patternSource.value), flagsFix]; | ||
}; | ||
@@ -473,0 +478,0 @@ } |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getFirstConsumedCharPlusAfter = void 0; | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
function getFirstConsumedCharPlusAfter(element, direction, flags) { | ||
const consumed = regexp_ast_analysis_1.getFirstConsumedChar(element, direction, flags); | ||
if (!consumed.empty) { | ||
return consumed; | ||
} | ||
return regexp_ast_analysis_1.FirstConsumedChars.concat([consumed, regexp_ast_analysis_1.getFirstConsumedCharAfter(element, direction, flags)], flags); | ||
} | ||
exports.getFirstConsumedCharPlusAfter = getFirstConsumedCharPlusAfter; |
@@ -13,6 +13,8 @@ "use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.getPossiblyConsumedChar = exports.extractCaptures = exports.getRegExpNodeFromExpression = void 0; | ||
exports.getPossiblyConsumedChar = exports.extractCaptures = exports.getRegExpNodeFromExpression = exports.getFirstConsumedCharPlusAfter = void 0; | ||
const regexpp_1 = require("regexpp"); | ||
const ast_utils_1 = require("../ast-utils"); | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
var common_1 = require("./common"); | ||
Object.defineProperty(exports, "getFirstConsumedCharPlusAfter", { enumerable: true, get: function () { return common_1.getFirstConsumedCharPlusAfter; } }); | ||
__exportStar(require("./is-covered"), exports); | ||
@@ -59,3 +61,3 @@ __exportStar(require("./is-equals"), exports); | ||
exports.extractCaptures = extractCaptures; | ||
function getPossiblyConsumedChar(element, context) { | ||
function getPossiblyConsumedChar(element, flags) { | ||
const ranges = []; | ||
@@ -67,3 +69,3 @@ let exact = true; | ||
d.type === "CharacterSet") { | ||
const c = context.toCharSet(d); | ||
const c = regexp_ast_analysis_1.toCharSet(d, flags); | ||
ranges.push(...c.ranges); | ||
@@ -73,3 +75,3 @@ exact = exact && !c.isEmpty; | ||
else if (d.type === "Backreference" && !regexp_ast_analysis_1.isEmptyBackreference(d)) { | ||
const c = getPossiblyConsumedChar(d.resolved, context); | ||
const c = getPossiblyConsumedChar(d.resolved, flags); | ||
ranges.push(...c.char.ranges); | ||
@@ -89,5 +91,5 @@ exact = exact && c.exact && c.char.size < 2; | ||
}); | ||
const char = regexp_ast_analysis_1.Chars.empty(context.flags).union(ranges); | ||
const char = regexp_ast_analysis_1.Chars.empty(flags).union(ranges); | ||
return { char, exact }; | ||
} | ||
exports.getPossiblyConsumedChar = getPossiblyConsumedChar; |
@@ -5,2 +5,3 @@ "use strict"; | ||
const is_equals_1 = require("./is-equals"); | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
class NormalizedOther { | ||
@@ -21,3 +22,3 @@ constructor(node) { | ||
static fromElement(element, options) { | ||
return new NormalizedCharacter(options.toCharSet(element)); | ||
return new NormalizedCharacter(regexp_ast_analysis_1.toCharSet(element, options.flags)); | ||
} | ||
@@ -220,3 +221,3 @@ } | ||
right.type === "NormalizedOther") { | ||
return is_equals_1.isEqualNodes(left.node, right.node, options.toCharSet); | ||
return is_equals_1.isEqualNodes(left.node, right.node, options.flags); | ||
} | ||
@@ -232,3 +233,3 @@ return false; | ||
} | ||
return is_equals_1.isEqualNodes(left.node, right.node, options.toCharSet); | ||
return is_equals_1.isEqualNodes(left.node, right.node, options.flags); | ||
} | ||
@@ -235,0 +236,0 @@ return false; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isEqualNodes = void 0; | ||
function isEqualChar(a, b, toCharSet) { | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
function isEqualChar(a, b, flags) { | ||
if (a.type === "Character") { | ||
@@ -31,9 +32,9 @@ if (b.type === "Character") { | ||
} | ||
return toCharSet(a).equals(toCharSet(b)); | ||
return regexp_ast_analysis_1.toCharSet(a, flags).equals(regexp_ast_analysis_1.toCharSet(b, flags)); | ||
} | ||
const EQUALS_CHECKER = { | ||
Alternative(a, b, toCharSet, shortCircuit) { | ||
return isEqualElements(a.elements, b.elements, toCharSet, shortCircuit); | ||
Alternative(a, b, flags, shortCircuit) { | ||
return isEqualElements(a.elements, b.elements, flags, shortCircuit); | ||
}, | ||
Assertion(a, b, toCharSet, shortCircuit) { | ||
Assertion(a, b, flags, shortCircuit) { | ||
if (a.kind === "start" || a.kind === "end") { | ||
@@ -47,3 +48,3 @@ return a.kind === b.kind; | ||
if (b.kind === a.kind && a.negate === b.negate) { | ||
return isEqualAlternatives(a.alternatives, b.alternatives, toCharSet, shortCircuit); | ||
return isEqualAlternatives(a.alternatives, b.alternatives, flags, shortCircuit); | ||
} | ||
@@ -57,17 +58,17 @@ return false; | ||
}, | ||
CapturingGroup(a, b, toCharSet, shortCircuit) { | ||
CapturingGroup(a, b, flags, shortCircuit) { | ||
return (a.name === b.name && | ||
isEqualAlternatives(a.alternatives, b.alternatives, toCharSet, shortCircuit)); | ||
isEqualAlternatives(a.alternatives, b.alternatives, flags, shortCircuit)); | ||
}, | ||
Character(a, b, toCharSet) { | ||
return isEqualChar(a, b, toCharSet); | ||
Character(a, b, flags) { | ||
return isEqualChar(a, b, flags); | ||
}, | ||
CharacterClass(a, b, toCharSet) { | ||
return isEqualChar(a, b, toCharSet); | ||
CharacterClass(a, b, flags) { | ||
return isEqualChar(a, b, flags); | ||
}, | ||
CharacterClassRange(a, b, toCharSet) { | ||
return isEqualChar(a, b, toCharSet); | ||
CharacterClassRange(a, b, flags) { | ||
return isEqualChar(a, b, flags); | ||
}, | ||
CharacterSet(a, b, toCharSet) { | ||
return isEqualChar(a, b, toCharSet); | ||
CharacterSet(a, b, flags) { | ||
return isEqualChar(a, b, flags); | ||
}, | ||
@@ -82,17 +83,17 @@ Flags(a, b) { | ||
}, | ||
Group(a, b, toCharSet, shortCircuit) { | ||
return isEqualAlternatives(a.alternatives, b.alternatives, toCharSet, shortCircuit); | ||
Group(a, b, flags, shortCircuit) { | ||
return isEqualAlternatives(a.alternatives, b.alternatives, flags, shortCircuit); | ||
}, | ||
Pattern(a, b, toCharSet, shortCircuit) { | ||
return isEqualAlternatives(a.alternatives, b.alternatives, toCharSet, shortCircuit); | ||
Pattern(a, b, flags, shortCircuit) { | ||
return isEqualAlternatives(a.alternatives, b.alternatives, flags, shortCircuit); | ||
}, | ||
Quantifier(a, b, toCharSet, shortCircuit) { | ||
Quantifier(a, b, flags, shortCircuit) { | ||
return (a.min === b.min && | ||
a.max === b.max && | ||
a.greedy === b.greedy && | ||
isEqualNodes(a.element, b.element, toCharSet, shortCircuit)); | ||
isEqualNodes(a.element, b.element, flags, shortCircuit)); | ||
}, | ||
RegExpLiteral(a, b, toCharSet, shortCircuit) { | ||
return (isEqualNodes(a.pattern, b.pattern, toCharSet, shortCircuit) && | ||
isEqualNodes(a.flags, b.flags, toCharSet, shortCircuit)); | ||
RegExpLiteral(a, b, flags, shortCircuit) { | ||
return (isEqualNodes(a.pattern, b.pattern, flags, shortCircuit) && | ||
isEqualNodes(a.flags, b.flags, flags, shortCircuit)); | ||
}, | ||
@@ -106,5 +107,5 @@ }; | ||
} | ||
function isEqualNodes(a, b, toCharSet, shortCircuit) { | ||
function isEqualNodes(a, b, flags, shortCircuit) { | ||
if (isToCharSetElement(a) && isToCharSetElement(b)) { | ||
return isEqualChar(a, b, toCharSet); | ||
return isEqualChar(a, b, flags); | ||
} | ||
@@ -121,3 +122,3 @@ if (a.type !== b.type) { | ||
if (/[(*+?[\\{|]/.test(a.raw) || /[(*+?[\\{|]/.test(b.raw)) { | ||
return EQUALS_CHECKER[a.type](a, b, toCharSet, shortCircuit); | ||
return EQUALS_CHECKER[a.type](a, b, flags, shortCircuit); | ||
} | ||
@@ -127,3 +128,3 @@ return a.raw === b.raw; | ||
exports.isEqualNodes = isEqualNodes; | ||
function isEqualElements(a, b, toCharSet, shortCircuit) { | ||
function isEqualElements(a, b, flags, shortCircuit) { | ||
if (a.length !== b.length) { | ||
@@ -135,3 +136,3 @@ return false; | ||
const be = b[index]; | ||
if (!isEqualNodes(ae, be, toCharSet, shortCircuit)) { | ||
if (!isEqualNodes(ae, be, flags, shortCircuit)) { | ||
return false; | ||
@@ -142,3 +143,3 @@ } | ||
} | ||
function isEqualAlternatives(a, b, toCharSet, shortCircuit) { | ||
function isEqualAlternatives(a, b, flags, shortCircuit) { | ||
if (a.length !== b.length) { | ||
@@ -149,3 +150,3 @@ return false; | ||
for (const ae of a) { | ||
const bIndex = beList.findIndex((be) => isEqualNodes(ae, be, toCharSet, shortCircuit)); | ||
const bIndex = beList.findIndex((be) => isEqualNodes(ae, be, flags, shortCircuit)); | ||
if (bIndex >= 0) { | ||
@@ -152,0 +153,0 @@ beList.splice(bIndex, 1); |
@@ -6,3 +6,4 @@ "use strict"; | ||
const regexp_ast_analysis_1 = require("regexp-ast-analysis"); | ||
function canReorder(alternatives, context, options = {}) { | ||
const alternative_prefix_1 = require("./regexp-ast/alternative-prefix"); | ||
function canReorder(alternatives, flags, options = {}) { | ||
const { ignoreCapturingGroups = false, matchingDirection } = options; | ||
@@ -16,4 +17,4 @@ const target = asSet(alternatives); | ||
const eqClasses = dir === "unknown" | ||
? getDirectionIndependedDeterminismEqClasses(slice, context) | ||
: getDeterminismEqClasses(slice, dir, context); | ||
? getDirectionIndependedDeterminismEqClasses(slice, flags) | ||
: getDeterminismEqClasses(slice, dir, flags); | ||
if (!ignoreCapturingGroups && | ||
@@ -31,3 +32,3 @@ !canReorderCapturingGroups(target, slice, eqClasses)) { | ||
return (canReorderBasedOnLength(eq) || | ||
canReorderBasedOnConsumedChars(eq, context)); | ||
canReorderBasedOnConsumedChars(eq, flags)); | ||
}); | ||
@@ -66,13 +67,13 @@ } | ||
} | ||
function getDeterminismEqClasses(alternatives, dir, context) { | ||
function getDeterminismEqClasses(alternatives, dir, flags) { | ||
if (dir === "unknown") { | ||
return getDirectionIndependedDeterminismEqClasses(alternatives, context); | ||
return getDirectionIndependedDeterminismEqClasses(alternatives, flags); | ||
} | ||
return getDirectionalDeterminismEqClasses(alternatives, dir, context); | ||
return getDirectionalDeterminismEqClasses(alternatives, dir, flags); | ||
} | ||
exports.getDeterminismEqClasses = getDeterminismEqClasses; | ||
function getDirectionIndependedDeterminismEqClasses(alternatives, context) { | ||
const ltr = getDirectionalDeterminismEqClasses(alternatives, "ltr", context); | ||
const rtl = getDirectionalDeterminismEqClasses(alternatives, "rtl", context); | ||
const disjoint = mergeOverlappingSets(new Set([...ltr, ...rtl]), (s) => s); | ||
function getDirectionIndependedDeterminismEqClasses(alternatives, flags) { | ||
const ltr = getDirectionalDeterminismEqClasses(alternatives, "ltr", flags); | ||
const rtl = getDirectionalDeterminismEqClasses(alternatives, "rtl", flags); | ||
const disjoint = mergeOverlappingSets([...ltr, ...rtl], (s) => s); | ||
const result = []; | ||
@@ -88,8 +89,9 @@ for (const sets of disjoint) { | ||
} | ||
function getDirectionalDeterminismEqClasses(alternatives, dir, context) { | ||
function getDirectionalDeterminismEqClasses(alternatives, dir, flags) { | ||
const getPrefixCharSets = cachedFn((a) => { | ||
const prefix = getTopLevelAlternativePrefix(a, dir, context); | ||
let prefix = alternative_prefix_1.getLongestPrefix(a, dir, flags); | ||
let all = 0; | ||
for (let i = prefix.length - 1; i >= 0; i--) { | ||
if (prefix[i].isAll) { | ||
prefix.pop(); | ||
all++; | ||
} | ||
@@ -100,2 +102,5 @@ else { | ||
} | ||
if (all > 0) { | ||
prefix = prefix.slice(0, prefix.length - all); | ||
} | ||
return prefix; | ||
@@ -116,8 +121,8 @@ }); | ||
function subdivide(eqClass, index) { | ||
if (eqClass.size < 2) { | ||
return [[...eqClass]]; | ||
if (eqClass.length < 2) { | ||
return [eqClass]; | ||
} | ||
for (const prefix of eqClass) { | ||
if (index >= prefix.characters.length) { | ||
return [[...eqClass]]; | ||
return [eqClass]; | ||
} | ||
@@ -132,178 +137,72 @@ } | ||
} | ||
return subdivide(new Set(prefixes), 0).map((eq) => eq.map((p) => p.alternative)); | ||
return subdivide(prefixes, 0).map((eq) => eq.map((p) => p.alternative)); | ||
} | ||
function* mergeOverlappingSets(sets, getElements) { | ||
if (sets.size < 2) { | ||
yield sets; | ||
return; | ||
} | ||
const map = new Map(); | ||
let disjoint = true; | ||
for (const s of sets) { | ||
for (const e of getElements(s)) { | ||
const entry = map.get(e); | ||
if (entry === undefined) { | ||
map.set(e, { type: "Set", set: new Set([s]) }); | ||
} | ||
else if (entry.type === "Set") { | ||
entry.set.add(s); | ||
disjoint = false; | ||
} | ||
class SetEquivalence { | ||
constructor(count) { | ||
this.count = count; | ||
this.indexes = []; | ||
for (let i = 0; i < count; i++) { | ||
this.indexes.push(i); | ||
} | ||
} | ||
function resolve(key) { | ||
let e = key; | ||
for (;;) { | ||
const entry = map.get(e); | ||
if (entry && entry.type === "Ref") { | ||
e = entry.key; | ||
makeEqual(a, b) { | ||
let aValue = this.indexes[a]; | ||
let bValue = this.indexes[b]; | ||
while (aValue !== bValue) { | ||
if (aValue < bValue) { | ||
this.indexes[b] = aValue; | ||
b = bValue; | ||
bValue = this.indexes[b]; | ||
} | ||
else { | ||
return e; | ||
this.indexes[a] = bValue; | ||
a = aValue; | ||
aValue = this.indexes[a]; | ||
} | ||
} | ||
} | ||
if (!disjoint) { | ||
for (const s of sets) { | ||
const toMergeSet = new Set(); | ||
for (const e of getElements(s)) { | ||
toMergeSet.add(resolve(e)); | ||
getEquivalenceSets() { | ||
let counter = 0; | ||
for (let i = 0; i < this.count; i++) { | ||
if (i === this.indexes[i]) { | ||
this.indexes[i] = counter++; | ||
} | ||
if (toMergeSet.size < 2) { | ||
continue; | ||
else { | ||
this.indexes[i] = this.indexes[this.indexes[i]]; | ||
} | ||
const toMerge = [...toMergeSet]; | ||
const intoKey = toMerge[0]; | ||
const intoSet = map.get(intoKey); | ||
for (let i = 1; i < toMerge.length; i++) { | ||
const mergeKey = toMerge[i]; | ||
const mergeSet = map.get(mergeKey); | ||
mergeSet.set.forEach((p) => intoSet.set.add(p)); | ||
map.set(mergeKey, { type: "Ref", key: intoKey }); | ||
} | ||
} | ||
return { | ||
count: counter, | ||
indexes: this.indexes, | ||
}; | ||
} | ||
for (const value of map.values()) { | ||
if (value.type === "Set") { | ||
yield value.set; | ||
} | ||
} | ||
} | ||
function getTopLevelAlternativePrefix(alternative, dir, context) { | ||
const { prefix, complete } = getAlternativePrefix(alternative, dir, context); | ||
if (complete) { | ||
const last = dir === "ltr" | ||
? alternative.elements[alternative.elements.length - 1] | ||
: alternative.elements[0]; | ||
if (last) { | ||
const cs = regexp_ast_analysis_1.getFirstCharAfter(last, dir, context.flags).char; | ||
if (!cs.isEmpty) { | ||
prefix.push(cs); | ||
} | ||
} | ||
function mergeOverlappingSets(sets, getElements) { | ||
if (sets.length < 2) { | ||
return [sets]; | ||
} | ||
return prefix; | ||
} | ||
function getAlternativePrefix(alternative, dir, context) { | ||
const prefix = []; | ||
const elements = dir === "ltr" | ||
? alternative.elements | ||
: [...alternative.elements].reverse(); | ||
let lastComplete = undefined; | ||
for (const e of elements) { | ||
if (regexp_ast_analysis_1.isZeroLength(e)) { | ||
continue; | ||
} | ||
let elementPrefix; | ||
switch (e.type) { | ||
case "Character": | ||
case "CharacterClass": | ||
case "CharacterSet": | ||
elementPrefix = getCharAlternativePrefix(e, dir, context); | ||
break; | ||
case "CapturingGroup": | ||
case "Group": | ||
elementPrefix = getGroupAlternativePrefix(e, dir, context); | ||
break; | ||
case "Quantifier": | ||
elementPrefix = getQuantifierAlternativePrefix(e, dir, context); | ||
break; | ||
default: | ||
elementPrefix = undefined; | ||
} | ||
if (elementPrefix) { | ||
prefix.push(...elementPrefix.prefix); | ||
if (!elementPrefix.complete) { | ||
return { prefix, complete: false }; | ||
const eq = new SetEquivalence(sets.length); | ||
const elementMap = new Map(); | ||
for (let i = 0; i < sets.length; i++) { | ||
const s = sets[i]; | ||
for (const e of getElements(s)) { | ||
const elementSet = elementMap.get(e); | ||
if (elementSet === undefined) { | ||
elementMap.set(e, i); | ||
} | ||
lastComplete = e; | ||
} | ||
else { | ||
let cs; | ||
if (lastComplete === undefined) { | ||
const fcc = regexp_ast_analysis_1.getFirstConsumedChar(alternative, dir, context.flags); | ||
cs = fcc.empty ? fcc.char.union(fcc.look.char) : fcc.char; | ||
} | ||
else { | ||
cs = regexp_ast_analysis_1.getFirstCharAfter(lastComplete, dir, context.flags).char; | ||
eq.makeEqual(i, elementSet); | ||
} | ||
if (!cs.isEmpty) { | ||
prefix.push(cs); | ||
} | ||
return { prefix, complete: false }; | ||
} | ||
} | ||
return { prefix, complete: true }; | ||
} | ||
function getGroupAlternativePrefix(group, dir, context) { | ||
if (group.alternatives.length === 1) { | ||
return getAlternativePrefix(group.alternatives[0], dir, context); | ||
const eqSets = eq.getEquivalenceSets(); | ||
const result = []; | ||
for (let i = 0; i < eqSets.count; i++) { | ||
result.push([]); | ||
} | ||
return undefined; | ||
} | ||
function getCharAlternativePrefix(element, _dir, context) { | ||
const cs = context.toCharSet(element); | ||
if (cs.isEmpty) { | ||
return undefined; | ||
for (let i = 0; i < sets.length; i++) { | ||
result[eqSets.indexes[i]].push(sets[i]); | ||
} | ||
return { prefix: [cs], complete: true }; | ||
return result; | ||
} | ||
function getQuantifierAlternativePrefix(quant, dir, context) { | ||
if (quant.min > 100) { | ||
return undefined; | ||
} | ||
if (quant.min < 1) { | ||
return undefined; | ||
} | ||
let inner; | ||
switch (quant.element.type) { | ||
case "CapturingGroup": | ||
case "Group": | ||
inner = getGroupAlternativePrefix(quant.element, dir, context); | ||
break; | ||
case "Character": | ||
case "CharacterClass": | ||
case "CharacterSet": | ||
inner = getCharAlternativePrefix(quant.element, dir, context); | ||
break; | ||
default: | ||
inner = undefined; | ||
} | ||
if (!inner) { | ||
return undefined; | ||
} | ||
if (!inner.complete || inner.prefix.length === 0) { | ||
return inner; | ||
} | ||
const prefix = []; | ||
for (let i = 0; i < quant.min; i++) { | ||
prefix.push(...inner.prefix); | ||
} | ||
if (quant.min === quant.max) { | ||
return { prefix, complete: true }; | ||
} | ||
const after = regexp_ast_analysis_1.getFirstCharAfter(quant, dir, context.flags); | ||
prefix.push(prefix[0].union(after.char)); | ||
return { prefix, complete: false }; | ||
} | ||
function canReorderBasedOnLength(slice) { | ||
@@ -313,3 +212,3 @@ const lengthRange = regexp_ast_analysis_1.getLengthRange(slice); | ||
} | ||
function canReorderBasedOnConsumedChars(slice, context) { | ||
function canReorderBasedOnConsumedChars(slice, flags) { | ||
if (slice.some(regexp_ast_analysis_1.isPotentiallyZeroLength)) { | ||
@@ -322,5 +221,5 @@ return false; | ||
} | ||
const consumedChars = regexp_ast_analysis_1.Chars.empty(context.flags).union(...slice.map((a) => getConsumedChars(a, context))); | ||
return (regexp_ast_analysis_1.getFirstCharAfter(parent, "rtl", context.flags).char.isDisjointWith(consumedChars) && | ||
regexp_ast_analysis_1.getFirstCharAfter(parent, "ltr", context.flags).char.isDisjointWith(consumedChars)); | ||
const consumedChars = regexp_ast_analysis_1.Chars.empty(flags).union(...slice.map((a) => getConsumedChars(a, flags))); | ||
return (regexp_ast_analysis_1.getFirstCharAfter(parent, "rtl", flags).char.isDisjointWith(consumedChars) && | ||
regexp_ast_analysis_1.getFirstCharAfter(parent, "ltr", flags).char.isDisjointWith(consumedChars)); | ||
} | ||
@@ -357,3 +256,3 @@ function getAlternativesSlice(set) { | ||
} | ||
function getConsumedChars(element, context) { | ||
function getConsumedChars(element, flags) { | ||
const sets = []; | ||
@@ -364,10 +263,10 @@ regexp_ast_analysis_1.hasSomeDescendant(element, (d) => { | ||
d.type === "CharacterSet") { | ||
sets.push(context.toCharSet(d)); | ||
sets.push(regexp_ast_analysis_1.toCharSet(d, flags)); | ||
} | ||
else if (d.type === "Backreference" && !regexp_ast_analysis_1.isEmptyBackreference(d)) { | ||
sets.push(getConsumedChars(d.resolved, context)); | ||
sets.push(getConsumedChars(d.resolved, flags)); | ||
} | ||
return false; | ||
}, (d) => d.type !== "Assertion" && d.type !== "CharacterClass"); | ||
return regexp_ast_analysis_1.Chars.empty(context.flags).union(...sets); | ||
return regexp_ast_analysis_1.Chars.empty(flags).union(...sets); | ||
} | ||
@@ -374,0 +273,0 @@ function containsCapturingGroup(node) { |
@@ -14,2 +14,4 @@ "use strict"; | ||
const no_assertion_capturing_group_1 = __importDefault(require("../rules/no-assertion-capturing-group")); | ||
const no_contradiction_with_assertion_1 = __importDefault(require("../rules/no-contradiction-with-assertion")); | ||
const no_control_character_1 = __importDefault(require("../rules/no-control-character")); | ||
const no_dupe_characters_character_class_1 = __importDefault(require("../rules/no-dupe-characters-character-class")); | ||
@@ -19,2 +21,3 @@ const no_dupe_disjunctions_1 = __importDefault(require("../rules/no-dupe-disjunctions")); | ||
const no_empty_capturing_group_1 = __importDefault(require("../rules/no-empty-capturing-group")); | ||
const no_empty_character_class_1 = __importDefault(require("../rules/no-empty-character-class")); | ||
const no_empty_group_1 = __importDefault(require("../rules/no-empty-group")); | ||
@@ -27,2 +30,3 @@ const no_empty_lookarounds_assertion_1 = __importDefault(require("../rules/no-empty-lookarounds-assertion")); | ||
const no_legacy_features_1 = __importDefault(require("../rules/no-legacy-features")); | ||
const no_misleading_unicode_character_1 = __importDefault(require("../rules/no-misleading-unicode-character")); | ||
const no_non_standard_flag_1 = __importDefault(require("../rules/no-non-standard-flag")); | ||
@@ -59,3 +63,5 @@ const no_obscure_range_1 = __importDefault(require("../rules/no-obscure-range")); | ||
const prefer_escape_replacement_dollar_char_1 = __importDefault(require("../rules/prefer-escape-replacement-dollar-char")); | ||
const prefer_lookaround_1 = __importDefault(require("../rules/prefer-lookaround")); | ||
const prefer_named_backreference_1 = __importDefault(require("../rules/prefer-named-backreference")); | ||
const prefer_named_capture_group_1 = __importDefault(require("../rules/prefer-named-capture-group")); | ||
const prefer_plus_quantifier_1 = __importDefault(require("../rules/prefer-plus-quantifier")); | ||
@@ -72,2 +78,3 @@ const prefer_predefined_assertion_1 = __importDefault(require("../rules/prefer-predefined-assertion")); | ||
const prefer_w_1 = __importDefault(require("../rules/prefer-w")); | ||
const require_unicode_regexp_1 = __importDefault(require("../rules/require-unicode-regexp")); | ||
const sort_alternatives_1 = __importDefault(require("../rules/sort-alternatives")); | ||
@@ -86,2 +93,4 @@ const sort_character_class_elements_1 = __importDefault(require("../rules/sort-character-class-elements")); | ||
no_assertion_capturing_group_1.default, | ||
no_contradiction_with_assertion_1.default, | ||
no_control_character_1.default, | ||
no_dupe_characters_character_class_1.default, | ||
@@ -91,2 +100,3 @@ no_dupe_disjunctions_1.default, | ||
no_empty_capturing_group_1.default, | ||
no_empty_character_class_1.default, | ||
no_empty_group_1.default, | ||
@@ -99,2 +109,3 @@ no_empty_lookarounds_assertion_1.default, | ||
no_legacy_features_1.default, | ||
no_misleading_unicode_character_1.default, | ||
no_non_standard_flag_1.default, | ||
@@ -131,3 +142,5 @@ no_obscure_range_1.default, | ||
prefer_escape_replacement_dollar_char_1.default, | ||
prefer_lookaround_1.default, | ||
prefer_named_backreference_1.default, | ||
prefer_named_capture_group_1.default, | ||
prefer_plus_quantifier_1.default, | ||
@@ -144,2 +157,3 @@ prefer_predefined_assertion_1.default, | ||
prefer_w_1.default, | ||
require_unicode_regexp_1.default, | ||
sort_alternatives_1.default, | ||
@@ -146,0 +160,0 @@ sort_character_class_elements_1.default, |
{ | ||
"name": "eslint-plugin-regexp", | ||
"version": "1.1.0", | ||
"version": "1.2.0", | ||
"description": "ESLint plugin for finding RegExp mistakes and RegExp style guide violations.", | ||
@@ -84,3 +84,3 @@ "engines": { | ||
"raw-loader": "^4.0.1", | ||
"stylelint": "^13.6.1", | ||
"stylelint": "^13.13.1", | ||
"stylelint-config-standard": "^22.0.0", | ||
@@ -97,5 +97,6 @@ "stylelint-plugin-stylus": "^0.11.0", | ||
"eslint-utils": "^3.0.0", | ||
"grapheme-splitter": "^1.0.4", | ||
"jsdoctypeparser": "^9.0.0", | ||
"refa": "^0.9.0", | ||
"regexp-ast-analysis": "^0.2.4", | ||
"regexp-ast-analysis": "^0.3.0", | ||
"regexpp": "^3.2.0", | ||
@@ -102,0 +103,0 @@ "scslre": "^0.1.6" |
@@ -107,5 +107,8 @@ # Introduction | ||
|:--------|:------------|:---| | ||
| [regexp/no-contradiction-with-assertion](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-contradiction-with-assertion.html) | disallow elements that contradict assertions | | | ||
| [regexp/no-control-character](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-control-character.html) | disallow control characters | | | ||
| [regexp/no-dupe-disjunctions](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-dupe-disjunctions.html) | disallow duplicate disjunctions | :star: | | ||
| [regexp/no-empty-alternative](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-alternative.html) | disallow alternatives without elements | :star: | | ||
| [regexp/no-empty-capturing-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-capturing-group.html) | disallow capturing group that captures empty. | :star: | | ||
| [regexp/no-empty-character-class](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-character-class.html) | disallow character classes that match no characters | | | ||
| [regexp/no-empty-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-group.html) | disallow empty group | :star: | | ||
@@ -116,2 +119,3 @@ | [regexp/no-empty-lookarounds-assertion](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-empty-lookarounds-assertion.html) | disallow empty lookahead assertion or empty lookbehind assertion | :star: | | ||
| [regexp/no-lazy-ends](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-lazy-ends.html) | disallow lazy quantifiers at the end of an expression | :star: | | ||
| [regexp/no-misleading-unicode-character](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-misleading-unicode-character.html) | disallow multi-code-point characters in character classes and quantifiers | :wrench: | | ||
| [regexp/no-optional-assertion](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-optional-assertion.html) | disallow optional assertions | :star: | | ||
@@ -158,2 +162,3 @@ | [regexp/no-potentially-useless-backreference](https://ota-meshi.github.io/eslint-plugin-regexp/rules/no-potentially-useless-backreference.html) | disallow backreferences that reference a group that might not be matched | :star: | | ||
| [regexp/prefer-regexp-test](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-regexp-test.html) | enforce that `RegExp#test` is used instead of `String#match` and `RegExp#exec` | :wrench: | | ||
| [regexp/require-unicode-regexp](https://ota-meshi.github.io/eslint-plugin-regexp/rules/require-unicode-regexp.html) | enforce the use of the `u` flag | :wrench: | | ||
| [regexp/sort-alternatives](https://ota-meshi.github.io/eslint-plugin-regexp/rules/sort-alternatives.html) | sort alternatives if order doesn't matter | :wrench: | | ||
@@ -172,3 +177,5 @@ | ||
| [regexp/prefer-d](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-d.html) | enforce using `\d` | :star::wrench: | | ||
| [regexp/prefer-lookaround](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-lookaround.html) | prefer lookarounds over capturing group that do not replace | :wrench: | | ||
| [regexp/prefer-named-backreference](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-named-backreference.html) | enforce using named backreferences | :wrench: | | ||
| [regexp/prefer-named-capture-group](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-named-capture-group.html) | enforce using named capture groups | | | ||
| [regexp/prefer-plus-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-plus-quantifier.html) | enforce using `+` quantifier | :star::wrench: | | ||
@@ -175,0 +182,0 @@ | [regexp/prefer-question-quantifier](https://ota-meshi.github.io/eslint-plugin-regexp/rules/prefer-question-quantifier.html) | enforce using `?` quantifier | :star::wrench: | |
585490
10.07%121
7.08%14553
9.47%232
3.11%9
12.5%+ Added
+ Added
+ Added
Updated