Skip to content

Commit 601721c

Browse files
CopilotOrangeX4
andcommitted
Add flexible references, positional title syntax, counter continuation, and input validation
Co-authored-by: OrangeX4 <34951714+OrangeX4@users.noreply.github.com>
1 parent 2b8e33a commit 601721c

9 files changed

Lines changed: 242 additions & 52 deletions

File tree

core.typ

Lines changed: 103 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,20 @@
120120

121121
// `update` method for this richer-counter
122122
// only support array of integers as counter value
123-
let update(counter-value) = [
124-
#metadata((
125-
kind: "richer-counter:update",
126-
identifier: identifier,
127-
value: counter-value,
128-
))
129-
#label("richer-counter:update:" + identifier)
130-
]
123+
let update(counter-value) = {
124+
assert(
125+
type(counter-value) == array and counter-value.all(v => type(v) == int),
126+
message: "Counter value for '" + identifier + "' must be an array of integers, e.g. (1, 2).",
127+
)
128+
[
129+
#metadata((
130+
kind: "richer-counter:update",
131+
identifier: identifier,
132+
value: counter-value,
133+
))
134+
#label("richer-counter:update:" + identifier)
135+
]
136+
}
131137

132138
// find updates of own partial (!) counter in certain range
133139
let updates-during(after-key, before-key) = {
@@ -315,10 +321,16 @@
315321
}
316322

