13 - Feature request: Sort by modification date, treating folder and files equally

- working solution - added new explicitly named sort method 'advanced modified' on top of 'modified'
- unit tests to be added before merge and release
This commit is contained in:
SebastianMC 2022-09-23 20:10:31 +02:00
parent 527df1a74f
commit 5aed85c0bd
4 changed files with 61 additions and 11 deletions

View File

@ -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

View File

@ -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<FolderItemForSorting> = (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);

View File

@ -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,

View File

@ -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()