#29 - Feature: priorities of sorting rules
This commit is contained in:
parent
1300caf291
commit
c397782a99
|
@ -57,6 +57,7 @@ export interface CustomSortGroup {
|
|||
matchFilenameWithExt?: boolean
|
||||
foldersOnly?: boolean
|
||||
withMetadataFieldName?: string // for 'with-metadata:'
|
||||
priority?: number
|
||||
}
|
||||
|
||||
export interface CustomSortSpec {
|
||||
|
@ -68,9 +69,10 @@ export interface CustomSortSpec {
|
|||
outsidersFilesGroupIdx?: number
|
||||
outsidersFoldersGroupIdx?: number
|
||||
itemsToHide?: Set<string>
|
||||
plugin?: Plugin // to hand over the access to App instance to the sorting engine
|
||||
priorityOrder?: Array<number> // Indexes of groups in evaluation order
|
||||
|
||||
// For internal transient use
|
||||
plugin?: Plugin // to hand over the access to App instance to the sorting engine
|
||||
_mCache?: MetadataCache
|
||||
}
|
||||
|
||||
|
|
|
@ -788,6 +788,52 @@ describe('determineSortingGroup', () => {
|
|||
} as FolderItemForSorting);
|
||||
})
|
||||
})
|
||||
|
||||
it('should correctly apply priority group', () => {
|
||||
// given
|
||||
const file: TFile = mockTFile('Abcdef!', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333);
|
||||
const sortSpec: CustomSortSpec = {
|
||||
groups: [{
|
||||
filesOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.MatchAll
|
||||
}, {
|
||||
foldersOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.MatchAll
|
||||
}, {
|
||||
exactSuffix: "def!",
|
||||
priority: 2,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.ExactSuffix
|
||||
}, {
|
||||
exactText: "Abcdef!",
|
||||
order: CustomSortOrder.alphabetical,
|
||||
priority: 3,
|
||||
type: CustomSortGroupType.ExactName
|
||||
}, {
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}],
|
||||
outsidersGroupIdx: 4,
|
||||
targetFoldersPaths: ['/'],
|
||||
priorityOrder: [3,2,0,1]
|
||||
}
|
||||
|
||||
// when
|
||||
const result = determineSortingGroup(file, sortSpec)
|
||||
|
||||
// then
|
||||
expect(result).toEqual({
|
||||
groupIdx: 3,
|
||||
isFolder: false,
|
||||
sortString: "Abcdef!.md",
|
||||
ctimeNewest: MOCK_TIMESTAMP + 222,
|
||||
ctimeOldest: MOCK_TIMESTAMP + 222,
|
||||
mtime: MOCK_TIMESTAMP + 333,
|
||||
path: 'Some parent folder/Abcdef!.md'
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('determineFolderDatesIfNeeded', () => {
|
||||
|
|
|
@ -129,8 +129,10 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
|||
const entryAsTFile: TFile = entry as TFile
|
||||
const basename: string = aFolder ? entry.name : entryAsTFile.basename
|
||||
|
||||
for (groupIdx = 0; groupIdx < spec.groups.length; groupIdx++) {
|
||||
const numOfGroupsToCheck: number = spec.priorityOrder ? spec.priorityOrder.length : spec.groups.length
|
||||
for (let idx = 0; idx < numOfGroupsToCheck; idx++) {
|
||||
matchedGroup = null
|
||||
groupIdx = spec.priorityOrder ? spec.priorityOrder[idx] : idx
|
||||
const group: CustomSortGroup = spec.groups[groupIdx];
|
||||
if (group.foldersOnly && aFile) continue;
|
||||
if (group.filesOnly && aFolder) continue;
|
||||
|
@ -218,12 +220,12 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
|||
break
|
||||
}
|
||||
if (determined) {
|
||||
break;
|
||||
break; // No need to check other sorting groups
|
||||
}
|
||||
}
|
||||
|
||||
// the final groupIdx for undetermined folder entry is either the last+1 groupIdx or idx of explicitly defined outsiders group
|
||||
let determinedGroupIdx: number | undefined = groupIdx;
|
||||
const idxAfterLastGroupIdx: number = spec.groups.length
|
||||
let determinedGroupIdx: number | undefined = determined ? groupIdx! : idxAfterLastGroupIdx
|
||||
|
||||
if (!determined) {
|
||||
// Automatically assign the index to outsiders group, if relevant was configured
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
escapeRegexUnsafeCharacters,
|
||||
extractNumericSortingSymbol,
|
||||
hasMoreThanOneNumericSortingSymbol,
|
||||
NumberNormalizerFn,
|
||||
NumberNormalizerFn, ProblemCode,
|
||||
RegexpUsedAs,
|
||||
RomanNumberNormalizerFn,
|
||||
SortingSpecProcessor
|
||||
|
@ -535,7 +535,7 @@ describe('SortingSpecProcessor', () => {
|
|||
const txtInputSimplistic1: string = `
|
||||
target-folder: /*
|
||||
/:files
|
||||
//folders
|
||||
/folders
|
||||
`
|
||||
|
||||
const expectedSortSpecForSimplistic1: { [key: string]: CustomSortSpec } = {
|
||||
|
@ -545,11 +545,12 @@ const expectedSortSpecForSimplistic1: { [key: string]: CustomSortSpec } = {
|
|||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}, {
|
||||
foldersOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}],
|
||||
outsidersFilesGroupIdx: 0,
|
||||
outsidersGroupIdx: 1,
|
||||
outsidersFoldersGroupIdx: 1,
|
||||
targetFoldersPaths: ['/*']
|
||||
}
|
||||
}
|
||||
|
@ -561,11 +562,12 @@ const expectedWildcardMatchingTreeForSimplistic1 = {
|
|||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}, {
|
||||
foldersOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}],
|
||||
outsidersFilesGroupIdx: 0,
|
||||
outsidersGroupIdx: 1,
|
||||
outsidersFoldersGroupIdx: 1,
|
||||
targetFoldersPaths: ['/*']
|
||||
},
|
||||
"subtree": {}
|
||||
|
@ -574,7 +576,7 @@ const expectedWildcardMatchingTreeForSimplistic1 = {
|
|||
const txtInputSimplistic2: string = `
|
||||
target-folder: /
|
||||
/:files
|
||||
//folders
|
||||
/folders
|
||||
`
|
||||
|
||||
const expectedSortSpecForSimplistic2: { [key: string]: CustomSortSpec } = {
|
||||
|
@ -584,11 +586,12 @@ const expectedSortSpecForSimplistic2: { [key: string]: CustomSortSpec } = {
|
|||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}, {
|
||||
foldersOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}],
|
||||
outsidersFilesGroupIdx: 0,
|
||||
outsidersGroupIdx: 1,
|
||||
outsidersFoldersGroupIdx: 1,
|
||||
targetFoldersPaths: ['/']
|
||||
}
|
||||
}
|
||||
|
@ -760,6 +763,123 @@ describe('SortingSpecProcessor edge case', () => {
|
|||
})
|
||||
})
|
||||
|
||||
const txtInputPriorityGroups1: string = `
|
||||
target-folder: /
|
||||
/:files
|
||||
/folders
|
||||
/! /:files Fi...
|
||||
/!! /folders Fo...
|
||||
/!!! ...def!
|
||||
Plain text
|
||||
/! % Anything
|
||||
`
|
||||
|
||||
const txtInputPriorityGroups2: string = `
|
||||
target-folder: /
|
||||
/! /:files Fi...
|
||||
/!! /folders Fo...
|
||||
/!!! ...def!
|
||||
/! Anything
|
||||
`
|
||||
|
||||
const expectedSortSpecForPriorityGroups1: { [key: string]: CustomSortSpec } = {
|
||||
"/": {
|
||||
groups: [{
|
||||
filesOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}, {
|
||||
foldersOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}, {
|
||||
exactPrefix: "Fi",
|
||||
filesOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
priority: 1,
|
||||
type: CustomSortGroupType.ExactPrefix
|
||||
}, {
|
||||
exactPrefix: "Fo",
|
||||
foldersOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
priority: 2,
|
||||
type: CustomSortGroupType.ExactPrefix
|
||||
}, {
|
||||
exactSuffix: "def!",
|
||||
priority: 3,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.ExactSuffix
|
||||
}, {
|
||||
exactText: "Plain text",
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.ExactName
|
||||
},{
|
||||
exactText: "Anything",
|
||||
order: CustomSortOrder.alphabetical,
|
||||
priority: 1,
|
||||
type: CustomSortGroupType.ExactName
|
||||
}],
|
||||
outsidersFilesGroupIdx: 0,
|
||||
outsidersFoldersGroupIdx: 1,
|
||||
targetFoldersPaths: ['/'],
|
||||
priorityOrder: [4,3,2,6,5]
|
||||
}
|
||||
}
|
||||
|
||||
const expectedSortSpecForPriorityGroups2: { [key: string]: CustomSortSpec } = {
|
||||
"/": {
|
||||
groups: [{
|
||||
exactPrefix: "Fi",
|
||||
filesOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
priority: 1,
|
||||
type: CustomSortGroupType.ExactPrefix
|
||||
}, {
|
||||
exactPrefix: "Fo",
|
||||
foldersOnly: true,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
priority: 2,
|
||||
type: CustomSortGroupType.ExactPrefix
|
||||
}, {
|
||||
exactSuffix: "def!",
|
||||
priority: 3,
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.ExactSuffix
|
||||
}, {
|
||||
exactText: "Anything",
|
||||
order: CustomSortOrder.alphabetical,
|
||||
priority: 1,
|
||||
type: CustomSortGroupType.ExactName
|
||||
}, {
|
||||
order: CustomSortOrder.alphabetical,
|
||||
type: CustomSortGroupType.Outsiders
|
||||
}],
|
||||
outsidersGroupIdx: 4,
|
||||
targetFoldersPaths: ['/'],
|
||||
priorityOrder: [2,1,0,3]
|
||||
}
|
||||
}
|
||||
|
||||
describe('SortingSpecProcessor', () => {
|
||||
let processor: SortingSpecProcessor;
|
||||
beforeEach(() => {
|
||||
processor = new SortingSpecProcessor();
|
||||
});
|
||||
it('should recognize the sorting groups with priority example 1', () => {
|
||||
const inputTxtArr: Array<string> = txtInputPriorityGroups1.split('\n')
|
||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||
expect(result?.sortSpecByPath).toEqual(expectedSortSpecForPriorityGroups1)
|
||||
expect(result?.sortSpecByWildcard).toBeUndefined()
|
||||
})
|
||||
it('should recognize the sorting groups with priority example 2', () => {
|
||||
const inputTxtArr: Array<string> = txtInputPriorityGroups2.split('\n')
|
||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||
expect(result?.sortSpecByPath).toEqual(expectedSortSpecForPriorityGroups2)
|
||||
expect(result?.sortSpecByWildcard).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
const txtInputTargetFolderMultiSpecA: string = `
|
||||
target-folder: .
|
||||
< a-z
|
||||
|
@ -1111,6 +1231,22 @@ target-folder: AAA
|
|||
sorting: standard
|
||||
`
|
||||
|
||||
const txtInputErrorPriorityAlone: string = `
|
||||
/!
|
||||
`
|
||||
|
||||
const txtInputErrorPriorityEmptyFilePattern: string = `
|
||||
/!! /:
|
||||
`
|
||||
|
||||
const txtInputErrorPriorityEmptyFolderPattern: string = `
|
||||
/!!! /
|
||||
`
|
||||
|
||||
const txtInputErrorPriorityEmptyPattern: string = `
|
||||
/! %
|
||||
`
|
||||
|
||||
const txtInputEmptySpec: string = ``
|
||||
|
||||
describe('SortingSpecProcessor error detection and reporting', () => {
|
||||
|
@ -1207,6 +1343,42 @@ describe('SortingSpecProcessor error detection and reporting', () => {
|
|||
`${ERR_PREFIX} 14:StandardObsidianSortAllowedOnlyAtFolderLevel The standard Obsidian sort order is only allowed at a folder level (not nested syntax) ${ERR_SUFFIX_IN_LINE(4)}`)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT(' sorting: standard'))
|
||||
})
|
||||
it('should recognize error: priority indicator alone', () => {
|
||||
const inputTxtArr: Array<string> = txtInputErrorPriorityAlone.split('\n')
|
||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||
expect(result).toBeNull()
|
||||
expect(errorsLogger).toHaveBeenCalledTimes(2)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(1,
|
||||
`${ERR_PREFIX} 15:PriorityNotAllowedOnOutsidersGroup Priority is not allowed for sorting group with empty match-pattern ${ERR_SUFFIX_IN_LINE(2)}`)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT('/!'))
|
||||
})
|
||||
it('should recognize error: priority indicator with empty file pattern', () => {
|
||||
const inputTxtArr: Array<string> = txtInputErrorPriorityEmptyFilePattern.split('\n')
|
||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||
expect(result).toBeNull()
|
||||
expect(errorsLogger).toHaveBeenCalledTimes(2)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(1,
|
||||
`${ERR_PREFIX} 15:PriorityNotAllowedOnOutsidersGroup Priority is not allowed for sorting group with empty match-pattern ${ERR_SUFFIX_IN_LINE(2)}`)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT('/!! /:'))
|
||||
})
|
||||
it('should recognize error: priority indicator with empty folder pattern', () => {
|
||||
const inputTxtArr: Array<string> = txtInputErrorPriorityEmptyFolderPattern.split('\n')
|
||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||
expect(result).toBeNull()
|
||||
expect(errorsLogger).toHaveBeenCalledTimes(2)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(1,
|
||||
`${ERR_PREFIX} 15:PriorityNotAllowedOnOutsidersGroup Priority is not allowed for sorting group with empty match-pattern ${ERR_SUFFIX_IN_LINE(2)}`)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT('/!!! /'))
|
||||
})
|
||||
it('should recognize error: priority indicator with empty pattern', () => {
|
||||
const inputTxtArr: Array<string> = txtInputErrorPriorityEmptyPattern.split('\n')
|
||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||
expect(result).toBeNull()
|
||||
expect(errorsLogger).toHaveBeenCalledTimes(2)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(1,
|
||||
`${ERR_PREFIX} 15:PriorityNotAllowedOnOutsidersGroup Priority is not allowed for sorting group with empty match-pattern ${ERR_SUFFIX_IN_LINE(2)}`)
|
||||
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT('/! %'))
|
||||
})
|
||||
it('should recognize empty spec', () => {
|
||||
const inputTxtArr: Array<string> = txtInputEmptySpec.split('\n')
|
||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||
|
|
|
@ -46,6 +46,7 @@ interface ParsedSortingGroup {
|
|||
arraySpec?: Array<string>
|
||||
outsidersGroup?: boolean // Mutually exclusive with plainSpec and arraySpec
|
||||
itemToHide?: boolean
|
||||
priority?: number
|
||||
}
|
||||
|
||||
export enum ProblemCode {
|
||||
|
@ -63,7 +64,8 @@ export enum ProblemCode {
|
|||
ItemToHideExactNameWithExtRequired,
|
||||
ItemToHideNoSupportForThreeDots,
|
||||
DuplicateWildcardSortSpecForSameFolder,
|
||||
StandardObsidianSortAllowedOnlyAtFolderLevel
|
||||
StandardObsidianSortAllowedOnlyAtFolderLevel,
|
||||
PriorityNotAllowedOnOutsidersGroup
|
||||
}
|
||||
|
||||
const ContextFreeProblems = new Set<ProblemCode>([
|
||||
|
@ -174,7 +176,8 @@ const FilesWithExtGroupVerboseLexeme: string = '/:files.'
|
|||
const FilesWithExtGroupShortLexeme: string = '/:.'
|
||||
const FoldersGroupVerboseLexeme: string = '/folders'
|
||||
const FoldersGroupShortLexeme: string = '/'
|
||||
const AnyTypeGroupLexeme: string = '%' // See % as a combination of / and :
|
||||
const AnyTypeGroupLexemeShort: string = '%' // See % as a combination of / and :
|
||||
const AnyTypeGroupLexeme: string = '/%' // See % as a combination of / and :
|
||||
const HideItemShortLexeme: string = '--%' // See % as a combination of / and :
|
||||
const HideItemVerboseLexeme: string = '/--hide:'
|
||||
|
||||
|
@ -182,11 +185,26 @@ const MetadataFieldIndicatorLexeme: string = 'with-metadata:'
|
|||
|
||||
const CommentPrefix: string = '//'
|
||||
|
||||
const FileGroupModifierPrio1Lexeme: string = '/!'
|
||||
const FileGroupModifierPrio2Lexeme: string = '/!!'
|
||||
const FileGroupModifierPrio3Lexeme: string = '/!!!'
|
||||
|
||||
const PRIO_1: number = 1
|
||||
const PRIO_2: number = 2
|
||||
const PRIO_3: number = 3
|
||||
|
||||
const SortingGroupPriorityPrefixes: { [key: string]: number } = {
|
||||
[FileGroupModifierPrio1Lexeme]: PRIO_1,
|
||||
[FileGroupModifierPrio2Lexeme]: PRIO_2,
|
||||
[FileGroupModifierPrio3Lexeme]: PRIO_3
|
||||
}
|
||||
|
||||
interface SortingGroupType {
|
||||
filesOnly?: boolean
|
||||
filenameWithExt?: boolean // The text matching criteria should apply to filename + extension
|
||||
foldersOnly?: boolean
|
||||
itemToHide?: boolean
|
||||
priority?: number
|
||||
}
|
||||
|
||||
const SortingGroupPrefixes: { [key: string]: SortingGroupType } = {
|
||||
|
@ -196,6 +214,7 @@ const SortingGroupPrefixes: { [key: string]: SortingGroupType } = {
|
|||
[FilesWithExtGroupVerboseLexeme]: {filesOnly: true, filenameWithExt: true},
|
||||
[FoldersGroupShortLexeme]: {foldersOnly: true},
|
||||
[FoldersGroupVerboseLexeme]: {foldersOnly: true},
|
||||
[AnyTypeGroupLexemeShort]: {},
|
||||
[AnyTypeGroupLexeme]: {},
|
||||
[HideItemShortLexeme]: {itemToHide: true},
|
||||
[HideItemVerboseLexeme]: {itemToHide: true}
|
||||
|
@ -664,19 +683,38 @@ export class SortingSpecProcessor {
|
|||
}
|
||||
|
||||
private parseSortingGroupSpec = (line: string): ParsedSortingGroup | null => {
|
||||
const s: string = line.trim()
|
||||
let s: string = line.trim()
|
||||
|
||||
if (hasMoreThanOneNumericSortingSymbol(s)) {
|
||||
this.problem(ProblemCode.TooManyNumericSortingSymbols, 'Maximum one numeric sorting indicator allowed per line')
|
||||
return null
|
||||
}
|
||||
|
||||
const priorityPrefixAlone: number = SortingGroupPriorityPrefixes[s]
|
||||
if (priorityPrefixAlone) {
|
||||
this.problem(ProblemCode.PriorityNotAllowedOnOutsidersGroup, 'Priority is not allowed for sorting group with empty match-pattern')
|
||||
return null
|
||||
}
|
||||
|
||||
let groupPriority: number | undefined = undefined
|
||||
for (const priorityPrefix of Object.keys(SortingGroupPriorityPrefixes)) {
|
||||
if (s.startsWith(priorityPrefix + ' ')) {
|
||||
groupPriority = SortingGroupPriorityPrefixes[priorityPrefix]
|
||||
s = s.substring(priorityPrefix.length).trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const prefixAlone: SortingGroupType = SortingGroupPrefixes[s]
|
||||
if (prefixAlone) {
|
||||
if (prefixAlone.itemToHide) {
|
||||
this.problem(ProblemCode.ItemToHideExactNameWithExtRequired, 'Exact name with ext of file or folders to hide is required')
|
||||
return null
|
||||
} else { // !prefixAlone.itemToHide
|
||||
if (groupPriority) {
|
||||
this.problem(ProblemCode.PriorityNotAllowedOnOutsidersGroup, 'Priority is not allowed for sorting group with empty match-pattern')
|
||||
return null
|
||||
} else {
|
||||
return {
|
||||
outsidersGroup: true,
|
||||
filesOnly: prefixAlone.filesOnly,
|
||||
|
@ -684,6 +722,7 @@ export class SortingSpecProcessor {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const prefix of Object.keys(SortingGroupPrefixes)) {
|
||||
if (s.startsWith(prefix + ' ')) {
|
||||
|
@ -700,12 +739,26 @@ export class SortingSpecProcessor {
|
|||
plainSpec: s.substring(prefix.length + 1),
|
||||
filesOnly: sortingGroupType.filesOnly,
|
||||
foldersOnly: sortingGroupType.foldersOnly,
|
||||
matchFilenameWithExt: sortingGroupType.filenameWithExt
|
||||
matchFilenameWithExt: sortingGroupType.filenameWithExt,
|
||||
priority: groupPriority ?? undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (groupPriority) {
|
||||
if (s === '') {
|
||||
// Edge case: line with only priority prefix and no other content
|
||||
this.problem(ProblemCode.PriorityNotAllowedOnOutsidersGroup, 'Priority is not allowed for sorting group with empty match-pattern')
|
||||
return null
|
||||
} else {
|
||||
// Edge case: line with only priority prefix and no other known syntax, yet some content
|
||||
return {
|
||||
plainSpec: s,
|
||||
priority: groupPriority
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -731,8 +784,14 @@ export class SortingSpecProcessor {
|
|||
if (newGroup) {
|
||||
if (this.adjustSortingGroupForNumericSortingSymbol(newGroup)) {
|
||||
if (this.ctx.currentSpec) {
|
||||
this.ctx.currentSpec.groups.push(newGroup)
|
||||
const groupIdx = this.ctx.currentSpec.groups.push(newGroup) - 1
|
||||
this.ctx.currentSpecGroup = newGroup
|
||||
// Consume group with priority
|
||||
if (group.priority && group.priority > 0) {
|
||||
newGroup.priority = group.priority
|
||||
this.addExpediteGroupInfo(this.ctx.currentSpec, group.priority, groupIdx)
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
return false
|
||||
|
@ -794,7 +853,7 @@ export class SortingSpecProcessor {
|
|||
})
|
||||
}
|
||||
|
||||
// Populate sorting order for a bit more efficient sorting later on
|
||||
// Populate sorting order down the hierarchy for more clean sorting logic later on
|
||||
for (let group of spec.groups) {
|
||||
if (!group.order) {
|
||||
group.order = spec.defaultOrder ?? DEFAULT_SORT_ORDER
|
||||
|
@ -802,6 +861,18 @@ export class SortingSpecProcessor {
|
|||
}
|
||||
}
|
||||
|
||||
// If any priority sorting group was present in the spec, determine the groups evaluation order
|
||||
if (spec.priorityOrder) {
|
||||
// priorityOrder array already contains at least one priority group, so append all non-priority groups for the final order
|
||||
// (Outsiders groups are ignored intentionally)
|
||||
for (let idx=0; idx < spec.groups.length; idx++) {
|
||||
const group: CustomSortGroup = spec.groups[idx]
|
||||
if (group.priority === undefined && group.type !== CustomSortGroupType.Outsiders) {
|
||||
spec.priorityOrder.push(idx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const CURRENT_FOLDER_PREFIX: string = `${CURRENT_FOLDER_SYMBOL}/`
|
||||
|
||||
// Replace the dot-folder names (coming from: 'target-folder: .') with actual folder names
|
||||
|
@ -1108,4 +1179,21 @@ export class SortingSpecProcessor {
|
|||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private addExpediteGroupInfo = (spec: CustomSortSpec, groupPriority: number, groupIdx: number) => {
|
||||
if (!spec.priorityOrder) {
|
||||
spec.priorityOrder = []
|
||||
}
|
||||
let inserted: boolean = false
|
||||
for (let idx=0; idx<spec.priorityOrder.length; idx++) {
|
||||
if (groupPriority > spec.groups[spec.priorityOrder[idx]].priority!) {
|
||||
spec.priorityOrder.splice(idx, 0, groupIdx)
|
||||
inserted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
spec.priorityOrder.push(groupIdx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue