diff --git a/src/custom-sort/custom-sort.spec.ts b/src/custom-sort/custom-sort.spec.ts index d0ee7c8..bfa1a83 100644 --- a/src/custom-sort/custom-sort.spec.ts +++ b/src/custom-sort/custom-sort.spec.ts @@ -1,4 +1,11 @@ -import {CachedMetadata, MetadataCache, Pos, TFile, TFolder, Vault} from 'obsidian'; +import { + CachedMetadata, + MetadataCache, + Pos, + TFile, + TFolder, + Vault +} from 'obsidian'; import { DEFAULT_FOLDER_CTIME, DEFAULT_FOLDER_MTIME, @@ -8,8 +15,10 @@ import { FolderItemForSorting, getSorterFnFor, matchGroupRegex, - ProcessingContext, + ProcessingContext, sorterByBookmarkOrder, + sorterByFolderCDate, + sorterByFolderMDate, sorterByMetadataField, SorterFn } from './custom-sort'; @@ -2791,7 +2800,7 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => { expect(result2).toBe(EQUAL_OR_UNCOMPARABLE) expect(result3).toBe(EQUAL_OR_UNCOMPARABLE) }) - it('should put the item with metadata later if the second one has no metadata (reverse order)', () => { + it('should put the item with metadata earlier if the second one has no metadata (reverse order)', () => { // given const itemA: Partial = { metadataFieldValue: '15', @@ -2807,8 +2816,8 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => { const result2: number = sorter(itemB as FolderItemForSorting, itemA as FolderItemForSorting) // then - expect(result1).toBe(SORT_FIRST_GOES_LATER) - expect(result2).toBe(SORT_FIRST_GOES_EARLIER) + expect(result1).toBe(SORT_FIRST_GOES_EARLIER) + expect(result2).toBe(SORT_FIRST_GOES_LATER) }) it('should refrain from comparing if no metadata on both items', () => { // given @@ -2849,8 +2858,8 @@ describe('sorterByMetadataField', () => { [false,'mmm','mmm',EQUAL_OR_UNCOMPARABLE, 'c', 'c'], [false,'mmm','mmm',EQUAL_OR_UNCOMPARABLE, 'd', 'e'], [false,'mmm','mmm',EQUAL_OR_UNCOMPARABLE, 'e', 'd'], - [false,'abc',undefined,1, 'a','a'], - [false,undefined,'klm',-1, 'b','b'], + [false,'abc',undefined,-1, 'a','a'], + [false,undefined,'klm',1, 'b','b'], [false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','a'], [false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','b'], [false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'd','c'], @@ -2884,8 +2893,8 @@ describe('sorterByBookmarkOrder', () => { [false,30,30,0, 'c', 'c'], // not possible in reality - each bookmark order is unique by definition - covered for clarity [false,1,1,0, 'd', 'e'], // ------//----- [false,2,2,0, 'e', 'd'], // ------//----- - [false,3,undefined,1, 'a','a'], - [false,undefined,4,-1, 'b','b'], + [false,3,undefined,-1, 'a','a'], + [false,undefined,4,1, 'b','b'], [false,undefined,undefined,0, 'a','a'], [false,undefined,undefined,0, 'a','b'], [false,undefined,undefined,0, 'd','c'], @@ -2902,3 +2911,69 @@ describe('sorterByBookmarkOrder', () => { expect(normalizedResult).toBe(order) }) }) + +const OLDER_TIME: number = 1000000 +const NEWER_TIME: number = OLDER_TIME + 1000 +const EOU: number = EQUAL_OR_UNCOMPARABLE + +describe('sorterByFolderMDate', () => { + it.each([ + [DEFAULT_FOLDER_MTIME, DEFAULT_FOLDER_MTIME, EOU, EOU, EOU, EOU], + [OLDER_TIME, OLDER_TIME, EOU, EOU, EOU, EOU], + [OLDER_TIME, NEWER_TIME, -1, 1, 1, -1], + [DEFAULT_FOLDER_MTIME, NEWER_TIME, 1, -1, 1, -1], + [NEWER_TIME, DEFAULT_FOLDER_MTIME, -1, 1, -1, 1] + ])('comparing %s and %s should return %s (reversed params %s) and %s for reverse order (and %s for reversed order reversed params)', + (dateA: number, dateB: number, orderStraight: number, orderStraightRevParams: number, orderReverse: number, orderReverseRevParams: number) => { + const sorterFnStraight = sorterByFolderMDate() + const sorterFnReverse = sorterByFolderMDate(true) + const itemA: Partial = {mtime: dateA} + const itemB: Partial = {mtime: dateB} + const resultS1 = sorterFnStraight(itemA as FolderItemForSorting, itemB as FolderItemForSorting) + const resultS2 = sorterFnStraight(itemB as FolderItemForSorting, itemA as FolderItemForSorting) + const resultR1 = sorterFnReverse(itemA as FolderItemForSorting, itemB as FolderItemForSorting) + const resultR2 = sorterFnReverse(itemB as FolderItemForSorting, itemA as FolderItemForSorting) + + const normalizedResultS1 = resultS1 < 0 ? -1 : ((resultS1 > 0) ? 1 : resultS1) + const normalizedResultS2 = resultS2 < 0 ? -1 : ((resultS2 > 0) ? 1 : resultS2) + const normalizedResultR1 = resultR1 < 0 ? -1 : ((resultR1 > 0) ? 1 : resultR1) + const normalizedResultR2 = resultR2 < 0 ? -1 : ((resultR2 > 0) ? 1 : resultR2) + + // then + expect(normalizedResultS1).toBe(orderStraight) + expect(normalizedResultS2).toBe(orderStraightRevParams) + expect(normalizedResultR1).toBe(orderReverse) + expect(normalizedResultR2).toBe(orderReverseRevParams) + }) +}) + +describe('sorterByFolderCDate', () => { + it.each([ + [DEFAULT_FOLDER_CTIME, DEFAULT_FOLDER_CTIME, EOU, EOU, EOU, EOU], + [OLDER_TIME, OLDER_TIME, EOU, EOU, EOU, EOU], + [OLDER_TIME, NEWER_TIME, -1, 1, 1, -1], + [DEFAULT_FOLDER_CTIME, NEWER_TIME, 1, -1, 1, -1], + [NEWER_TIME, DEFAULT_FOLDER_CTIME, -1, 1, -1, 1] + ])('comparing %s and %s should return %s (reversed params %s) and %s for reverse order (and %s for reversed order reversed params)', + (dateA: number, dateB: number, orderStraight: number, orderStraightRevParams: number, orderReverse: number, orderReverseRevParams: number) => { + const sorterFnStraight = sorterByFolderCDate() + const sorterFnReverse = sorterByFolderCDate(true) + const itemA: Partial = {ctime: dateA} + const itemB: Partial = {ctime: dateB} + const resultS1 = sorterFnStraight(itemA as FolderItemForSorting, itemB as FolderItemForSorting) + const resultS2 = sorterFnStraight(itemB as FolderItemForSorting, itemA as FolderItemForSorting) + const resultR1 = sorterFnReverse(itemA as FolderItemForSorting, itemB as FolderItemForSorting) + const resultR2 = sorterFnReverse(itemB as FolderItemForSorting, itemA as FolderItemForSorting) + + const normalizedResultS1 = resultS1 < 0 ? -1 : ((resultS1 > 0) ? 1 : resultS1) + const normalizedResultS2 = resultS2 < 0 ? -1 : ((resultS2 > 0) ? 1 : resultS2) + const normalizedResultR1 = resultR1 < 0 ? -1 : ((resultR1 > 0) ? 1 : resultR1) + const normalizedResultR2 = resultR2 < 0 ? -1 : ((resultR2 > 0) ? 1 : resultR2) + + // then + expect(normalizedResultS1).toBe(orderStraight) + expect(normalizedResultS2).toBe(orderStraightRevParams) + expect(normalizedResultR1).toBe(orderReverse) + expect(normalizedResultR2).toBe(orderReverseRevParams) + }) +}) diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts index 6d1fb23..275f163 100644 --- a/src/custom-sort/custom-sort.ts +++ b/src/custom-sort/custom-sort.ts @@ -112,14 +112,14 @@ export const sorterByMetadataField = (reverseOrder?: boolean, trueAlphabetical?: return sortResult } // Item with metadata goes before the w/o metadata - if (amdata) return -1 - if (bmdata) return 1 + if (amdata) return reverseOrder ? 1 : -1 + if (bmdata) return reverseOrder ? -1 : 1 return EQUAL_OR_UNCOMPARABLE } } -export const sorterByBookmarkOrder:(reverseOrder?: boolean, trueAlphabetical?: boolean) => SorterFn = (reverseOrder: boolean, trueAlphabetical?: boolean) => { +export const sorterByBookmarkOrder:(reverseOrder?: boolean, trueAlphabetical?: boolean) => SorterFn = (reverseOrder: boolean) => { return (a: FolderItemForSorting, b: FolderItemForSorting) => { if (reverseOrder) { [a, b] = [b, a] @@ -129,8 +129,40 @@ export const sorterByBookmarkOrder:(reverseOrder?: boolean, trueAlphabetical?: b return a.bookmarkedIdx - b.bookmarkedIdx } // Item with bookmark order goes before the w/o bookmark info - if (a.bookmarkedIdx) return -1 - if (b.bookmarkedIdx) return 1 + if (a.bookmarkedIdx) return reverseOrder ? 1 : -1 + if (b.bookmarkedIdx) return reverseOrder ? -1 : 1 + + return EQUAL_OR_UNCOMPARABLE + } +} + +export const sorterByFolderCDate:(reverseOrder?: boolean) => SorterFn = (reverseOrder?: boolean) => { + return (a: FolderItemForSorting, b: FolderItemForSorting) => { + if (reverseOrder) { + [a, b] = [b, a] + } + if (a.ctime && b.ctime) { + return a.ctime - b.ctime + } + // Folder with determined ctime always goes before empty folder (=> undetermined ctime) + if (a.ctime) return reverseOrder ? 1 : -1 + if (b.ctime) return reverseOrder ? -1 : 1 + + return EQUAL_OR_UNCOMPARABLE + } +} + +export const sorterByFolderMDate:(reverseOrder?: boolean) => SorterFn = (reverseOrder?: boolean) => { + return (a: FolderItemForSorting, b: FolderItemForSorting) => { + if (reverseOrder) { + [a, b] = [b, a] + } + if (a.mtime && b.mtime) { + return a.mtime - b.mtime + } + // Folder with determined mtime always goes before empty folder (=> undetermined ctime) + if (a.mtime) return reverseOrder ? 1 : -1 + if (b.mtime) return reverseOrder ? -1 : 1 return EQUAL_OR_UNCOMPARABLE } @@ -142,13 +174,13 @@ const Sorters: { [key in CustomSortOrder]: SorterFn } = { [CustomSortOrder.alphabeticalReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => CollatorCompare(b.sortString, a.sortString), [CustomSortOrder.trueAlphabeticalReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => CollatorTrueAlphabeticalCompare(b.sortString, a.sortString), [CustomSortOrder.byModifiedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (a.mtime - b.mtime), - [CustomSortOrder.byModifiedTimeAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.mtime - b.mtime, + [CustomSortOrder.byModifiedTimeAdvanced]: sorterByFolderMDate(), [CustomSortOrder.byModifiedTimeReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (b.mtime - a.mtime), - [CustomSortOrder.byModifiedTimeReverseAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.mtime - a.mtime, + [CustomSortOrder.byModifiedTimeReverseAdvanced]: sorterByFolderMDate(true), [CustomSortOrder.byCreatedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (a.ctime - b.ctime), - [CustomSortOrder.byCreatedTimeAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.ctime - b.ctime, + [CustomSortOrder.byCreatedTimeAdvanced]: sorterByFolderCDate(), [CustomSortOrder.byCreatedTimeReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (b.ctime - a.ctime), - [CustomSortOrder.byCreatedTimeReverseAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.ctime - a.ctime, + [CustomSortOrder.byCreatedTimeReverseAdvanced]: sorterByFolderCDate(true), [CustomSortOrder.byMetadataFieldAlphabetical]: sorterByMetadataField(StraightOrder, !TrueAlphabetical, SortingLevelId.forPrimary), [CustomSortOrder.byMetadataFieldTrueAlphabetical]: sorterByMetadataField(StraightOrder, TrueAlphabetical, SortingLevelId.forPrimary), [CustomSortOrder.byMetadataFieldAlphabeticalReverse]: sorterByMetadataField(ReverseOrder, !TrueAlphabetical, SortingLevelId.forPrimary), @@ -292,6 +324,8 @@ const isByMetadata = (order: CustomSortOrder | undefined) => { order === CustomSortOrder.byMetadataFieldTrueAlphabetical || order === CustomSortOrder.byMetadataFieldTrueAlphabeticalReverse } +// IMPORTANT: do not change the value of below constants +// It is used in sorter to discern empty folders (thus undetermined dates) from other folders export const DEFAULT_FOLDER_MTIME: number = 0 export const DEFAULT_FOLDER_CTIME: number = 0