Merge branch 'master' into 127-folder-and-file-with-the-same-basename-sorting

# Conflicts:
#	src/custom-sort/sorting-spec-processor.spec.ts
This commit is contained in:
SebastianMC 2024-01-25 21:23:54 +01:00
commit 9cf01e9591
2 changed files with 171 additions and 11 deletions

View File

@ -14,8 +14,16 @@ import {
RomanNumberNormalizerFn, RomanNumberNormalizerFn,
SortingSpecProcessor SortingSpecProcessor
} from "./sorting-spec-processor" } from "./sorting-spec-processor"
import {CustomSortGroupType, CustomSortOrder, CustomSortSpec, IdentityNormalizerFn} from "./custom-sort-types"; import {
import {FolderMatchingRegexp, FolderMatchingTreeNode} from "./folder-matching-rules"; CustomSortGroupType,
CustomSortOrder,
CustomSortSpec,
IdentityNormalizerFn
} from "./custom-sort-types";
import {
FolderMatchingRegexp,
FolderMatchingTreeNode
} from "./folder-matching-rules";
const txtInputExampleA: string = ` const txtInputExampleA: string = `
order-asc: a-z order-asc: a-z
@ -562,6 +570,68 @@ describe('SortingSpecProcessor', () => {
}) })
}) })
const txtInputThreeDotsCases: string = `
target-folder: AAA
...
....
// Only in the below scenario the / is treated as empty-separator and swallowed
./...
// Below tricky and not obvious cases
../...
.../..
../...S
S.../..
S../...S
`
const expectedSortSpecForThreeDotsCases: { [key: string]: CustomSortSpec } = {
"AAA": {
groups: [{
type: CustomSortGroupType.MatchAll
},{
exactSuffix: '.',
type: CustomSortGroupType.ExactSuffix
},{
exactPrefix: '.',
type: CustomSortGroupType.ExactPrefix
},{
exactPrefix: '..',
type: CustomSortGroupType.ExactPrefix
},{
exactSuffix: '/..',
type: CustomSortGroupType.ExactSuffix
},{
exactPrefix: '..',
exactSuffix: 'S',
type: CustomSortGroupType.ExactHeadAndTail
},{
exactPrefix: 'S',
exactSuffix: '/..',
type: CustomSortGroupType.ExactHeadAndTail
},{
exactPrefix: 'S..',
exactSuffix: 'S',
type: CustomSortGroupType.ExactHeadAndTail
},{
type: CustomSortGroupType.Outsiders
}],
outsidersGroupIdx: 8,
targetFoldersPaths: ['AAA']
}
}
describe('SortingSpecProcessor', () => {
let processor: SortingSpecProcessor;
beforeEach(() => {
processor = new SortingSpecProcessor();
});
it('should correctly handle some of three-dots scenarios', () => {
const inputTxtArr: Array<string> = txtInputThreeDotsCases.split('\n')
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
expect(result?.sortSpecByPath).toEqual(expectedSortSpecForThreeDotsCases)
})
})
const txtInputTrueAlphabeticalSortAttr: string = ` const txtInputTrueAlphabeticalSortAttr: string = `
target-folder: True Alpha target-folder: True Alpha
< true a-z < true a-z
@ -2823,6 +2893,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
@ -367,7 +371,7 @@ const inlineRegexSymbolsArrEscapedForRegex: Array<string> = [
escapeRegexUnsafeCharacters(InlineRegexSymbol_Digit2), escapeRegexUnsafeCharacters(InlineRegexSymbol_Digit2),
escapeRegexUnsafeCharacters(InlineRegexSymbol_0_to_3), escapeRegexUnsafeCharacters(InlineRegexSymbol_0_to_3),
escapeRegexUnsafeCharacters(InlineRegexSymbol_CapitalLetter), escapeRegexUnsafeCharacters(InlineRegexSymbol_CapitalLetter),
escapeRegexUnsafeCharacters(InlineRegexSymbol_LowercaseLetter) escapeRegexUnsafeCharacters(InlineRegexSymbol_LowercaseLetter),
] ]
interface RegexExpr { interface RegexExpr {
@ -382,7 +386,7 @@ const inlineRegexSymbolsToRegexExpressionsArr: { [key: string]: RegexExpr} = {
[InlineRegexSymbol_Digit2]: {regexExpr: '[0-9]'}, [InlineRegexSymbol_Digit2]: {regexExpr: '[0-9]'},
[InlineRegexSymbol_0_to_3]: {regexExpr: '[0-3]'}, [InlineRegexSymbol_0_to_3]: {regexExpr: '[0-3]'},
[InlineRegexSymbol_CapitalLetter]: {regexExpr: '[\\p{Lu}\\p{Lt}]', isUnicode: true, isCaseSensitive: true}, [InlineRegexSymbol_CapitalLetter]: {regexExpr: '[\\p{Lu}\\p{Lt}]', isUnicode: true, isCaseSensitive: true},
[InlineRegexSymbol_LowercaseLetter]: {regexExpr: '\\p{Ll}', isUnicode: true, isCaseSensitive: true} [InlineRegexSymbol_LowercaseLetter]: {regexExpr: '\\p{Ll}', isUnicode: true, isCaseSensitive: true},
} }
const inlineRegexSymbolsDetectionRegex = new RegExp(inlineRegexSymbolsArrEscapedForRegex.join('|'), 'gi') const inlineRegexSymbolsDetectionRegex = new RegExp(inlineRegexSymbolsArrEscapedForRegex.join('|'), 'gi')
@ -1556,7 +1560,7 @@ export class SortingSpecProcessor {
[Attribute.OrderUnspecified]: this.validateOrderAttrValue.bind(this) [Attribute.OrderUnspecified]: this.validateOrderAttrValue.bind(this)
} }
convertPlainStringSortingGroupSpecToArraySpec = (spec: string): Array<string> => { convertPlainStringSortingGroupSpecToArraySpec = (spec: string): Array<string> => {
spec = spec.trim() spec = spec.trim()
if (isThreeDots(spec)) { if (isThreeDots(spec)) {
return [ThreeDots] return [ThreeDots]
@ -1565,16 +1569,30 @@ export class SortingSpecProcessor {
return [ThreeDots, spec.substring(ThreeDotsLength)]; return [ThreeDots, spec.substring(ThreeDotsLength)];
} }
if (spec.endsWith(ThreeDots)) { if (spec.endsWith(ThreeDots)) {
return [spec.substring(0, spec.length - ThreeDotsLength), ThreeDots]; if (spec.endsWith(AmbigueFourDotsEscaper)) {
return [spec.substring(0, spec.length - AmbigueFourDotsEscaperLength + AmbigueFourDotsEscaperOverlap), ThreeDots];
} else {
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) {
return [ if (idxOfAmbigueFourDotsEscaper >= 0 &&
spec.substring(0, idx), idxOfAmbigueFourDotsEscaper === idx - (AmbigueFourDotsEscaperLength - ThreeDotsLength) ) {
ThreeDots, return [
spec.substring(idx + ThreeDotsLength) spec.substring(0, idxOfAmbigueFourDotsEscaper + AmbigueFourDotsEscaperOverlap),
]; ThreeDots,
spec.substring(idx + ThreeDotsLength)
];
} else {
return [
spec.substring(0, idx),
ThreeDots,
spec.substring(idx + ThreeDotsLength)
];
}
} }
// Unrecognized, treat as exact match // Unrecognized, treat as exact match