Skip to content

Commit b4d55c8

Browse files
committed
#111 - changed the sorting behavior for items with undefined attributes
- 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
1 parent b972097 commit b4d55c8

File tree

2 files changed

+127
-18
lines changed

2 files changed

+127
-18
lines changed

src/custom-sort/custom-sort.spec.ts

Lines changed: 84 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import {CachedMetadata, MetadataCache, Pos, TFile, TFolder, Vault} from 'obsidian';
1+
import {
2+
CachedMetadata,
3+
MetadataCache,
4+
Pos,
5+
TFile,
6+
TFolder,
7+
Vault
8+
} from 'obsidian';
29
import {
310
DEFAULT_FOLDER_CTIME,
411
DEFAULT_FOLDER_MTIME,
@@ -8,8 +15,10 @@ import {
815
FolderItemForSorting,
916
getSorterFnFor,
1017
matchGroupRegex,
11-
ProcessingContext,
18+
ProcessingContext,
1219
sorterByBookmarkOrder,
20+
sorterByFolderCDate,
21+
sorterByFolderMDate,
1322
sorterByMetadataField,
1423
SorterFn
1524
} from './custom-sort';
@@ -2791,7 +2800,7 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => {
27912800
expect(result2).toBe(EQUAL_OR_UNCOMPARABLE)
27922801
expect(result3).toBe(EQUAL_OR_UNCOMPARABLE)
27932802
})
2794-
it('should put the item with metadata later if the second one has no metadata (reverse order)', () => {
2803+
it('should put the item with metadata earlier if the second one has no metadata (reverse order)', () => {
27952804
// given
27962805
const itemA: Partial<FolderItemForSorting> = {
27972806
metadataFieldValue: '15',
@@ -2807,8 +2816,8 @@ describe('CustomSortOrder.byMetadataFieldAlphabeticalReverse', () => {
28072816
const result2: number = sorter(itemB as FolderItemForSorting, itemA as FolderItemForSorting)
28082817

28092818
// then
2810-
expect(result1).toBe(SORT_FIRST_GOES_LATER)
2811-
expect(result2).toBe(SORT_FIRST_GOES_EARLIER)
2819+
expect(result1).toBe(SORT_FIRST_GOES_EARLIER)
2820+
expect(result2).toBe(SORT_FIRST_GOES_LATER)
28122821
})
28132822
it('should refrain from comparing if no metadata on both items', () => {
28142823
// given
@@ -2849,8 +2858,8 @@ describe('sorterByMetadataField', () => {
28492858
[false,'mmm','mmm',EQUAL_OR_UNCOMPARABLE, 'c', 'c'],
28502859
[false,'mmm','mmm',EQUAL_OR_UNCOMPARABLE, 'd', 'e'],
28512860
[false,'mmm','mmm',EQUAL_OR_UNCOMPARABLE, 'e', 'd'],
2852-
[false,'abc',undefined,1, 'a','a'],
2853-
[false,undefined,'klm',-1, 'b','b'],
2861+
[false,'abc',undefined,-1, 'a','a'],
2862+
[false,undefined,'klm',1, 'b','b'],
28542863
[false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','a'],
28552864
[false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'a','b'],
28562865
[false,undefined,undefined,EQUAL_OR_UNCOMPARABLE, 'd','c'],
@@ -2884,8 +2893,8 @@ describe('sorterByBookmarkOrder', () => {
28842893
[false,30,30,0, 'c', 'c'], // not possible in reality - each bookmark order is unique by definition - covered for clarity
28852894
[false,1,1,0, 'd', 'e'], // ------//-----
28862895
[false,2,2,0, 'e', 'd'], // ------//-----
2887-
[false,3,undefined,1, 'a','a'],
2888-
[false,undefined,4,-1, 'b','b'],
2896+
[false,3,undefined,-1, 'a','a'],
2897+
[false,undefined,4,1, 'b','b'],
28892898
[false,undefined,undefined,0, 'a','a'],
28902899
[false,undefined,undefined,0, 'a','b'],
28912900
[false,undefined,undefined,0, 'd','c'],
@@ -2902,3 +2911,69 @@ describe('sorterByBookmarkOrder', () => {
29022911
expect(normalizedResult).toBe(order)
29032912
})
29042913
})
2914+
2915+
const OLDER_TIME: number = 1000000
2916+
const NEWER_TIME: number = OLDER_TIME + 1000
2917+
const EOU: number = EQUAL_OR_UNCOMPARABLE
2918+
2919+
describe('sorterByFolderMDate', () => {
2920+
it.each([
2921+
[DEFAULT_FOLDER_MTIME, DEFAULT_FOLDER_MTIME, EOU, EOU, EOU, EOU],
2922+
[OLDER_TIME, OLDER_TIME, EOU, EOU, EOU, EOU],
2923+
[OLDER_TIME, NEWER_TIME, -1, 1, 1, -1],
2924+
[DEFAULT_FOLDER_MTIME, NEWER_TIME, 1, -1, 1, -1],
2925+
[NEWER_TIME, DEFAULT_FOLDER_MTIME, -1, 1, -1, 1]
2926+
])('comparing %s and %s should return %s (reversed params %s) and %s for reverse order (and %s for reversed order reversed params)',
2927+
(dateA: number, dateB: number, orderStraight: number, orderStraightRevParams: number, orderReverse: number, orderReverseRevParams: number) => {
2928+
const sorterFnStraight = sorterByFolderMDate()
2929+
const sorterFnReverse = sorterByFolderMDate(true)
2930+
const itemA: Partial<FolderItemForSorting> = {mtime: dateA}
2931+
const itemB: Partial<FolderItemForSorting> = {mtime: dateB}
2932+
const resultS1 = sorterFnStraight(itemA as FolderItemForSorting, itemB as FolderItemForSorting)
2933+
const resultS2 = sorterFnStraight(itemB as FolderItemForSorting, itemA as FolderItemForSorting)
2934+
const resultR1 = sorterFnReverse(itemA as FolderItemForSorting, itemB as FolderItemForSorting)
2935+
const resultR2 = sorterFnReverse(itemB as FolderItemForSorting, itemA as FolderItemForSorting)
2936+
2937+
const normalizedResultS1 = resultS1 < 0 ? -1 : ((resultS1 > 0) ? 1 : resultS1)
2938+
const normalizedResultS2 = resultS2 < 0 ? -1 : ((resultS2 > 0) ? 1 : resultS2)
2939+
const normalizedResultR1 = resultR1 < 0 ? -1 : ((resultR1 > 0) ? 1 : resultR1)
2940+
const normalizedResultR2 = resultR2 < 0 ? -1 : ((resultR2 > 0) ? 1 : resultR2)
2941+
2942+
// then
2943+
expect(normalizedResultS1).toBe(orderStraight)
2944+
expect(normalizedResultS2).toBe(orderStraightRevParams)
2945+
expect(normalizedResultR1).toBe(orderReverse)
2946+
expect(normalizedResultR2).toBe(orderReverseRevParams)
2947+
})
2948+
})
2949+
2950+
describe('sorterByFolderCDate', () => {
2951+
it.each([
2952+
[DEFAULT_FOLDER_CTIME, DEFAULT_FOLDER_CTIME, EOU, EOU, EOU, EOU],
2953+
[OLDER_TIME, OLDER_TIME, EOU, EOU, EOU, EOU],
2954+
[OLDER_TIME, NEWER_TIME, -1, 1, 1, -1],
2955+
[DEFAULT_FOLDER_CTIME, NEWER_TIME, 1, -1, 1, -1],
2956+
[NEWER_TIME, DEFAULT_FOLDER_CTIME, -1, 1, -1, 1]
2957+
])('comparing %s and %s should return %s (reversed params %s) and %s for reverse order (and %s for reversed order reversed params)',
2958+
(dateA: number, dateB: number, orderStraight: number, orderStraightRevParams: number, orderReverse: number, orderReverseRevParams: number) => {
2959+
const sorterFnStraight = sorterByFolderCDate()
2960+
const sorterFnReverse = sorterByFolderCDate(true)
2961+
const itemA: Partial<FolderItemForSorting> = {ctime: dateA}
2962+
const itemB: Partial<FolderItemForSorting> = {ctime: dateB}
2963+
const resultS1 = sorterFnStraight(itemA as FolderItemForSorting, itemB as FolderItemForSorting)
2964+
const resultS2 = sorterFnStraight(itemB as FolderItemForSorting, itemA as FolderItemForSorting)
2965+
const resultR1 = sorterFnReverse(itemA as FolderItemForSorting, itemB as FolderItemForSorting)
2966+
const resultR2 = sorterFnReverse(itemB as FolderItemForSorting, itemA as FolderItemForSorting)
2967+
2968+
const normalizedResultS1 = resultS1 < 0 ? -1 : ((resultS1 > 0) ? 1 : resultS1)
2969+
const normalizedResultS2 = resultS2 < 0 ? -1 : ((resultS2 > 0) ? 1 : resultS2)
2970+
const normalizedResultR1 = resultR1 < 0 ? -1 : ((resultR1 > 0) ? 1 : resultR1)
2971+
const normalizedResultR2 = resultR2 < 0 ? -1 : ((resultR2 > 0) ? 1 : resultR2)
2972+
2973+
// then
2974+
expect(normalizedResultS1).toBe(orderStraight)
2975+
expect(normalizedResultS2).toBe(orderStraightRevParams)
2976+
expect(normalizedResultR1).toBe(orderReverse)
2977+
expect(normalizedResultR2).toBe(orderReverseRevParams)
2978+
})
2979+
})

src/custom-sort/custom-sort.ts

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ export const sorterByMetadataField = (reverseOrder?: boolean, trueAlphabetical?:
112112
return sortResult
113113
}
114114
// Item with metadata goes before the w/o metadata
115-
if (amdata) return -1
116-
if (bmdata) return 1
115+
if (amdata) return reverseOrder ? 1 : -1
116+
if (bmdata) return reverseOrder ? -1 : 1
117117

118118
return EQUAL_OR_UNCOMPARABLE
119119
}
120120
}
121121

122-
export const sorterByBookmarkOrder:(reverseOrder?: boolean, trueAlphabetical?: boolean) => SorterFn = (reverseOrder: boolean, trueAlphabetical?: boolean) => {
122+
export const sorterByBookmarkOrder:(reverseOrder?: boolean, trueAlphabetical?: boolean) => SorterFn = (reverseOrder: boolean) => {
123123
return (a: FolderItemForSorting, b: FolderItemForSorting) => {
124124
if (reverseOrder) {
125125
[a, b] = [b, a]
@@ -129,8 +129,40 @@ export const sorterByBookmarkOrder:(reverseOrder?: boolean, trueAlphabetical?: b
129129
return a.bookmarkedIdx - b.bookmarkedIdx
130130
}
131131
// Item with bookmark order goes before the w/o bookmark info
132-
if (a.bookmarkedIdx) return -1
133-
if (b.bookmarkedIdx) return 1
132+
if (a.bookmarkedIdx) return reverseOrder ? 1 : -1
133+
if (b.bookmarkedIdx) return reverseOrder ? -1 : 1
134+
135+
return EQUAL_OR_UNCOMPARABLE
136+
}
137+
}
138+
139+
export const sorterByFolderCDate:(reverseOrder?: boolean) => SorterFn = (reverseOrder?: boolean) => {
140+
return (a: FolderItemForSorting, b: FolderItemForSorting) => {
141+
if (reverseOrder) {
142+
[a, b] = [b, a]
143+
}
144+
if (a.ctime && b.ctime) {
145+
return a.ctime - b.ctime
146+
}
147+
// Folder with determined ctime always goes before empty folder (=> undetermined ctime)
148+
if (a.ctime) return reverseOrder ? 1 : -1
149+
if (b.ctime) return reverseOrder ? -1 : 1
150+
151+
return EQUAL_OR_UNCOMPARABLE
152+
}
153+
}
154+
155+
export const sorterByFolderMDate:(reverseOrder?: boolean) => SorterFn = (reverseOrder?: boolean) => {
156+
return (a: FolderItemForSorting, b: FolderItemForSorting) => {
157+
if (reverseOrder) {
158+
[a, b] = [b, a]
159+
}
160+
if (a.mtime && b.mtime) {
161+
return a.mtime - b.mtime
162+
}
163+
// Folder with determined mtime always goes before empty folder (=> undetermined ctime)
164+
if (a.mtime) return reverseOrder ? 1 : -1
165+
if (b.mtime) return reverseOrder ? -1 : 1
134166

135167
return EQUAL_OR_UNCOMPARABLE
136168
}
@@ -142,13 +174,13 @@ const Sorters: { [key in CustomSortOrder]: SorterFn } = {
142174
[CustomSortOrder.alphabeticalReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => CollatorCompare(b.sortString, a.sortString),
143175
[CustomSortOrder.trueAlphabeticalReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => CollatorTrueAlphabeticalCompare(b.sortString, a.sortString),
144176
[CustomSortOrder.byModifiedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (a.mtime - b.mtime),
145-
[CustomSortOrder.byModifiedTimeAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.mtime - b.mtime,
177+
[CustomSortOrder.byModifiedTimeAdvanced]: sorterByFolderMDate(),
146178
[CustomSortOrder.byModifiedTimeReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (b.mtime - a.mtime),
147-
[CustomSortOrder.byModifiedTimeReverseAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.mtime - a.mtime,
179+
[CustomSortOrder.byModifiedTimeReverseAdvanced]: sorterByFolderMDate(true),
148180
[CustomSortOrder.byCreatedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (a.ctime - b.ctime),
149-
[CustomSortOrder.byCreatedTimeAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.ctime - b.ctime,
181+
[CustomSortOrder.byCreatedTimeAdvanced]: sorterByFolderCDate(),
150182
[CustomSortOrder.byCreatedTimeReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => (a.isFolder && b.isFolder) ? CollatorCompare(a.sortString, b.sortString) : (b.ctime - a.ctime),
151-
[CustomSortOrder.byCreatedTimeReverseAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.ctime - a.ctime,
183+
[CustomSortOrder.byCreatedTimeReverseAdvanced]: sorterByFolderCDate(true),
152184
[CustomSortOrder.byMetadataFieldAlphabetical]: sorterByMetadataField(StraightOrder, !TrueAlphabetical, SortingLevelId.forPrimary),
153185
[CustomSortOrder.byMetadataFieldTrueAlphabetical]: sorterByMetadataField(StraightOrder, TrueAlphabetical, SortingLevelId.forPrimary),
154186
[CustomSortOrder.byMetadataFieldAlphabeticalReverse]: sorterByMetadataField(ReverseOrder, !TrueAlphabetical, SortingLevelId.forPrimary),
@@ -292,6 +324,8 @@ const isByMetadata = (order: CustomSortOrder | undefined) => {
292324
order === CustomSortOrder.byMetadataFieldTrueAlphabetical || order === CustomSortOrder.byMetadataFieldTrueAlphabeticalReverse
293325
}
294326

327+
// IMPORTANT: do not change the value of below constants
328+
// It is used in sorter to discern empty folders (thus undetermined dates) from other folders
295329
export const DEFAULT_FOLDER_MTIME: number = 0
296330
export const DEFAULT_FOLDER_CTIME: number = 0
297331

0 commit comments

Comments
 (0)