Repository navigation
Add template syntax validation and parse blocks from a syntax tree - #155
Merged
Merged
Conversation
Add DocxTemplate.ValidateTemplateSyntax(), which checks a template
without a model and reports all syntax errors at once: unclosed blocks,
closing tags without an opener or with a mismatching name, else /
separator / case tags outside their block, unknown inline keywords,
malformed tags such as {{Name} and unbalanced parentheses or
unterminated strings in expressions.
The new TemplateSyntaxParser produces a syntax tree with the same shape
as the ContentBlock tree, and BuildBlockTree now creates the blocks from
it instead of running its own stack-based matching. This keeps a single
parser for validation and rendering. Process() throws with all errors of
severity Error; warnings (malformed tags, suspicious expressions) are only
reported by ValidateTemplateSyntax().
PatternMatcher gets an overload with an error callback so invalid
patterns can be collected instead of throwing on the first one. Closing
tags may omit the implicit model prefix ({{#ds.Items}}...{{/Items}}).
Fix the loop end tag typo {{/.HardwarRevisions}} in ComplexTemplate.docx
that the validator uncovered.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
- Treat everything between {{:ignore}} and {{/:ignore}} as opaque text:
tags inside are neither validated nor marked, so documentation of the
syntax or TOC fields can no longer abort rendering. An ignore block is
only closed by {{/:ignore}}.
- Downgrade mismatching closing tags ({{#Items}}...{{/Orders}}) to a
warning - rendering closes the current block regardless of the name,
as it always did.
- Report {{#}} without a collection name, run the expression check on
switch selectors and case values, and warn about {{#s}} / {{#c}}
without an argument (parsed as a loop over 's' / 'c').
- Refuse ValidateTemplateSyntax after a failed Process, like Process
itself does.
- Capture the part text in BuildBlockTree instead of a separate
pre-pass in GetTemplateSchema, which built a second CharacterMap for
every part.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Shorten the "Advanced Model Binding" heading so it gets a readable anchor and list syntax validation under features. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
DocxTemplate.ValidateTemplateSyntax()checks a template without a model and returns all syntax errors at once (TemplateSyntaxErrorwithSeverity,Part,Tag,Message,Context).TemplateSyntaxParserproduces a syntax tree with the same shape as theContentBlocktree;BuildBlockTreecreates the blocks from it. The stack-based matching inExpandLoops(StartBlock/CloseBlock/AutoCloseCaseOrDefaultIfNeeded) is gone.PatternMatchergets an overload with an error callback so invalid patterns can be collected instead of throwing on the first one.{{:ignore}}and{{/:ignore}}is treated as opaque text: tags inside are neither validated nor marked (previously they were still turned into blocks and their markers removed byCleanup). An ignore block is only closed by{{/:ignore}}.Detected
{{:}}outside a condition / twice,{{:s:}}outside a collection loop,{{#case}}without{{#switch}},{{#}}/{{#switch}}/{{#case}}without name or expression, unknown inline keyword{{#Items}}…{{/Orders}},{?{…}}…{{/Items}}) – rendering closes the current block anyway; the implicit model prefix may be omitted ({{#ds.Items}}…{{/Items}}){{Name},{{first name}}, stray}}){{#s}}/{{#c}}without argumentBehaviour changes in
Process()Process()now throws anOpenXmlTemplateExceptionlisting all errors of severity Error before touching the document. Warnings never block rendering.{{:}}inside a loop,{{:s:}}in a range loop and{{#case}}outside a switch were silently wrong or failed at render time; they are parse errors now.{{/}}inside{{:ignore}}…{{/:ignore}}no longer closes the ignore block (it is plain text there).{{/}}or a mismatching closing tag name render as before.The validator uncovered a typo in
ComplexTemplate.docx({{/.HardwarRevisions}}), fixed in the XML.Test plan
TemplateSyntaxValidationTest(valid templates, every error class and its severity, header/footer parts, tags split across runs,Process()throws with all errors / renders despite warnings, ignore blocks render verbatim, validation afterGetTemplateSchema()and after a failedProcess(), all resource templates validate cleanly)main🤖 Generated with Claude Code