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:
parent
527df1a74f
commit
5aed85c0bd
|
@ -11,7 +11,9 @@ export enum CustomSortOrder {
|
||||||
alphabetical = 1, // = 1 to allow: if (customSortOrder) { ...
|
alphabetical = 1, // = 1 to allow: if (customSortOrder) { ...
|
||||||
alphabeticalReverse,
|
alphabeticalReverse,
|
||||||
byModifiedTime,
|
byModifiedTime,
|
||||||
|
byModifiedTimeAdvanced,
|
||||||
byModifiedTimeReverse,
|
byModifiedTimeReverse,
|
||||||
|
byModifiedTimeReverseAdvanced,
|
||||||
byCreatedTime,
|
byCreatedTime,
|
||||||
byCreatedTimeReverse,
|
byCreatedTimeReverse,
|
||||||
standardObsidian, // Let the folder sorting be in hands of Obsidian, whatever user selected in the UI
|
standardObsidian, // Let the folder sorting be in hands of Obsidian, whatever user selected in the UI
|
||||||
|
|
|
@ -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 {CustomSortGroup, CustomSortGroupType, CustomSortOrder, CustomSortSpec} from "./custom-sort-types";
|
||||||
import {isDefined} from "../utils/utils";
|
import {isDefined} from "../utils/utils";
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ interface FolderItemForSorting {
|
||||||
ctime: number
|
ctime: number
|
||||||
mtime: number
|
mtime: number
|
||||||
isFolder: boolean
|
isFolder: boolean
|
||||||
|
folder?: TFolder
|
||||||
}
|
}
|
||||||
|
|
||||||
type SorterFn = (a: FolderItemForSorting, b: FolderItemForSorting) => number
|
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.alphabetical]: (a: FolderItemForSorting, b: FolderItemForSorting) => Collator(a.sortString, b.sortString),
|
||||||
[CustomSortOrder.alphabeticalReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => Collator(b.sortString, a.sortString),
|
[CustomSortOrder.alphabeticalReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => Collator(b.sortString, a.sortString),
|
||||||
[CustomSortOrder.byModifiedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.mtime - b.mtime,
|
[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.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.byCreatedTime]: (a: FolderItemForSorting, b: FolderItemForSorting) => a.ctime - b.ctime,
|
||||||
[CustomSortOrder.byCreatedTimeReverse]: (a: FolderItemForSorting, b: FolderItemForSorting) => b.ctime - a.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
|
// The plain obvious 'entry instanceof TFolder' doesn't work inside Jest unit tests, hence a workaround below
|
||||||
return !!((entry as any).isRoot);
|
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 {
|
export const determineSortingGroup = function (entry: TFile | TFolder, spec: CustomSortSpec): FolderItemForSorting {
|
||||||
let groupIdx: number
|
let groupIdx: number
|
||||||
let determined: boolean = false
|
let determined: boolean = false
|
||||||
|
@ -165,14 +171,37 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
||||||
sortString: matchedGroup ? (matchedGroup + '//' + entry.name) : entry.name,
|
sortString: matchedGroup ? (matchedGroup + '//' + entry.name) : entry.name,
|
||||||
matchGroup: matchedGroup ?? undefined,
|
matchGroup: matchedGroup ?? undefined,
|
||||||
isFolder: aFolder,
|
isFolder: aFolder,
|
||||||
|
folder: aFolder ? (entry as TFolder) : undefined,
|
||||||
path: entry.path,
|
path: entry.path,
|
||||||
ctime: aFile ? entryAsTFile.stat.ctime : 0,
|
ctime: aFile ? entryAsTFile.stat.ctime : DEFAULT_FOLDER_CTIME,
|
||||||
mtime: aFile ? entryAsTFile.stat.mtime : 0
|
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[]) {
|
export const folderSort = function (sortingSpec: CustomSortSpec, order: string[]) {
|
||||||
let fileExplorer = this.fileExplorer
|
let fileExplorer = this.fileExplorer
|
||||||
|
const sortingGroupsCardinality: {[key: number]: number} = {}
|
||||||
|
|
||||||
const folderItems: Array<FolderItemForSorting> = (sortingSpec.itemsToHide ?
|
const folderItems: Array<FolderItemForSorting> = (sortingSpec.itemsToHide ?
|
||||||
this.file.children.filter((entry: TFile | TFolder) => {
|
this.file.children.filter((entry: TFile | TFolder) => {
|
||||||
|
@ -180,9 +209,27 @@ export const folderSort = function (sortingSpec: CustomSortSpec, order: string[]
|
||||||
})
|
})
|
||||||
:
|
:
|
||||||
this.file.children)
|
this.file.children)
|
||||||
.map((entry: TFile | TFolder) =>
|
.map((entry: TFile | TFolder) => {
|
||||||
determineSortingGroup(entry, sortingSpec)
|
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) {
|
folderItems.sort(function (itA: FolderItemForSorting, itB: FolderItemForSorting) {
|
||||||
return compareTwoItems(itA, itB, sortingSpec);
|
return compareTwoItems(itA, itB, sortingSpec);
|
||||||
|
|
|
@ -31,7 +31,7 @@ target-folder: tricky folder
|
||||||
target-folder: /
|
target-folder: /
|
||||||
/: Con...
|
/: Con...
|
||||||
/
|
/
|
||||||
> modified
|
> advanced modified
|
||||||
/:
|
/:
|
||||||
< modified
|
< modified
|
||||||
/: Ref...
|
/: Ref...
|
||||||
|
@ -78,7 +78,7 @@ target-folder: tricky folder
|
||||||
target-folder: /
|
target-folder: /
|
||||||
/:files Con...
|
/:files Con...
|
||||||
/folders
|
/folders
|
||||||
> modified
|
> advanced modified
|
||||||
/:files
|
/:files
|
||||||
< modified
|
< modified
|
||||||
/:files Ref...
|
/:files Ref...
|
||||||
|
@ -163,7 +163,7 @@ const expectedSortSpecsExampleA: { [key: string]: CustomSortSpec } = {
|
||||||
type: CustomSortGroupType.ExactPrefix
|
type: CustomSortGroupType.ExactPrefix
|
||||||
}, {
|
}, {
|
||||||
foldersOnly: true,
|
foldersOnly: true,
|
||||||
order: CustomSortOrder.byModifiedTimeReverse,
|
order: CustomSortOrder.byModifiedTimeReverseAdvanced,
|
||||||
type: CustomSortGroupType.Outsiders
|
type: CustomSortGroupType.Outsiders
|
||||||
}, {
|
}, {
|
||||||
filesOnly: true,
|
filesOnly: true,
|
||||||
|
|
|
@ -86,6 +86,7 @@ const OrderLiterals: { [key: string]: CustomSortOrderAscDescPair } = {
|
||||||
'a-z': {asc: CustomSortOrder.alphabetical, desc: CustomSortOrder.alphabeticalReverse},
|
'a-z': {asc: CustomSortOrder.alphabetical, desc: CustomSortOrder.alphabeticalReverse},
|
||||||
'created': {asc: CustomSortOrder.byCreatedTime, desc: CustomSortOrder.byCreatedTimeReverse},
|
'created': {asc: CustomSortOrder.byCreatedTime, desc: CustomSortOrder.byCreatedTimeReverse},
|
||||||
'modified': {asc: CustomSortOrder.byModifiedTime, desc: CustomSortOrder.byModifiedTimeReverse},
|
'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
|
// Advanced, for edge cases of secondary sorting, when if regexp match is the same, override the alphabetical sorting by full name
|
||||||
'a-z, created': {
|
'a-z, created': {
|
||||||
|
@ -399,7 +400,7 @@ export class SortingSpecProcessor {
|
||||||
lineIdx++
|
lineIdx++
|
||||||
this.currentEntryLine = entryLine
|
this.currentEntryLine = entryLine
|
||||||
this.currentEntryLineIdx = lineIdx
|
this.currentEntryLineIdx = lineIdx
|
||||||
this.currentSortingSpecContainerFilePath = `${folderPath}/${sortingSpecFileName}`
|
this.currentSortingSpecContainerFilePath = `${folderPath === '/' ? '' : folderPath}/${sortingSpecFileName}`
|
||||||
this.problemAlreadyReportedForCurrentLine = false
|
this.problemAlreadyReportedForCurrentLine = false
|
||||||
|
|
||||||
const trimmedEntryLine: string = entryLine.trim()
|
const trimmedEntryLine: string = entryLine.trim()
|
||||||
|
|
Loading…
Reference in New Issue