C#: Scaffold strategies for class members and attributes in template engine#7063
Merged
knutwannheden merged 10 commits intomainfrom Mar 19, 2026
Conversation
Captures now carry an internal `CaptureKind` (Expression, Type, Name) that tells the template engine what syntactic position the placeholder occupies. This allows `BuildTypePreamble` to dispatch on kind and generate appropriate scaffold code for each position. New factory methods `Capture.Type()` and `Capture.Name()` create captures with the correct kind pre-set while preserving compile-time type safety via the generic parameter.
- Thread `dependencies` (IReadOnlyDictionary<string, string>) through CSharpTemplate.Create, CSharpPattern.Create, and TemplateEngine.Parse for future NuGet-based type attribution during scaffold parsing - Include dependencies in the template cache key - Add Capture.Expression() factory with explicit CaptureKind.Expression - Update Of<T> docs to steer toward position-specific factories
…nd Attribute templates The template engine previously only supported a single scaffold strategy (wrapping code in a method body). This adds a ScaffoldKind enum and context-specific factory methods to CSharpTemplate and CSharpPattern so that class members, attributes, and expressions can be scaffolded and extracted with correct AST structure and type attribution.
…type, and multi-member support - Factor out class framing and preamble from BuildScaffold switch cases - Use `object __v__` instead of `var __e__` for expression scaffold (avoids invalid C# when templating null/default) - ExtractClassMember now handles multiple members by returning filtered block - ExtractAttribute provides actionable error messages at each stage - Add cache isolation test verifying different scaffold kinds don't collide
Statement() returns ExpressionStatement as-is; Create() auto-unwraps to the inner expression. This is the key semantic difference that motivated the explicit Statement scaffold kind.
- Fix critical bug: ExtractExpression now searches for __v__ by name instead of using FindFirst, which would find preamble fields first when typed captures are present - Fix stale doc comments referencing var __e__ (now object __v__) - ClassMember scaffold auto-appends semicolon for field declarations - Add ExpressionWithTypedCapture test covering the preamble bug
Allows recipe authors to use CSharpTemplate.Apply() with substitution values from imperative extraction, not just from CSharpPattern.Match(). This unlocks Attribute and ClassMember templates for recipes that manually extract values from the AST.
AutoFormat now restores the prefix set by ApplyCoordinates after formatting. The formatter handles internal whitespace (argument spacing) while the prefix (indentation/newlines before the node) comes from the original tree being replaced. This matches the JS/TS template engine approach where prefix is set before formatting and preserved through it. Fixes the issue where Attribute templates got unwanted newline+indent prefix from the formatter seeing them as standalone class member attributes.
e7d15f1 to
b30cbf6
Compare
b30cbf6 to
29872cc
Compare
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
ScaffoldKindenum (Expression,Statement,ClassMember,Attribute) to control how template code is wrapped and extractedExpression(),Statement(),ClassMember(),Attribute()on bothCSharpTemplateandCSharpPatternCreate()for backward compatibility (auto-unwrapsExpressionStatement)Previously the template engine only supported expression/statement context (wrapping in a method body). This adds purpose-built scaffolds so class members, attributes, and standalone expressions can be templated with correct AST structure and type attribution.
Usage examples
Key design decisions
ScaffoldKindisinternal— consumers use the factory methodsCreate()unchanged: usesnullscaffold kind → legacy auto-unwrap pathStatement()does NOT auto-unwrapExpressionStatement(unlikeCreate())object __v__(notvar) to handlenull/default;when code doesn't end with;or}Test plan
ScaffoldStrategyTests.cscovering all scaffold kindsCSharpTemplateandCSharpPatternfactory methods