From 8ea694fcf418246602839a879f268c49476420f8 Mon Sep 17 00:00:00 2001 From: SebastianMC <23032356+SebastianMC@users.noreply.github.com> Date: Mon, 28 Aug 2023 23:41:23 +0200 Subject: [PATCH] #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) --- src/custom-sort/custom-sort.spec.ts | 29 ++++++++++++------------ src/custom-sort/custom-sort.ts | 34 +++++++++++++---------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/src/custom-sort/custom-sort.spec.ts b/src/custom-sort/custom-sort.spec.ts index c1e2a2f..8281db1 100644 --- a/src/custom-sort/custom-sort.spec.ts +++ b/src/custom-sort/custom-sort.spec.ts @@ -4,6 +4,7 @@ import { DEFAULT_FOLDER_MTIME, determineFolderDatesIfNeeded, determineSortingGroup, + EQUAL_OR_UNCOMPARABLE, FolderItemForSorting, matchGroupRegex, sorterByMetadataField, @@ -2315,7 +2316,7 @@ describe('CustomSortOrder.byMetadataFieldAlphabetical', () => { expect(result1).toBe(SORT_FIRST_GOES_EARLIER) 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 const itemA: Partial = { sortString: 'ccc' @@ -2331,9 +2332,9 @@ describe('CustomSortOrder.byMetadataFieldAlphabetical', () => { const result3: number = sorter(itemB as FolderItemForSorting, itemB as FolderItemForSorting) // then - expect(result1).toBe(SORT_FIRST_GOES_EARLIER) - expect(result2).toBe(SORT_FIRST_GOES_LATER) - expect(result3).toBe(SORT_ITEMS_ARE_EQUAL) + expect(result1).toBe(EQUAL_OR_UNCOMPARABLE) + expect(result2).toBe(EQUAL_OR_UNCOMPARABLE) + expect(result3).toBe(EQUAL_OR_UNCOMPARABLE) }) }) @@ -2397,7 +2398,7 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => { expect(result1).toBe(SORT_FIRST_GOES_LATER) 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 const itemA: Partial = { sortString: 'ccc' @@ -2413,9 +2414,9 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => { const result3: number = sorter(itemB as FolderItemForSorting, itemB as FolderItemForSorting) // then - expect(result1).toBe(SORT_FIRST_GOES_LATER) - expect(result2).toBe(SORT_FIRST_GOES_EARLIER) - expect(result3).toBe(SORT_ITEMS_ARE_EQUAL) + expect(result1).toBe(EQUAL_OR_UNCOMPARABLE) + expect(result2).toBe(EQUAL_OR_UNCOMPARABLE) + expect(result3).toBe(EQUAL_OR_UNCOMPARABLE) }) }) @@ -2428,9 +2429,9 @@ describe('sorterByMetadataField', () => { [true,'mmm','mmm',1, 'e', 'd'], [true,'abc',undefined,-1, 'a','a'], [true,undefined,'klm',1, 'b','b'], - [true,undefined,undefined,0, 'a','a'], - [true,undefined,undefined,-1, 'a','b'], - [true,undefined,undefined,1, 'd','c'], + [true,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','a'], + [true,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','b'], + [true,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'd','c'], [false,'abc','def',1, 'a', 'a'], [false,'xyz','klm',-1, 'b', 'b'], [false,'mmm','mmm',0, 'c', 'c'], @@ -2438,9 +2439,9 @@ describe('sorterByMetadataField', () => { [false,'mmm','mmm',-1, 'e', 'd'], [false,'abc',undefined,1, 'a','a'], [false,undefined,'klm',-1, 'b','b'], - [false,undefined,undefined,0, 'a','a'], - [false,undefined,undefined,1, 'a','b'], - [false,undefined,undefined,-1, 'd','c'], + [false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','a'], + [false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','b'], + [false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'd','c'], ])('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) => { diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts index e089eac..1cf29d3 100644 --- a/src/custom-sort/custom-sort.ts +++ b/src/custom-sort/custom-sort.ts @@ -4,6 +4,7 @@ import { FrontMatterCache, InstalledPlugin, MetadataCache, + Plugin, requireApiVersion, TAbstractFile, TFile, @@ -38,7 +39,6 @@ import { import { expandMacros } from "./macros"; -import {Obj} from "tern"; export interface ProcessingContext { // For internal transient use @@ -82,6 +82,8 @@ const TrueAlphabetical: boolean = true const ReverseOrder: boolean = true const StraightOrder: boolean = false +export const EQUAL_OR_UNCOMPARABLE: number = 0 + export const sorterByMetadataField:(reverseOrder?: boolean, trueAlphabetical?: boolean) => SorterFn = (reverseOrder: boolean, trueAlphabetical?: boolean) => { const collatorCompareFn: CollatorCompareFn = trueAlphabetical ? CollatorTrueAlphabeticalCompare : CollatorCompare 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 if (a.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 { const compareTwoItems = (itA: FolderItemForSorting, itB: FolderItemForSorting) => { if (itA.groupIdx != undefined && itB.groupIdx != undefined) { if (itA.groupIdx === itB.groupIdx) { const group: CustomSortGroup | undefined = sortSpec.groups[itA.groupIdx] - - if (group.secondaryOrder) { - return getSorterFnFor(group.secondaryOrder ?? CustomSortOrder.default, currentUIselectedSorting)(itA, itB) - } else { - return getSorterFnFor(group?.order ?? CustomSortOrder.default, currentUIselectedSorting)(itA, itB) - } + const primary: number = group?.order ? getSorterFnFor(group.order, currentUIselectedSorting)(itA, itB) : EQUAL_OR_UNCOMPARABLE + if (primary !== EQUAL_OR_UNCOMPARABLE) return primary + const secondary: number = group?.secondaryOrder ? getSorterFnFor(group.secondaryOrder, currentUIselectedSorting)(itA, itB) : EQUAL_OR_UNCOMPARABLE + if (secondary !== EQUAL_OR_UNCOMPARABLE) return secondary + 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 { return itA.groupIdx - itB.groupIdx; }