Major refactoring of sorting structures. Refactored the code. Unit tests not touched, broken.

This commit is contained in:
SebastianMC 2024-11-06 11:23:23 +01:00
parent 99cea92322
commit eeb581120d
4 changed files with 78 additions and 106 deletions

View File

@ -50,15 +50,6 @@ export enum CustomSortOrder {
default = alphabeticalWithFilesPreferred default = alphabeticalWithFilesPreferred
} }
export interface RecognizedOrderValue {
order: CustomSortOrder
applyToMetadataField?: string
metadataValueExtractor?: MDataExtractor
secondaryOrder?: CustomSortOrder
secondaryApplyToMetadataField?: string
secondaryMetadataValueExtractor?: MDataExtractor
}
export type NormalizerFn = (s: string) => string | null export type NormalizerFn = (s: string) => string | null
export const IdentityNormalizerFn: NormalizerFn = (s: string) => s export const IdentityNormalizerFn: NormalizerFn = (s: string) => s
@ -67,6 +58,17 @@ export interface RegExpSpec {
normalizerFn?: NormalizerFn normalizerFn?: NormalizerFn
} }
export interface CustomSort {
order: CustomSortOrder // mandatory
byMetadata?: string
metadataValueExtractor?: MDataExtractor
}
export interface RecognizedSorting {
primary?: CustomSort
secondary?: CustomSort
}
export interface CustomSortGroup { export interface CustomSortGroup {
type: CustomSortGroupType type: CustomSortGroupType
exactText?: string exactText?: string
@ -74,12 +76,8 @@ export interface CustomSortGroup {
regexPrefix?: RegExpSpec regexPrefix?: RegExpSpec
exactSuffix?: string exactSuffix?: string
regexSuffix?: RegExpSpec regexSuffix?: RegExpSpec
order?: CustomSortOrder sorting?: CustomSort
byMetadataField?: string // for 'by-metadata:' sorting if the order is by metadata alphabetical or reverse secondarySorting?: CustomSort
metadataFieldValueExtractor?: MDataExtractor // and its sorting value extractor
secondaryOrder?: CustomSortOrder
byMetadataFieldSecondary?: string // for 'by-metadata:' sorting if the order is by metadata alphabetical or reverse
metadataFieldSecondaryValueExtractor?: MDataExtractor
filesOnly?: boolean filesOnly?: boolean
matchFilenameWithExt?: boolean matchFilenameWithExt?: boolean
foldersOnly?: boolean foldersOnly?: boolean
@ -92,12 +90,8 @@ export interface CustomSortGroup {
export interface CustomSortSpec { export interface CustomSortSpec {
// plays only informative role about the original parsed 'target-folder:' values // plays only informative role about the original parsed 'target-folder:' values
targetFoldersPaths: Array<string> // For root use '/' targetFoldersPaths: Array<string> // For root use '/'
defaultOrder?: CustomSortOrder defaultSorting?: CustomSort
defaultSecondaryOrder?: CustomSortOrder defaultSecondarySorting?: CustomSort
byMetadataField?: string // for 'by-metadata:' if the defaultOrder is by metadata
metadataFieldValueExtractor?: MDataExtractor // and its sorting value extractor
byMetadataFieldSecondary?: string
metadataFieldSecondaryValueExtractor?: MDataExtractor
groups: Array<CustomSortGroup> groups: Array<CustomSortGroup>
groupsShadow?: Array<CustomSortGroup> // A shallow copy of groups, used at applying sorting for items in a folder. groupsShadow?: Array<CustomSortGroup> // 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) // Stores folder-specific values (e.g. macros expanded with folder-specific values)

View File

@ -63,12 +63,12 @@ export const collectSortingAndGroupingTypes = (sortSpec?: CustomSortSpec|null):
} }
} }
if (!sortSpec) return has if (!sortSpec) return has
doCheck(has, sortSpec.defaultOrder) doCheck(has, sortSpec.defaultSorting?.order)
doCheck(has, sortSpec.defaultSecondaryOrder) doCheck(has, sortSpec.defaultSecondarySorting?.order)
if (sortSpec.groups) { if (sortSpec.groups) {
for (let group of sortSpec.groups) { for (let group of sortSpec.groups) {
doCheck(has, group.order, group.type) doCheck(has, group.sorting?.order, group.type)
doCheck(has, group.secondaryOrder) doCheck(has, group.secondarySorting?.order)
} }
} }
return has return has

View File

@ -17,6 +17,7 @@ import {
ObsidianIconFolder_PluginInstance ObsidianIconFolder_PluginInstance
} from '../utils/ObsidianIconFolderPluginSignature' } from '../utils/ObsidianIconFolderPluginSignature'
import { import {
CustomSort,
CustomSortGroup, CustomSortGroup,
CustomSortGroupType, CustomSortGroupType,
CustomSortOrder, CustomSortOrder,
@ -294,18 +295,18 @@ export const StandardPlainObsidianComparator = (order: string): PlainSorterFn =>
} }
} }
export const getSorterFnFor = (sorting: CustomSortOrder, currentUIselectedSorting?: string, sortLevelId?: SortingLevelId): SorterFn => { export const getSorterFnFor = (order: CustomSortOrder, currentUIselectedSorting?: string, sortLevelId?: SortingLevelId): SorterFn => {
if (sorting === CustomSortOrder.standardObsidian) { if (order === CustomSortOrder.standardObsidian) {
sorting = StandardObsidianToCustomSort[currentUIselectedSorting ?? 'alphabetical'] ?? CustomSortOrder.alphabetical order = StandardObsidianToCustomSort[currentUIselectedSorting ?? 'alphabetical'] ?? CustomSortOrder.alphabetical
return StandardObsidianComparator(sorting) return StandardObsidianComparator(order)
} else { } else {
// Some sorters have to know at which sorting level they are used // Some sorters have to know at which sorting level they are used
switch(sortLevelId) { switch(sortLevelId) {
case SortingLevelId.forSecondary: return SortersForSecondary[sorting] ?? Sorters[sorting] case SortingLevelId.forSecondary: return SortersForSecondary[order] ?? Sorters[order]
case SortingLevelId.forDerivedPrimary: return SortersForDerivedPrimary[sorting] ?? Sorters[sorting] case SortingLevelId.forDerivedPrimary: return SortersForDerivedPrimary[order] ?? Sorters[order]
case SortingLevelId.forDerivedSecondary: return SortersForDerivedSecondary[sorting] ?? Sorters[sorting] case SortingLevelId.forDerivedSecondary: return SortersForDerivedSecondary[order] ?? Sorters[order]
case SortingLevelId.forPrimary: case SortingLevelId.forPrimary:
default: return Sorters[sorting] default: return Sorters[order]
} }
} }
} }
@ -315,13 +316,13 @@ export const getComparator = (sortSpec: CustomSortSpec, currentUIselectedSorting
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 primary: number = group?.order ? getSorterFnFor(group.order, currentUIselectedSorting, SortingLevelId.forPrimary)(itA, itB) : EQUAL_OR_UNCOMPARABLE const primary: number = group?.sorting ? getSorterFnFor(group.sorting.order, currentUIselectedSorting, SortingLevelId.forPrimary)(itA, itB) : EQUAL_OR_UNCOMPARABLE
if (primary !== EQUAL_OR_UNCOMPARABLE) return primary if (primary !== EQUAL_OR_UNCOMPARABLE) return primary
const secondary: number = group?.secondaryOrder ? getSorterFnFor(group.secondaryOrder, currentUIselectedSorting, SortingLevelId.forSecondary)(itA, itB) : EQUAL_OR_UNCOMPARABLE const secondary: number = group?.secondarySorting ? getSorterFnFor(group.secondarySorting.order, currentUIselectedSorting, SortingLevelId.forSecondary)(itA, itB) : EQUAL_OR_UNCOMPARABLE
if (secondary !== EQUAL_OR_UNCOMPARABLE) return secondary if (secondary !== EQUAL_OR_UNCOMPARABLE) return secondary
const folderLevel: number = sortSpec.defaultOrder ? getSorterFnFor(sortSpec.defaultOrder, currentUIselectedSorting, SortingLevelId.forDerivedPrimary)(itA, itB) : EQUAL_OR_UNCOMPARABLE const folderLevel: number = sortSpec.defaultSorting ? getSorterFnFor(sortSpec.defaultSorting.order, currentUIselectedSorting, SortingLevelId.forDerivedPrimary)(itA, itB) : EQUAL_OR_UNCOMPARABLE
if (folderLevel !== EQUAL_OR_UNCOMPARABLE) return folderLevel if (folderLevel !== EQUAL_OR_UNCOMPARABLE) return folderLevel
const folderLevelSecondary: number = sortSpec.defaultSecondaryOrder ? getSorterFnFor(sortSpec.defaultSecondaryOrder, currentUIselectedSorting, SortingLevelId.forDerivedSecondary)(itA, itB) : EQUAL_OR_UNCOMPARABLE const folderLevelSecondary: number = sortSpec.defaultSecondarySorting ? getSorterFnFor(sortSpec.defaultSecondarySorting.order, currentUIselectedSorting, SortingLevelId.forDerivedSecondary)(itA, itB) : EQUAL_OR_UNCOMPARABLE
if (folderLevelSecondary !== EQUAL_OR_UNCOMPARABLE) return folderLevelSecondary if (folderLevelSecondary !== EQUAL_OR_UNCOMPARABLE) return folderLevelSecondary
const defaultForUnspecified: number = getSorterFnFor(CustomSortOrder.default, undefined, SortingLevelId.forDefaultWhenUnspecified)(itA, itB) const defaultForUnspecified: number = getSorterFnFor(CustomSortOrder.default, undefined, SortingLevelId.forDefaultWhenUnspecified)(itA, itB)
return defaultForUnspecified return defaultForUnspecified
@ -567,10 +568,10 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
if (determined && determinedGroupIdx !== undefined) { // <-- defensive code, maybe too defensive if (determined && determinedGroupIdx !== undefined) { // <-- defensive code, maybe too defensive
const group: CustomSortGroup = spec.groups[determinedGroupIdx]; const group: CustomSortGroup = spec.groups[determinedGroupIdx];
const isPrimaryOrderByMetadata: boolean = isByMetadata(group?.order) const isPrimaryOrderByMetadata: boolean = isByMetadata(group?.sorting?.order)
const isSecondaryOrderByMetadata: boolean = isByMetadata(group?.secondaryOrder) const isSecondaryOrderByMetadata: boolean = isByMetadata(group?.secondarySorting?.order)
const isDerivedPrimaryByMetadata: boolean = isByMetadata(spec.defaultOrder) const isDerivedPrimaryByMetadata: boolean = isByMetadata(spec.defaultSorting?.order)
const isDerivedSecondaryByMetadata: boolean = isByMetadata(spec.defaultSecondaryOrder) const isDerivedSecondaryByMetadata: boolean = isByMetadata(spec.defaultSecondarySorting?.order)
if (isPrimaryOrderByMetadata || isSecondaryOrderByMetadata || isDerivedPrimaryByMetadata || isDerivedSecondaryByMetadata) { if (isPrimaryOrderByMetadata || isSecondaryOrderByMetadata || isDerivedPrimaryByMetadata || isDerivedSecondaryByMetadata) {
if (ctx?._mCache) { if (ctx?._mCache) {
// For folders - scan metadata of 'folder note' // For folders - scan metadata of 'folder note'
@ -586,26 +587,26 @@ export const determineSortingGroup = function (entry: TFile | TFolder, spec: Cus
} }
if (isPrimaryOrderByMetadata) metadataValueToSortBy = if (isPrimaryOrderByMetadata) metadataValueToSortBy =
mdataValueFromFMCaches ( mdataValueFromFMCaches (
group?.byMetadataField || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING, group.sorting!.byMetadata || group.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING,
group?.metadataFieldValueExtractor, group.sorting!.metadataValueExtractor,
frontMatterCache, frontMatterCache,
prioFrontMatterCache) prioFrontMatterCache)
if (isSecondaryOrderByMetadata) metadataValueSecondaryToSortBy = if (isSecondaryOrderByMetadata) metadataValueSecondaryToSortBy =
mdataValueFromFMCaches ( mdataValueFromFMCaches (
group?.byMetadataFieldSecondary || group?.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING, group.secondarySorting!.byMetadata || group.withMetadataFieldName || DEFAULT_METADATA_FIELD_FOR_SORTING,
group?.metadataFieldSecondaryValueExtractor, group.secondarySorting!.metadataValueExtractor,
frontMatterCache, frontMatterCache,
prioFrontMatterCache) prioFrontMatterCache)
if (isDerivedPrimaryByMetadata) metadataValueDerivedPrimaryToSortBy = if (isDerivedPrimaryByMetadata) metadataValueDerivedPrimaryToSortBy =
mdataValueFromFMCaches ( mdataValueFromFMCaches (
spec.byMetadataField || DEFAULT_METADATA_FIELD_FOR_SORTING, spec.defaultSorting!.byMetadata || DEFAULT_METADATA_FIELD_FOR_SORTING,
spec.metadataFieldValueExtractor, spec.defaultSorting!.metadataValueExtractor,
frontMatterCache, frontMatterCache,
prioFrontMatterCache) prioFrontMatterCache)
if (isDerivedSecondaryByMetadata) metadataValueDerivedSecondaryToSortBy = if (isDerivedSecondaryByMetadata) metadataValueDerivedSecondaryToSortBy =
mdataValueFromFMCaches ( mdataValueFromFMCaches (
spec.byMetadataFieldSecondary || DEFAULT_METADATA_FIELD_FOR_SORTING, spec.defaultSecondarySorting!.byMetadata || DEFAULT_METADATA_FIELD_FOR_SORTING,
spec.metadataFieldSecondaryValueExtractor, spec.defaultSecondarySorting!.metadataValueExtractor,
frontMatterCache, frontMatterCache,
prioFrontMatterCache) prioFrontMatterCache)
} }
@ -707,12 +708,12 @@ export const determineDatesForFolder = (folder: TFolder, recursive?: boolean): [
} }
export const determineFolderDatesIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec) => { export const determineFolderDatesIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec) => {
const foldersDatesNeeded = sortOrderNeedsFolderDates(sortingSpec.defaultOrder, sortingSpec.defaultSecondaryOrder) const foldersDatesNeeded = sortOrderNeedsFolderDates(sortingSpec.defaultSorting?.order, sortingSpec.defaultSecondarySorting?.order)
const foldersDeepDatesNeeded = sortOrderNeedsFolderDeepDates(sortingSpec.defaultOrder, sortingSpec.defaultSecondaryOrder) const foldersDeepDatesNeeded = sortOrderNeedsFolderDeepDates(sortingSpec.defaultSorting?.order, sortingSpec.defaultSecondarySorting?.order)
const groupOrders = sortingSpec.groups?.map((group) => ({ const groupOrders = sortingSpec.groups?.map((group) => ({
foldersDatesNeeded: sortOrderNeedsFolderDates(group.order, group.secondaryOrder), foldersDatesNeeded: sortOrderNeedsFolderDates(group.sorting?.order, group.secondarySorting?.order),
foldersDeepDatesNeeded: sortOrderNeedsFolderDeepDates(group.order, group.secondaryOrder) foldersDeepDatesNeeded: sortOrderNeedsFolderDeepDates(group.sorting?.order, group.secondarySorting?.order)
})) }))
folderItems.forEach((item) => { folderItems.forEach((item) => {
@ -732,15 +733,15 @@ export const determineFolderDatesIfNeeded = (folderItems: Array<FolderItemForSor
export const determineBookmarksOrderIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec, plugin: BookmarksPluginInterface) => { export const determineBookmarksOrderIfNeeded = (folderItems: Array<FolderItemForSorting>, sortingSpec: CustomSortSpec, plugin: BookmarksPluginInterface) => {
if (!plugin) return if (!plugin) return
const folderDefaultSortRequiresBookmarksOrder: boolean = !!(sortingSpec.defaultOrder && sortOrderNeedsBookmarksOrder(sortingSpec.defaultOrder, sortingSpec.defaultSecondaryOrder)) const folderDefaultSortRequiresBookmarksOrder: boolean = !!(sortingSpec.defaultSorting && sortOrderNeedsBookmarksOrder(sortingSpec.defaultSorting.order, sortingSpec.defaultSecondarySorting?.order))
folderItems.forEach((item) => { folderItems.forEach((item) => {
let groupSortRequiresBookmarksOrder: boolean = false let groupSortRequiresBookmarksOrder: boolean = false
if (!folderDefaultSortRequiresBookmarksOrder) { if (!folderDefaultSortRequiresBookmarksOrder) {
const groupIdx: number | undefined = item.groupIdx const groupIdx: number | undefined = item.groupIdx
if (groupIdx !== undefined) { if (groupIdx !== undefined) {
const groupOrder: CustomSortOrder | undefined = sortingSpec.groups[groupIdx].order const groupOrder: CustomSortOrder | undefined = sortingSpec.groups[groupIdx].sorting?.order
const groupSecondaryOrder: CustomSortOrder | undefined = sortingSpec.groups[groupIdx].secondaryOrder const groupSecondaryOrder: CustomSortOrder | undefined = sortingSpec.groups[groupIdx].secondarySorting?.order
groupSortRequiresBookmarksOrder = sortOrderNeedsBookmarksOrder(groupOrder, groupSecondaryOrder) groupSortRequiresBookmarksOrder = sortOrderNeedsBookmarksOrder(groupOrder, groupSecondaryOrder)
} }
} }

View File

@ -1,4 +1,5 @@
import { import {
CustomSort,
CustomSortGroup, CustomSortGroup,
CustomSortGroupType, CustomSortGroupType,
CustomSortOrder, CustomSortOrder,
@ -6,7 +7,7 @@ import {
DEFAULT_METADATA_FIELD_FOR_SORTING, DEFAULT_METADATA_FIELD_FOR_SORTING,
IdentityNormalizerFn, IdentityNormalizerFn,
NormalizerFn, NormalizerFn,
RecognizedOrderValue, RecognizedSorting,
RegExpSpec RegExpSpec
} from "./custom-sort-types"; } from "./custom-sort-types";
import {isDefined, last} from "../utils/utils"; import {isDefined, last} from "../utils/utils";
@ -113,12 +114,6 @@ interface CustomSortOrderAscDescPair {
desc: CustomSortOrder desc: CustomSortOrder
} }
interface CustomSortOrderSpec {
order: CustomSortOrder
byMetadataField?: string
metadataFieldExtractor?: MDataExtractor
}
const MAX_SORT_LEVEL: number = 1 const MAX_SORT_LEVEL: number = 1
// remember about .toLowerCase() before comparison! // remember about .toLowerCase() before comparison!
@ -1081,34 +1076,28 @@ export class SortingSpecProcessor {
if (!this.ctx.currentSpec) { if (!this.ctx.currentSpec) {
this.ctx.currentSpec = this.putNewSpecForNewTargetFolder() this.ctx.currentSpec = this.putNewSpecForNewTargetFolder()
} }
if (this.ctx.currentSpec.defaultOrder) { if (this.ctx.currentSpec.defaultSorting) {
const folderPathsForProblemMsg: string = this.ctx.currentSpec.targetFoldersPaths.join(' :: '); const folderPathsForProblemMsg: string = this.ctx.currentSpec.targetFoldersPaths.join(' :: ');
this.problem(ProblemCode.DuplicateOrderAttr, `Duplicate order specification for folder(s) ${folderPathsForProblemMsg}`) this.problem(ProblemCode.DuplicateOrderAttr, `Duplicate order specification for folder(s) ${folderPathsForProblemMsg}`)
return false; return false;
} }
this.ctx.currentSpec.defaultOrder = (attr.value as RecognizedOrderValue).order const rs: RecognizedSorting = attr.value // Syntax sugar
this.ctx.currentSpec.byMetadataField = (attr.value as RecognizedOrderValue).applyToMetadataField this.ctx.currentSpec.defaultSorting = rs.primary
this.ctx.currentSpec.metadataFieldValueExtractor = (attr.value as RecognizedOrderValue).metadataValueExtractor this.ctx.currentSpec.defaultSecondarySorting = rs.secondary
this.ctx.currentSpec.defaultSecondaryOrder = (attr.value as RecognizedOrderValue).secondaryOrder
this.ctx.currentSpec.byMetadataFieldSecondary = (attr.value as RecognizedOrderValue).secondaryApplyToMetadataField
this.ctx.currentSpec.metadataFieldSecondaryValueExtractor = (attr.value as RecognizedOrderValue).secondaryMetadataValueExtractor
return true; return true;
} else if (attr.nesting > 0) { // For now only distinguishing nested (indented) and not-nested (not-indented), the depth doesn't matter } else if (attr.nesting > 0) { // For now only distinguishing nested (indented) and not-nested (not-indented), the depth doesn't matter
if (!this.ctx.currentSpec || !this.ctx.currentSpecGroup) { if (!this.ctx.currentSpec || !this.ctx.currentSpecGroup) {
this.problem(ProblemCode.DanglingOrderAttr, `Nested (indented) attribute requires prior sorting group definition`) this.problem(ProblemCode.DanglingOrderAttr, `Nested (indented) attribute requires prior sorting group definition`)
return false; return false;
} }
if (this.ctx.currentSpecGroup.order) { if (this.ctx.currentSpecGroup.sorting) {
const folderPathsForProblemMsg: string = this.ctx.currentSpec.targetFoldersPaths.join(' :: '); const folderPathsForProblemMsg: string = this.ctx.currentSpec.targetFoldersPaths.join(' :: ');
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;
} }
this.ctx.currentSpecGroup.order = (attr.value as RecognizedOrderValue).order const rs: RecognizedSorting = attr.value // Syntax sugar
this.ctx.currentSpecGroup.byMetadataField = (attr.value as RecognizedOrderValue).applyToMetadataField this.ctx.currentSpecGroup.sorting = rs.primary
this.ctx.currentSpecGroup.metadataFieldValueExtractor = (attr.value as RecognizedOrderValue).metadataValueExtractor this.ctx.currentSpecGroup.secondarySorting = rs.secondary
this.ctx.currentSpecGroup.secondaryOrder = (attr.value as RecognizedOrderValue).secondaryOrder
this.ctx.currentSpecGroup.byMetadataFieldSecondary = (attr.value as RecognizedOrderValue).secondaryApplyToMetadataField
this.ctx.currentSpecGroup.metadataFieldSecondaryValueExtractor = (attr.value as RecognizedOrderValue).secondaryMetadataValueExtractor
return true; return true;
} }
} }
@ -1397,7 +1386,7 @@ export class SortingSpecProcessor {
currentCombinedGroupIdx = i currentCombinedGroupIdx = i
} else { } else {
// Ensure that the preceding group doesn't contain sorting order // Ensure that the preceding group doesn't contain sorting order
if (spec.groups[i - 1].order) { if (spec.groups[i - 1].sorting) {
this.problem(ProblemCode.OnlyLastCombinedGroupCanSpecifyOrder, 'Predecessor group of combined group cannot contain order specification. Put it at the last of group in combined groups') this.problem(ProblemCode.OnlyLastCombinedGroupCanSpecifyOrder, 'Predecessor group of combined group cannot contain order specification. Put it at the last of group in combined groups')
return false return false
} }
@ -1412,34 +1401,26 @@ export class SortingSpecProcessor {
// Populate sorting order within combined groups // Populate sorting order within combined groups
if (anyCombinedGroupPresent) { if (anyCombinedGroupPresent) {
let orderForCombinedGroup: CustomSortOrder | undefined let sortingForCombinedGroup: CustomSort|undefined
let byMetadataFieldForCombinedGroup: string | undefined let secondarySortingForCombinedGroup: CustomSort|undefined
let secondaryOrderForCombinedGroup: CustomSortOrder | undefined
let secondaryByMetadataFieldForCombinedGroup: string | undefined
let idxOfCurrentCombinedGroup: number | undefined = undefined let idxOfCurrentCombinedGroup: number | undefined = undefined
for (let i = spec.groups.length - 1; i >= 0; i--) { for (let i = spec.groups.length - 1; i >= 0; i--) {
const group: CustomSortGroup = spec.groups[i] const group: CustomSortGroup = spec.groups[i]
if (group.combineWithIdx !== undefined) { if (group.combineWithIdx !== undefined) {
if (group.combineWithIdx === idxOfCurrentCombinedGroup) { // a subsequent (2nd, 3rd, ...) group of combined (counting from the end) if (group.combineWithIdx === idxOfCurrentCombinedGroup) { // a subsequent (2nd, 3rd, ...) group of combined (counting from the end)
group.order = orderForCombinedGroup group.sorting = sortingForCombinedGroup
group.byMetadataField = byMetadataFieldForCombinedGroup group.secondarySorting = secondarySortingForCombinedGroup
group.secondaryOrder = secondaryOrderForCombinedGroup
group.byMetadataFieldSecondary = secondaryByMetadataFieldForCombinedGroup
} else { // the first group of combined (counting from the end) } else { // the first group of combined (counting from the end)
idxOfCurrentCombinedGroup = group.combineWithIdx idxOfCurrentCombinedGroup = group.combineWithIdx
orderForCombinedGroup = group.order // could be undefined sortingForCombinedGroup = group.sorting
byMetadataFieldForCombinedGroup = group.byMetadataField // could be undefined secondarySortingForCombinedGroup = group.secondarySorting
secondaryOrderForCombinedGroup = group.secondaryOrder // could be undefined
secondaryByMetadataFieldForCombinedGroup = group.byMetadataFieldSecondary // could be undefined
} }
} else { } else {
// for sanity // for sanity
idxOfCurrentCombinedGroup = undefined idxOfCurrentCombinedGroup = undefined
orderForCombinedGroup = undefined sortingForCombinedGroup = undefined
byMetadataFieldForCombinedGroup = undefined secondarySortingForCombinedGroup = undefined
secondaryOrderForCombinedGroup = undefined
secondaryByMetadataFieldForCombinedGroup = undefined
} }
} }
} }
@ -1481,13 +1462,13 @@ export class SortingSpecProcessor {
} }
} }
private internalValidateOrderAttrValue = (sortOrderSpecText: string, prefixLexeme: string): Array<CustomSortOrderSpec>|AttrError|null => { private internalValidateOrderAttrValue = (sortOrderSpecText: string, prefixLexeme: string): Array<CustomSort>|AttrError|null => {
if (sortOrderSpecText.indexOf(CommentPrefix) >= 0) { if (sortOrderSpecText.indexOf(CommentPrefix) >= 0) {
sortOrderSpecText = sortOrderSpecText.substring(0, sortOrderSpecText.indexOf(CommentPrefix)) sortOrderSpecText = sortOrderSpecText.substring(0, sortOrderSpecText.indexOf(CommentPrefix))
} }
const sortLevels: Array<string> = `${prefixLexeme||''} ${sortOrderSpecText}`.trim().split(OrderLevelsSeparator) const sortLevels: Array<string> = `${prefixLexeme||''} ${sortOrderSpecText}`.trim().split(OrderLevelsSeparator)
let sortOrderSpec: Array<CustomSortOrderSpec> = [] let sortOrderSpec: Array<CustomSort> = []
// Max two levels are supported, excess levels specs are ignored // Max two levels are supported, excess levels specs are ignored
for (let level: number = 0; level <= MAX_SORT_LEVEL && level < sortLevels.length; level++) { for (let level: number = 0; level <= MAX_SORT_LEVEL && level < sortLevels.length; level++) {
@ -1583,22 +1564,18 @@ export class SortingSpecProcessor {
} }
sortOrderSpec[level] = { sortOrderSpec[level] = {
order: order!, order: order!,
byMetadataField: metadataName, byMetadata: metadataName,
metadataFieldExtractor: metadataExtractor metadataValueExtractor: metadataExtractor
} }
} }
return sortOrderSpec return sortOrderSpec
} }
private validateOrderAttrValue: AttrValueValidatorFn = (v: string, attr: Attribute, attrLexeme: string): RecognizedOrderValue|AttrError|null => { private validateOrderAttrValue: AttrValueValidatorFn = (v: string, attr: Attribute, attrLexeme: string): RecognizedSorting|AttrError|null => {
const recognized: Array<CustomSortOrderSpec>|AttrError|null = this.internalValidateOrderAttrValue(v, attrLexeme) const recognized: Array<CustomSort>|AttrError|null = this.internalValidateOrderAttrValue(v, attrLexeme)
return recognized ? (recognized instanceof AttrError ? recognized : { return recognized ? (recognized instanceof AttrError ? recognized : {
order: recognized[0].order, primary: recognized[0],
applyToMetadataField: recognized[0].byMetadataField, secondary: recognized[1]
metadataValueExtractor: recognized[0].metadataFieldExtractor,
secondaryOrder: recognized[1]?.order,
secondaryApplyToMetadataField: recognized[1]?.byMetadataField,
secondaryMetadataValueExtractor: recognized[1]?.metadataFieldExtractor
}) : null; }) : null;
} }