In LaTeX, using \tikzmarknode
, I can draw tikz pictures directly on top of code listings. e.g.
Is there a way to do something similar with cetz and triple-backquote code segments? I could not find any way to place markers like tikzmark inside a triple backquote block.
1 Like
Andrew
April 20, 2025, 2:37am
2
Does this count?
#import "@preview/cetz:0.3.4"
#let node(body) = block(
stroke: 1pt + rgb("#706898"),
fill: rgb("#ebe8e8"),
inset: 0.3em,
radius: 0.5em,
text(0.6em, body),
)
#let line = cetz.draw.line.with(
mark: (start: "o", end: (symbol: "stealth", fill: black), scale: 0.7),
stroke: (dash: "dashed", thickness: 0.5pt),
)
#let code = ```
example : ∀ (p q: Prop), p ∨ q → q ∨ p := by
intro p q h
cases h
⋅ apply Or.inr
assumption
⋅ apply Or.inl
assumption
```
#let node1 = node($
p, q & : sans("Prop") \
h & : p or q \
& tack q or p
$)
#let node2 = node($
p, q & : sans("Prop") \
h & : p \
& tack q or p \
p, q & : sans("Prop") \
h & : q \
& tack q or p
$)
#cetz.canvas({
import cetz.draw: content
content((0, 0), anchor: "north-west", code)
content((6, -1.5), name: "node1", node1)
content((4.4, -2.8), name: "node2", node2)
line((2.6, -0.55), "node1")
line((1.9, -0.95), "node2")
})
output
1 Like
Is there a way to determine the coordinate of the two nodes automatically?
bluss
April 20, 2025, 9:57am
5
It’s certainly possible to some extent. You can use codly
for a code display with many features, for example line highlights and line labels (detailed by code line and code column).
The label gives you access to the absolute position of that word in the code through the query system. I wonder if there’s some way to use labels as coordinates in cetz.
Proof of concept / "bad example"
This example shows that it is possible. I think it would be better to do this with cetz instead of typst drawing primitives. As seen in the example, the text after the figure starts without taking the extent of the annotation boxes into account.
#import "@preview/codly:1.3.0": *
#show: codly-init.with()
// Testing out codly's feature that allows labels on individual lines of code
#let code = raw(block: true,
"example : ∀ (p q: Prop), p ∨ q → q ∨ p := by
intro p q h
cases h
⋅ apply Or.inr
assumption
⋅ apply Or.inl
assumption
")
#codly(highlights:(
(line: 2, start: 3, end: none, label: <mark1>),
(line: 3, start: 3, end: none, label: <mark2>),
), annotations: (
))
#figure(
code
)<myfigure>
Test my labels @mark1 and @mark2
/// Draw a line from the label's code line down to a box
/// xoffset: x-offset from label coordinate to the start of the line
/// xline, yline: length of the line to the box.
#let drawbox(at-label, content, yoffset: 1em, xoffset: 20em, xline: 5em, yline: 5em,
line-style: (:), block-style: (:)) = context {
let codeline = query(at-label).first()
let pos = codeline.location().position()
let xstart = pos.x - 30mm // page margin, needs to be updated
let ystart = pos.y - 30mm
let xbox = xstart + xoffset + xline
let ybox = ystart + yoffset + yline
place(top + left, dx: xoffset + xstart, dy: ystart + yoffset,
line(start: (0%, 0%), end: (xline, yline), ..line-style))
place(top + left, dx: xbox, dy: ybox, block(..block-style, content))
}
#let drawbox = drawbox.with(
block-style: (inset: 0.75em, stroke: 0.5pt + purple.darken(60%), fill: gray.lighten(90%).transparentize(25%), radius: 0.5em),
line-style: (stroke: (thickness: 0.5pt, dash: "dashed")))
#let annot1 = $
p, q & : sans("Prop") \
h & : p or q \
& tack q or p
$
#drawbox(<mark1>, annot1, xline: 4em, yline: 1em)
#let annot2 = $
p, q & : sans("Prop") \
h & : p \
& tack q or p \
p, q & : sans("Prop") \
h & : q \
& tack q or p
$
#drawbox(<mark2>, annot2, yline: 2em, xoffset: 11.5em)
#lorem(20)
2 Likes
Automatically in the sense that I can put a mark object after intro p q h
and use a name to refer to that object’s coordinate.
Andrew
April 21, 2025, 8:01pm
7
Well, there is no easy way to do it other than what Codly provides, or recreating it from scratch, which I assume is pretty trivial. Does How can I overlay cetz diagrams on top of code? - #5 by bluss work for you?