Skip to content

Commit

Permalink
🧮 Allow enumeration to start at a specified number (#861)
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanc1 authored Jan 19, 2024
1 parent 2be01f1 commit ffc1061
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 16 deletions.
6 changes: 6 additions & 0 deletions .changeset/nasty-mugs-happen.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'myst-frontmatter': patch
'myst-transforms': patch
---

Allow enumeration to start at a different number
19 changes: 18 additions & 1 deletion packages/myst-frontmatter/src/numbering/numbering.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ cases:
- title: invalid extras keys are removed
raw:
numbering:
list: 1
list: 'invalid'
normalized: {}
errors: 1
- title: full object returns self
Expand Down Expand Up @@ -62,3 +62,20 @@ cases:
heading_4: true
heading_5: true
heading_6: true
- title: Allow numbers to start at
raw:
numbering:
figure: 2
list: 1
normalized:
numbering:
figure: 2
list: 1
- title: Numbers can't be negative or fractional
raw:
numbering:
figure: 1.5 # Must not be a fraction
list: -1 # Must be positive
something: 0 # This should just be true
normalized: {}
errors: 3
14 changes: 12 additions & 2 deletions packages/myst-frontmatter/src/numbering/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
defined,
incrementOptions,
validateBoolean,
validateNumber,
validateObjectKeys,
validateString,
} from 'simple-validators';
Expand Down Expand Up @@ -56,8 +57,17 @@ export function validateNumbering(input: any, opts: ValidationOptions): Numberin
.filter((key) => !NUMBERING_OPTIONS.includes(key)) // For all the unknown options
.forEach((key) => {
if (defined(value[key])) {
const bool = validateBoolean(value[key], incrementOptions(key, opts));
if (defined(bool)) output[key] = bool;
if (typeof value[key] === 'number') {
const number = validateNumber(value[key], {
...incrementOptions(key, opts),
integer: true,
min: 1,
});
if (defined(number)) output[key] = number;
} else {
const bool = validateBoolean(value[key], incrementOptions(key, opts));
if (defined(bool)) output[key] = bool;
}
}
});
if (Object.keys(output).length === 0) return undefined;
Expand Down
37 changes: 24 additions & 13 deletions packages/myst-transforms/src/enumerate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ type Target = {
};

type TargetCounts = {
heading?: (number | null)[];
heading: (number | null)[];
} & Record<string, { main: number; sub: number }>;

export type StateOptions = {
Expand All @@ -96,12 +96,12 @@ export type StateOptions = {

export type NumberingOptions = {
enumerator?: string;
figure?: boolean;
subfigure?: boolean;
equation?: boolean;
subequation?: boolean;
table?: boolean;
code?: boolean;
figure?: boolean | number;
subfigure?: boolean | number;
equation?: boolean | number;
subequation?: boolean | number;
table?: boolean | number;
code?: boolean | number;
heading_1?: boolean;
heading_2?: boolean;
heading_3?: boolean;
Expand Down Expand Up @@ -184,11 +184,11 @@ function shouldEnumerate(
if (typeof override === 'boolean') return override;
if (kind === 'heading' && node.type === 'heading') {
return (
numbering[`heading_${node.depth}` as keyof Omit<NumberingOptions, 'enumerator'>] ?? false
!!numbering[`heading_${node.depth}` as keyof Omit<NumberingOptions, 'enumerator'>] ?? false
);
}
if (node.subcontainer) return numbering.subfigure ?? false;
return numbering[kind as keyof Omit<NumberingOptions, 'enumerator'>] ?? false;
if (node.subcontainer) return !!numbering.subfigure ?? false;
return !!numbering[kind as keyof Omit<NumberingOptions, 'enumerator'>] ?? false;
}

/**
Expand Down Expand Up @@ -252,7 +252,9 @@ export class ReferenceState implements IReferenceState {
numbering?: boolean | NumberingOptions;
file?: VFile;
}) {
this.targetCounts = opts?.targetCounts || {};
this.targetCounts = opts?.targetCounts || ({} as TargetCounts);
// Initialize the heading counts (it is different)
this.targetCounts.heading ??= [0, 0, 0, 0, 0, 0];
if (typeof opts?.numbering === 'boolean') {
this.numberAll = opts?.numbering;
this.numbering = {};
Expand All @@ -267,6 +269,16 @@ export class ReferenceState implements IReferenceState {
...opts?.numbering,
};
}
// Set the offset counts if the numbering does not start at zero
Object.entries(opts?.numbering ?? {}).forEach(([key, val]) => {
if (typeof val === 'number') {
if (key in ['heading_1', 'heading_2', 'heading_3', 'heading_4', 'heading_5', 'heading_6']) {
this.targetCounts.heading[Number.parseInt(key.slice(-1), 10) - 1] = val;
} else {
this.targetCounts[key] = { main: val, sub: 0 };
}
}
});
this.targets = {};
this.file = opts?.file;
}
Expand Down Expand Up @@ -341,7 +353,6 @@ export class ReferenceState implements IReferenceState {
if (kind === TargetKind.heading && node.type === 'heading') {
// Ideally initializeNumberedHeadingDepths is called before incrementing
// heading count to do a better job initializing headers based on tree
if (!this.targetCounts.heading) this.targetCounts.heading = [0, 0, 0, 0, 0, 0];
this.targetCounts.heading = incrementHeadingCounts(node.depth, this.targetCounts.heading);
enumerator = formatHeadingEnumerator(this.targetCounts.heading, this.numbering.enumerator);
node.enumerator = enumerator;
Expand Down Expand Up @@ -563,7 +574,7 @@ function getCaptionLabel(kind?: string, subcontainer?: boolean) {
* Requires container to be enumerated.
*
* By default, captionNumber is only added if caption already exists.
* However, for subcontainers, captionNumber is always added.
* However, for sub-containers, captionNumber is always added.
*/
export function addContainerCaptionNumbersTransform(
tree: GenericParent,
Expand Down

0 comments on commit ffc1061

Please sign in to comment.