#89 - Support for multi-level sorting
- added run-time execution logic for multi-level sorting - modified the sorter-by-metadata (required change for multi-level sorting) - adjusted unit tests - NO NEW UNIT TESTS - no syntax added to support specification of multi-level (remains to be done)
This commit is contained in:
parent
879c11b62f
commit
8ea694fcf4
|
@ -4,6 +4,7 @@ import {
|
||||||
DEFAULT_FOLDER_MTIME,
|
DEFAULT_FOLDER_MTIME,
|
||||||
determineFolderDatesIfNeeded,
|
determineFolderDatesIfNeeded,
|
||||||
determineSortingGroup,
|
determineSortingGroup,
|
||||||
|
EQUAL_OR_UNCOMPARABLE,
|
||||||
FolderItemForSorting,
|
FolderItemForSorting,
|
||||||
matchGroupRegex,
|
matchGroupRegex,
|
||||||
sorterByMetadataField,
|
sorterByMetadataField,
|
||||||
|
@ -2315,7 +2316,7 @@ describe('CustomSortOrder.byMetadataFieldAlphabetical', () => {
|
||||||
expect(result1).toBe(SORT_FIRST_GOES_EARLIER)
|
expect(result1).toBe(SORT_FIRST_GOES_EARLIER)
|
||||||
expect(result2).toBe(SORT_FIRST_GOES_LATER)
|
expect(result2).toBe(SORT_FIRST_GOES_LATER)
|
||||||
})
|
})
|
||||||
it('should correctly fallback to alphabetical if no metadata on both items', () => {
|
it('should refuse comparison if no metadata on both items', () => {
|
||||||
// given
|
// given
|
||||||
const itemA: Partial<FolderItemForSorting> = {
|
const itemA: Partial<FolderItemForSorting> = {
|
||||||
sortString: 'ccc'
|
sortString: 'ccc'
|
||||||
|
@ -2331,9 +2332,9 @@ describe('CustomSortOrder.byMetadataFieldAlphabetical', () => {
|
||||||
const result3: number = sorter(itemB as FolderItemForSorting, itemB as FolderItemForSorting)
|
const result3: number = sorter(itemB as FolderItemForSorting, itemB as FolderItemForSorting)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(result1).toBe(SORT_FIRST_GOES_EARLIER)
|
expect(result1).toBe(EQUAL_OR_UNCOMPARABLE)
|
||||||
expect(result2).toBe(SORT_FIRST_GOES_LATER)
|
expect(result2).toBe(EQUAL_OR_UNCOMPARABLE)
|
||||||
expect(result3).toBe(SORT_ITEMS_ARE_EQUAL)
|
expect(result3).toBe(EQUAL_OR_UNCOMPARABLE)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2397,7 +2398,7 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => {
|
||||||
expect(result1).toBe(SORT_FIRST_GOES_LATER)
|
expect(result1).toBe(SORT_FIRST_GOES_LATER)
|
||||||
expect(result2).toBe(SORT_FIRST_GOES_EARLIER)
|
expect(result2).toBe(SORT_FIRST_GOES_EARLIER)
|
||||||
})
|
})
|
||||||
it('should correctly fallback to alphabetical reverse if no metadata on both items', () => {
|
it('should refrain from comparing if no metadata on both items', () => {
|
||||||
// given
|
// given
|
||||||
const itemA: Partial<FolderItemForSorting> = {
|
const itemA: Partial<FolderItemForSorting> = {
|
||||||
sortString: 'ccc'
|
sortString: 'ccc'
|
||||||
|
@ -2413,9 +2414,9 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => {
|
||||||
const result3: number = sorter(itemB as FolderItemForSorting, itemB as FolderItemForSorting)
|
const result3: number = sorter(itemB as FolderItemForSorting, itemB as FolderItemForSorting)
|
||||||
|
|
||||||
// then
|
// then
|
||||||
expect(result1).toBe(SORT_FIRST_GOES_LATER)
|
expect(result1).toBe(EQUAL_OR_UNCOMPARABLE)
|
||||||
expect(result2).toBe(SORT_FIRST_GOES_EARLIER)
|
expect(result2).toBe(EQUAL_OR_UNCOMPARABLE)
|
||||||
expect(result3).toBe(SORT_ITEMS_ARE_EQUAL)
|
expect(result3).toBe(EQUAL_OR_UNCOMPARABLE)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -2428,9 +2429,9 @@ describe('sorterByMetadataField', () => {
|
||||||
[true,'mmm','mmm',1, 'e', 'd'],
|
[true,'mmm','mmm',1, 'e', 'd'],
|
||||||
[true,'abc',undefined,-1, 'a','a'],
|
[true,'abc',undefined,-1, 'a','a'],
|
||||||
[true,undefined,'klm',1, 'b','b'],
|
[true,undefined,'klm',1, 'b','b'],
|
||||||
[true,undefined,undefined,0, 'a','a'],
|
[true,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','a'],
|
||||||
[true,undefined,undefined,-1, 'a','b'],
|
[true,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','b'],
|
||||||
[true,undefined,undefined,1, 'd','c'],
|
[true,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'd','c'],
|
||||||
[false,'abc','def',1, 'a', 'a'],
|
[false,'abc','def',1, 'a', 'a'],
|
||||||
[false,'xyz','klm',-1, 'b', 'b'],
|
[false,'xyz','klm',-1, 'b', 'b'],
|
||||||
[false,'mmm','mmm',0, 'c', 'c'],
|
[false,'mmm','mmm',0, 'c', 'c'],
|
||||||
|
@ -2438,9 +2439,9 @@ describe('sorterByMetadataField', () => {
|
||||||
[false,'mmm','mmm',-1, 'e', 'd'],
|
[false,'mmm','mmm',-1, 'e', 'd'],
|
||||||
[false,'abc',undefined,1, 'a','a'],
|
[false,'abc',undefined,1, 'a','a'],
|
||||||
[false,undefined,'klm',-1, 'b','b'],
|
[false,undefined,'klm',-1, 'b','b'],
|
||||||
[false,undefined,undefined,0, 'a','a'],
|
[false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','a'],
|
||||||
[false,undefined,undefined,1, 'a','b'],
|
[false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','b'],
|
||||||
[false,undefined,undefined,-1, 'd','c'],
|
[false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'd','c'],
|
||||||
|
|
||||||
])('straight order %s, comparing %s and %s should return %s for sortStrings %s and %s',
|
])('straight order %s, comparing %s and %s should return %s for sortStrings %s and %s',
|
||||||
(straight: boolean, metadataA: string|undefined, metadataB: string|undefined, order: number, sortStringA: string, sortStringB) => {
|
(straight: boolean, metadataA: string|undefined, metadataB: string|undefined, order: number, sortStringA: string, sortStringB) => {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
FrontMatterCache,
|
FrontMatterCache,
|
||||||
InstalledPlugin,
|
InstalledPlugin,
|
||||||
MetadataCache,
|
MetadataCache,
|
||||||
|
Plugin,
|
||||||
requireApiVersion,
|
requireApiVersion,
|
||||||
TAbstractFile,
|
TAbstractFile,
|
||||||
TFile,
|
TFile,
|
||||||
|
@ -38,7 +39,6 @@ import {
|
||||||
import {
|
import {
|
||||||
expandMacros
|
expandMacros
|
||||||
} from "./macros";
|
} from "./macros";
|
||||||
import {Obj} from "tern";
|
|
||||||
|
|
||||||
export interface ProcessingContext {
|
export interface ProcessingContext {
|
||||||
// For internal transient use
|
// For internal transient use
|
||||||
|
@ -82,6 +82,8 @@ const TrueAlphabetical: boolean = true
|
||||||
const ReverseOrder: boolean = true
|
const ReverseOrder: boolean = true
|
||||||
const StraightOrder: boolean = false
|
const StraightOrder: boolean = false
|
||||||
|
|
||||||
|
export const EQUAL_OR_UNCOMPARABLE: number = 0
|
||||||
|
|
||||||
export const sorterByMetadataField:(reverseOrder?: boolean, trueAlphabetical?: boolean) => SorterFn = (reverseOrder: boolean, trueAlphabetical?: boolean) => {
|
export const sorterByMetadataField:(reverseOrder?: boolean, trueAlphabetical?: boolean) => SorterFn = (reverseOrder: boolean, trueAlphabetical?: boolean) => {
|
||||||
const collatorCompareFn: CollatorCompareFn = trueAlphabetical ? CollatorTrueAlphabeticalCompare : CollatorCompare
|
const collatorCompareFn: CollatorCompareFn = trueAlphabetical ? CollatorTrueAlphabeticalCompare : CollatorCompare
|
||||||
return (a: FolderItemForSorting, b: FolderItemForSorting) => {
|
return (a: FolderItemForSorting, b: FolderItemForSorting) => {
|
||||||
|
@ -100,8 +102,8 @@ export const sorterByMetadataField:(reverseOrder?: boolean, trueAlphabetical?: b
|
||||||
// Item with metadata goes before the w/o metadata
|
// Item with metadata goes before the w/o metadata
|
||||||
if (a.metadataFieldValue) return -1
|
if (a.metadataFieldValue) return -1
|
||||||
if (b.metadataFieldValue) return 1
|
if (b.metadataFieldValue) return 1
|
||||||
// Fallback -> requested sort by metadata, yet none of two items contain it, use alphabetical by name
|
|
||||||
return collatorCompareFn(a.sortString, b.sortString)
|
return EQUAL_OR_UNCOMPARABLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,27 +192,21 @@ export const getSorterFnFor = (sorting: CustomSortOrder, currentUIselectedSortin
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// logic of primary and secondary sorting:
|
|
||||||
// 0th - compare groupIdx
|
|
||||||
// 1st - apply primary, if 0 then
|
|
||||||
// 2nd - apply secondary if present, if 0 or not present then
|
|
||||||
// 3rd - apply folder-level order (which also will probably return 0)
|
|
||||||
// 4th - apply the UI-selected
|
|
||||||
// 5th - if not known, use CustomSortOrder.default
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function getComparator(sortSpec: CustomSortSpec, currentUIselectedSorting?: string): SorterFn {
|
function getComparator(sortSpec: CustomSortSpec, currentUIselectedSorting?: string): SorterFn {
|
||||||
const compareTwoItems = (itA: FolderItemForSorting, itB: FolderItemForSorting) => {
|
const compareTwoItems = (itA: FolderItemForSorting, itB: FolderItemForSorting) => {
|
||||||
if (itA.groupIdx != undefined && itB.groupIdx != undefined) {
|
if (itA.groupIdx != undefined && itB.groupIdx != undefined) {
|
||||||
if (itA.groupIdx === itB.groupIdx) {
|
if (itA.groupIdx === itB.groupIdx) {
|
||||||
const group: CustomSortGroup | undefined = sortSpec.groups[itA.groupIdx]
|
const group: CustomSortGroup | undefined = sortSpec.groups[itA.groupIdx]
|
||||||
|
const primary: number = group?.order ? getSorterFnFor(group.order, currentUIselectedSorting)(itA, itB) : EQUAL_OR_UNCOMPARABLE
|
||||||
if (group.secondaryOrder) {
|
if (primary !== EQUAL_OR_UNCOMPARABLE) return primary
|
||||||
return getSorterFnFor(group.secondaryOrder ?? CustomSortOrder.default, currentUIselectedSorting)(itA, itB)
|
const secondary: number = group?.secondaryOrder ? getSorterFnFor(group.secondaryOrder, currentUIselectedSorting)(itA, itB) : EQUAL_OR_UNCOMPARABLE
|
||||||
} else {
|
if (secondary !== EQUAL_OR_UNCOMPARABLE) return secondary
|
||||||
return getSorterFnFor(group?.order ?? CustomSortOrder.default, currentUIselectedSorting)(itA, itB)
|
const folderLevel: number = sortSpec.defaultOrder ? getSorterFnFor(sortSpec.defaultOrder, currentUIselectedSorting)(itA, itB) : EQUAL_OR_UNCOMPARABLE
|
||||||
}
|
if (folderLevel !== EQUAL_OR_UNCOMPARABLE) return folderLevel
|
||||||
|
const uiSelected: number = currentUIselectedSorting ? getSorterFnFor(CustomSortOrder.standardObsidian, currentUIselectedSorting)(itA, itB) : EQUAL_OR_UNCOMPARABLE
|
||||||
|
if (uiSelected !== EQUAL_OR_UNCOMPARABLE) return uiSelected
|
||||||
|
const lastResort: number = getSorterFnFor(CustomSortOrder.default)(itA, itB)
|
||||||
|
return lastResort
|
||||||
} else {
|
} else {
|
||||||
return itA.groupIdx - itB.groupIdx;
|
return itA.groupIdx - itB.groupIdx;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue