diff --git a/spec/index.html b/spec/index.html
index 6125735e..e3bf0e95 100644
--- a/spec/index.html
+++ b/spec/index.html
@@ -55,6 +55,7 @@
Options
| `--assets-dir` | `assetsDir` | Directory in which to place assets when using `--assets=external`. Defaults to "assets". |
| `--lint-spec` | `lintSpec` | Enforce some style and correctness checks. |
| `--error-formatter` | | The eslint formatter to be used for printing warnings and errors when using `--verbose`. Either the name of a built-in eslint formatter or the package name of an installed eslint compatible formatter. |
+ | `--max-clause-depth N` | | Warn when clauses exceed a nesting depth of N. |
| `--strict` | | Exit with an error if there are warnings. Cannot be used with `--watch`. |
| `--multipage` | | Emit a distinct page for each top-level clause. |
diff --git a/src/args.ts b/src/args.ts
index f7aa0277..9c4453cc 100644
--- a/src/args.ts
+++ b/src/args.ts
@@ -67,6 +67,12 @@ export const options = [
description:
'The formatter for warnings and errors; either a path prefixed with "." or "./", or package name, of an installed eslint compatible formatter (default: eslint-formatter-codeframe)',
},
+ {
+ name: 'max-clause-depth',
+ type: Number,
+ description:
+ 'The maximum nesting depth for clauses; exceeding this will cause a warning. Defaults to no limit.',
+ },
{
name: 'multipage',
type: Boolean,
diff --git a/src/clauseNums.ts b/src/clauseNums.ts
index f77f1631..aec3334d 100644
--- a/src/clauseNums.ts
+++ b/src/clauseNums.ts
@@ -9,6 +9,7 @@ export default function iterator(spec: Spec): ClauseNumberIterator {
const ids: (string | number[])[] = [];
let inAnnex = false;
let currentLevel = 0;
+ let hasWarnedForExcessNesting = false;
return {
next(clauseStack: Clause[], node: HTMLElement) {
@@ -26,10 +27,19 @@ export default function iterator(spec: Spec): ClauseNumberIterator {
spec.warn({
type: 'node',
node,
- ruleId: 'skipped-caluse',
+ ruleId: 'skipped-clause',
message: 'clause is being numbered without numbering its parent clause',
});
}
+ if (!hasWarnedForExcessNesting && level + 1 > (spec.opts.maxClauseDepth ?? Infinity)) {
+ spec.warn({
+ type: 'node',
+ node,
+ ruleId: 'max-clause-depth',
+ message: `clause exceeds maximum nesting depth of ${spec.opts.maxClauseDepth}`,
+ });
+ hasWarnedForExcessNesting = true;
+ }
const nextNum = annex ? nextAnnexNum : nextClauseNum;
diff --git a/src/cli.ts b/src/cli.ts
index cf96ee93..fd038dbc 100644
--- a/src/cli.ts
+++ b/src/cli.ts
@@ -113,6 +113,9 @@ const build = debounce(async function build() {
if (args['mark-effects']) {
opts.markEffects = true;
}
+ if (args['max-clause-depth']) {
+ opts.maxClauseDepth = args['max-clause-depth'];
+ }
if (args['no-toc'] != null) {
opts.toc = !args['no-toc'];
}
diff --git a/src/ecmarkup.ts b/src/ecmarkup.ts
index 34aae168..815b37e8 100644
--- a/src/ecmarkup.ts
+++ b/src/ecmarkup.ts
@@ -32,6 +32,7 @@ export interface Options {
copyright?: boolean;
date?: Date;
location?: string;
+ maxClauseDepth?: number;
multipage?: boolean;
extraBiblios?: ExportedBiblio[];
contributors?: string;
diff --git a/test/errors.js b/test/errors.js
index a99bfb5c..a4fc5275 100644
--- a/test/errors.js
+++ b/test/errors.js
@@ -1237,4 +1237,49 @@ ${M}
`);
});
});
+
+ describe('max clause depth', () => {
+ it('max depth', async () => {
+ await assertError(
+ positioned`
+
+ One
+ ${M}
+ Two
+
+
+ Not warned
+
+
+ `,
+ {
+ ruleId: 'max-clause-depth',
+ nodeType: 'emu-clause',
+ message: 'clause exceeds maximum nesting depth of 1',
+ },
+ {
+ maxClauseDepth: 1,
+ },
+ );
+ });
+
+ it('negative', async () => {
+ await assertErrorFree(
+ `
+
+ One
+
+ Two
+
+
+ Not warned
+
+
+ `,
+ {
+ maxClauseDepth: 2,
+ },
+ );
+ });
+ });
});