Skip to content

Commit

Permalink
#111 - changed the sorting behavior for items with undefined attributes
Browse files Browse the repository at this point in the history
- sorting by bookmarks, item w/o bookmark pushed to the bottom in both: asc and desc orders
- sorting by metadata, item w/o metadata pushed to the bottom in both, asc and desc orders
- sorting by creation or modification date, advanced mode: empty folders (or folders having only folder children) are pushed to the bottom, regardless of asc or desc order
  • Loading branch information
SebastianMC committed Nov 13, 2023
1 parent b972097 commit b4d55c8
Show file tree
Hide file tree
Showing 2 changed files with 127 additions and 18 deletions.
93 changes: 84 additions & 9 deletions src/custom-sort/custom-sort.spec.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -8,8 +15,10 @@ import {
FolderItemForSorting,
getSorterFnFor,
matchGroupRegex,
ProcessingContext,
ProcessingContext,
sorterByBookmarkOrder,
sorterByFolderCDate,
sorterByFolderMDate,
sorterByMetadataField,
SorterFn
} from './custom-sort';
Expand Down Expand Up @@ -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<FolderItemForSorting> = {
metadataFieldValue: '15',
Expand All @@ -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
Expand Down Expand Up @@ -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'],
Expand Down Expand Up @@ -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'],
Expand All @@ -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<FolderItemForSorting> = {mtime: dateA}
const itemB: Partial<FolderItemForSorting> = {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<FolderItemForSorting> = {ctime: dateA}
const itemB: Partial<FolderItemForSorting> = {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)
})
})
52 changes: 43 additions & 9 deletions src/custom-sort/custom-sort.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand All @@ -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
}
Expand All @@ -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),
Expand Down Expand Up @@ -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

Expand Down

0 comments on commit b4d55c8

Please sign in to comment.