Major improvement: added support for determining and applying sort order currently selected in Obsidian UI
- the meaning of CustomSortOrder.standardObsidian changes from a fixed one to what is actually selected in Obsidian UI - the CustomSortOrder.standardObsidian can be applied at a folder level (as the default for folder) and at a group level (this is a major addition) - added a mapping of Obsidian UI sorting methods onto internal plugin sorting methods, plus addition of the Obsidian UI logic to push folders to the top unconditionally - !!! NO NEW UNIT TESTS FOR THIS FEATURE - must add later - not tested manually, as the commits extraction and pushing is done as part of #88 github issue
This commit is contained in:
parent
16f5d61818
commit
45f5918598
|
@ -8,6 +8,7 @@ import {
|
||||||
matchGroupRegex,
|
matchGroupRegex,
|
||||||
sorterByMetadataField,
|
sorterByMetadataField,
|
||||||
SorterFn,
|
SorterFn,
|
||||||
|
getSorterFnFor,
|
||||||
Sorters
|
Sorters
|
||||||
} from './custom-sort';
|
} from './custom-sort';
|
||||||
import {CustomSortGroupType, CustomSortOrder, CustomSortSpec, RegExpSpec} from './custom-sort-types';
|
import {CustomSortGroupType, CustomSortOrder, CustomSortSpec, RegExpSpec} from './custom-sort-types';
|
||||||
|
|
|
@ -64,6 +64,8 @@ export interface FolderItemForSorting {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SorterFn = (a: FolderItemForSorting, b: FolderItemForSorting) => number
|
export type SorterFn = (a: FolderItemForSorting, b: FolderItemForSorting) => number
|
||||||
|
export type PlainSorterFn = (a: TAbstractFile, b: TAbstractFile) => number
|
||||||
|
export type PlainFileOnlySorterFn = (a: TFile, b: TFile) => number
|
||||||
export type CollatorCompareFn = (a: string, b: string) => number
|
export type CollatorCompareFn = (a: string, b: string) => number
|
||||||
|
|
||||||
// Syntax sugar
|
// Syntax sugar
|
||||||
|
@ -112,19 +114,83 @@ export let Sorters: { [key in CustomSortOrder]: SorterFn } = {
|
||||||
[CustomSortOrder.byMetadataFieldAlphabeticalReverse]: sorterByMetadataField(ReverseOrder),
|
[CustomSortOrder.byMetadataFieldAlphabeticalReverse]: sorterByMetadataField(ReverseOrder),
|
||||||
[CustomSortOrder.byMetadataFieldTrueAlphabeticalReverse]: sorterByMetadataField(ReverseOrder, TrueAlphabetical),
|
[CustomSortOrder.byMetadataFieldTrueAlphabeticalReverse]: sorterByMetadataField(ReverseOrder, TrueAlphabetical),
|
||||||
|
|
||||||
// This is a fallback entry which should not be used - the plugin code should refrain from custom sorting at all
|
// This is a fallback entry which should not be used - the getSorterFor() function below should protect against it
|
||||||
[CustomSortOrder.standardObsidian]: (a: FolderItemForSorting, b: FolderItemForSorting) => CollatorCompare(a.sortString, b.sortString),
|
[CustomSortOrder.standardObsidian]: (a: FolderItemForSorting, b: FolderItemForSorting) => CollatorCompare(a.sortString, b.sortString),
|
||||||
};
|
};
|
||||||
|
|
||||||
function compareTwoItems(itA: FolderItemForSorting, itB: FolderItemForSorting, sortSpec: CustomSortSpec) {
|
// OS - Obsidian Sort
|
||||||
|
const OS_alphabetical = 'alphabetical'
|
||||||
|
const OS_alphabeticalReverse = 'alphabeticalReverse'
|
||||||
|
const OS_byModifiedTime = 'byModifiedTime'
|
||||||
|
const OS_byModifiedTimeReverse = 'byModifiedTimeReverse'
|
||||||
|
const OS_byCreatedTime = 'byCreatedTime'
|
||||||
|
const OS_byCreatedTimeReverse = 'byCreatedTimeReverse'
|
||||||
|
|
||||||
|
export const ObsidianStandardDefaultSortingName = OS_alphabetical
|
||||||
|
|
||||||
|
const StandardObsidianToCustomSort: {[key: string]: CustomSortOrder} = {
|
||||||
|
[OS_alphabetical]: CustomSortOrder.alphabetical,
|
||||||
|
[OS_alphabeticalReverse]: CustomSortOrder.alphabeticalReverse,
|
||||||
|
[OS_byModifiedTime]: CustomSortOrder.byModifiedTimeReverse, // In Obsidian labeled as 'Modified time (new to old)'
|
||||||
|
[OS_byModifiedTimeReverse]: CustomSortOrder.byModifiedTime, // In Obsidian labeled as 'Modified time (old to new)'
|
||||||
|
[OS_byCreatedTime]: CustomSortOrder.byCreatedTimeReverse, // In Obsidian labeled as 'Created time (new to old)'
|
||||||
|
[OS_byCreatedTimeReverse]: CustomSortOrder.byCreatedTime // In Obsidian labeled as 'Created time (old to new)'
|
||||||
|
}
|
||||||
|
|
||||||
|
const StandardObsidianToPlainSortFn: {[key: string]: PlainFileOnlySorterFn} = {
|
||||||
|
[OS_alphabetical]: (a: TFile, b: TFile) => CollatorCompare(a.basename, b.basename),
|
||||||
|
[OS_alphabeticalReverse]: (a: TFile, b: TFile) => -StandardObsidianToPlainSortFn[OS_alphabetical](a,b),
|
||||||
|
[OS_byModifiedTime]: (a: TFile, b: TFile) => b.stat.mtime - a.stat.mtime,
|
||||||
|
[OS_byModifiedTimeReverse]: (a: TFile, b: TFile) => -StandardObsidianToPlainSortFn[OS_byModifiedTime](a,b),
|
||||||
|
[OS_byCreatedTime]: (a: TFile, b: TFile) => b.stat.ctime - a.stat.ctime,
|
||||||
|
[OS_byCreatedTimeReverse]: (a: TFile, b: TFile) => -StandardObsidianToPlainSortFn[OS_byCreatedTime](a,b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Standard Obsidian comparator keeps folders in the top sorted alphabetically
|
||||||
|
const StandardObsidianComparator = (order: CustomSortOrder): SorterFn => {
|
||||||
|
const customSorterFn = Sorters[order]
|
||||||
|
return (a: FolderItemForSorting, b: FolderItemForSorting): number => {
|
||||||
|
return a.isFolder || b.isFolder
|
||||||
|
?
|
||||||
|
(a.isFolder && !b.isFolder ? -1 : (b.isFolder && !a.isFolder ? 1 : Sorters[CustomSortOrder.alphabetical](a,b)))
|
||||||
|
:
|
||||||
|
customSorterFn(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equivalent of StandardObsidianComparator working directly on TAbstractFile items
|
||||||
|
export const StandardPlainObsidianComparator = (order: string): PlainSorterFn => {
|
||||||
|
const fileSorterFn = StandardObsidianToPlainSortFn[order] || StandardObsidianToCustomSort[OS_alphabetical]
|
||||||
|
return (a: TAbstractFile, b: TAbstractFile): number => {
|
||||||
|
const aIsFolder: boolean = a instanceof TFolder
|
||||||
|
const bIsFolder: boolean = b instanceof TFolder
|
||||||
|
return aIsFolder || bIsFolder
|
||||||
|
?
|
||||||
|
(aIsFolder && !bIsFolder ? -1 : (bIsFolder && !aIsFolder ? 1 : CollatorCompare(a.name,b.name)))
|
||||||
|
:
|
||||||
|
fileSorterFn(a as TFile, b as TFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getSorterFnFor = (sorting: CustomSortOrder, currentUIselectedSorting?: string): SorterFn => {
|
||||||
|
if (sorting === CustomSortOrder.standardObsidian) {
|
||||||
|
sorting = StandardObsidianToCustomSort[currentUIselectedSorting ?? 'alphabetical'] ?? CustomSortOrder.alphabetical
|
||||||
|
return StandardObsidianComparator(sorting)
|
||||||
|
} else {
|
||||||
|
return Sorters[sorting]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getComparator(sortSpec: CustomSortSpec, currentUIselectedSorting?: string): SorterFn {
|
||||||
|
const compareTwoItems = (itA: FolderItemForSorting, itB: FolderItemForSorting) => {
|
||||||
if (itA.groupIdx != undefined && itB.groupIdx != undefined) {
|
if (itA.groupIdx != undefined && itB.groupIdx != undefined) {
|
||||||
if (itA.groupIdx === itB.groupIdx) {
|
if (itA.groupIdx === itB.groupIdx) {
|
||||||
const group: CustomSortGroup | undefined = sortSpec.groups[itA.groupIdx]
|
const group: CustomSortGroup | undefined = sortSpec.groups[itA.groupIdx]
|
||||||
const matchingGroupPresentOnBothSidesAndEqual: boolean = itA.matchGroup !== undefined && itA.matchGroup === itB.matchGroup
|
const matchingGroupPresentOnBothSidesAndEqual: boolean = itA.matchGroup !== undefined && itA.matchGroup === itB.matchGroup
|
||||||
if (matchingGroupPresentOnBothSidesAndEqual && group.secondaryOrder) {
|
if (matchingGroupPresentOnBothSidesAndEqual && group.secondaryOrder) {
|
||||||
return Sorters[group.secondaryOrder ?? CustomSortOrder.default](itA, itB)
|
return getSorterFnFor(group.secondaryOrder ?? CustomSortOrder.default, currentUIselectedSorting)(itA, itB)
|
||||||
} else {
|
} else {
|
||||||
return Sorters[group?.order ?? CustomSortOrder.default](itA, itB)
|
return getSorterFnFor(group?.order ?? CustomSortOrder.default, currentUIselectedSorting)(itA, itB)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return itA.groupIdx - itB.groupIdx;
|
return itA.groupIdx - itB.groupIdx;
|
||||||
|
@ -133,9 +199,11 @@ function compareTwoItems(itA: FolderItemForSorting, itB: FolderItemForSorting, s
|
||||||
// should never happen - groupIdx is not known for at least one of items to compare.
|
// should never happen - groupIdx is not known for at least one of items to compare.
|
||||||
// The logic of determining the index always sets some idx
|
// The logic of determining the index always sets some idx
|
||||||
// Yet for sanity and to satisfy TS code analyzer a fallback to default behavior below
|
// Yet for sanity and to satisfy TS code analyzer a fallback to default behavior below
|
||||||
return Sorters[CustomSortOrder.default](itA, itB)
|
return getSorterFnFor(CustomSortOrder.default, currentUIselectedSorting)(itA, itB)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return compareTwoItems
|
||||||
|
}
|
||||||
|
|
||||||
const isFolder = (entry: TAbstractFile) => {
|
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
|
||||||
|
@ -270,7 +338,7 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
|
||||||
break
|
break
|
||||||
case CustomSortGroupType.StarredOnly:
|
case CustomSortGroupType.StarredOnly:
|
||||||
if (ctx?.starredPluginInstance) {
|
if (ctx?.starredPluginInstance) {
|
||||||
let starred: boolean = determineStarredStatusOf(entry, aFile, ctx.starredPluginInstance)
|
const starred: boolean = determineStarredStatusOf(entry, aFile, ctx.starredPluginInstance)
|
||||||
if (starred) {
|
if (starred) {
|
||||||
determined = true
|
determined = true
|
||||||
}
|
}
|
||||||
|
@ -458,9 +526,9 @@ export const folderSort = function (sortingSpec: CustomSortSpec, order: string[]
|
||||||
// Finally, for advanced sorting by modified date, for some folders the modified date has to be determined
|
// Finally, for advanced sorting by modified date, for some folders the modified date has to be determined
|
||||||
determineFolderDatesIfNeeded(folderItems, sortingSpec)
|
determineFolderDatesIfNeeded(folderItems, sortingSpec)
|
||||||
|
|
||||||
folderItems.sort(function (itA: FolderItemForSorting, itB: FolderItemForSorting) {
|
const comparator: SorterFn = getComparator(sortingSpec, fileExplorer.sortOrder)
|
||||||
return compareTwoItems(itA, itB, sortingSpec);
|
|
||||||
});
|
folderItems.sort(comparator)
|
||||||
|
|
||||||
const items = folderItems
|
const items = folderItems
|
||||||
.map((item: FolderItemForSorting) => fileExplorer.fileItems[item.path])
|
.map((item: FolderItemForSorting) => fileExplorer.fileItems[item.path])
|
||||||
|
|
|
@ -489,16 +489,23 @@ describe('SortingSpecProcessor', () => {
|
||||||
const txtInputStandardObsidianSortAttr: string = `
|
const txtInputStandardObsidianSortAttr: string = `
|
||||||
target-folder: AAA
|
target-folder: AAA
|
||||||
sorting: standard
|
sorting: standard
|
||||||
|
/ Some folder
|
||||||
|
sorting: standard
|
||||||
`
|
`
|
||||||
|
|
||||||
const expectedSortSpecForObsidianStandardSorting: { [key: string]: CustomSortSpec } = {
|
const expectedSortSpecForObsidianStandardSorting: { [key: string]: CustomSortSpec } = {
|
||||||
"AAA": {
|
"AAA": {
|
||||||
defaultOrder: CustomSortOrder.standardObsidian,
|
defaultOrder: CustomSortOrder.standardObsidian,
|
||||||
groups: [{
|
groups: [{
|
||||||
|
exactText: 'Some folder',
|
||||||
|
foldersOnly: true,
|
||||||
|
order: CustomSortOrder.standardObsidian,
|
||||||
|
type: CustomSortGroupType.ExactName
|
||||||
|
}, {
|
||||||
order: CustomSortOrder.standardObsidian,
|
order: CustomSortOrder.standardObsidian,
|
||||||
type: CustomSortGroupType.Outsiders
|
type: CustomSortGroupType.Outsiders
|
||||||
}],
|
}],
|
||||||
outsidersGroupIdx: 0,
|
outsidersGroupIdx: 1,
|
||||||
targetFoldersPaths: ['AAA']
|
targetFoldersPaths: ['AAA']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1649,11 +1656,13 @@ const txtInputErrorTooManyNumericSortSymbols: string = `
|
||||||
% Chapter\\R+ ... page\\d+
|
% Chapter\\R+ ... page\\d+
|
||||||
`
|
`
|
||||||
|
|
||||||
|
/* No longer applicable
|
||||||
const txtInputErrorNestedStandardObsidianSortAttr: string = `
|
const txtInputErrorNestedStandardObsidianSortAttr: string = `
|
||||||
target-folder: AAA
|
target-folder: AAA
|
||||||
/ Some folder
|
/ Some folder
|
||||||
sorting: standard
|
sorting: standard
|
||||||
`
|
`
|
||||||
|
*/
|
||||||
|
|
||||||
const txtInputErrorPriorityEmptyFilePattern: string = `
|
const txtInputErrorPriorityEmptyFilePattern: string = `
|
||||||
/!! /:
|
/!! /:
|
||||||
|
@ -1754,6 +1763,7 @@ describe('SortingSpecProcessor error detection and reporting', () => {
|
||||||
`${ERR_PREFIX} 9:TooManySortingSymbols Maximum one sorting symbol allowed per line ${ERR_SUFFIX_IN_LINE(2)}`)
|
`${ERR_PREFIX} 9:TooManySortingSymbols Maximum one sorting symbol allowed per line ${ERR_SUFFIX_IN_LINE(2)}`)
|
||||||
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT('% Chapter\\R+ ... page\\d+ '))
|
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT('% Chapter\\R+ ... page\\d+ '))
|
||||||
})
|
})
|
||||||
|
/* Problem no longer applicable
|
||||||
it('should recognize error: nested standard obsidian sorting attribute', () => {
|
it('should recognize error: nested standard obsidian sorting attribute', () => {
|
||||||
const inputTxtArr: Array<string> = txtInputErrorNestedStandardObsidianSortAttr.split('\n')
|
const inputTxtArr: Array<string> = txtInputErrorNestedStandardObsidianSortAttr.split('\n')
|
||||||
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
const result = processor.parseSortSpecFromText(inputTxtArr, 'mock-folder', 'custom-name-note.md')
|
||||||
|
@ -1763,6 +1773,7 @@ describe('SortingSpecProcessor error detection and reporting', () => {
|
||||||
`${ERR_PREFIX} 14:StandardObsidianSortAllowedOnlyAtFolderLevel The standard Obsidian sort order is only allowed at a folder level (not nested syntax) ${ERR_SUFFIX_IN_LINE(4)}`)
|
`${ERR_PREFIX} 14:StandardObsidianSortAllowedOnlyAtFolderLevel The standard Obsidian sort order is only allowed at a folder level (not nested syntax) ${ERR_SUFFIX_IN_LINE(4)}`)
|
||||||
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT(' sorting: standard'))
|
expect(errorsLogger).toHaveBeenNthCalledWith(2, ERR_LINE_TXT(' sorting: standard'))
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
it('should recognize error: priority indicator alone', () => {
|
it('should recognize error: priority indicator alone', () => {
|
||||||
const inputTxtArr: Array<string> = `
|
const inputTxtArr: Array<string> = `
|
||||||
/!
|
/!
|
||||||
|
|
|
@ -69,7 +69,7 @@ export enum ProblemCode {
|
||||||
ItemToHideExactNameWithExtRequired,
|
ItemToHideExactNameWithExtRequired,
|
||||||
ItemToHideNoSupportForThreeDots,
|
ItemToHideNoSupportForThreeDots,
|
||||||
DuplicateWildcardSortSpecForSameFolder,
|
DuplicateWildcardSortSpecForSameFolder,
|
||||||
StandardObsidianSortAllowedOnlyAtFolderLevel,
|
ProblemNoLongerApplicable_StandardObsidianSortAllowedOnlyAtFolderLevel, // Placeholder kept to avoid refactoring of many unit tests (hardcoded error codes)
|
||||||
PriorityNotAllowedOnOutsidersGroup,
|
PriorityNotAllowedOnOutsidersGroup,
|
||||||
TooManyPriorityPrefixes,
|
TooManyPriorityPrefixes,
|
||||||
CombiningNotAllowedOnOutsidersGroup,
|
CombiningNotAllowedOnOutsidersGroup,
|
||||||
|
@ -971,10 +971,6 @@ export class SortingSpecProcessor {
|
||||||
this.problem(ProblemCode.DuplicateOrderAttr, `Duplicate order specification for a sorting rule of folder ${folderPathsForProblemMsg}`)
|
this.problem(ProblemCode.DuplicateOrderAttr, `Duplicate order specification for a sorting rule of folder ${folderPathsForProblemMsg}`)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if ((attr.value as RecognizedOrderValue).order === CustomSortOrder.standardObsidian) {
|
|
||||||
this.problem(ProblemCode.StandardObsidianSortAllowedOnlyAtFolderLevel, `The standard Obsidian sort order is only allowed at a folder level (not nested syntax)`)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
this.ctx.currentSpecGroup.order = (attr.value as RecognizedOrderValue).order
|
this.ctx.currentSpecGroup.order = (attr.value as RecognizedOrderValue).order
|
||||||
this.ctx.currentSpecGroup.byMetadataField = (attr.value as RecognizedOrderValue).applyToMetadataField
|
this.ctx.currentSpecGroup.byMetadataField = (attr.value as RecognizedOrderValue).applyToMetadataField
|
||||||
this.ctx.currentSpecGroup.secondaryOrder = (attr.value as RecognizedOrderValue).secondaryOrder
|
this.ctx.currentSpecGroup.secondaryOrder = (attr.value as RecognizedOrderValue).secondaryOrder
|
||||||
|
|
36
src/main.ts
36
src/main.ts
|
@ -16,7 +16,9 @@ import {
|
||||||
Vault
|
Vault
|
||||||
} from 'obsidian';
|
} from 'obsidian';
|
||||||
import {around} from 'monkey-around';
|
import {around} from 'monkey-around';
|
||||||
import {folderSort} from './custom-sort/custom-sort';
|
import {
|
||||||
|
folderSort
|
||||||
|
} from './custom-sort/custom-sort';
|
||||||
import {SortingSpecProcessor, SortSpecsCollection} from './custom-sort/sorting-spec-processor';
|
import {SortingSpecProcessor, SortSpecsCollection} from './custom-sort/sorting-spec-processor';
|
||||||
import {CustomSortOrder, CustomSortSpec} from './custom-sort/custom-sort-types';
|
import {CustomSortOrder, CustomSortSpec} from './custom-sort/custom-sort-types';
|
||||||
|
|
||||||
|
@ -30,6 +32,8 @@ import {
|
||||||
ICON_SORT_SUSPENDED_SYNTAX_ERROR
|
ICON_SORT_SUSPENDED_SYNTAX_ERROR
|
||||||
} from "./custom-sort/icons";
|
} from "./custom-sort/icons";
|
||||||
|
|
||||||
|
import {lastPathComponent} from "./utils/utils";
|
||||||
|
|
||||||
interface CustomSortPluginSettings {
|
interface CustomSortPluginSettings {
|
||||||
additionalSortspecFile: string
|
additionalSortspecFile: string
|
||||||
suspended: boolean
|
suspended: boolean
|
||||||
|
@ -309,6 +313,18 @@ export default class CustomSortPlugin extends Plugin {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
determineSortSpecForFolder(folderPath: string, folderName?: string): CustomSortSpec|null|undefined {
|
||||||
|
folderName = folderName ?? lastPathComponent(folderPath)
|
||||||
|
let sortSpec: CustomSortSpec | null | undefined = this.sortSpecCache?.sortSpecByPath?.[folderPath]
|
||||||
|
sortSpec = sortSpec ?? this.sortSpecCache?.sortSpecByName?.[folderName]
|
||||||
|
|
||||||
|
if (!sortSpec && this.sortSpecCache?.sortSpecByWildcard) {
|
||||||
|
// when no sorting spec found directly by folder path, check for wildcard-based match
|
||||||
|
sortSpec = this.sortSpecCache?.sortSpecByWildcard.folderMatch(folderPath, folderName)
|
||||||
|
}
|
||||||
|
return sortSpec
|
||||||
|
}
|
||||||
|
|
||||||
// For the idea of monkey-patching credits go to https://github.com/nothingislost/obsidian-bartender
|
// For the idea of monkey-patching credits go to https://github.com/nothingislost/obsidian-bartender
|
||||||
patchFileExplorerFolder(patchableFileExplorer?: FileExplorerView): boolean {
|
patchFileExplorerFolder(patchableFileExplorer?: FileExplorerView): boolean {
|
||||||
let plugin = this;
|
let plugin = this;
|
||||||
|
@ -332,23 +348,10 @@ export default class CustomSortPlugin extends Plugin {
|
||||||
setIcon(plugin.ribbonIconEl, ICON_SORT_ENABLED_ACTIVE)
|
setIcon(plugin.ribbonIconEl, ICON_SORT_ENABLED_ACTIVE)
|
||||||
}
|
}
|
||||||
|
|
||||||
// if custom sort is not specified, use the UI-selected
|
|
||||||
const folder: TFolder = this.file
|
const folder: TFolder = this.file
|
||||||
let sortSpec: CustomSortSpec | null | undefined = plugin.sortSpecCache?.sortSpecByPath?.[folder.path]
|
let sortSpec: CustomSortSpec | null | undefined = plugin.determineSortSpecForFolder(folder.path, folder.name)
|
||||||
sortSpec = sortSpec ?? plugin.sortSpecCache?.sortSpecByName?.[folder.name]
|
|
||||||
if (sortSpec) {
|
if (sortSpec) {
|
||||||
if (sortSpec.defaultOrder === CustomSortOrder.standardObsidian) {
|
|
||||||
sortSpec = null // A folder is explicitly excluded from custom sorting plugin
|
|
||||||
}
|
|
||||||
} else if (plugin.sortSpecCache?.sortSpecByWildcard) {
|
|
||||||
// when no sorting spec found directly by folder path, check for wildcard-based match
|
|
||||||
sortSpec = plugin.sortSpecCache?.sortSpecByWildcard.folderMatch(folder.path, folder.name)
|
|
||||||
if (sortSpec?.defaultOrder === CustomSortOrder.standardObsidian) {
|
|
||||||
sortSpec = null // A folder is explicitly excluded from custom sorting plugin
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (sortSpec) {
|
|
||||||
sortSpec.plugin = plugin
|
|
||||||
return folderSort.call(this, sortSpec, ...args);
|
return folderSort.call(this, sortSpec, ...args);
|
||||||
} else {
|
} else {
|
||||||
return old.call(this, ...args);
|
return old.call(this, ...args);
|
||||||
|
@ -371,7 +374,6 @@ export default class CustomSortPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
onunload() {
|
onunload() {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatusBar() {
|
updateStatusBar() {
|
||||||
|
|
|
@ -53,5 +53,7 @@ declare module 'obsidian' {
|
||||||
createFolderDom(folder: TFolder): FileExplorerFolder;
|
createFolderDom(folder: TFolder): FileExplorerFolder;
|
||||||
|
|
||||||
requestSort(): void;
|
requestSort(): void;
|
||||||
|
|
||||||
|
sortOrder: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
import {lastPathComponent, extractParentFolderPath} from "./utils";
|
||||||
|
|
||||||
|
describe('lastPathComponent and extractParentFolderPath', () => {
|
||||||
|
it.each([
|
||||||
|
['a folder', '', 'a folder'],
|
||||||
|
['a/subfolder', 'a', 'subfolder'],
|
||||||
|
['parent/child', 'parent', 'child'],
|
||||||
|
['','',''],
|
||||||
|
[' ','',''],
|
||||||
|
['/strange', '', 'strange'],
|
||||||
|
['a/b/c/', 'a/b/c', ''],
|
||||||
|
['d d d/e e e/f f f/ggg ggg', 'd d d/e e e/f f f', 'ggg ggg'],
|
||||||
|
['/','',''],
|
||||||
|
[' / ','',''],
|
||||||
|
[' /','',''],
|
||||||
|
['/ ','','']
|
||||||
|
])('should from %s extract %s and %s', (path: string, parentPath: string, lastComponent: string) => {
|
||||||
|
const extractedParentPath: string = extractParentFolderPath(path)
|
||||||
|
const extractedLastComponent: string = lastPathComponent(path)
|
||||||
|
expect(extractedParentPath).toBe(parentPath)
|
||||||
|
expect(extractedLastComponent).toBe(lastComponent)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
|
@ -6,3 +6,13 @@ export function isDefined(o: any): boolean {
|
||||||
export function last<T>(o: Array<T>): T | undefined {
|
export function last<T>(o: Array<T>): T | undefined {
|
||||||
return o?.length > 0 ? o[o.length - 1] : undefined
|
return o?.length > 0 ? o[o.length - 1] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function lastPathComponent(path: string): string {
|
||||||
|
const lastPathSeparatorIdx = (path ?? '').lastIndexOf('/')
|
||||||
|
return lastPathSeparatorIdx >= 0 ? path.substring(lastPathSeparatorIdx + 1).trim() : path.trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
export function extractParentFolderPath(path: string): string {
|
||||||
|
const lastPathSeparatorIdx = (path ?? '').lastIndexOf('/')
|
||||||
|
return lastPathSeparatorIdx > 0 ? path.substring(0, lastPathSeparatorIdx).trim() : ''
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue