Skip to content

Commit 0c379b2

Browse files
refactor(seq-fields): share parsed attr projection
Add sequenceFieldAttrsFromParsed beside the SEQ parser and use it from import, raw field insertion, caption insertion/configuration, and PM recompute. This keeps parsed instruction attrs and null/default normalization in one place.
1 parent eaa557f commit 0c379b2

6 files changed

Lines changed: 90 additions & 49 deletions

File tree

packages/super-editor/src/editors/v1/core/super-converter/field-references/shared/seq-instruction.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,19 @@ const TOKEN_PATTERN = /"((?:[^"\\]|\\.)*)"|\\[#*]|\\[^\s]+|[^\s]+/g;
2121
* hasGeneralFormat: boolean,
2222
* unknownSwitches: string[],
2323
* }} ParsedSeqInstruction
24+
*
25+
* @typedef {{
26+
* identifier: string,
27+
* fieldArgument: string,
28+
* sequenceMode: SeqMode,
29+
* hideResult: boolean,
30+
* restartNumber: number | null,
31+
* restartLevel: number | null,
32+
* format: string,
33+
* hasGeneralFormat: boolean,
34+
* pageNumberFieldFormat: import('@superdoc/contracts').PageNumberFieldFormat | null,
35+
* numericPictureFormat: SeqNumericPictureFormat | null,
36+
* }} SequenceFieldParsedAttrs
2437
*/
2538

2639
/**
@@ -149,6 +162,27 @@ export function normalizeSeqIdentifier(identifier) {
149162
return typeof identifier === 'string' ? identifier.trim() : '';
150163
}
151164

165+
/**
166+
* Project parsed SEQ instruction metadata into sequenceField PM attrs.
167+
*
168+
* @param {ParsedSeqInstruction} parsed
169+
* @returns {SequenceFieldParsedAttrs}
170+
*/
171+
export function sequenceFieldAttrsFromParsed(parsed) {
172+
return {
173+
identifier: parsed.identifier,
174+
fieldArgument: parsed.fieldArgument,
175+
sequenceMode: parsed.sequenceMode,
176+
hideResult: parsed.hideResult,
177+
restartNumber: parsed.restartNumber,
178+
restartLevel: parsed.restartLevel,
179+
format: parsed.hasGeneralFormat ? parsed.format : 'ARABIC',
180+
hasGeneralFormat: parsed.hasGeneralFormat,
181+
pageNumberFieldFormat: parsed.pageNumberFieldFormat ?? null,
182+
numericPictureFormat: parsed.numericPictureFormat,
183+
};
184+
}
185+
152186
/**
153187
* @param {string} instruction
154188
*/

packages/super-editor/src/editors/v1/core/super-converter/field-references/shared/seq-instruction.test.js

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { describe, expect, it } from 'vitest';
2-
import { isSeqInstruction, normalizeSeqIdentifier, parseSeqInstruction } from './seq-instruction.js';
2+
import {
3+
isSeqInstruction,
4+
normalizeSeqIdentifier,
5+
parseSeqInstruction,
6+
sequenceFieldAttrsFromParsed,
7+
} from './seq-instruction.js';
38

