diff --git a/src/element.typ b/src/element.typ index e42d2d8..0b9bf19 100644 --- a/src/element.typ +++ b/src/element.typ @@ -993,12 +993,21 @@ prefix: "", typecheck: true, allow-unknown-fields: false, + synthesize: none, + contextual: auto, ) = { assert(type(display) == function, message: "element: please specify a show rule in 'display:' to determine how your element is displayed.") assert(type(fields) == array, message: "element: please specify an array of fields, creating each field with the 'field' function.") assert(type(prefix) == str, message: "element: the prefix must be a string.") assert(type(typecheck) == bool, message: "element: the 'typecheck' argument must be a boolean (true to enable typechecking, false to disable).") assert(type(allow-unknown-fields) == bool, message: "element: the 'allow-unknown-fields' argument must be a boolean.") + assert(synthesize == none or type(synthesize) == function, message: "element: 'synthesize' must be 'none' or a function element fields => element fields.") + assert(contextual == auto or type(contextual) == bool, message: "element: 'contextual' must be 'auto' (true if using a contextual feature) or a boolean (true to wrap the output in a 'context { ... }', false to not).") + + if contextual == auto { + // Provide separate context for synthesize. + contextual = synthesize != none + } let eid = base.unique-id(prefix, name) let lbl-show = label(lbl-show-head + eid) @@ -1227,9 +1236,39 @@ finalized-chain } - let body = display(constructed-fields) - let tag = [#metadata((kind: "instance", body: body, fields: constructed-fields, func: modified-constructor, eid: eid, fields-known: true, valid: true))] - let shown = [#[#body#tag]#lbl-show] + let shown = { + let tag = (kind: "instance", body: none, fields: constructed-fields, func: modified-constructor, eid: eid, fields-known: true, valid: true) + + if contextual { + // Use context for synthesize as well + context { + let synthesized-fields = if synthesize == none { + constructed-fields + } else { + synthesize(constructed-fields) + } + let body = display(synthesized-fields) + + let tag = tag + tag.fields = synthesized-fields + tag.body = body + + [#[#body#metadata(tag)]#lbl-show] + } + } else { + let synthesized-fields = if synthesize == none { + constructed-fields + } else { + synthesize(constructed-fields) + } + let body = display(synthesized-fields) + + tag.fields = synthesized-fields + tag.body = body + + [#[#body#metadata(tag)]#lbl-show] + } + } if data-changed { show lbl-get: set bibliography(title: [#metadata(global-data)#lbl-get]) diff --git a/test/unit/elements/synthesize/.gitignore b/test/unit/elements/synthesize/.gitignore new file mode 100644 index 0000000..7ecdc7c --- /dev/null +++ b/test/unit/elements/synthesize/.gitignore @@ -0,0 +1 @@ +# added by typst-test diff --git a/test/unit/elements/synthesize/ref/1.png b/test/unit/elements/synthesize/ref/1.png new file mode 100644 index 0000000..3690123 Binary files /dev/null and b/test/unit/elements/synthesize/ref/1.png differ diff --git a/test/unit/elements/synthesize/test.typ b/test/unit/elements/synthesize/test.typ new file mode 100644 index 0000000..d70bd57 --- /dev/null +++ b/test/unit/elements/synthesize/test.typ @@ -0,0 +1,40 @@ +#import "/test/unit/base.typ": empty +#show: empty + +#import "/src/lib.typ" as e: element, field + +#let count = counter("adb") + +#let (wock, wock-e) = element( + "wock", + display: it => { + assert.eq(it.color, red) + assert.eq(it.inner, [Hello!]) + (it.test)() + }, + fields: ( + field("color", color, default: red), + field("inner", content, default: [Hello!]), + field("test", function, default: () => {}) + ), + synthesize: it => { + it.resolved-value = count.get().first() + it + } +) + +#show wock-e.sel: it => { + count.step() + it +} + +#wock(test: () => count.get().first() == 0) +#wock(test: () => count.get().first() == 1) +#wock(test: () => count.get().first() == 2) + +#show wock-e.sel: it => { + assert.eq(e.data(it).fields.resolved-value, 3) + it +} + +#wock()