Skip to content

Commit a0bd47b

Browse files
committed
#184 - Sort notes in Mmm-dd-yyyy format
1 parent 008f6c0 commit a0bd47b

File tree

6 files changed

+123
-19
lines changed

6 files changed

+123
-19
lines changed

src/custom-sort/custom-sort.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ const SortersForDerivedSecondary: { [key in CustomSortOrder]?: SorterFn } = {
240240
};
241241

242242
// OS - Obsidian Sort
243-
const OS_alphabetical = 'alphabetical'
243+
export const OS_alphabetical = 'alphabetical'
244244
const OS_alphabeticalReverse = 'alphabeticalReverse'
245245
export const OS_byModifiedTime = 'byModifiedTime'
246246
export const OS_byModifiedTimeReverse = 'byModifiedTimeReverse'
@@ -794,7 +794,7 @@ const folderSortCore = function (sortedFolder: TFolder, sortOrder: string, sorti
794794
};
795795

796796
// Returns a sorted copy of the input array, intentionally to keep it intact
797-
export const sortFolderItemsForBookmarking = function (folder: TFolder, items: Array<TAbstractFile>, sortingSpec: CustomSortSpec|null|undefined, ctx: ProcessingContext, uiSortOrder: string): Array<TAbstractFile> {
797+
export const sortFolderItems = function (folder: TFolder, items: Array<TAbstractFile>, sortingSpec: CustomSortSpec|null|undefined, ctx: ProcessingContext, uiSortOrder: string): Array<TAbstractFile> {
798798
if (sortingSpec) {
799799
const folderItemsByPath: { [key: string]: TAbstractFile } = {}
800800

@@ -831,6 +831,9 @@ export const sortFolderItemsForBookmarking = function (folder: TFolder, items: A
831831
}
832832
};
833833

834+
// Exported legacy function name for backward compatibility
835+
export const sortFolderItemsForBookmarking = sortFolderItems
836+
834837
export const _unitTests = {
835838
fileGoesFirstWhenSameBasenameAsFolder: fileGoesFirstWhenSameBasenameAsFolder,
836839
folderGoesFirstWhenSameBasenameAsFolder: folderGoesFirstWhenSameBasenameAsFolder

src/custom-sort/matchers.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import {toString} from "builtin-modules";
2-
31
export const RomanNumberRegexStr: string = ' *([MDCLXVI]+)'; // Roman number
42
export const CompoundRomanNumberDotRegexStr: string = ' *([MDCLXVI]+(?:\\.[MDCLXVI]+)*)';// Compound Roman number with dot as separator
53
export const CompoundRomanNumberDashRegexStr: string = ' *([MDCLXVI]+(?:-[MDCLXVI]+)*)'; // Compound Roman number with dash as separator
@@ -9,6 +7,7 @@ export const CompoundNumberDotRegexStr: string = ' *(\\d+(?:\\.\\d+)*)'; // Comp
97
export const CompoundNumberDashRegexStr: string = ' *(\\d+(?:-\\d+)*)'; // Compound number with dash as separator
108

119
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
10+
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
1211

1312
export const DOT_SEPARATOR = '.'
1413
export const DASH_SEPARATOR = '-'
@@ -104,17 +103,23 @@ export function getNormalizedRomanNumber(s: string, separator?: string, places?:
104103
}
105104
}
106105

107-
const DAY_POSITIONS = '00'.length
108-
const MONTH_POSITIONS = '00'.length
109-
const YEAR_POSITIONS = '0000'.length
106+
export const DAY_POSITIONS = '00'.length
107+
export const MONTH_POSITIONS = '00'.length
108+
export const YEAR_POSITIONS = '0000'.length
110109

111110
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
112111

113-
export function getNormalizedDate_dd_Mmm_yyyy_NormalizerFn(s: string): string | null {
114-
// Assumption - the regex date matched against input s, no extensive defensive coding needed
115-
const components = s.split('-')
116-
const day = prependWithZeros(components[0], DAY_POSITIONS)
117-
const month = prependWithZeros( `${1 + MONTHS.indexOf(components[1])}`, MONTH_POSITIONS)
118-
const year = prependWithZeros(components[2], YEAR_POSITIONS)
119-
return `${year}-${month}-${day}//`
112+
export function getNormalizedDate_NormalizerFn_for(separator: string, dayIdx: number, monthIdx: number, yearIdx: number, months?: string[]) {
113+
return (s: string): string | null => {
114+
// Assumption - the regex date matched against input s, no extensive defensive coding needed
115+
const components = s.split(separator)
116+
const day = prependWithZeros(components[dayIdx], DAY_POSITIONS)
117+
const monthValue = months ? `${1 + MONTHS.indexOf(components[monthIdx])}` : components[monthIdx]
118+
const month = prependWithZeros(monthValue, MONTH_POSITIONS)
119+
const year = prependWithZeros(components[yearIdx], YEAR_POSITIONS)
120+
return `${year}-${month}-${day}//`
121+
}
120122
}
123+
124+
export const getNormalizedDate_dd_Mmm_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for('-', 0, 1, 2, MONTHS)
125+
export const getNormalizedDate_Mmm_dd_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for('-', 1, 0, 2, MONTHS)

src/custom-sort/sorting-spec-processor.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import {
1717
CompoundRomanNumberDotRegexStr,
1818
DASH_SEPARATOR,
1919
Date_dd_Mmm_yyyy_RegexStr,
20+
Date_Mmm_dd_yyyy_RegexStr,
2021
DOT_SEPARATOR,
2122
getNormalizedDate_dd_Mmm_yyyy_NormalizerFn,
23+
getNormalizedDate_Mmm_dd_yyyy_NormalizerFn,
2224
getNormalizedNumber,
2325
getNormalizedRomanNumber,
2426
NumberRegexStr,
@@ -351,6 +353,7 @@ const InlineRegexSymbol_Digit2: string = '\\[0-9]'
351353
const InlineRegexSymbol_0_to_3: string = '\\[0-3]'
352354

353355
const Date_dd_Mmm_yyyy_RegexSymbol: string = '\\[dd-Mmm-yyyy]'
356+
const Date_Mmm_dd_yyyy_RegexSymbol: string = '\\[Mmm-dd-yyyy]'
354357

355358
const InlineRegexSymbol_CapitalLetter: string = '\\C'
356359
const InlineRegexSymbol_LowercaseLetter: string = '\\l'
@@ -370,7 +373,8 @@ const sortingSymbolsArr: Array<string> = [
370373
escapeRegexUnsafeCharacters(CompoundRomanNumberDashRegexSymbol),
371374
escapeRegexUnsafeCharacters(WordInASCIIRegexSymbol),
372375
escapeRegexUnsafeCharacters(WordInAnyLanguageRegexSymbol),
373-
escapeRegexUnsafeCharacters(Date_dd_Mmm_yyyy_RegexSymbol)
376+
escapeRegexUnsafeCharacters(Date_dd_Mmm_yyyy_RegexSymbol),
377+
escapeRegexUnsafeCharacters(Date_Mmm_dd_yyyy_RegexSymbol)
374378
]
375379

376380
const sortingSymbolsRegex = new RegExp(sortingSymbolsArr.join('|'), 'gi')
@@ -439,6 +443,7 @@ export const NumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumb
439443
export const CompoundDotNumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumber(s, DOT_SEPARATOR)
440444
export const CompoundDashNumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumber(s, DASH_SEPARATOR)
441445
export const Date_dd_Mmm_yyyy_NormalizerFn: NormalizerFn = (s: string) => getNormalizedDate_dd_Mmm_yyyy_NormalizerFn(s)
446+
export const Date_Mmm_dd_yyyy_NormalizerFn: NormalizerFn = (s: string) => getNormalizedDate_Mmm_dd_yyyy_NormalizerFn(s)
442447

443448
export enum AdvancedRegexType {
444449
None, // to allow if (advancedRegex)
@@ -450,7 +455,8 @@ export enum AdvancedRegexType {
450455
CompoundDashRomanNumber,
451456
WordInASCII,
452457
WordInAnyLanguage,
453-
Date_dd_Mmm_yyyy
458+
Date_dd_Mmm_yyyy,
459+
Date_Mmm_dd_yyyy
454460
}
455461

456462
const sortingSymbolToRegexpStr: { [key: string]: RegExpSpecStr } = {
@@ -499,6 +505,11 @@ const sortingSymbolToRegexpStr: { [key: string]: RegExpSpecStr } = {
499505
regexpStr: Date_dd_Mmm_yyyy_RegexStr,
500506
normalizerFn: Date_dd_Mmm_yyyy_NormalizerFn,
501507
advancedRegexType: AdvancedRegexType.Date_dd_Mmm_yyyy
508+
},
509+
[Date_Mmm_dd_yyyy_RegexSymbol]: { // Intentionally retain character case
510+
regexpStr: Date_Mmm_dd_yyyy_RegexStr,
511+
normalizerFn: Date_Mmm_dd_yyyy_NormalizerFn,
512+
advancedRegexType: AdvancedRegexType.Date_Mmm_dd_yyyy
502513
}
503514
}
504515

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {
2+
TAbstractFile,
3+
TFolder,
4+
Vault
5+
} from "obsidian";
6+
import {
7+
DEFAULT_FOLDER_CTIME,
8+
determineFolderDatesIfNeeded,
9+
determineSortingGroup,
10+
FolderItemForSorting, OS_alphabetical, OS_byCreatedTime, ProcessingContext, sortFolderItems
11+
} from "../../custom-sort/custom-sort";
12+
import {
13+
CustomSortGroupType,
14+
CustomSortOrder,
15+
CustomSortSpec
16+
} from "../../custom-sort/custom-sort-types";
17+
import {
18+
TIMESTAMP_OLDEST,
19+
TIMESTAMP_NEWEST,
20+
mockTFolderWithChildren,
21+
mockTFolderWithDateNamedChildren,
22+
TIMESTAMP_DEEP_NEWEST,
23+
TIMESTAMP_DEEP_OLDEST,
24+
} from "../mocks";
25+
import {
26+
SortingSpecProcessor
27+
} from "../../custom-sort/sorting-spec-processor";
28+
29+
describe('sortFolderItems', () => {
30+
it('should correctly handle Mmm-dd-yyyy pattern in file names', () => {
31+
// given
32+
const processor: SortingSpecProcessor = new SortingSpecProcessor()
33+
const sortSpecTxt =
34+
` ... \\[Mmm-dd-yyyy]
35+
> a-z
36+
`
37+
const PARENT_PATH = 'parent/folder/path'
38+
const sortSpecsCollection = processor.parseSortSpecFromText(
39+
sortSpecTxt.split('\n'),
40+
PARENT_PATH,
41+
'file name with the sorting, irrelevant here'
42+
)
43+
44+
const folder: TFolder = mockTFolderWithDateNamedChildren(PARENT_PATH)
45+
const sortSpec: CustomSortSpec = sortSpecsCollection?.sortSpecByPath![PARENT_PATH]!
46+
47+
const ctx: ProcessingContext = {}
48+
49+
// when
50+
const result: Array<TAbstractFile> = sortFolderItems(folder, folder.children, sortSpec, ctx, OS_alphabetical)
51+
52+
// then
53+
const orderedNames = result.map(f => f.name)
54+
expect(orderedNames).toEqual([
55+
'CCC Feb-28-2025',
56+
'BBB Dec-23-2024.md',
57+
'DDD Jul-15-2024.md',
58+
'AAA Jan-01-2012'
59+
])
60+
})
61+
})
62+
63+
64+

src/test/mocks.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import {
33
TFolder,
44
Vault
55
} from "obsidian";
6+
import {
7+
lastPathComponent
8+
} from "../utils/utils";
69

710
export const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => {
811
return {
@@ -25,7 +28,7 @@ export const mockTFolder = (name: string, children?: Array<TFolder|TFile>, paren
2528
isRoot(): boolean { return name === '/' },
2629
vault: {} as Vault, // To satisfy TS typechecking
2730
path: `${name}`,
28-
name: name,
31+
name: lastPathComponent(name),
2932
parent: parent ?? ({} as TFolder), // To satisfy TS typechecking
3033
children: children ?? []
3134
}
@@ -53,3 +56,12 @@ export const mockTFolderWithChildren = (name: string): TFolder => {
5356

5457
return mockTFolder(name, [child1, child2, child3, child4, child5, subfolder1, subfolder2])
5558
}
59+
60+
export const mockTFolderWithDateNamedChildren = (name: string): TFolder => {
61+
const child1: TFolder = mockTFolder('AAA Jan-01-2012')
62+
const child2: TFile = mockTFile('BBB Dec-23-2024', 'md')
63+
const child3: TFolder = mockTFolder('CCC Feb-28-2025')
64+
const child4: TFile = mockTFile('DDD Jul-15-2024', 'md')
65+
66+
return mockTFolder(name, [child1, child2, child3, child4])
67+
}

src/test/unit/sorting-spec-processor.spec.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
CompoundDotNumberNormalizerFn,
55
ConsumedFolderMatchingRegexp,
66
consumeFolderByRegexpExpression,
7-
convertPlainStringToRegex, Date_dd_Mmm_yyyy_NormalizerFn,
7+
convertPlainStringToRegex,
8+
Date_dd_Mmm_yyyy_NormalizerFn,
9+
Date_Mmm_dd_yyyy_NormalizerFn,
810
detectSortingSymbols,
911
escapeRegexUnsafeCharacters,
1012
extractSortingSymbol,
@@ -410,11 +412,17 @@ const expectedSortSpecsExampleSortingSymbols: { [key: string]: CustomSortSpec }
410412
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,
411413
normalizerFn: Date_dd_Mmm_yyyy_NormalizerFn
412414
}
415+
}, {
416+
type: CustomSortGroupType.ExactName,
417+
regexPrefix: {
418+
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,
419+
normalizerFn: Date_Mmm_dd_yyyy_NormalizerFn
420+
}
413421
}, {
414422
type: CustomSortGroupType.Outsiders
415423
}],
416424
targetFoldersPaths: ['mock-folder'],
417-
outsidersGroupIdx: 8
425+
outsidersGroupIdx: 9
418426
}
419427
}
420428

@@ -427,6 +435,7 @@ And this kind of... \\D+plain syntax???
427435
Here goes ASCII word \\a+
428436
\\A+. is for any modern language word
429437
\\[dd-Mmm-yyyy] for the specific date format of 12-Apr-2024
438+
\\[Mmm-dd-yyyy] for the specific date format of Apr-01-2024
430439
`
431440

432441
describe('SortingSpecProcessor', () => {

0 commit comments

Comments
 (0)