diff --git a/src/custom-sort/custom-sort-types.ts b/src/custom-sort/custom-sort-types.ts index ed79c19..bc727d9 100644 --- a/src/custom-sort/custom-sort-types.ts +++ b/src/custom-sort/custom-sort-types.ts @@ -11,7 +11,9 @@ export enum CustomSortOrder { alphabetical = 1, // = 1 to allow: if (customSortOrder) { ... alphabeticalReverse, byModifiedTime, + byModifiedTimeAdvanced, byModifiedTimeReverse, + byModifiedTimeReverseAdvanced, byCreatedTime, byCreatedTimeReverse, standardObsidian, // Let the folder sorting be in hands of Obsidian, whatever user selected in the UI diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts index 460de86..39637a6 100644 --- a/src/custom-sort/custom-sort.ts +++ b/src/custom-sort/custom-sort.ts @@ -1,4 +1,4 @@ -import {requireApiVersion, TFile, TFolder} from 'obsidian'; +import {requireApiVersion, TAbstractFile, TFile, TFolder} from 'obsidian'; import {CustomSortGroup, CustomSortGroupType, CustomSortOrder, CustomSortSpec} from "./custom-sort-types"; import {isDefined} from "../utils/utils"; @@ -16,6 +16,7 @@ interface FolderItemForSorting { ctime: number mtime: number isFolder: boolean + folder?: TFolder } type SorterFn = (a: FolderItemForSorting, b: FolderItemForSorting) => number @@ -24,7 +25,9 @@ let Sorters: { [key in CustomSortOrder]: SorterFn } = { [CustomSortOrder.alphabetical]: (a: FolderItemForSorting, b: FolderItemForSorting) => Collator(a.sortString, b.sortString), [CustomSortOrder.alphabeticalReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => Collator(b.sortString, a.sortString), [CustomSortOrder.byModifiedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.mtime - b.mtime, + [CustomSortOrder.byModifiedTimeAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.mtime - b.mtime, [CustomSortOrder.byModifiedTimeReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.mtime - a.mtime, + [CustomSortOrder.byModifiedTimeReverseAdvanced]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.mtime - a.mtime, [CustomSortOrder.byCreatedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.ctime - b.ctime, [CustomSortOrder.byCreatedTimeReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.ctime - a.ctime, @@ -52,11 +55,14 @@ function compareTwoItems(itA: FolderItemForSorting, itB: FolderItemForSorting, s } } -const isFolder = (entry: TFile | TFolder) => { +const isFolder = (entry: TAbstractFile) => { // The plain obvious 'entry instanceof TFolder' doesn't work inside Jest unit tests, hence a workaround below return !!((entry as any).isRoot); } +export const DEFAULT_FOLDER_MTIME: number = 0 +export const DEFAULT_FOLDER_CTIME: number = 0 + export const determineSortingGroup = function (entry: TFile | TFolder, spec: CustomSortSpec): FolderItemForSorting { let groupIdx: number let determined: boolean = false @@ -165,14 +171,37 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus sortString: matchedGroup ? (matchedGroup + '//' + entry.name) : entry.name, matchGroup: matchedGroup ?? undefined, isFolder: aFolder, + folder: aFolder ? (entry as TFolder) : undefined, path: entry.path, - ctime: aFile ? entryAsTFile.stat.ctime : 0, - mtime: aFile ? entryAsTFile.stat.mtime : 0 + ctime: aFile ? entryAsTFile.stat.ctime : DEFAULT_FOLDER_CTIME, + mtime: aFile ? entryAsTFile.stat.mtime : DEFAULT_FOLDER_MTIME } } +export const sortOrderNeedsFoldersMDate = (order: CustomSortOrder | undefined): boolean => { + return order === CustomSortOrder.byModifiedTimeAdvanced || order === CustomSortOrder.byModifiedTimeReverseAdvanced +} + +// Syntax sugar for readability +export type ModifiedTime = number + +export const determineModifiedDateForFolder = (folder: TFolder): ModifiedTime => { + let mtimeOfFolder: ModifiedTime = DEFAULT_FOLDER_MTIME + folder.children.forEach((item) => { + if (!isFolder(item)) { + const file: TFile = item as TFile + if (file.stat.mtime > mtimeOfFolder) { + mtimeOfFolder = file.stat.mtime + } + } + }) + return mtimeOfFolder +} + + export const folderSort = function (sortingSpec: CustomSortSpec, order: string[]) { let fileExplorer = this.fileExplorer + const sortingGroupsCardinality: {[key: number]: number} = {} const folderItems: Array = (sortingSpec.itemsToHide ? this.file.children.filter((entry: TFile | TFolder) => { @@ -180,9 +209,27 @@ export const folderSort = function (sortingSpec: CustomSortSpec, order: string[] }) : this.file.children) - .map((entry: TFile | TFolder) => - determineSortingGroup(entry, sortingSpec) - ) + .map((entry: TFile | TFolder) => { + const itemForSorting: FolderItemForSorting = determineSortingGroup(entry, sortingSpec) + const groupIdx: number | undefined = itemForSorting.groupIdx + if (groupIdx !== undefined) { + sortingGroupsCardinality[groupIdx] = 1 + (sortingGroupsCardinality[groupIdx] ?? 0) + } + return itemForSorting + }) + + // Finally, for advanced sorting by modified date, for some of the folders the modified date has to be determined + folderItems.forEach((item) => { + const groupIdx: number | undefined = item.groupIdx + if (groupIdx !== undefined) { + const groupOrder: CustomSortOrder | undefined = sortingSpec.groups[groupIdx].order + if (sortOrderNeedsFoldersMDate(groupOrder)) { + if (item.folder) { + item.mtime = determineModifiedDateForFolder(item.folder) + } + } + } + }) folderItems.sort(function (itA: FolderItemForSorting, itB: FolderItemForSorting) { return compareTwoItems(itA, itB, sortingSpec); diff --git a/src/custom-sort/sorting-spec-processor.spec.ts b/src/custom-sort/sorting-spec-processor.spec.ts index d680158..b96eef4 100644 --- a/src/custom-sort/sorting-spec-processor.spec.ts +++ b/src/custom-sort/sorting-spec-processor.spec.ts @@ -31,7 +31,7 @@ target-folder: tricky folder target-folder: / /: Con... / - > modified + > advanced modified /: < modified /: Ref... @@ -78,7 +78,7 @@ target-folder: tricky folder target-folder: / /:files Con... /folders - > modified + > advanced modified /:files < modified /:files Ref... @@ -163,7 +163,7 @@ const expectedSortSpecsExampleA: { [key: string]: CustomSortSpec } = { type: CustomSortGroupType.ExactPrefix }, { foldersOnly: true, - order: CustomSortOrder.byModifiedTimeReverse, + order: CustomSortOrder.byModifiedTimeReverseAdvanced, type: CustomSortGroupType.Outsiders }, { filesOnly: true, diff --git a/src/custom-sort/sorting-spec-processor.ts b/src/custom-sort/sorting-spec-processor.ts index 3f0050d..146e835 100644 --- a/src/custom-sort/sorting-spec-processor.ts +++ b/src/custom-sort/sorting-spec-processor.ts @@ -86,6 +86,7 @@ const OrderLiterals: { [key: string]: CustomSortOrderAscDescPair } = { 'a-z': {asc: CustomSortOrder.alphabetical, desc: CustomSortOrder.alphabeticalReverse}, 'created': {asc: CustomSortOrder.byCreatedTime, desc: CustomSortOrder.byCreatedTimeReverse}, 'modified': {asc: CustomSortOrder.byModifiedTime, desc: CustomSortOrder.byModifiedTimeReverse}, + 'advanced modified': {asc: CustomSortOrder.byModifiedTimeAdvanced, desc: CustomSortOrder.byModifiedTimeReverseAdvanced}, // Advanced, for edge cases of secondary sorting, when if regexp match is the same, override the alphabetical sorting by full name 'a-z, created': { @@ -399,7 +400,7 @@ export class SortingSpecProcessor { lineIdx++ this.currentEntryLine = entryLine this.currentEntryLineIdx = lineIdx - this.currentSortingSpecContainerFilePath = `${folderPath}/${sortingSpecFileName}` + this.currentSortingSpecContainerFilePath = `${folderPath === '/' ? '' : folderPath}/${sortingSpecFileName}` this.problemAlreadyReportedForCurrentLine = false const trimmedEntryLine: string = entryLine.trim()