diff --git a/README.md b/README.md index a10a9b4..1912981 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ > | :exclamation: Breaking changes in Obsidian - always use the newest version of the plugin| > |----------------------------------------------| +> | - Obsidian 1.7.2 - no fixes for Obsidian breaking changes available --> [More details](README.notice.for.Obsidian.1.7.2.md)| > | - Obsidian 1.6.3 - update the plugin to 2.1.11 or newer --> [More details](README.notice.for.Obsidian.1.6.3.md)| > | - Obsidian 1.6.0 - update the plugin to 2.1.9 or newer --> [More details](README.notice.for.Obsidian.1.6.0.md)| > | - Obsidian 1.5.4 - update the plugin to 2.1.7 or newer --> [More details](README.notice.for.Obsidian.1.5.4.md)| diff --git a/README.notice.for.Obsidian.1.7.2.md b/README.notice.for.Obsidian.1.7.2.md new file mode 100644 index 0000000..fd81cdc --- /dev/null +++ b/README.notice.for.Obsidian.1.7.2.md @@ -0,0 +1,20 @@ +> | :exclamation: Breaking changes in Obsidian 1.7.2 (and newer) - no fixes available| +> |----------------------------------------------| +> +> Obsidian team introduced some more breaking changes in a not-backward-compatible way starting from Obsidian 1.7.2. +> +> The observed issues are minor, no fixes available (a work in progress with unknown release date): +> - the custom sort plugin is **unable to automatically apply the custom sort on start** +> - prevalent on mobile +> - for the _Lazy Plugin Loader_ plugin occurs by definition +> - **workaround**: custom sorting has to be applied manually via the ribbon icon or the command 'sort on' +> - the custom sort plugin **keeps showing the notifications** with each change to any note +> - **workaround**: disable the notifications +> +> For more details of the observed misbehaviors you can go to: +> - [#161: Find out how to automatically apply custom sort on app start / vault (re)load etc.](https://github.com/SebastianMC/obsidian-custom-sort/issues/161) +> - [#162: \[bug\]\[minor\] Obsidian 1.7.2 breaking changes - when File Explorer is not displayed an attempt to apply custom sort fails with error](https://github.com/SebastianMC/obsidian-custom-sort/issues/162) +> - [#163: Obsidian 1.7.2 - automatic sorting fails when launching Obsidian](https://github.com/SebastianMC/obsidian-custom-sort/issues/163) +> - [#165: Obsidian 1.7.3 - Constant Notifications "Custom sorting ON" and "Parsing custom sorting specification SUCCEEDED!"](https://github.com/SebastianMC/obsidian-custom-sort/issues/165) +> - [#169: Not working on mobile](https://github.com/SebastianMC/obsidian-custom-sort/issues/169) +> diff --git a/manifest.json b/manifest.json index a90d7fa..c41d9d2 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "custom-sort", "name": "Custom File Explorer sorting", - "version": "2.1.14-beta-171", + "version": "2.1.15-", "minAppVersion": "0.16.2", "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 6c79c4b..76cc5a0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-custom-sort", - "version": "2.1.14", + "version": "2.1.15", "description": "Custom Sort plugin for Obsidian (https://obsidian.md)", "main": "main.js", "scripts": { diff --git a/src/custom-sort/custom-sort.ts b/src/custom-sort/custom-sort.ts index 6da64a2..69edfe7 100644 --- a/src/custom-sort/custom-sort.ts +++ b/src/custom-sort/custom-sort.ts @@ -241,7 +241,7 @@ const SortersForDerivedSecondary: { [key in CustomSortOrder]?: SorterFn } = { }; // OS - Obsidian Sort -const OS_alphabetical = 'alphabetical' +export const OS_alphabetical = 'alphabetical' const OS_alphabeticalReverse = 'alphabeticalReverse' export const OS_byModifiedTime = 'byModifiedTime' export const OS_byModifiedTimeReverse = 'byModifiedTimeReverse' @@ -812,7 +812,7 @@ const folderSortCore = function (sortedFolder: TFolder, sortOrder: string, sorti }; // Returns a sorted copy of the input array, intentionally to keep it intact -export const sortFolderItemsForBookmarking = function (folder: TFolder, items: Array, sortingSpec: CustomSortSpec|null|undefined, ctx: ProcessingContext, uiSortOrder: string): Array { +export const sortFolderItems = function (folder: TFolder, items: Array, sortingSpec: CustomSortSpec|null|undefined, ctx: ProcessingContext, uiSortOrder: string): Array { if (sortingSpec) { const folderItemsByPath: { [key: string]: TAbstractFile } = {} @@ -849,6 +849,9 @@ export const sortFolderItemsForBookmarking = function (folder: TFolder, items: A } }; +// Exported legacy function name for backward compatibility +export const sortFolderItemsForBookmarking = sortFolderItems + export const _unitTests = { fileGoesFirstWhenSameBasenameAsFolder: fileGoesFirstWhenSameBasenameAsFolder, folderGoesFirstWhenSameBasenameAsFolder: folderGoesFirstWhenSameBasenameAsFolder diff --git a/src/custom-sort/matchers.ts b/src/custom-sort/matchers.ts index 6fda71e..31504a9 100644 --- a/src/custom-sort/matchers.ts +++ b/src/custom-sort/matchers.ts @@ -1,5 +1,3 @@ -import {toString} from "builtin-modules"; - export const RomanNumberRegexStr: string = ' *([MDCLXVI]+)'; // Roman number export const CompoundRomanNumberDotRegexStr: string = ' *([MDCLXVI]+(?:\\.[MDCLXVI]+)*)';// Compound Roman number with dot as separator export const CompoundRomanNumberDashRegexStr: string = ' *([MDCLXVI]+(?:-[MDCLXVI]+)*)'; // Compound Roman number with dash as separator @@ -9,6 +7,7 @@ export const CompoundNumberDotRegexStr: string = ' *(\\d+(?:\\.\\d+)*)'; // Comp export const CompoundNumberDashRegexStr: string = ' *(\\d+(?:-\\d+)*)'; // Compound number with dash as separator export const Date_dd_Mmm_yyyy_RegexStr: string = ' *([0-3]*[0-9]-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\\d{4})'; // Date like 01-Jan-2020 +export const Date_Mmm_dd_yyyy_RegexStr: string = ' *((?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-[0-3]*[0-9]-\\d{4})'; // Date like Jan-01-2020 export const DOT_SEPARATOR = '.' export const DASH_SEPARATOR = '-' @@ -123,16 +122,4 @@ export function getNormalizedDate_NormalizerFn_for(separator: string, dayIdx: nu } export const getNormalizedDate_dd_Mmm_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for('-', 0, 1, 2, MONTHS) - -/* -// Assumption - the regex date matched against input s, no extensive defensive coding needed -const components = s.split('-') -const day = prependWithZeros(components[0], DAY_POSITIONS) -const month = prependWithZeros( `${1 + MONTHS.indexOf(components[1])}`, MONTH_POSITIONS) -const year = prependWithZeros(components[2], YEAR_POSITIONS) -return `${year}-${month}-${day}//` - - */ - - - +export const getNormalizedDate_Mmm_dd_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for('-', 1, 0, 2, MONTHS) diff --git a/src/custom-sort/sorting-spec-processor.ts b/src/custom-sort/sorting-spec-processor.ts index 591fea5..85460db 100644 --- a/src/custom-sort/sorting-spec-processor.ts +++ b/src/custom-sort/sorting-spec-processor.ts @@ -17,8 +17,10 @@ import { CompoundRomanNumberDotRegexStr, DASH_SEPARATOR, Date_dd_Mmm_yyyy_RegexStr, + Date_Mmm_dd_yyyy_RegexStr, DOT_SEPARATOR, getNormalizedDate_dd_Mmm_yyyy_NormalizerFn, + getNormalizedDate_Mmm_dd_yyyy_NormalizerFn, getNormalizedNumber, getNormalizedRomanNumber, NumberRegexStr, @@ -358,6 +360,7 @@ const InlineRegexSymbol_Digit2: string = '\\[0-9]' const InlineRegexSymbol_0_to_3: string = '\\[0-3]' const Date_dd_Mmm_yyyy_RegexSymbol: string = '\\[dd-Mmm-yyyy]' +const Date_Mmm_dd_yyyy_RegexSymbol: string = '\\[Mmm-dd-yyyy]' const InlineRegexSymbol_CapitalLetter: string = '\\C' const InlineRegexSymbol_LowercaseLetter: string = '\\l' @@ -377,7 +380,8 @@ const sortingSymbolsArr: Array = [ escapeRegexUnsafeCharacters(CompoundRomanNumberDashRegexSymbol), escapeRegexUnsafeCharacters(WordInASCIIRegexSymbol), escapeRegexUnsafeCharacters(WordInAnyLanguageRegexSymbol), - escapeRegexUnsafeCharacters(Date_dd_Mmm_yyyy_RegexSymbol) + escapeRegexUnsafeCharacters(Date_dd_Mmm_yyyy_RegexSymbol), + escapeRegexUnsafeCharacters(Date_Mmm_dd_yyyy_RegexSymbol) ] const sortingSymbolsRegex = new RegExp(sortingSymbolsArr.join('|'), 'gi') @@ -446,6 +450,7 @@ export const NumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumb export const CompoundDotNumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumber(s, DOT_SEPARATOR) export const CompoundDashNumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumber(s, DASH_SEPARATOR) export const Date_dd_Mmm_yyyy_NormalizerFn: NormalizerFn = (s: string) => getNormalizedDate_dd_Mmm_yyyy_NormalizerFn(s) +export const Date_Mmm_dd_yyyy_NormalizerFn: NormalizerFn = (s: string) => getNormalizedDate_Mmm_dd_yyyy_NormalizerFn(s) export enum AdvancedRegexType { None, // to allow if (advancedRegex) @@ -457,7 +462,8 @@ export enum AdvancedRegexType { CompoundDashRomanNumber, WordInASCII, WordInAnyLanguage, - Date_dd_Mmm_yyyy + Date_dd_Mmm_yyyy, + Date_Mmm_dd_yyyy } const sortingSymbolToRegexpStr: { [key: string]: RegExpSpecStr } = { @@ -506,6 +512,11 @@ const sortingSymbolToRegexpStr: { [key: string]: RegExpSpecStr } = { regexpStr: Date_dd_Mmm_yyyy_RegexStr, normalizerFn: Date_dd_Mmm_yyyy_NormalizerFn, advancedRegexType: AdvancedRegexType.Date_dd_Mmm_yyyy + }, + [Date_Mmm_dd_yyyy_RegexSymbol]: { // Intentionally retain character case + regexpStr: Date_Mmm_dd_yyyy_RegexStr, + normalizerFn: Date_Mmm_dd_yyyy_NormalizerFn, + advancedRegexType: AdvancedRegexType.Date_Mmm_dd_yyyy } } diff --git a/src/test/int/dates-in-names.int.test.ts b/src/test/int/dates-in-names.int.test.ts new file mode 100644 index 0000000..66487e5 --- /dev/null +++ b/src/test/int/dates-in-names.int.test.ts @@ -0,0 +1,64 @@ +import { + TAbstractFile, + TFolder, + Vault +} from "obsidian"; +import { + DEFAULT_FOLDER_CTIME, + determineFolderDatesIfNeeded, + determineSortingGroup, + FolderItemForSorting, OS_alphabetical, OS_byCreatedTime, ProcessingContext, sortFolderItems +} from "../../custom-sort/custom-sort"; +import { + CustomSortGroupType, + CustomSortOrder, + CustomSortSpec +} from "../../custom-sort/custom-sort-types"; +import { + TIMESTAMP_OLDEST, + TIMESTAMP_NEWEST, + mockTFolderWithChildren, + mockTFolderWithDateNamedChildren, + TIMESTAMP_DEEP_NEWEST, + TIMESTAMP_DEEP_OLDEST, +} from "../mocks"; +import { + SortingSpecProcessor +} from "../../custom-sort/sorting-spec-processor"; + +describe('sortFolderItems', () => { + it('should correctly handle Mmm-dd-yyyy pattern in file names', () => { + // given + const processor: SortingSpecProcessor = new SortingSpecProcessor() + const sortSpecTxt = +` ... \\[Mmm-dd-yyyy] + > a-z +` + const PARENT_PATH = 'parent/folder/path' + const sortSpecsCollection = processor.parseSortSpecFromText( + sortSpecTxt.split('\n'), + PARENT_PATH, + 'file name with the sorting, irrelevant here' + ) + + const folder: TFolder = mockTFolderWithDateNamedChildren(PARENT_PATH) + const sortSpec: CustomSortSpec = sortSpecsCollection?.sortSpecByPath![PARENT_PATH]! + + const ctx: ProcessingContext = {} + + // when + const result: Array = sortFolderItems(folder, folder.children, sortSpec, ctx, OS_alphabetical) + + // then + const orderedNames = result.map(f => f.name) + expect(orderedNames).toEqual([ + 'CCC Feb-28-2025', + 'BBB Dec-23-2024.md', + 'DDD Jul-15-2024.md', + 'AAA Jan-01-2012' + ]) + }) +}) + + + diff --git a/src/test/mocks.ts b/src/test/mocks.ts index 4b86251..a5853ef 100644 --- a/src/test/mocks.ts +++ b/src/test/mocks.ts @@ -3,6 +3,9 @@ import { TFolder, Vault } from "obsidian"; +import { + lastPathComponent +} from "../utils/utils"; export const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => { return { @@ -25,7 +28,7 @@ export const mockTFolder = (name: string, children?: Array, paren isRoot(): boolean { return name === '/' }, vault: {} as Vault, // To satisfy TS typechecking path: `${name}`, - name: name, + name: lastPathComponent(name), parent: parent ?? ({} as TFolder), // To satisfy TS typechecking children: children ?? [] } @@ -53,3 +56,12 @@ export const mockTFolderWithChildren = (name: string): TFolder => { return mockTFolder(name, [child1, child2, child3, child4, child5, subfolder1, subfolder2]) } + +export const mockTFolderWithDateNamedChildren = (name: string): TFolder => { + const child1: TFolder = mockTFolder('AAA Jan-01-2012') + const child2: TFile = mockTFile('BBB Dec-23-2024', 'md') + const child3: TFolder = mockTFolder('CCC Feb-28-2025') + const child4: TFile = mockTFile('DDD Jul-15-2024', 'md') + + return mockTFolder(name, [child1, child2, child3, child4]) +} diff --git a/src/test/unit/sorting-spec-processor.spec.ts b/src/test/unit/sorting-spec-processor.spec.ts index 922affb..44e8e2e 100644 --- a/src/test/unit/sorting-spec-processor.spec.ts +++ b/src/test/unit/sorting-spec-processor.spec.ts @@ -6,6 +6,7 @@ import { consumeFolderByRegexpExpression, convertPlainStringToRegex, Date_dd_Mmm_yyyy_NormalizerFn, + Date_Mmm_dd_yyyy_NormalizerFn, detectSortingSymbols, escapeRegexUnsafeCharacters, extractSortingSymbol, @@ -363,17 +364,6 @@ const expectedSortSpecsExampleA: { [key: string]: CustomSortSpec } = { } } -const txtInputExampleSortingSymbols: string = ` -/folders Chapter \\.d+ ... -/:files ...section \\-r+. -% Appendix \\-d+ (attachments) -Plain syntax\\R+ ... works? -And this kind of... \\D+plain syntax??? -Here goes ASCII word \\a+ -\\A+. is for any modern language word -\\[dd-Mmm-yyyy] for the specific date format of 12-Apr-2024 -` - const expectedSortSpecsExampleSortingSymbols: { [key: string]: CustomSortSpec } = { "mock-folder": { groups: [{ @@ -428,14 +418,32 @@ const expectedSortSpecsExampleSortingSymbols: { [key: string]: CustomSortSpec } regex: /^ *([0-3]*[0-9]-(?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-\d{4}) for the specific date format of 12\-Apr\-2024$/i, normalizerFn: Date_dd_Mmm_yyyy_NormalizerFn } + }, { + type: CustomSortGroupType.ExactName, + regexPrefix: { + regex: /^ *((?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-[0-3]*[0-9]-\d{4}) for the specific date format of Apr\-01\-2024$/i, + normalizerFn: Date_Mmm_dd_yyyy_NormalizerFn + } }, { type: CustomSortGroupType.Outsiders }], targetFoldersPaths: ['mock-folder'], - outsidersGroupIdx: 8 + outsidersGroupIdx: 9 } } +const txtInputExampleSortingSymbols: string = ` +/folders Chapter \\.d+ ... +/:files ...section \\-r+. +% Appendix \\-d+ (attachments) +Plain syntax\\R+ ... works? +And this kind of... \\D+plain syntax??? +Here goes ASCII word \\a+ +\\A+. is for any modern language word +\\[dd-Mmm-yyyy] for the specific date format of 12-Apr-2024 +\\[Mmm-dd-yyyy] for the specific date format of Apr-01-2024 +` + const txtInputExampleMDataExtractors1: string = ` < a-z by-metadata: created-by using-extractor: date(dd/mm/yyyy) /folders Chapter... diff --git a/versions.json b/versions.json index e4c0478..69c703f 100644 --- a/versions.json +++ b/versions.json @@ -47,5 +47,6 @@ "2.1.11": "0.16.2", "2.1.12": "0.16.2", "2.1.13": "0.16.2", - "2.1.14": "0.16.2" + "2.1.14": "0.16.2", + "2.1.15": "0.16.2" }