317323
/// Useful functions for the frame.
324+
// Helper: check whether a title value is non-empty (works for both str and content)
325+
let has-nonempty-title(title) = (type(title) == str and title != "") or (type(title) == content and title != [] and title != [#""])
318326
let get-prefix(get-loc, number: auto, supplement: auto) = [#if supplement == auto { supplement-i18n } else {
319327
supplement
320-
} #if number == auto { display-number(get-loc: get-loc)() } else { number }]
321-
let get-full-title(prefix, title) = [#prefix#{ if title != "" [ (#title)] }]
328+
} #if number == auto { display-number(get-loc: get-loc)() } else if type(number) == array {
329+
context std.numbering(get-numbering(get-loc()), ..number)
330+
} else { number }]
331+
let get-full-title(prefix, title) = [#prefix#{
332+
if has-nonempty-title(title) [ (#title)]
333+
}]
322334
/// Frame with the counter.
323335
let frame(
324336
title: "",
@@ -330,44 +342,66 @@
330342
get-full-title: get-full-title,
331343
..args,
332344
body,
333-
) = figure(
334-
kind: identifier,
335-
supplement: if supplement == auto { supplement-i18n } else { supplement },
336-
caption: title,
337-
outlined: outlined,
338-
numbering: if number == auto { numbering } else { (..args) => (kind: "static", value: number) },
339-
{
340-
[#metadata((
341-
identifier: identifier,
342-
number: number,
345+
) = {
346+
// Support positional title syntax: #theorem[Title][Body]
347+
let actual-title = if args.pos().len() > 0 and title == "" {
348+
args.pos().first()
349+
} else {
350+
title
351+
}
352+
// For array numbers, emit counter update BEFORE the figure so that
353+
// theorion-display-number (which reads at el.location()) sees the updated value.
354+
if type(number) == array and number.len() > 0 {
355+
let target = number.slice(0, -1) + (number.last() - 1,)
356+
(frame-counter.update)(target)
357+
}
358+
figure(
359+
kind: identifier,
360+
supplement: if supplement == auto { supplement-i18n } else { supplement },
361+
caption: actual-title,
362+
outlined: outlined,
363+
numbering: if number == auto or type(number) == array {
364+
numbering
365+
} else {
366+
(..fig-args) => (kind: "static", value: number)
367+
},
368+
{
369+
[#metadata((
370+
identifier: identifier,
371+
number: number,
372+
supplement: supplement,
373+
supplement-map: supplement-map,
374+
supplement-i18n: supplement-i18n,
375+
kind: identifier,
376+
counter: frame-counter,
377+
title: actual-title,
378+
numbering: numbering,
379+
outlined: outlined,
380+
get-prefix: get-prefix,
381+
get-full-title: get-full-title,
382+
render: render,
383+
args: args,
384+
body: body,
385+
)) <theorion-frame-metadata>]
386+
let prefix = get-prefix(
387+
here,
388+
number: if type(number) == array { auto } else { number },
343389
supplement: supplement,
344-
supplement-map: supplement-map,
345-
supplement-i18n: supplement-i18n,
346-
kind: identifier,
347-
counter: frame-counter,
348-
title: title,
349-
numbering: numbering,
350-
outlined: outlined,
351-
get-prefix: get-prefix,
352-
get-full-title: get-full-title,
353-
render: render,
354-
args: args,
355-
body: body,
356-
)) <theorion-frame-metadata>]
357-
let prefix = get-prefix(here, number: number, supplement: supplement)
358-
render(
359-
prefix: prefix,
360-
title: title,
361-
full-title: get-full-title(prefix, title),
362-
..args,
363-
body,
364-
)
365-
// Update the counter.
366-
if numbering != none and number == auto {
367-
(frame-counter.step)()
368-
}
369-
},
370-
)
390+
)
391+
render(
392+
prefix: prefix,
393+
title: actual-title,
394+
full-title: get-full-title(prefix, actual-title),
395+
..args.named(),
396+
body,
397+
)
398+
// Update the counter.
399+
if numbering != none and (number == auto or type(number) == array) {
400+
(frame-counter.step)()
401+
}
402+
},
403+
)
404+
}
371405
/// Frame without the counter.
372406
let frame-box = frame.with(
373407
numbering: none,
@@ -406,12 +440,29 @@
406440
let el = it.element
407441
if el != none and el.func() == figure and el.kind == identifier {
408442
link(el.location(), {
409-
let supplement = if it.supplement == auto { el.supplement } else { it.supplement }
410-
if supplement != none {
411-
supplement
412-
" "
443+
if it.supplement == [-] {
444+
// @label[-]: number only, no supplement
445+
context theorion-display-number(el)
446+
} else if it.supplement == [!!] {
447+
// @label[!!]: supplement + number + title
448+
let supplement = el.supplement
449+
if supplement != none {
450+
supplement
451+
" "
452+
}
453+
context theorion-display-number(el)
454+
context {
455+
let title = el.caption.body
456+
if has-nonempty-title(title) [ (#title)]
457+
}
458+
} else {
459+
let supplement = if it.supplement == auto { el.supplement } else { it.supplement }
460+
if supplement != none {
461+
supplement
462+
" "
463+
}
464+
context theorion-display-number(el)
413465
}
414-
context theorion-display-number(el)
415466
})
416467
} else {
417468
it
14.6 KB
Loading
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/// Test: flexible reference supplements - @label[-] for number only, @label[!!] for full with title
2+
#import "/lib.typ": *
3+
4+
#set page(width: 300pt, height: auto, margin: (x: 15pt, y: 15pt))
5+
#set heading(numbering: "1.")
6+
7+
#show: show-theorion
8+
9+
= Flexible References
10+
11+
#theorem(title: "Euclid's Theorem")[
12+
There are infinitely many prime numbers.
13+
] <thm:euclid>
14+
15+
#definition[
16+
A prime has no divisors other than 1 and itself.
17+
] <def:prime>
18+
19+
== Default Reference
20+
21+
@thm:euclid (supplement + number)
22+
23+
== Number Only Reference
24+
25+
@thm:euclid[-] (number only, no supplement)
26+
27+
== Full Reference with Title
28+
29+
@thm:euclid[!!] (supplement + number + title)
30+
31+
== References to Untitled Theorem
32+
33+
@def:prime (supplement + number)
34+
35+
@def:prime[-] (number only)
36+
37+
@def:prime[!!] (supplement + number, no title to append)
7.89 KB
Loading
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/// Test: nested proof environments with separate QED symbols
2+
#import "/lib.typ": *
3+
4+
#set page(width: 300pt, height: auto, margin: (x: 15pt, y: 15pt))
5+
6+
#show: show-theorion
7+
8+
= Nested Proofs
9+
10+
== Simple nested proof
11+
12+
#proof[
13+
This is the outer proof.
14+
#proof[
15+
This is the inner proof.
16+
Both proofs get their own QED symbol.
17+
]
18+
The outer proof continues and ends here.
19+
]
20+
21+
== Triple nesting
22+
23+
#proof[
24+
Outer start.
25+
#proof[
26+
Middle start.
27+
#proof[
28+
Innermost proof.
29+
]
30+
Middle end.
31+
]
32+
Outer end.
33+
]
12.9 KB
Loading
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/// Test: number continuation - #theorem(number: (n1, n2)) updates counter for continuation
2+
#import "/lib.typ": *
3+
4+
#set page(width: 300pt, height: auto, margin: (x: 15pt, y: 15pt))
5+
#set heading(numbering: "1.")
6+
7+
#show: show-theorion
8+
9+
= Counter Continuation
10+
11+
== Set counter via array number
12+
13+
#theorem(number: (1, 3))[
14+
This theorem is explicitly set to 1.3.
15+
The counter is updated so the next auto-numbered theorem continues from here.
16+
]
17+
18+
#theorem[
19+
This should be 1.4 (continues from 1.3).
20+
]
21+
22+
#theorem[
23+
This should be 1.5.
24+
]
25+
26+
== String number (no continuation)
27+
28+
#theorem(number: "99.99")[
29+
This uses a static string number. The counter is NOT updated.
30+
]
31+
32+
#theorem[
33+
This continues from where it left off before the static override (1.6).
34+
]
16 KB
Loading
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/// Test: positional title syntax - #theorem[Title][Body]
2+
#import "/lib.typ": *
3+
4+
#set page(width: 300pt, height: auto, margin: (x: 15pt, y: 15pt))
5+
#set heading(numbering: "1.")
6+
7+
#show: show-theorion
8+
9+
= Positional Title Syntax
10+
11+
== Named parameter (current syntax)
12+
13+
#theorem(title: "Named Theorem")[
14+
This uses the named title parameter.
15+
]
16+
17+
== Positional title (new syntax)
18+
19+
#theorem[Positional Theorem][
20+
This uses positional title as first content argument.
21+
]
22+
23+
#definition[Prime Number][
24+
A natural number greater than 1 with no divisors other than 1 and itself.
25+
] <def:prime-pos>
26+
27+
== Reference to positionally-titled theorem
28+
29+
@def:prime-pos[!!] shows the positional title in the reference.
30+
31+
== No title (body only)
32+
33+
#theorem[
34+
This theorem has no title.
35+
]

0 commit comments

Comments
 (0)