#184 - Sort notes in Mmm-dd-yyyy format
This commit is contained in:
parent
008f6c03f7
commit
a0bd47b210
|
@ -240,7 +240,7 @@ const SortersForDerivedSecondary: { [key in CustomSortOrder]?: SorterFn } = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// OS - Obsidian Sort
|
// OS - Obsidian Sort
|
||||||
const OS_alphabetical = 'alphabetical'
|
export const OS_alphabetical = 'alphabetical'
|
||||||
const OS_alphabeticalReverse = 'alphabeticalReverse'
|
const OS_alphabeticalReverse = 'alphabeticalReverse'
|
||||||
export const OS_byModifiedTime = 'byModifiedTime'
|
export const OS_byModifiedTime = 'byModifiedTime'
|
||||||
export const OS_byModifiedTimeReverse = 'byModifiedTimeReverse'
|
export const OS_byModifiedTimeReverse = 'byModifiedTimeReverse'
|
||||||
|
@ -794,7 +794,7 @@ const folderSortCore = function (sortedFolder: TFolder, sortOrder: string, sorti
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns a sorted copy of the input array, intentionally to keep it intact
|
// Returns a sorted copy of the input array, intentionally to keep it intact
|
||||||
export const sortFolderItemsForBookmarking = function (folder: TFolder, items: Array<TAbstractFile>, sortingSpec: CustomSortSpec|null|undefined, ctx: ProcessingContext, uiSortOrder: string): Array<TAbstractFile> {
|
export const sortFolderItems = function (folder: TFolder, items: Array<TAbstractFile>, sortingSpec: CustomSortSpec|null|undefined, ctx: ProcessingContext, uiSortOrder: string): Array<TAbstractFile> {
|
||||||
if (sortingSpec) {
|
if (sortingSpec) {
|
||||||
const folderItemsByPath: { [key: string]: TAbstractFile } = {}
|
const folderItemsByPath: { [key: string]: TAbstractFile } = {}
|
||||||
|
|
||||||
|
@ -831,6 +831,9 @@ export const sortFolderItemsForBookmarking = function (folder: TFolder, items: A
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Exported legacy function name for backward compatibility
|
||||||
|
export const sortFolderItemsForBookmarking = sortFolderItems
|
||||||
|
|
||||||
export const _unitTests = {
|
export const _unitTests = {
|
||||||
fileGoesFirstWhenSameBasenameAsFolder: fileGoesFirstWhenSameBasenameAsFolder,
|
fileGoesFirstWhenSameBasenameAsFolder: fileGoesFirstWhenSameBasenameAsFolder,
|
||||||
folderGoesFirstWhenSameBasenameAsFolder: folderGoesFirstWhenSameBasenameAsFolder
|
folderGoesFirstWhenSameBasenameAsFolder: folderGoesFirstWhenSameBasenameAsFolder
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import {toString} from "builtin-modules";
|
|
||||||
|
|
||||||
export const RomanNumberRegexStr: string = ' *([MDCLXVI]+)'; // Roman number
|
export const RomanNumberRegexStr: string = ' *([MDCLXVI]+)'; // Roman number
|
||||||
export const CompoundRomanNumberDotRegexStr: string = ' *([MDCLXVI]+(?:\\.[MDCLXVI]+)*)';// Compound Roman number with dot as separator
|
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
|
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 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_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 DOT_SEPARATOR = '.'
|
||||||
export const DASH_SEPARATOR = '-'
|
export const DASH_SEPARATOR = '-'
|
||||||
|
@ -104,17 +103,23 @@ export function getNormalizedRomanNumber(s: string, separator?: string, places?:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DAY_POSITIONS = '00'.length
|
export const DAY_POSITIONS = '00'.length
|
||||||
const MONTH_POSITIONS = '00'.length
|
export const MONTH_POSITIONS = '00'.length
|
||||||
const YEAR_POSITIONS = '0000'.length
|
export const YEAR_POSITIONS = '0000'.length
|
||||||
|
|
||||||
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
|
const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']
|
||||||
|
|
||||||
export function getNormalizedDate_dd_Mmm_yyyy_NormalizerFn(s: string): string | null {
|
export function getNormalizedDate_NormalizerFn_for(separator: string, dayIdx: number, monthIdx: number, yearIdx: number, months?: string[]) {
|
||||||
// Assumption - the regex date matched against input s, no extensive defensive coding needed
|
return (s: string): string | null => {
|
||||||
const components = s.split('-')
|
// Assumption - the regex date matched against input s, no extensive defensive coding needed
|
||||||
const day = prependWithZeros(components[0], DAY_POSITIONS)
|
const components = s.split(separator)
|
||||||
const month = prependWithZeros( `${1 + MONTHS.indexOf(components[1])}`, MONTH_POSITIONS)
|
const day = prependWithZeros(components[dayIdx], DAY_POSITIONS)
|
||||||
const year = prependWithZeros(components[2], YEAR_POSITIONS)
|
const monthValue = months ? `${1 + MONTHS.indexOf(components[monthIdx])}` : components[monthIdx]
|
||||||
return `${year}-${month}-${day}//`
|
const month = prependWithZeros(monthValue, MONTH_POSITIONS)
|
||||||
|
const year = prependWithZeros(components[yearIdx], YEAR_POSITIONS)
|
||||||
|
return `${year}-${month}-${day}//`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getNormalizedDate_dd_Mmm_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for('-', 0, 1, 2, MONTHS)
|
||||||
|
export const getNormalizedDate_Mmm_dd_yyyy_NormalizerFn = getNormalizedDate_NormalizerFn_for('-', 1, 0, 2, MONTHS)
|
||||||
|
|
|
@ -17,8 +17,10 @@ import {
|
||||||
CompoundRomanNumberDotRegexStr,
|
CompoundRomanNumberDotRegexStr,
|
||||||
DASH_SEPARATOR,
|
DASH_SEPARATOR,
|
||||||
Date_dd_Mmm_yyyy_RegexStr,
|
Date_dd_Mmm_yyyy_RegexStr,
|
||||||
|
Date_Mmm_dd_yyyy_RegexStr,
|
||||||
DOT_SEPARATOR,
|
DOT_SEPARATOR,
|
||||||
getNormalizedDate_dd_Mmm_yyyy_NormalizerFn,
|
getNormalizedDate_dd_Mmm_yyyy_NormalizerFn,
|
||||||
|
getNormalizedDate_Mmm_dd_yyyy_NormalizerFn,
|
||||||
getNormalizedNumber,
|
getNormalizedNumber,
|
||||||
getNormalizedRomanNumber,
|
getNormalizedRomanNumber,
|
||||||
NumberRegexStr,
|
NumberRegexStr,
|
||||||
|
@ -351,6 +353,7 @@ const InlineRegexSymbol_Digit2: string = '\\[0-9]'
|
||||||
const InlineRegexSymbol_0_to_3: string = '\\[0-3]'
|
const InlineRegexSymbol_0_to_3: string = '\\[0-3]'
|
||||||
|
|
||||||
const Date_dd_Mmm_yyyy_RegexSymbol: string = '\\[dd-Mmm-yyyy]'
|
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_CapitalLetter: string = '\\C'
|
||||||
const InlineRegexSymbol_LowercaseLetter: string = '\\l'
|
const InlineRegexSymbol_LowercaseLetter: string = '\\l'
|
||||||
|
@ -370,7 +373,8 @@ const sortingSymbolsArr: Array<string> = [
|
||||||
escapeRegexUnsafeCharacters(CompoundRomanNumberDashRegexSymbol),
|
escapeRegexUnsafeCharacters(CompoundRomanNumberDashRegexSymbol),
|
||||||
escapeRegexUnsafeCharacters(WordInASCIIRegexSymbol),
|
escapeRegexUnsafeCharacters(WordInASCIIRegexSymbol),
|
||||||
escapeRegexUnsafeCharacters(WordInAnyLanguageRegexSymbol),
|
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')
|
const sortingSymbolsRegex = new RegExp(sortingSymbolsArr.join('|'), 'gi')
|
||||||
|
@ -439,6 +443,7 @@ export const NumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumb
|
||||||
export const CompoundDotNumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumber(s, DOT_SEPARATOR)
|
export const CompoundDotNumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumber(s, DOT_SEPARATOR)
|
||||||
export const CompoundDashNumberNormalizerFn: NormalizerFn = (s: string) => getNormalizedNumber(s, DASH_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_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 {
|
export enum AdvancedRegexType {
|
||||||
None, // to allow if (advancedRegex)
|
None, // to allow if (advancedRegex)
|
||||||
|
@ -450,7 +455,8 @@ export enum AdvancedRegexType {
|
||||||
CompoundDashRomanNumber,
|
CompoundDashRomanNumber,
|
||||||
WordInASCII,
|
WordInASCII,
|
||||||
WordInAnyLanguage,
|
WordInAnyLanguage,
|
||||||
Date_dd_Mmm_yyyy
|
Date_dd_Mmm_yyyy,
|
||||||
|
Date_Mmm_dd_yyyy
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortingSymbolToRegexpStr: { [key: string]: RegExpSpecStr } = {
|
const sortingSymbolToRegexpStr: { [key: string]: RegExpSpecStr } = {
|
||||||
|
@ -499,6 +505,11 @@ const sortingSymbolToRegexpStr: { [key: string]: RegExpSpecStr } = {
|
||||||
regexpStr: Date_dd_Mmm_yyyy_RegexStr,
|
regexpStr: Date_dd_Mmm_yyyy_RegexStr,
|
||||||
normalizerFn: Date_dd_Mmm_yyyy_NormalizerFn,
|
normalizerFn: Date_dd_Mmm_yyyy_NormalizerFn,
|
||||||
advancedRegexType: AdvancedRegexType.Date_dd_Mmm_yyyy
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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<TAbstractFile> = 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'
|
||||||
|
])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,9 @@ import {
|
||||||
TFolder,
|
TFolder,
|
||||||
Vault
|
Vault
|
||||||
} from "obsidian";
|
} from "obsidian";
|
||||||
|
import {
|
||||||
|
lastPathComponent
|
||||||
|
} from "../utils/utils";
|
||||||
|
|
||||||
export const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => {
|
export const mockTFile = (basename: string, ext: string, size?: number, ctime?: number, mtime?: number): TFile => {
|
||||||
return {
|
return {
|
||||||
|
@ -25,7 +28,7 @@ export const mockTFolder = (name: string, children?: Array<TFolder|TFile>, paren
|
||||||
isRoot(): boolean { return name === '/' },
|
isRoot(): boolean { return name === '/' },
|
||||||
vault: {} as Vault, // To satisfy TS typechecking
|
vault: {} as Vault, // To satisfy TS typechecking
|
||||||
path: `${name}`,
|
path: `${name}`,
|
||||||
name: name,
|
name: lastPathComponent(name),
|
||||||
parent: parent ?? ({} as TFolder), // To satisfy TS typechecking
|
parent: parent ?? ({} as TFolder), // To satisfy TS typechecking
|
||||||
children: children ?? []
|
children: children ?? []
|
||||||
}
|
}
|
||||||
|
@ -53,3 +56,12 @@ export const mockTFolderWithChildren = (name: string): TFolder => {
|
||||||
|
|
||||||
return mockTFolder(name, [child1, child2, child3, child4, child5, subfolder1, subfolder2])
|
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])
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,9 @@ import {
|
||||||
CompoundDotNumberNormalizerFn,
|
CompoundDotNumberNormalizerFn,
|
||||||
ConsumedFolderMatchingRegexp,
|
ConsumedFolderMatchingRegexp,
|
||||||
consumeFolderByRegexpExpression,
|
consumeFolderByRegexpExpression,
|
||||||
convertPlainStringToRegex, Date_dd_Mmm_yyyy_NormalizerFn,
|
convertPlainStringToRegex,
|
||||||
|
Date_dd_Mmm_yyyy_NormalizerFn,
|
||||||
|
Date_Mmm_dd_yyyy_NormalizerFn,
|
||||||
detectSortingSymbols,
|
detectSortingSymbols,
|
||||||
escapeRegexUnsafeCharacters,
|
escapeRegexUnsafeCharacters,
|
||||||
extractSortingSymbol,
|
extractSortingSymbol,
|
||||||
|
@ -410,11 +412,17 @@ 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,
|
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
|
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
|
type: CustomSortGroupType.Outsiders
|
||||||
}],
|
}],
|
||||||
targetFoldersPaths: ['mock-folder'],
|
targetFoldersPaths: ['mock-folder'],
|
||||||
outsidersGroupIdx: 8
|
outsidersGroupIdx: 9
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -427,6 +435,7 @@ And this kind of... \\D+plain syntax???
|
||||||
Here goes ASCII word \\a+
|
Here goes ASCII word \\a+
|
||||||
\\A+. is for any modern language word
|
\\A+. is for any modern language word
|
||||||
\\[dd-Mmm-yyyy] for the specific date format of 12-Apr-2024
|
\\[dd-Mmm-yyyy] for the specific date format of 12-Apr-2024
|
||||||
|
\\[Mmm-dd-yyyy] for the specific date format of Apr-01-2024
|
||||||
`
|
`
|
||||||
|
|
||||||
describe('SortingSpecProcessor', () => {
|
describe('SortingSpecProcessor', () => {
|
||||||
|
|
Loading…
Reference in New Issue