diff --git a/manifest.json b/manifest.json index 4e7528d..9cfa374 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "custom-sort", "name": "Custom File Explorer sorting", - "version": "1.7.2", + "version": "1.8.0", "minAppVersion": "0.15.0", "description": "Allows for manual and automatic, config-driven reordering and sorting of files and folders in File Explorer", "author": "SebastianMC", diff --git a/package.json b/package.json index b5df604..2d84c94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-custom-sort", - "version": "1.7.2", + "version": "1.8.0", "description": "Custom Sort plugin for Obsidian (https://obsidian.md)", "main": "main.js", "scripts": { diff --git a/src/custom-sort/custom-sort-types.ts b/src/custom-sort/custom-sort-types.ts index 21c232f..d4ce774 100644 --- a/src/custom-sort/custom-sort-types.ts +++ b/src/custom-sort/custom-sort-types.ts @@ -73,6 +73,8 @@ export interface CustomSortSpec { defaultOrder?: CustomSortOrder byMetadataField?: string // for 'by-metadata:' if the defaultOrder is by metadata alphabetical or reverse groups: Array + groupsShadow?: Array // A shallow copy of groups, used at applying sorting for items in a folder. + // Stores folder-specific values (e.g. macros expanded with folder-specific values) outsidersGroupIdx?: number outsidersFilesGroupIdx?: number outsidersFoldersGroupIdx?: number diff --git a/src/custom-sort/custom-sort.spec.ts b/src/custom-sort/custom-sort.spec.ts index 59e1a21..aa27043 100644 --- a/src/custom-sort/custom-sort.spec.ts +++ b/src/custom-sort/custom-sort.spec.ts @@ -707,6 +707,33 @@ describe('determineSortingGroup', () => { path: 'Some parent folder/References.md' }); }) + it('should consume shadow group instead of group, if shadow is present', () => { + // given + const file: TFile = mockTFile('gs-123', 'md', 111, MOCK_TIMESTAMP + 222, MOCK_TIMESTAMP + 333); + const sortSpec: CustomSortSpec = { + targetFoldersPaths: ['/'], + groups: [{ + type: CustomSortGroupType.ExactName, + exactText: 'g-123' + }], + groupsShadow: [{ + type: CustomSortGroupType.ExactName, + exactText: 'gs-123' + }] + } + // when + const result = determineSortingGroup(file, sortSpec) + + // then + expect(result).toEqual({ + groupIdx: 0, // This indicates match! + isFolder: false, + sortString: "gs-123.md", + ctime: MOCK_TIMESTAMP + 222, + mtime: MOCK_TIMESTAMP + 333, + path: 'Some parent folder/gs-123.md' + }); + }) }) describe('CustomSortGroupType.byMetadataFieldAlphabetical', () => { it('should ignore the file item if it has no direct metadata', () => { diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts index ac51f0c..afbe76e 100644 --- a/src/custom-sort/custom-sort.ts +++ b/src/custom-sort/custom-sort.ts @@ -23,7 +23,12 @@ import { NormalizerFn, RegExpSpec } from "./custom-sort-types"; -import {isDefined} from "../utils/utils"; +import { + isDefined +} from "../utils/utils"; +import { + expandMacros +} from "./macros"; import { BookmarksPluginInterface } from "../utils/BookmarksCorePluginSignature"; @@ -273,7 +278,7 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus for (let idx = 0; idx < numOfGroupsToCheck; idx++) { matchedGroup = null groupIdx = spec.priorityOrder ? spec.priorityOrder[idx] : idx - const group: CustomSortGroup = spec.groups[groupIdx]; + const group: CustomSortGroup = spec.groupsShadow ? spec.groupsShadow[groupIdx] : spec.groups[groupIdx]; if (group.foldersOnly && aFile) continue; if (group.filesOnly && aFolder) continue; const nameForMatching: string = group.matchFilenameWithExt ? entry.name : basename; @@ -555,6 +560,13 @@ export const determineBookmarksOrderIfNeeded = (folderItems: Array Object.assign({} as CustomSortGroup, group)) + + // expand folder-specific macros + const parentFolderName: string|undefined = this.file.name + expandMacros(sortingSpec, parentFolderName) + const folderItems: Array = (sortingSpec.itemsToHide ? this.file.children.filter((entry: TFile | TFolder) => { return !sortingSpec.itemsToHide!.has(entry.name) diff --git a/src/custom-sort/macros.spec.ts b/src/custom-sort/macros.spec.ts new file mode 100644 index 0000000..487fadf --- /dev/null +++ b/src/custom-sort/macros.spec.ts @@ -0,0 +1,99 @@ +import {expandMacros, expandMacrosInString} from "./macros"; +import * as MacrosModule from './macros' +import {CustomSortGroup, CustomSortSpec} from "./custom-sort-types"; + +describe('expandMacrosInString', () => { + it.each([ + ['', ''], + ['123', '123'], + [' 123 ', ' 123 '], + [' Abc{:%parent-folder-name%:}Def ', ' Abc{:%parent-folder-name%:}Def '], + ['{:%parent-folder-name%:}Def ', '{:%parent-folder-name%:}Def '], + [' Abc{:%parent-folder-name%:}', ' Abc{:%parent-folder-name%:}'], + [' {:%parent-folder-name%:} xyz {:%parent-folder-name%:}', ' {:%parent-folder-name%:} xyz {:%parent-folder-name%:}'], + [' {:%unknown%:} ',' {:%unknown%:} '] + ])('%s should transform to %s when no parent folder', (source: string, expanded: string) => { + const result1 = expandMacrosInString(source) + const result2 = expandMacrosInString(source, '') + expect(result1).toBe(expanded) + expect(result2).toBe(expanded) + }) + it.each([ + ['', ''], + ['123', '123'], + [' 123 ', ' 123 '], + [' Abc{:%parent-folder-name%:}Def ', ' AbcSubFolder 5Def '], + ['{:%parent-folder-name%:}Def ', 'SubFolder 5Def '], + [' Abc{:%parent-folder-name%:}', ' AbcSubFolder 5'], + [' {:%parent-folder-name%:} xyz {:%parent-folder-name%:}', ' SubFolder 5 xyz {:%parent-folder-name%:}'], + [' {:%unknown%:} ',' {:%unknown%:} '] + ])('%s should transform to %s when parent folder specified', (source: string, expanded: string) => { + const PARENT = 'SubFolder 5' + const result = expandMacrosInString(source, PARENT) + expect(result).toBe(expanded) + }) +}) + +function mockGroup(gprefix: string, group: string, prefix: string, full: string, suffix: string): CustomSortGroup { + const g: Partial = { + exactText: gprefix + group + full, + exactPrefix: gprefix + group + prefix, + exactSuffix: gprefix + group + suffix + } + return g as CustomSortGroup +} + +describe('expandMacros', () => { + it('should invoke expand in all relevant text fields on all groups', () => { + const sortSpec: Partial = { + groups: [ + mockGroup('g-', '1-', 'abc', 'def', 'ghi'), + mockGroup('g-', '2-', 'abc', 'def', 'ghi'), + ], + groupsShadow: [ + mockGroup('gs-', '1-', 'abc', 'def', 'ghi'), + mockGroup('gs-', '2-', 'abc', 'def', 'ghi'), + ] + } + const sp = jest.spyOn(MacrosModule, 'expandMacrosInString') + const ParentFolder = 'Parent folder name' + expandMacros(sortSpec as CustomSortSpec, ParentFolder) + expect(sp).toBeCalledTimes(6) + expect(sp).toHaveBeenNthCalledWith(1, 'gs-1-def', ParentFolder) + expect(sp).toHaveBeenNthCalledWith(2, 'gs-1-abc', ParentFolder) + expect(sp).toHaveBeenNthCalledWith(3, 'gs-1-ghi', ParentFolder) + expect(sp).toHaveBeenNthCalledWith(4, 'gs-2-def', ParentFolder) + expect(sp).toHaveBeenNthCalledWith(5, 'gs-2-abc', ParentFolder) + expect(sp).toHaveBeenNthCalledWith(6, 'gs-2-ghi', ParentFolder) + }) + it('should expand correctly in all relevant text fields on all groups, based on shadow groups', () => { + const sortSpec: Partial = { + groups: [ + mockGroup('g-', '1-', 'abc{:%parent-folder-name%:}', 'de{:%parent-folder-name%:}f', '{:%parent-folder-name%:}ghi'), + mockGroup('g-', '2-', '{:%parent-folder-name%:}abc', 'd{:%parent-folder-name%:}ef', 'ghi{:%parent-folder-name%:}'), + ], + groupsShadow: [ + mockGroup('gs-', '1-', 'abc{:%parent-folder-name%:}', 'de{:%parent-folder-name%:}f', '{:%parent-folder-name%:}ghi'), + mockGroup('gs-', '2-', '{:%parent-folder-name%:}abc', 'd{:%parent-folder-name%:}ef', 'ghi{:%parent-folder-name%:}'), + ] + } + const originalSortSpec: Partial = { + groups: [...sortSpec.groups!], + groupsShadow: [...sortSpec.groupsShadow!] + } + const ParentFolder = 'Parent folder name' + expandMacros(sortSpec as CustomSortSpec, ParentFolder) + expect(sortSpec.groups![0].exactText).toBe(originalSortSpec.groups![0].exactText) + expect(sortSpec.groups![0].exactPrefix).toBe(originalSortSpec.groups![0].exactPrefix) + expect(sortSpec.groups![0].exactSuffix).toBe(originalSortSpec.groups![0].exactSuffix) + expect(sortSpec.groups![1].exactText).toBe(originalSortSpec.groups![1].exactText) + expect(sortSpec.groups![1].exactPrefix).toBe(originalSortSpec.groups![1].exactPrefix) + expect(sortSpec.groups![1].exactSuffix).toBe(originalSortSpec.groups![1].exactSuffix) + expect(sortSpec.groupsShadow![0].exactText).toBe('gs-1-deParent folder namef') + expect(sortSpec.groupsShadow![0].exactPrefix).toBe('gs-1-abcParent folder name') + expect(sortSpec.groupsShadow![0].exactSuffix).toBe('gs-1-Parent folder nameghi') + expect(sortSpec.groupsShadow![1].exactText).toBe('gs-2-dParent folder nameef') + expect(sortSpec.groupsShadow![1].exactPrefix).toBe('gs-2-Parent folder nameabc') + expect(sortSpec.groupsShadow![1].exactSuffix).toBe('gs-2-ghiParent folder name') + }) +}) diff --git a/src/custom-sort/macros.ts b/src/custom-sort/macros.ts new file mode 100644 index 0000000..917d46b --- /dev/null +++ b/src/custom-sort/macros.ts @@ -0,0 +1,34 @@ +import { + CustomSortSpec +} from "./custom-sort-types"; + +const MACRO_PREFIX: string = '{:' +const MACRO_SUFFIX: string = ':}' + +const PARENT_FOLDER_NAME_PLACEHOLDER: string = '%parent-folder-name%' + +const PARENT_FOLDER_NAME_MACRO: string = MACRO_PREFIX + PARENT_FOLDER_NAME_PLACEHOLDER + MACRO_SUFFIX + +export const expandMacrosInString = function(source: string|undefined, parentFolderName?: string|undefined): string|undefined { + if (source && parentFolderName) { + return source.replace(PARENT_FOLDER_NAME_MACRO, parentFolderName) + } else { + return source + } +} + +export const expandMacros = function(sortingSpec: CustomSortSpec, parentFolderName: string|undefined) { + sortingSpec.groupsShadow?.forEach((shadowGroup) => { + if (parentFolderName) { // root has no parent folder, ignore relevant macros for the root + if (shadowGroup.exactText) { + shadowGroup.exactText = expandMacrosInString(shadowGroup.exactText, parentFolderName) + } + if (shadowGroup.exactPrefix) { + shadowGroup.exactPrefix = expandMacrosInString(shadowGroup.exactPrefix, parentFolderName) + } + if (shadowGroup.exactSuffix) { + shadowGroup.exactSuffix = expandMacrosInString(shadowGroup.exactSuffix, parentFolderName) + } + } + }) +} diff --git a/versions.json b/versions.json index 5a1d335..1d43fb6 100644 --- a/versions.json +++ b/versions.json @@ -28,4 +28,5 @@ "1.7.0": "0.15.0", "1.7.1": "0.15.0", "1.7.2": "0.15.0" + "1.8.0": "0.15.0" }