#126 - Allow escaping the . (dot) character to remove ambiguity with the ... wildcard

- simplistic implementation - use `./...` syntax to remove ambiguity of `....`
This commit is contained in:
SebastianMC 2024-01-25 20:54:15 +01:00
parent f5fafc184f
commit e0208e2793
2 changed files with 122 additions and 26 deletions

View File

@ -530,38 +530,49 @@ describe('SortingSpecProcessor', () => {
const txtInputThreeDotsCases: string = ` const txtInputThreeDotsCases: string = `
target-folder: AAA target-folder: AAA
... ...
\\DOT...
.... ....
...\\DOT // Only in the below scenario the / is treated as empty-separator and swallowed
\\DOT...\\DOT ./...
..\\DOT... // Below tricky and not obvious cases
//\\......\\. ../...
.../..
../...S
S.../..
S../...S
` `
const expectedSortSpecForThreeDotsCases: { [key: string]: CustomSortSpec } = { const expectedSortSpecForThreeDotsCases: { [key: string]: CustomSortSpec } = {
"AAA": { "AAA": {
groups: [{ groups: [{
type: CustomSortGroupType.MatchAll type: CustomSortGroupType.MatchAll
},{
regexPrefix: { regex: /^\./i },
type: CustomSortGroupType.ExactPrefix
},{ },{
exactSuffix: '.', exactSuffix: '.',
type: CustomSortGroupType.ExactSuffix type: CustomSortGroupType.ExactSuffix
},{ },{
regexSuffix: { regex: /\.$/i }, exactPrefix: '.',
type: CustomSortGroupType.ExactPrefix
},{
exactPrefix: '..',
type: CustomSortGroupType.ExactPrefix
},{
exactSuffix: '/..',
type: CustomSortGroupType.ExactSuffix type: CustomSortGroupType.ExactSuffix
},{ },{
regexPrefix: { regex: /^\./i }, exactPrefix: '..',
regexSuffix: { regex: /\.$/i }, exactSuffix: 'S',
type: CustomSortGroupType.ExactHeadAndTail type: CustomSortGroupType.ExactHeadAndTail
},{ },{
regexPrefix: { regex: /^\.\.\./i }, exactPrefix: 'S',
type: CustomSortGroupType.ExactPrefix exactSuffix: '/..',
type: CustomSortGroupType.ExactHeadAndTail
},{
exactPrefix: 'S..',
exactSuffix: 'S',
type: CustomSortGroupType.ExactHeadAndTail
},{ },{
type: CustomSortGroupType.Outsiders type: CustomSortGroupType.Outsiders
}], }],
outsidersGroupIdx: 6, outsidersGroupIdx: 8,
targetFoldersPaths: ['AAA'] targetFoldersPaths: ['AAA']
} }
} }
@ -2839,6 +2850,78 @@ describe('convertPlainStringSortingGroupSpecToArraySpec', () => {
'...', 'tion. !!!' '...', 'tion. !!!'
]) ])
}) })
it('should recognize four dots escaper - variant 0', () => {
const s = './...'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'.', '...'
])
})
it('should recognize four dots escaper - variant 1', () => {
const s = '../...'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'..', '...'
])
})
it('should recognize four dots escaper - variant 2', () => {
const s = './...Some'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'.', '...', 'Some'
])
})
it('should recognize four dots escaper - variant 3', () => {
const s = 'Some./...'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'Some.','...'
])
})
it('should recognize four dots escaper - variant 3a', () => {
const s = 'Some./.....'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'Some./..','...'
])
})
it('should recognize four dots escaper - variant 3b', () => {
const s = 'Some./.....X'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'Some.','...', '..X'
])
})
it('should recognize four dots escaper - variant 4', () => {
const s = 'Some./...Some'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'Some.','...', 'Some'
])
})
it('should recognize four dots escaper - tricky variant 4', () => {
const s = 'Some./... haha ...Some'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'Some.','...', ' haha ...Some'
])
})
it('should recognize four dots escaper - tricky variant 5', () => {
const s = 'S.../..'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'S','...', '/..'
])
})
it('should NOT recognize four dots escaper - tricky variant 1', () => {
const s = 'Some... haha ./...Some'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'Some','...', ' haha ./...Some'
])
})
it('should NOT recognize four dots escaper - tricky variant 2', () => {
const s = 'Some... haha .../...Some'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'Some', '...', ' haha .../...Some'
])
})
it('should NOT recognize four dots escaper - tricky variant 3', () => {
const s = '.../...'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([
'...', '/...'
])
})
it('should recognize some edge case', () => { it('should recognize some edge case', () => {
const s = 'Edge...... ... ..... ... eee?' const s = 'Edge...... ... ..... ... eee?'
expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([ expect(processor.convertPlainStringSortingGroupSpecToArraySpec(s)).toEqual([

View File

@ -98,6 +98,10 @@ const ContextFreeProblems = new Set<ProblemCode>([
const ThreeDots = '...'; const ThreeDots = '...';
const ThreeDotsLength = ThreeDots.length; const ThreeDotsLength = ThreeDots.length;
const AmbigueFourDotsEscaper = './...'
const AmbigueFourDotsEscaperLength = AmbigueFourDotsEscaper.length
const AmbigueFourDotsEscaperOverlap = 1 // Number of leading chars in the Escaper to retain in original string
interface CustomSortOrderAscDescPair { interface CustomSortOrderAscDescPair {
asc: CustomSortOrder asc: CustomSortOrder
desc: CustomSortOrder desc: CustomSortOrder
@ -334,9 +338,6 @@ const CompoundNumberDashRegexSymbol: string = '\\-d+' // Compound number with d
const WordInASCIIRegexSymbol: string = '\\a+' const WordInASCIIRegexSymbol: string = '\\a+'
const WordInAnyLanguageRegexSymbol: string = '\\A+' const WordInAnyLanguageRegexSymbol: string = '\\A+'
// _1_ prefix indicates a lexeme which includes another lexeme and thus has to become first-to-scan
const _1_InlineRegexSymbol_Dot: string = '\\DOT'
const InlineRegexSymbol_Digit1: string = '\\d' const InlineRegexSymbol_Digit1: string = '\\d'
const InlineRegexSymbol_Digit2: string = '\\[0-9]' const InlineRegexSymbol_Digit2: string = '\\[0-9]'
const InlineRegexSymbol_0_to_3: string = '\\[0-3]' const InlineRegexSymbol_0_to_3: string = '\\[0-3]'
@ -364,7 +365,6 @@ const sortingSymbolsArr: Array<string> = [
const sortingSymbolsRegex = new RegExp(sortingSymbolsArr.join('|'), 'gi') const sortingSymbolsRegex = new RegExp(sortingSymbolsArr.join('|'), 'gi')
const inlineRegexSymbolsArrEscapedForRegex: Array<string> = [ const inlineRegexSymbolsArrEscapedForRegex: Array<string> = [
escapeRegexUnsafeCharacters(_1_InlineRegexSymbol_Dot),
escapeRegexUnsafeCharacters(InlineRegexSymbol_Digit1), escapeRegexUnsafeCharacters(InlineRegexSymbol_Digit1),
escapeRegexUnsafeCharacters(InlineRegexSymbol_Digit2), escapeRegexUnsafeCharacters(InlineRegexSymbol_Digit2),
escapeRegexUnsafeCharacters(InlineRegexSymbol_0_to_3), escapeRegexUnsafeCharacters(InlineRegexSymbol_0_to_3),
@ -380,7 +380,6 @@ interface RegexExpr {
// Don't be confused if the source lexeme is equal to the resulting regex piece, logically these two distinct spaces // Don't be confused if the source lexeme is equal to the resulting regex piece, logically these two distinct spaces
const inlineRegexSymbolsToRegexExpressionsArr: { [key: string]: RegexExpr} = { const inlineRegexSymbolsToRegexExpressionsArr: { [key: string]: RegexExpr} = {
[_1_InlineRegexSymbol_Dot]: {regexExpr: '\\.'},
[InlineRegexSymbol_Digit1]: {regexExpr: '\\d'}, [InlineRegexSymbol_Digit1]: {regexExpr: '\\d'},
[InlineRegexSymbol_Digit2]: {regexExpr: '[0-9]'}, [InlineRegexSymbol_Digit2]: {regexExpr: '[0-9]'},
[InlineRegexSymbol_0_to_3]: {regexExpr: '[0-3]'}, [InlineRegexSymbol_0_to_3]: {regexExpr: '[0-3]'},
@ -1568,17 +1567,31 @@ export class SortingSpecProcessor {
return [ThreeDots, spec.substring(ThreeDotsLength)]; return [ThreeDots, spec.substring(ThreeDotsLength)];
} }
if (spec.endsWith(ThreeDots)) { if (spec.endsWith(ThreeDots)) {
if (spec.endsWith(AmbigueFourDotsEscaper)) {
return [spec.substring(0, spec.length - AmbigueFourDotsEscaperLength + AmbigueFourDotsEscaperOverlap), ThreeDots];
} else {
return [spec.substring(0, spec.length - ThreeDotsLength), ThreeDots]; return [spec.substring(0, spec.length - ThreeDotsLength), ThreeDots];
} }
}
const idx = spec.indexOf(ThreeDots); const idx = spec.indexOf(ThreeDots);
const idxOfAmbigueFourDotsEscaper = spec.indexOf(AmbigueFourDotsEscaper)
if (idx > 0) { if (idx > 0) {
if (idxOfAmbigueFourDotsEscaper >= 0 &&
idxOfAmbigueFourDotsEscaper === idx - (AmbigueFourDotsEscaperLength - ThreeDotsLength) ) {
return [
spec.substring(0, idxOfAmbigueFourDotsEscaper + AmbigueFourDotsEscaperOverlap),
ThreeDots,
spec.substring(idx + ThreeDotsLength)
];
} else {
return [ return [
spec.substring(0, idx), spec.substring(0, idx),
ThreeDots, ThreeDots,
spec.substring(idx + ThreeDotsLength) spec.substring(idx + ThreeDotsLength)
]; ];
} }
}
// Unrecognized, treat as exact match // Unrecognized, treat as exact match
return [spec]; return [spec];