49
describe('parseSeqInstruction', () => {
510
it.each([
@@ -156,3 +161,33 @@ describe('normalizeSeqIdentifier', () => {
156161
expect(normalizeSeqIdentifier(null)).toBe('');
157162
});
158163
});
164+
165+
describe('sequenceFieldAttrsFromParsed', () => {
166+
it('projects parsed SEQ metadata into normalized sequenceField attrs', () => {
167+
const attrs = sequenceFieldAttrsFromParsed(parseSeqInstruction('SEQ Figure \\r 3 \\* roman'));
168+
169+
expect(attrs).toEqual({
170+
identifier: 'Figure',
171+
fieldArgument: '',
172+
sequenceMode: 'next',
173+
hideResult: false,
174+
restartNumber: 3,
175+
restartLevel: null,
176+
format: 'roman',
177+
hasGeneralFormat: true,
178+
pageNumberFieldFormat: { format: 'lowerRoman' },
179+
numericPictureFormat: null,
180+
});
181+
});
182+
183+
it('keeps the parser default separate from the legacy PM attr default', () => {
184+
const parsed = parseSeqInstruction('SEQ Figure');
185+
186+
expect(parsed.format).toBe('Arabic');
187+
expect(sequenceFieldAttrsFromParsed(parsed)).toMatchObject({
188+
format: 'ARABIC',
189+
pageNumberFieldFormat: null,
190+
numericPictureFormat: null,
191+
});
192+
});
193+
});

packages/super-editor/src/editors/v1/core/super-converter/v3/handlers/sd/sequenceField/sequenceField-translator.js

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// @ts-check
22
import { NodeTranslator } from '@translator';
33
import { exportSchemaToJson, processOutputMarks } from '../../../../exporter.js';
4-
import { parseSeqInstruction } from '../../../../field-references/shared/seq-instruction.js';
4+
import {
5+
parseSeqInstruction,
6+
sequenceFieldAttrsFromParsed,
7+
} from '../../../../field-references/shared/seq-instruction.js';
58
import { buildInstructionElements } from '../shared/index.js';
69

710
/** @type {import('@translator').XmlNodeName} */
@@ -26,23 +29,15 @@ const encode = (params) => {
2629

2730
const instruction = node.attributes?.instruction || '';
2831
const parsed = parseSeqInstruction(instruction);
32+
const parsedAttrs = sequenceFieldAttrsFromParsed(parsed);
2933

3034
return {
3135
type: SD_NODE_NAME,
3236
attrs: {
3337
instruction,
3438
instructionTokens: node.attributes?.instructionTokens || null,
3539
// Raw instruction remains the export source of truth; these parsed attrs support import-time routing and later evaluation.
36-
identifier: parsed.identifier,
37-
fieldArgument: parsed.fieldArgument,
38-
sequenceMode: parsed.sequenceMode,
39-
hideResult: parsed.hideResult,
40-
restartNumber: parsed.restartNumber,
41-
restartLevel: parsed.restartLevel,
42-
format: parsed.format,
43-
hasGeneralFormat: parsed.hasGeneralFormat,
44-
pageNumberFieldFormat: parsed.pageNumberFieldFormat ?? null,
45-
numericPictureFormat: parsed.numericPictureFormat,
40+
...parsedAttrs,
4641
resolvedNumber: extractResolvedText(processedText),
4742
resolvedNumberIsCurrent: false,
4843
marksAsAttrs: node.marks || [],

packages/super-editor/src/editors/v1/document-api-adapters/helpers/sequence-field-updater.ts

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { SequenceFieldEvaluator } from '../../core/super-converter/field-referen
88
import {
99
normalizeSeqIdentifier,
1010
parseSeqInstruction,
11+
sequenceFieldAttrsFromParsed,
1112
} from '../../core/super-converter/field-references/shared/seq-instruction.js';
1213

1314
export type SequenceFieldUpdateScope =
@@ -117,20 +118,13 @@ function resolveNodeHeadingLevel(node: ProseMirrorNode, converterContext?: Conve
117118
function buildEvaluatedSequenceAttrs(node: ProseMirrorNode): SequenceFieldAttrs {
118119
const instruction = typeof node.attrs.instruction === 'string' ? node.attrs.instruction : '';
119120
const parsed = parseSeqInstruction(instruction);
121+
const parsedAttrs = sequenceFieldAttrsFromParsed(parsed);
120122

121123
return {
122124
...node.attrs,
123125
instruction,
124-
identifier: parsed.identifier || readStringAttr(node, 'identifier'),
125-
fieldArgument: parsed.fieldArgument,
126-
sequenceMode: parsed.sequenceMode,
127-
hideResult: parsed.hideResult,
128-
restartNumber: parsed.restartNumber,
129-
restartLevel: parsed.restartLevel,
130-
format: parsed.format,
131-
hasGeneralFormat: parsed.hasGeneralFormat,
132-
pageNumberFieldFormat: parsed.pageNumberFieldFormat ?? null,
133-
numericPictureFormat: parsed.numericPictureFormat,
126+
...parsedAttrs,
127+
identifier: parsedAttrs.identifier || readStringAttr(node, 'identifier'),
134128
};
135129
}
136130

packages/super-editor/src/editors/v1/document-api-adapters/plan-engine/caption-wrappers.ts

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ import {
3535
getSequenceFieldUpdaterConverterContext,
3636
updateSequenceFieldsInTransaction,
3737
} from '../helpers/sequence-field-updater.js';
38-
import { parseSeqInstruction } from '../../core/super-converter/field-references/shared/seq-instruction.js';
38+
import {
39+
parseSeqInstruction,
40+
sequenceFieldAttrsFromParsed,
41+
} from '../../core/super-converter/field-references/shared/seq-instruction.js';
3942

4043
// ---------------------------------------------------------------------------
4144
// Result helpers
@@ -140,16 +143,7 @@ export function captionsInsertWrapper(
140143
children.push(
141144
schema.nodes.sequenceField.create({
142145
instruction,
143-
identifier: parsed.identifier,
144-
fieldArgument: parsed.fieldArgument,
145-
sequenceMode: parsed.sequenceMode,
146-
hideResult: parsed.hideResult,
147-
restartNumber: parsed.restartNumber,
148-
restartLevel: parsed.restartLevel,
149-
format: parsed.format,
150-
hasGeneralFormat: parsed.hasGeneralFormat,
151-
pageNumberFieldFormat: parsed.pageNumberFieldFormat ?? null,
152-
numericPictureFormat: parsed.numericPictureFormat,
146+
...sequenceFieldAttrsFromParsed(parsed),
153147
resolvedNumber: '',
154148
resolvedNumberIsCurrent: false,
155149
sdBlockId: `seq-${Date.now()}`,
@@ -291,22 +285,19 @@ export function captionsConfigureWrapper(
291285
const format = CAPTION_FORMAT_TO_OOXML[input.format ?? 'decimal'] ?? 'ARABIC';
292286
const newInstruction = `SEQ ${input.label} \\* ${format}`;
293287
const parsed = parseSeqInstruction(newInstruction);
294-
const pageNumberFieldFormat = parsed.pageNumberFieldFormat ?? { format: 'decimal' };
288+
const parsedAttrs = sequenceFieldAttrsFromParsed(parsed);
295289
if (
296290
node.attrs.instruction === newInstruction &&
297291
node.attrs.format === format &&
298-
JSON.stringify(node.attrs.pageNumberFieldFormat) === JSON.stringify(pageNumberFieldFormat)
292+
JSON.stringify(node.attrs.pageNumberFieldFormat) === JSON.stringify(parsedAttrs.pageNumberFieldFormat)
299293
) {
300294
return true;
301295
}
302296

303297
tr.setNodeMarkup(tr.mapping.map(pos), undefined, {
304298
...node.attrs,
305299
instruction: newInstruction,
306-
format,
307-
pageNumberFieldFormat,
308-
hasGeneralFormat: true,
309-
numericPictureFormat: null,
300+
...parsedAttrs,
310301
resolvedNumberIsCurrent: false,
311302
});
312303
changed = true;

packages/super-editor/src/editors/v1/document-api-adapters/plan-engine/field-wrappers.ts

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import { parsePageNumberFieldSwitches } from '../../core/super-converter/field-r
3434
import {
3535
isSeqInstruction,
3636
parseSeqInstruction,
37+
sequenceFieldAttrsFromParsed,
3738
} from '../../core/super-converter/field-references/shared/seq-instruction.js';
3839
import {
3940
getSequenceFieldUpdaterConverterContext,
@@ -279,16 +280,7 @@ function insertRawField(
279280
parsed
280281
? {
281282
instruction: input.instruction,
282-
identifier: parsed.identifier,
283-
fieldArgument: parsed.fieldArgument,
284-
sequenceMode: parsed.sequenceMode,
285-
hideResult: parsed.hideResult,
286-
restartNumber: parsed.restartNumber,
287-
restartLevel: parsed.restartLevel,
288-
format: parsed.format,
289-
hasGeneralFormat: parsed.hasGeneralFormat,
290-
pageNumberFieldFormat: parsed.pageNumberFieldFormat ?? null,
291-
numericPictureFormat: parsed.numericPictureFormat,
283+
...sequenceFieldAttrsFromParsed(parsed),
292284
resolvedNumber: '',
293285
resolvedNumberIsCurrent: false,
294286
sdBlockId: `field-${Date.now()}`,

0 commit comments

Comments
 (0)